@danypops/pi-lector 0.13.9 → 0.14.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/extension/src/apply-patch/rendering.ts +2 -1
- package/extension/src/code-intelligence/operations.ts +109 -3
- package/extension/src/code-intelligence/rendering.ts +129 -5
- package/extension/src/cross-workspace-search/rendering.ts +10 -2
- package/extension/src/editor/editor-state.ts +38 -1
- package/extension/src/editor/index.ts +9 -1
- package/extension/src/editor/modal-editor-component.ts +9 -3
- package/extension/src/external-search/rendering.ts +46 -12
- package/extension/src/find-files/rendering.ts +2 -1
- package/extension/src/find-symbols/rendering.ts +2 -1
- package/extension/src/git/operations.ts +23 -1
- package/extension/src/git/rendering.ts +48 -8
- package/extension/src/index.ts +495 -55
- package/extension/src/line-edit/rendering.ts +2 -1
- package/extension/src/package-source/rendering.ts +4 -3
- package/extension/src/presentation/model-content.ts +62 -0
- package/extension/src/presentation/presentation-contract.ts +148 -0
- package/extension/src/presentation/tool-presentation.ts +170 -0
- package/extension/src/repo-cache/rendering.ts +4 -3
- package/extension/src/search/rendering.ts +6 -3
- package/extension/src/symbol-annotation/operations.ts +8 -4
- package/extension/src/vehicle-client.ts +4 -2
- package/extension/src/workspace-cache/rendering.ts +4 -3
- package/package.json +4 -4
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { GitDiffResult, GitLogEntry, GitStatusSummary, OperationOutputs } from "@danypops/lector";
|
|
1
|
+
import type { GitDiffResult, GitLogEntry, GitStatusSummary, OperationInputs, OperationOutputs } from "@danypops/lector";
|
|
2
2
|
import { lectorClient, withWorkspace, workspaceForDirectory } from "../lector-client.ts";
|
|
3
3
|
import { invokeLectorVehicleOperation, type LectorVehicleCall } from "../vehicle-client.ts";
|
|
4
4
|
|
|
@@ -6,6 +6,8 @@ type SymbolComparison = OperationOutputs["workspace.compareSymbolAcrossVersions"
|
|
|
6
6
|
type GitWorktreeAddResult = OperationOutputs["workspace.gitWorktreeAdd"];
|
|
7
7
|
type GitWorktreeRemoveResult = OperationOutputs["workspace.gitWorktreeRemove"];
|
|
8
8
|
type GitGrepResult = OperationOutputs["workspace.gitGrep"];
|
|
9
|
+
type GitHistoryGrepResult = OperationOutputs["workspace.gitGrepHistory"];
|
|
10
|
+
type GitHistoryGrepBounds = Omit<OperationInputs["workspace.gitGrepHistory"], "workspaceId" | "pattern" | "pathspecs">;
|
|
9
11
|
type GitListFilesResult = OperationOutputs["workspace.gitListFiles"];
|
|
10
12
|
|
|
11
13
|
/** Matches GIT_READ_PERMISSIONS' own declared value server-side (git/operation-registration.ts). */
|
|
@@ -42,6 +44,14 @@ export interface GitOperations {
|
|
|
42
44
|
maxBytes: number,
|
|
43
45
|
call: LectorVehicleCall,
|
|
44
46
|
): Promise<GitGrepResult>;
|
|
47
|
+
/** Bounded extended-regex search across commit trees reachable from every ref, with deterministic topological paging and exact-match deduplication. */
|
|
48
|
+
grepHistory(
|
|
49
|
+
directory: string,
|
|
50
|
+
pattern: string,
|
|
51
|
+
pathspecs: readonly string[] | undefined,
|
|
52
|
+
bounds: GitHistoryGrepBounds,
|
|
53
|
+
call: LectorVehicleCall,
|
|
54
|
+
): Promise<GitHistoryGrepResult>;
|
|
45
55
|
/** Every file path in `ref`'s own tree, no checkout -- pathspecs narrows the listing (prefix-based, not glob-based like grep's). */
|
|
46
56
|
listFiles(directory: string, ref: string, pathspecs: readonly string[] | undefined, maxResults: number, call: LectorVehicleCall): Promise<GitListFilesResult>;
|
|
47
57
|
/** True iff ancestorRef is a real ancestor of (or the exact same commit as) ref -- the backport/reachability check "was this fix ported to this branch" actually needs. */
|
|
@@ -139,6 +149,18 @@ export function createLectorGitOperations(): GitOperations {
|
|
|
139
149
|
),
|
|
140
150
|
);
|
|
141
151
|
},
|
|
152
|
+
async grepHistory(directory, pattern, pathspecs, bounds, call) {
|
|
153
|
+
return withWorkspace(
|
|
154
|
+
() => workspaceForDirectory(directory),
|
|
155
|
+
({ workspaceId }) =>
|
|
156
|
+
invokeLectorVehicleOperation<GitHistoryGrepResult>(
|
|
157
|
+
"workspace.gitGrepHistory",
|
|
158
|
+
{ workspaceId, pattern, pathspecs, ...bounds },
|
|
159
|
+
GIT_READ_PERMISSIONS,
|
|
160
|
+
call,
|
|
161
|
+
),
|
|
162
|
+
);
|
|
163
|
+
},
|
|
142
164
|
async listFiles(directory, ref, pathspecs, maxResults, call) {
|
|
143
165
|
return withWorkspace(
|
|
144
166
|
() => workspaceForDirectory(directory),
|
|
@@ -3,6 +3,7 @@ import { keyHint } from "@earendil-works/pi-coding-agent";
|
|
|
3
3
|
import { truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
|
|
4
4
|
import { renderDiffLines, renderTruncatedList, type TextMeasure } from "malevich-tui-components";
|
|
5
5
|
import type { LectorTheme } from "../lector-tui-theme.ts";
|
|
6
|
+
import { presentationTitle } from "../presentation/tool-presentation.ts";
|
|
6
7
|
|
|
7
8
|
/** Real ANSI-aware measurement, not Malevich's own ASCII-only default -- every diff line renderDiffLines receives is already theme.fg-styled. */
|
|
8
9
|
const measure: TextMeasure = { visibleWidth, truncateToWidth };
|
|
@@ -11,12 +12,24 @@ const DEFAULT_VISIBLE_FILES = 20;
|
|
|
11
12
|
const DEFAULT_VISIBLE_COMMITS = 10;
|
|
12
13
|
const DEFAULT_VISIBLE_DIFF_LINES = 60;
|
|
13
14
|
|
|
14
|
-
export type GitAction =
|
|
15
|
+
export type GitAction =
|
|
16
|
+
| "status"
|
|
17
|
+
| "log"
|
|
18
|
+
| "diff"
|
|
19
|
+
| "compare-symbol"
|
|
20
|
+
| "worktree-add"
|
|
21
|
+
| "worktree-remove"
|
|
22
|
+
| "show"
|
|
23
|
+
| "grep-ref"
|
|
24
|
+
| "grep-history"
|
|
25
|
+
| "ls-ref"
|
|
26
|
+
| "is-ancestor";
|
|
15
27
|
|
|
16
28
|
type SymbolComparison = OperationOutputs["workspace.compareSymbolAcrossVersions"];
|
|
17
29
|
type GitWorktreeAddResult = OperationOutputs["workspace.gitWorktreeAdd"];
|
|
18
30
|
type GitWorktreeRemoveResult = OperationOutputs["workspace.gitWorktreeRemove"];
|
|
19
31
|
type GitGrepResult = OperationOutputs["workspace.gitGrep"];
|
|
32
|
+
type GitHistoryGrepResult = OperationOutputs["workspace.gitGrepHistory"];
|
|
20
33
|
type GitListFilesResult = OperationOutputs["workspace.gitListFiles"];
|
|
21
34
|
|
|
22
35
|
export interface GitToolDetails {
|
|
@@ -29,6 +42,7 @@ export interface GitToolDetails {
|
|
|
29
42
|
readonly worktreeRemove?: GitWorktreeRemoveResult;
|
|
30
43
|
readonly showFile?: { readonly ref: string; readonly path: string; readonly content: string | undefined };
|
|
31
44
|
readonly grep?: GitGrepResult;
|
|
45
|
+
readonly historyGrep?: GitHistoryGrepResult;
|
|
32
46
|
readonly listFiles?: GitListFilesResult;
|
|
33
47
|
readonly isAncestor?: { readonly ancestorRef: string; readonly ref: string; readonly result: boolean };
|
|
34
48
|
}
|
|
@@ -48,31 +62,32 @@ export function formatGitCall(
|
|
|
48
62
|
theme: LectorTheme,
|
|
49
63
|
): string {
|
|
50
64
|
const action = typeof args.action === "string" ? args.action : "";
|
|
65
|
+
const label = theme.fg("toolTitle", theme.bold(presentationTitle("git", action)));
|
|
51
66
|
const directory = typeof args.directory === "string" ? args.directory : "";
|
|
52
67
|
if (action === "compare-symbol") {
|
|
53
68
|
const path = typeof args.path === "string" ? args.path : "";
|
|
54
69
|
const symbol = typeof args.symbol === "string" ? args.symbol : "";
|
|
55
70
|
const fromRef = typeof args.fromRef === "string" ? args.fromRef : "";
|
|
56
71
|
const toRef = typeof args.toRef === "string" ? ` -> ${args.toRef}` : "";
|
|
57
|
-
return `${
|
|
72
|
+
return `${label} ${theme.fg("accent", `${directory}/${path}`)} (${symbol}) ${fromRef}${toRef}`;
|
|
58
73
|
}
|
|
59
74
|
if (action === "is-ancestor") {
|
|
60
75
|
const ancestorRef = typeof args.ancestorRef === "string" ? args.ancestorRef : "";
|
|
61
76
|
const ref = typeof args.ref === "string" ? args.ref : "";
|
|
62
|
-
return `${
|
|
77
|
+
return `${label} ${theme.fg("accent", directory)} ${ancestorRef} -> ${ref}`;
|
|
63
78
|
}
|
|
64
|
-
if (action === "grep-ref") {
|
|
65
|
-
const ref = typeof args.ref === "string" ? args.ref : "";
|
|
79
|
+
if (action === "grep-ref" || action === "grep-history") {
|
|
80
|
+
const ref = action === "grep-ref" && typeof args.ref === "string" ? `${args.ref} ` : "";
|
|
66
81
|
const pattern = typeof args.pattern === "string" ? args.pattern : "";
|
|
67
|
-
return `${
|
|
82
|
+
return `${label} ${theme.fg("accent", directory)} ${ref}"${pattern}"`;
|
|
68
83
|
}
|
|
69
84
|
if (action === "show") {
|
|
70
85
|
const ref = typeof args.ref === "string" ? args.ref : "";
|
|
71
86
|
const path = typeof args.path === "string" ? args.path : "";
|
|
72
|
-
return `${
|
|
87
|
+
return `${label} ${theme.fg("accent", `${directory}/${path}`)} @ ${ref}`;
|
|
73
88
|
}
|
|
74
89
|
const ref = typeof args.ref === "string" ? ` ${args.ref}` : "";
|
|
75
|
-
return `${
|
|
90
|
+
return `${label} ${theme.fg("accent", directory)}${ref}`;
|
|
76
91
|
}
|
|
77
92
|
|
|
78
93
|
function formatGitWorktreeAddResult(result: GitWorktreeAddResult | undefined, theme: LectorTheme): string {
|
|
@@ -105,6 +120,30 @@ function formatGitGrepResult(result: GitGrepResult | undefined, expanded: boolea
|
|
|
105
120
|
return lines.join("\n");
|
|
106
121
|
}
|
|
107
122
|
|
|
123
|
+
function formatGitHistoryGrepResult(result: GitHistoryGrepResult | undefined, expanded: boolean, theme: LectorTheme): string {
|
|
124
|
+
if (!result || result.matches.length === 0) {
|
|
125
|
+
if (result?.deadlineReached) return theme.fg("warning", "No matches before the deadline.");
|
|
126
|
+
return theme.fg("dim", "No historical matches.");
|
|
127
|
+
}
|
|
128
|
+
const lines = renderTruncatedList({
|
|
129
|
+
items: result.matches,
|
|
130
|
+
expanded,
|
|
131
|
+
visibleCount: DEFAULT_VISIBLE_FILES,
|
|
132
|
+
formatItem: (match) => {
|
|
133
|
+
const occurrences = match.occurrences > 1 ? ` (${match.occurrences} commits)` : "";
|
|
134
|
+
return `${theme.fg("muted", match.commit.slice(0, 8))} ${theme.fg("accent", `${match.path}:${match.line}`)}:${match.text}${occurrences}`;
|
|
135
|
+
},
|
|
136
|
+
moreLine: moreLine(theme),
|
|
137
|
+
truncationWarning: result.deadlineReached
|
|
138
|
+
? theme.fg("warning", "(deadline reached)")
|
|
139
|
+
: result.truncated
|
|
140
|
+
? theme.fg("warning", "(bounded by maxMatches/maxBytes)")
|
|
141
|
+
: undefined,
|
|
142
|
+
});
|
|
143
|
+
if (result.nextCommitOffset !== undefined) lines.push(theme.fg("muted", `next commit offset: ${result.nextCommitOffset}`));
|
|
144
|
+
return lines.join("\n");
|
|
145
|
+
}
|
|
146
|
+
|
|
108
147
|
function formatGitListFilesResult(result: GitListFilesResult | undefined, expanded: boolean, theme: LectorTheme): string {
|
|
109
148
|
if (!result || result.paths.length === 0) return theme.fg("dim", "No files.");
|
|
110
149
|
const lines = renderTruncatedList({
|
|
@@ -133,6 +172,7 @@ export function formatGitResult(details: GitToolDetails | undefined, expanded: b
|
|
|
133
172
|
if (details.action === "worktree-remove") return formatGitWorktreeRemoveResult(details.worktreeRemove, theme);
|
|
134
173
|
if (details.action === "show") return formatGitShowFileResult(details.showFile, theme);
|
|
135
174
|
if (details.action === "grep-ref") return formatGitGrepResult(details.grep, expanded, theme);
|
|
175
|
+
if (details.action === "grep-history") return formatGitHistoryGrepResult(details.historyGrep, expanded, theme);
|
|
136
176
|
if (details.action === "ls-ref") return formatGitListFilesResult(details.listFiles, expanded, theme);
|
|
137
177
|
if (details.action === "is-ancestor") return formatGitIsAncestorResult(details.isAncestor, theme);
|
|
138
178
|
return formatGitDiffResult(details.result, expanded, theme);
|