pi-shorthand 0.10.0 → 0.12.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/package.json +2 -2
- package/src/display.ts +9 -11
- package/src/index.ts +31 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-shorthand",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"description": "Pi tool for editing repositories with Bun programs, with first-class support for TypeScript and JavaScript.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ast-grep",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
],
|
|
27
27
|
"type": "module",
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"shorthand-code": "0.
|
|
29
|
+
"shorthand-code": "0.12.0"
|
|
30
30
|
},
|
|
31
31
|
"peerDependencies": {
|
|
32
32
|
"@earendil-works/pi-ai": "*",
|
package/src/display.ts
CHANGED
|
@@ -25,8 +25,7 @@ const ANSI_CUBE_VALUES = [0, 95, 135, 175, 215, 255];
|
|
|
25
25
|
const ANSI_GRAY_VALUES = Array.from({ length: 24 }, (_, index) => 8 + index * 10);
|
|
26
26
|
|
|
27
27
|
const OUTPUT_PREVIEW_LINES = 5; // like Pi's bash tool
|
|
28
|
-
const
|
|
29
|
-
const LISTED_FILES = 8; // …showing this many, then "and N more files"
|
|
28
|
+
const LISTED_FILES = 8; // a diff too tall for the screen lists this many files, then "and N more files"
|
|
30
29
|
const EXPANDED_DIFF_LINES = 2000; // even expanded, a diff of hundreds of files stops here
|
|
31
30
|
|
|
32
31
|
export function callLine(
|
|
@@ -60,7 +59,8 @@ function indent(line: string): string {
|
|
|
60
59
|
return ` ${line}`;
|
|
61
60
|
}
|
|
62
61
|
|
|
63
|
-
|
|
62
|
+
/** `collapseDiff` swaps the applied diff for a list of its files; see FitsScreen for when. */
|
|
63
|
+
export function resultLines(run: RunResult, expanded: boolean, theme: Theme, collapseDiff = false): string[] {
|
|
64
64
|
const applied = run.changes.filter((change) => run.applied.includes(change.path));
|
|
65
65
|
const notApplied = run.changes.filter(
|
|
66
66
|
(change) => !run.applied.includes(change.path) && !run.rolledBack.includes(change.path),
|
|
@@ -95,7 +95,7 @@ export function resultLines(run: RunResult, expanded: boolean, theme: Theme): st
|
|
|
95
95
|
const sections: string[][] = [];
|
|
96
96
|
const background: ToolBackground =
|
|
97
97
|
run.exitCode === 0 && run.conflicts.length === 0 && run.rolledBack.length === 0 ? "toolSuccessBg" : "toolErrorBg";
|
|
98
|
-
if (applied.length > 0) sections.push(diffLines(applied, expanded, theme, background));
|
|
98
|
+
if (applied.length > 0) sections.push(diffLines(applied, collapseDiff && !expanded, theme, background));
|
|
99
99
|
if (output && (expanded || !error)) sections.push(outputLines(output, expanded, run.changes.length > 0, theme));
|
|
100
100
|
if (notApplied.length > 0) sections.push(notAppliedLines(notApplied, expanded, theme, background));
|
|
101
101
|
for (const section of sections) lines.push("", ...section);
|
|
@@ -157,14 +157,12 @@ function outputLines(output: string, expanded: boolean, labelled: boolean, theme
|
|
|
157
157
|
return [...label, hint, ...lines.slice(-OUTPUT_PREVIEW_LINES)];
|
|
158
158
|
}
|
|
159
159
|
|
|
160
|
-
/** Each file's diff under its name, in Pi's own diff style
|
|
161
|
-
function diffLines(changes: FileChange[],
|
|
162
|
-
|
|
163
|
-
const total = files.reduce((sum, file) => sum + file.length, 0);
|
|
164
|
-
if (!expanded && total > INLINE_DIFF_LINES) {
|
|
160
|
+
/** Each file's diff under its name, in Pi's own diff style, or when collapsed a list of the files. */
|
|
161
|
+
function diffLines(changes: FileChange[], collapse: boolean, theme: Theme, background: ToolBackground): string[] {
|
|
162
|
+
if (collapse) {
|
|
165
163
|
return [...fileList(changes, theme), theme.fg("muted", `(${keyHint("app.tools.expand", "to see the diff")})`)];
|
|
166
164
|
}
|
|
167
|
-
|
|
165
|
+
const files = changes.map((change) => [fileLine(change, theme), ...renderFileDiff(change, theme, background)]);
|
|
168
166
|
const lines = files.flatMap((file, index) => (index === 0 ? file : ["", ...file]));
|
|
169
167
|
if (lines.length <= EXPANDED_DIFF_LINES) return lines;
|
|
170
168
|
const more = lines.length - EXPANDED_DIFF_LINES;
|
|
@@ -316,7 +314,7 @@ function colorDistance(first: number[], second: number[]): number {
|
|
|
316
314
|
|
|
317
315
|
function notAppliedLines(changes: FileChange[], expanded: boolean, theme: Theme, background: ToolBackground): string[] {
|
|
318
316
|
const heading = theme.fg("muted", `Would have changed ${fileCount(changes)} · `) + stats(changes, theme);
|
|
319
|
-
if (expanded) return [heading, "", ...diffLines(changes,
|
|
317
|
+
if (expanded) return [heading, "", ...diffLines(changes, false, theme, background)];
|
|
320
318
|
return [heading + theme.fg("muted", ` (${keyHint("app.tools.expand", "to see the diff")})`)];
|
|
321
319
|
}
|
|
322
320
|
|
package/src/index.ts
CHANGED
|
@@ -7,7 +7,7 @@ import { createRequire } from "node:module";
|
|
|
7
7
|
import * as path from "node:path";
|
|
8
8
|
import { StringEnum } from "@earendil-works/pi-ai";
|
|
9
9
|
import { type ExtensionAPI, type Theme, truncateHead, truncateTail } from "@earendil-works/pi-coding-agent";
|
|
10
|
-
import { Text } from "@earendil-works/pi-tui";
|
|
10
|
+
import { type Component, Text } from "@earendil-works/pi-tui";
|
|
11
11
|
import { Type } from "typebox";
|
|
12
12
|
import {
|
|
13
13
|
DEFAULT_TIMEOUT_SECONDS,
|
|
@@ -35,7 +35,7 @@ Common operations:
|
|
|
35
35
|
- await refactor.renameFile({ from, to }) moves a TypeScript file and updates module paths that resolve to it.
|
|
36
36
|
- await refactor.move({ file, symbol, to }) moves a top-level declaration to another file and updates the imports that follow it.
|
|
37
37
|
|
|
38
|
-
See the shorthand skill for renames, moves and call-site migrations. Read its advanced-refactors.md guide only to extract code, move syntax or use rule objects. The default timeout is
|
|
38
|
+
See the shorthand skill for renames, moves and call-site migrations. Read its advanced-refactors.md guide only to extract code, move syntax or use rule objects. The default timeout is five seconds; request more for longer programs. Time spent inside the helpers above does not count toward it, up to 60 extra seconds.`;
|
|
39
39
|
|
|
40
40
|
const GRAPH_DESCRIPTION =
|
|
41
41
|
"- await graph.query(request | request[]) queries symbols and relationships; graph results can be passed to sg as scope. The graph shows the repository before this program's edits.\n";
|
|
@@ -151,7 +151,7 @@ export function renderCodeResult(
|
|
|
151
151
|
result: { content: readonly unknown[]; details?: unknown },
|
|
152
152
|
{ expanded, isPartial }: { expanded: boolean; isPartial: boolean },
|
|
153
153
|
theme: Theme,
|
|
154
|
-
):
|
|
154
|
+
): Component {
|
|
155
155
|
if (isPartial) {
|
|
156
156
|
const progress = (result.details as { progress?: string } | undefined)?.progress;
|
|
157
157
|
return new Text(theme.fg("muted", progress ? `running… ${progress}` : "running…"), 0, 0);
|
|
@@ -170,5 +170,32 @@ export function renderCodeResult(
|
|
|
170
170
|
}
|
|
171
171
|
const run = result.details as RunResult | undefined;
|
|
172
172
|
if (!run) return new Text(theme.fg("error", unstructuredResultText(result.content)), 0, 0);
|
|
173
|
-
|
|
173
|
+
const full = new Text(resultLines(run, expanded, theme).join("\n"), 0, 0);
|
|
174
|
+
if (expanded) return full;
|
|
175
|
+
return new FitsScreen(full, new Text(resultLines(run, false, theme, true).join("\n"), 0, 0));
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/** Rows Pi keeps for itself below a tool result: the editor, its borders and the footer. */
|
|
179
|
+
const PI_CHROME_ROWS = 8;
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Shows the whole result while it fits on screen once wrapped, like Pi's edit tool, and otherwise the version
|
|
183
|
+
* with the diff collapsed to a list of files. Measured on each render, so resizing the terminal is followed.
|
|
184
|
+
*/
|
|
185
|
+
export class FitsScreen implements Component {
|
|
186
|
+
constructor(
|
|
187
|
+
private readonly full: Component,
|
|
188
|
+
private readonly collapsed: Component,
|
|
189
|
+
private readonly rows = () => process.stdout.rows || 24,
|
|
190
|
+
) {}
|
|
191
|
+
|
|
192
|
+
render(width: number): string[] {
|
|
193
|
+
const lines = this.full.render(width);
|
|
194
|
+
return lines.length <= this.rows() - PI_CHROME_ROWS ? lines : this.collapsed.render(width);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
invalidate(): void {
|
|
198
|
+
this.full.invalidate();
|
|
199
|
+
this.collapsed.invalidate();
|
|
200
|
+
}
|
|
174
201
|
}
|