pi-readseek 0.8.4 → 0.8.6
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/README.md +7 -0
- package/dist/index.ts +138 -67
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -43,6 +43,12 @@ Add an optional `readseek` section to `~/.pi/agent/settings.json` (global) or
|
|
|
43
43
|
"grep": {
|
|
44
44
|
"maxLines": 2000,
|
|
45
45
|
"maxBytes": 51200
|
|
46
|
+
},
|
|
47
|
+
"display": {
|
|
48
|
+
"read": "compact",
|
|
49
|
+
"grep": "compact",
|
|
50
|
+
"edit": "expanded",
|
|
51
|
+
"write": "expanded"
|
|
46
52
|
}
|
|
47
53
|
}
|
|
48
54
|
}
|
|
@@ -58,6 +64,7 @@ Add an optional `readseek` section to `~/.pi/agent/settings.json` (global) or
|
|
|
58
64
|
- `timeoutMs`: ReadSeek command timeout in milliseconds.
|
|
59
65
|
- `grep.maxLines` and `grep.maxBytes`: output limits for `readSeek_grep`. Values
|
|
60
66
|
above the defaults are clamped.
|
|
67
|
+
- `display`: default display state (`"compact"` or `"expanded"`) per tool for `read`, `grep`, `edit`, and `write`.
|
|
61
68
|
|
|
62
69
|
## Licensing
|
|
63
70
|
|
package/dist/index.ts
CHANGED
|
@@ -1183,8 +1183,15 @@ var READSEEK_TOOL_REPLACEMENTS = {
|
|
|
1183
1183
|
grep: "readSeek_grep",
|
|
1184
1184
|
write: "readSeek_write"
|
|
1185
1185
|
};
|
|
1186
|
-
var READSEEK_KEYS = ["replacedTools", "imageMode", "syntaxValidation", "timeoutMs", "grep"];
|
|
1186
|
+
var READSEEK_KEYS = ["replacedTools", "imageMode", "syntaxValidation", "timeoutMs", "grep", "display"];
|
|
1187
1187
|
var READSEEK_GREP_KEYS = ["maxLines", "maxBytes"];
|
|
1188
|
+
var READSEEK_DISPLAY_KEYS = ["read", "grep", "edit", "write"];
|
|
1189
|
+
var DEFAULT_DISPLAY_MODES = {
|
|
1190
|
+
read: "compact",
|
|
1191
|
+
grep: "compact",
|
|
1192
|
+
edit: "expanded",
|
|
1193
|
+
write: "expanded"
|
|
1194
|
+
};
|
|
1188
1195
|
function globalSettingsPath() {
|
|
1189
1196
|
return join(homedir2(), ".pi/agent/settings.json");
|
|
1190
1197
|
}
|
|
@@ -1250,6 +1257,22 @@ function readReplacedTools(raw, source, warnings) {
|
|
|
1250
1257
|
});
|
|
1251
1258
|
return tools;
|
|
1252
1259
|
}
|
|
1260
|
+
function readDisplaySettings(raw, source, warnings) {
|
|
1261
|
+
if (!("display" in raw))
|
|
1262
|
+
return;
|
|
1263
|
+
if (!isRecord(raw.display)) {
|
|
1264
|
+
warnings.push(invalid(source, "readseek.display"));
|
|
1265
|
+
return;
|
|
1266
|
+
}
|
|
1267
|
+
warnUnknownKeys(raw.display, READSEEK_DISPLAY_KEYS, "readseek.display", source, warnings);
|
|
1268
|
+
const display = {};
|
|
1269
|
+
for (const tool of READSEEK_DISPLAY_KEYS) {
|
|
1270
|
+
const mode = readEnum(raw.display, tool, ["compact", "expanded"], `readseek.display.${tool}`, source, warnings);
|
|
1271
|
+
if (mode !== undefined)
|
|
1272
|
+
display[tool] = mode;
|
|
1273
|
+
}
|
|
1274
|
+
return Object.keys(display).length > 0 ? display : undefined;
|
|
1275
|
+
}
|
|
1253
1276
|
function validateSettings(raw, source) {
|
|
1254
1277
|
const settings = {};
|
|
1255
1278
|
const warnings = [];
|
|
@@ -1302,6 +1325,9 @@ function validateSettings(raw, source) {
|
|
|
1302
1325
|
warnings.push(invalid(source, "readseek.grep"));
|
|
1303
1326
|
}
|
|
1304
1327
|
}
|
|
1328
|
+
const display = readDisplaySettings(section, source, warnings);
|
|
1329
|
+
if (display !== undefined)
|
|
1330
|
+
settings.display = display;
|
|
1305
1331
|
return { settings, warnings };
|
|
1306
1332
|
}
|
|
1307
1333
|
var settingsFileCache = new Map;
|
|
@@ -1337,6 +1363,9 @@ function mergeSettings(base, override) {
|
|
|
1337
1363
|
const grep = { ...base.grep ?? {}, ...override.grep ?? {} };
|
|
1338
1364
|
if (Object.keys(grep).length > 0)
|
|
1339
1365
|
settings.grep = grep;
|
|
1366
|
+
const display = { ...base.display ?? {}, ...override.display ?? {} };
|
|
1367
|
+
if (Object.keys(display).length > 0)
|
|
1368
|
+
settings.display = display;
|
|
1340
1369
|
return settings;
|
|
1341
1370
|
}
|
|
1342
1371
|
function resolveReadSeekJsonSettings() {
|
|
@@ -1356,6 +1385,9 @@ function resolveReadSeekSyntaxValidation() {
|
|
|
1356
1385
|
function resolveReadSeekTimeoutMs() {
|
|
1357
1386
|
return resolveReadSeekJsonSettings().settings.timeoutMs;
|
|
1358
1387
|
}
|
|
1388
|
+
function resolveReadSeekToolDisplayMode(tool) {
|
|
1389
|
+
return resolveReadSeekJsonSettings().settings.display?.[tool] ?? DEFAULT_DISPLAY_MODES[tool];
|
|
1390
|
+
}
|
|
1359
1391
|
|
|
1360
1392
|
// src/readseek-client.ts
|
|
1361
1393
|
function normalizeLanguage(language) {
|
|
@@ -2946,15 +2978,15 @@ function summaryLine(summary, options = {}) {
|
|
|
2946
2978
|
const prefix = options.theme ? options.theme.fg(options.style ?? "dim", SUMMARY_PREFIX) : SUMMARY_PREFIX;
|
|
2947
2979
|
return appendExpandHint(`${prefix} ${summary}`, !!options.hidden);
|
|
2948
2980
|
}
|
|
2949
|
-
function isRendererExpanded(options, context) {
|
|
2950
|
-
return context?.expanded ?? options?.expanded ??
|
|
2981
|
+
function isRendererExpanded(options, context, defaultExpanded = false) {
|
|
2982
|
+
return context?.expanded ?? options?.expanded ?? defaultExpanded;
|
|
2951
2983
|
}
|
|
2952
|
-
function resolveRenderResultContext(options, rest) {
|
|
2984
|
+
function resolveRenderResultContext(options, rest, defaultExpanded = false) {
|
|
2953
2985
|
const context = rest[0] ?? options ?? {};
|
|
2954
2986
|
return {
|
|
2955
2987
|
isPartial: context.isPartial ?? options?.isPartial ?? false,
|
|
2956
2988
|
isError: context.isError ?? false,
|
|
2957
|
-
expanded: isRendererExpanded(options, context),
|
|
2989
|
+
expanded: isRendererExpanded(options, context, defaultExpanded),
|
|
2958
2990
|
width: context.width ?? options?.width,
|
|
2959
2991
|
cwd: context.cwd ?? process.cwd(),
|
|
2960
2992
|
context
|
|
@@ -2990,50 +3022,7 @@ function wrapWithHangingIndent(prefix, content, width, options = {}) {
|
|
|
2990
3022
|
const indent = " ".repeat(prefixWidth);
|
|
2991
3023
|
return wrapped.map((line, index) => tint(clampLineToWidth(index === 0 ? prefix + line : indent + line, normalized)));
|
|
2992
3024
|
}
|
|
2993
|
-
var HASHLINE_CONTENT_RE = /^(\d+:[0-9a-fA-F]+\|)(.*)$/;
|
|
2994
|
-
function wrapReadHashlinesForWidth(text, width) {
|
|
2995
|
-
if (width === undefined || width === null)
|
|
2996
|
-
return text;
|
|
2997
|
-
const normalized = normalizeWidth(width);
|
|
2998
|
-
const output = [];
|
|
2999
|
-
for (const line of text.split(`
|
|
3000
|
-
`)) {
|
|
3001
|
-
const match = line.match(HASHLINE_CONTENT_RE);
|
|
3002
|
-
if (!match) {
|
|
3003
|
-
output.push(line);
|
|
3004
|
-
continue;
|
|
3005
|
-
}
|
|
3006
|
-
if (visibleWidth(line) <= normalized) {
|
|
3007
|
-
output.push(line);
|
|
3008
|
-
continue;
|
|
3009
|
-
}
|
|
3010
|
-
const prefix = match[1];
|
|
3011
|
-
const content = match[2] ?? "";
|
|
3012
|
-
const prefixWidth = visibleWidth(prefix);
|
|
3013
|
-
const contentWidth = Math.max(1, normalized - prefixWidth);
|
|
3014
|
-
const wrappedContent = wrapTextWithAnsi(content, contentWidth).map((wrapped) => clampLineToWidth(wrapped, contentWidth));
|
|
3015
|
-
if (wrappedContent.length === 0) {
|
|
3016
|
-
output.push(clampLineToWidth(prefix, normalized));
|
|
3017
|
-
continue;
|
|
3018
|
-
}
|
|
3019
|
-
output.push(clampLineToWidth(prefix + wrappedContent[0], normalized));
|
|
3020
|
-
const indent = " ".repeat(prefixWidth);
|
|
3021
|
-
for (const continuation of wrappedContent.slice(1)) {
|
|
3022
|
-
output.push(clampLineToWidth(indent + continuation, normalized));
|
|
3023
|
-
}
|
|
3024
|
-
}
|
|
3025
|
-
return output.join(`
|
|
3026
|
-
`);
|
|
3027
|
-
}
|
|
3028
3025
|
var wrappedHashlinesCache = new WeakMap;
|
|
3029
|
-
function wrapReadHashlinesForWidthCached(cacheKey, text, width) {
|
|
3030
|
-
const cached = wrappedHashlinesCache.get(cacheKey);
|
|
3031
|
-
if (cached && cached.text === text && cached.width === width)
|
|
3032
|
-
return cached.wrapped;
|
|
3033
|
-
const wrapped = wrapReadHashlinesForWidth(text, width);
|
|
3034
|
-
wrappedHashlinesCache.set(cacheKey, { text, width, wrapped });
|
|
3035
|
-
return wrapped;
|
|
3036
|
-
}
|
|
3037
3026
|
function renderPendingResult(pendingLabel, width, theme) {
|
|
3038
3027
|
return new Text(clampLinesToWidth([summaryLine(pendingLabel, { theme, style: "muted" })], width).join(`
|
|
3039
3028
|
`), 0, 0);
|
|
@@ -3093,6 +3082,90 @@ ${linkedPathLine(theme, file.path, display, cwd, ` (${file.lines.length})`, widt
|
|
|
3093
3082
|
`), 0, 0);
|
|
3094
3083
|
}
|
|
3095
3084
|
|
|
3085
|
+
// src/tui-source-render.ts
|
|
3086
|
+
import { getLanguageFromPath, highlightCode } from "@earendil-works/pi-coding-agent";
|
|
3087
|
+
import { wrapTextWithAnsi as wrapTextWithAnsi2 } from "@earendil-works/pi-tui";
|
|
3088
|
+
var READ_ANCHOR_RE = /^(\d+:[0-9a-zA-Z]{1,16})\|(.*)$/;
|
|
3089
|
+
var GREP_ANCHOR_RE = /^(.*?):(>>| )(\d+):([0-9a-zA-Z]{1,16})\|(.*)$/;
|
|
3090
|
+
var SOURCE_THEME_STYLES = [
|
|
3091
|
+
"mdCodeBlock",
|
|
3092
|
+
"muted",
|
|
3093
|
+
"syntaxComment",
|
|
3094
|
+
"syntaxKeyword",
|
|
3095
|
+
"syntaxFunction",
|
|
3096
|
+
"syntaxVariable",
|
|
3097
|
+
"syntaxString",
|
|
3098
|
+
"syntaxNumber",
|
|
3099
|
+
"syntaxType",
|
|
3100
|
+
"syntaxOperator",
|
|
3101
|
+
"syntaxPunctuation",
|
|
3102
|
+
"toolDiffAdded",
|
|
3103
|
+
"toolDiffRemoved"
|
|
3104
|
+
];
|
|
3105
|
+
function wrapDisplayLines(lines, width) {
|
|
3106
|
+
if (width === undefined || width === null)
|
|
3107
|
+
return lines;
|
|
3108
|
+
const normalized = normalizeWidth(width);
|
|
3109
|
+
return lines.flatMap((line) => {
|
|
3110
|
+
const wrapped = wrapTextWithAnsi2(line, normalized);
|
|
3111
|
+
return wrapped.length > 0 ? wrapped : [""];
|
|
3112
|
+
});
|
|
3113
|
+
}
|
|
3114
|
+
function renderSourceBlock(lines, path2) {
|
|
3115
|
+
return highlightCode(lines.join(`
|
|
3116
|
+
`), getLanguageFromPath(path2));
|
|
3117
|
+
}
|
|
3118
|
+
function renderReadSourceForDisplay(text, path2, anchors, width) {
|
|
3119
|
+
const output = [];
|
|
3120
|
+
let sourceLines = [];
|
|
3121
|
+
const flushSourceLines = () => {
|
|
3122
|
+
if (sourceLines.length === 0)
|
|
3123
|
+
return;
|
|
3124
|
+
output.push(...wrapDisplayLines(renderSourceBlock(sourceLines, path2), width));
|
|
3125
|
+
sourceLines = [];
|
|
3126
|
+
};
|
|
3127
|
+
for (const line of text.split(`
|
|
3128
|
+
`)) {
|
|
3129
|
+
const match = line.match(READ_ANCHOR_RE);
|
|
3130
|
+
if (match && anchors.has(match[1])) {
|
|
3131
|
+
sourceLines.push(match[2] ?? "");
|
|
3132
|
+
continue;
|
|
3133
|
+
}
|
|
3134
|
+
flushSourceLines();
|
|
3135
|
+
output.push(...wrapDisplayLines([line], width));
|
|
3136
|
+
}
|
|
3137
|
+
flushSourceLines();
|
|
3138
|
+
return output.join(`
|
|
3139
|
+
`);
|
|
3140
|
+
}
|
|
3141
|
+
var readSourceCache = new WeakMap;
|
|
3142
|
+
function renderReadSourceForDisplayCached(cacheKey, text, path2, anchors, width, theme) {
|
|
3143
|
+
const themeKey = SOURCE_THEME_STYLES.map((style) => theme.fg(style, "")).join("");
|
|
3144
|
+
const anchorKey = [...anchors].join(`
|
|
3145
|
+
`);
|
|
3146
|
+
const cached = readSourceCache.get(cacheKey);
|
|
3147
|
+
if (cached && cached.text === text && cached.path === path2 && cached.width === width && cached.theme === themeKey && cached.anchors === anchorKey) {
|
|
3148
|
+
return cached.rendered;
|
|
3149
|
+
}
|
|
3150
|
+
const rendered = renderReadSourceForDisplay(text, path2, anchors, width);
|
|
3151
|
+
readSourceCache.set(cacheKey, { text, path: path2, width, theme: themeKey, anchors: anchorKey, rendered });
|
|
3152
|
+
return rendered;
|
|
3153
|
+
}
|
|
3154
|
+
function renderGrepSourceForDisplay(text, anchors, renderPath = (path2) => path2) {
|
|
3155
|
+
return text.split(`
|
|
3156
|
+
`).map((line) => {
|
|
3157
|
+
const match = line.match(GREP_ANCHOR_RE);
|
|
3158
|
+
if (!match)
|
|
3159
|
+
return line;
|
|
3160
|
+
const anchor = `${match[3]}:${match[4]}`;
|
|
3161
|
+
if (!anchors.has(anchor))
|
|
3162
|
+
return line;
|
|
3163
|
+
const separator = match[2] === ">>" ? ":" : "-";
|
|
3164
|
+
return `${renderPath(match[1])}${separator}${match[3]}${separator}${match[5] ?? ""}`;
|
|
3165
|
+
}).join(`
|
|
3166
|
+
`);
|
|
3167
|
+
}
|
|
3168
|
+
|
|
3096
3169
|
// src/register-tool.ts
|
|
3097
3170
|
import { Type } from "@sinclair/typebox";
|
|
3098
3171
|
function optionalIntOrString(description) {
|
|
@@ -3644,7 +3717,7 @@ function registerReadTool(pi, options = {}) {
|
|
|
3644
3717
|
return new Text2(clampLineToWidth(text, context.width), 0, 0);
|
|
3645
3718
|
},
|
|
3646
3719
|
renderResult(result, options2, theme, ...rest) {
|
|
3647
|
-
const { isPartial, isError, expanded, width } = resolveRenderResultContext(options2, rest);
|
|
3720
|
+
const { isPartial, isError, expanded, width, context } = resolveRenderResultContext(options2, rest, resolveReadSeekToolDisplayMode("read") === "expanded");
|
|
3648
3721
|
if (isPartial)
|
|
3649
3722
|
return renderPendingResult("pending read", width, theme);
|
|
3650
3723
|
const content = result.content?.[0];
|
|
@@ -3677,9 +3750,16 @@ function registerReadTool(pi, options = {}) {
|
|
|
3677
3750
|
summaryParts.push(badge);
|
|
3678
3751
|
const summary = summaryParts.join(" • ");
|
|
3679
3752
|
let text = summaryLine(summary, { hidden: !!textContent && !expanded, theme, style: "success" });
|
|
3680
|
-
if (expanded && textContent)
|
|
3753
|
+
if (expanded && textContent) {
|
|
3754
|
+
const anchors = new Set(readSeekValue.lines?.map((line) => line.anchor) ?? []);
|
|
3755
|
+
for (const item of readSeekValue.bundle?.localSupport ?? []) {
|
|
3756
|
+
for (const anchor of item.lineAnchors)
|
|
3757
|
+
anchors.add(anchor);
|
|
3758
|
+
}
|
|
3759
|
+
const sourcePath = readSeekValue.path ?? context.args?.path ?? "";
|
|
3681
3760
|
text += `
|
|
3682
|
-
` +
|
|
3761
|
+
` + renderReadSourceForDisplayCached(content, textContent, sourcePath, anchors, width, theme);
|
|
3762
|
+
}
|
|
3683
3763
|
return new Text2(clampLinesToWidth(text.split(`
|
|
3684
3764
|
`), width).join(`
|
|
3685
3765
|
`), 0, 0);
|
|
@@ -5170,7 +5250,7 @@ function registerEditTool(pi, options = {}) {
|
|
|
5170
5250
|
`));
|
|
5171
5251
|
},
|
|
5172
5252
|
renderResult(result, options2, theme, ...rest) {
|
|
5173
|
-
const { isPartial, isError, expanded, width, context } = resolveRenderResultContext(options2, rest);
|
|
5253
|
+
const { isPartial, isError, expanded, width, context } = resolveRenderResultContext(options2, rest, resolveReadSeekToolDisplayMode("edit") === "expanded");
|
|
5174
5254
|
if (isPartial) {
|
|
5175
5255
|
return renderPendingResult("pending edit", width, theme);
|
|
5176
5256
|
}
|
|
@@ -5995,7 +6075,7 @@ function registerGrepTool(pi, options = {}) {
|
|
|
5995
6075
|
return new Text5(clampLineToWidth(text, context.width), 0, 0);
|
|
5996
6076
|
},
|
|
5997
6077
|
renderResult(result, options2, theme, ...rest) {
|
|
5998
|
-
const { isPartial, isError, expanded, cwd, width } = resolveRenderResultContext(options2, rest);
|
|
6078
|
+
const { isPartial, isError, expanded, cwd, width } = resolveRenderResultContext(options2, rest, resolveReadSeekToolDisplayMode("grep") === "expanded");
|
|
5999
6079
|
if (isPartial)
|
|
6000
6080
|
return renderPendingResult("pending grep", width, theme);
|
|
6001
6081
|
const content = result.content?.[0];
|
|
@@ -6027,19 +6107,10 @@ function registerGrepTool(pi, options = {}) {
|
|
|
6027
6107
|
});
|
|
6028
6108
|
for (const badge of info.badges)
|
|
6029
6109
|
text += theme.fg(badge.startsWith("⚠") ? "warning" : "dim", ` ${badge}`);
|
|
6030
|
-
if (expanded && readSeekValue?.records) {
|
|
6031
|
-
const
|
|
6032
|
-
|
|
6033
|
-
|
|
6034
|
-
fileCounts.set(r.path, (fileCounts.get(r.path) ?? 0) + 1);
|
|
6035
|
-
for (const [filePath, count] of [...fileCounts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 20)) {
|
|
6036
|
-
const display = path2.relative(cwd, filePath) || filePath;
|
|
6037
|
-
text += `
|
|
6038
|
-
${linkedPathLine(theme, filePath, display, cwd, ` (${count})`, width)}`;
|
|
6039
|
-
}
|
|
6040
|
-
if (fileCounts.size > 20)
|
|
6041
|
-
text += `
|
|
6042
|
-
` + theme.fg("muted", ` … and ${fileCounts.size - 20} more files`);
|
|
6110
|
+
if (expanded && textContent && readSeekValue?.records) {
|
|
6111
|
+
const anchors = new Set(readSeekValue.records.map((record) => record.anchor));
|
|
6112
|
+
text += `
|
|
6113
|
+
` + renderGrepSourceForDisplay(textContent, anchors, (displayPath) => linkToolPath(theme.fg("dim", displayPath), displayPath, cwd));
|
|
6043
6114
|
}
|
|
6044
6115
|
return new Text5(clampLinesToWidth(text.split(`
|
|
6045
6116
|
`), width).join(`
|
|
@@ -6884,7 +6955,7 @@ function registerWriteTool(pi, options = {}) {
|
|
|
6884
6955
|
`));
|
|
6885
6956
|
},
|
|
6886
6957
|
renderResult(result, options2, theme, ...rest) {
|
|
6887
|
-
const { isPartial, expanded, width, context } = resolveRenderResultContext(options2, rest);
|
|
6958
|
+
const { isPartial, expanded, width, context } = resolveRenderResultContext(options2, rest, resolveReadSeekToolDisplayMode("write") === "expanded");
|
|
6888
6959
|
if (isPartial)
|
|
6889
6960
|
return renderPendingResult("pending write", width, theme);
|
|
6890
6961
|
const details = result.details ?? {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-readseek",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.6",
|
|
4
4
|
"description": "Pi extension for readseek-backed hash-anchored read/edit/grep, structural code maps, structural search, and file exploration",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"node": ">=20.0.0"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@jarkkojs/readseek": "^0.8.
|
|
43
|
+
"@jarkkojs/readseek": "^0.8.6",
|
|
44
44
|
"diff": "^8.0.3",
|
|
45
45
|
"xxhash-wasm": "^1.1.0"
|
|
46
46
|
},
|