@danypops/pi-lector 0.1.7 → 0.1.8
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 +4 -0
- package/extension/src/code-intelligence-operations.ts +19 -40
- package/extension/src/cross-workspace-search-operations.ts +2 -6
- package/extension/src/cross-workspace-search-rendering.ts +5 -2
- package/extension/src/find-symbols-operations.ts +3 -4
- package/extension/src/find-symbols-rendering.ts +8 -5
- package/extension/src/index.ts +119 -58
- package/extension/src/package-source-operations.ts +21 -0
- package/extension/src/package-source-rendering.ts +37 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -8,6 +8,10 @@ daemon-backed, hash-guarded filesystem. Requires a running Lector daemon
|
|
|
8
8
|
pi install npm:@danypops/pi-lector
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
+
`package_source` resolves an installed npm package through its lockfile and registry metadata, verifies an exact Git commit, and registers the package source read-only for `search_code`, `find_symbols`, and the semantic tools.
|
|
12
|
+
|
|
13
|
+
Symbol and semantic tool results identify their backend and fidelity. `typescript-language-server` results are semantic; compiler or parser fallback results are structural and list their limitations.
|
|
14
|
+
|
|
11
15
|
`populate_symbol_graph` submits bounded background work and waits briefly. If the
|
|
12
16
|
graph is still loading, it returns a job id immediately; `job_status` polls that id
|
|
13
17
|
later without forcing the agent into a blocking or blind polling loop.
|
|
@@ -1,16 +1,4 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
CallHierarchyEntry,
|
|
3
|
-
Diagnostic,
|
|
4
|
-
DocumentSymbolEntry,
|
|
5
|
-
Hover,
|
|
6
|
-
IncomingCall,
|
|
7
|
-
JobSnapshot,
|
|
8
|
-
OutgoingCall,
|
|
9
|
-
PopulateSymbolGraphResult,
|
|
10
|
-
SymbolEdgeKind,
|
|
11
|
-
SymbolNode,
|
|
12
|
-
WorkspaceLocation,
|
|
13
|
-
} from "@danypops/lector";
|
|
1
|
+
import type { JobSnapshot, OperationOutputs, PopulateSymbolGraphResult, SymbolEdgeKind, SymbolNode } from "@danypops/lector";
|
|
14
2
|
import { lectorClient, withWorkspace, workspaceForCodeIntelligencePath } from "./lector-client.ts";
|
|
15
3
|
|
|
16
4
|
/**
|
|
@@ -27,15 +15,15 @@ import { lectorClient, withWorkspace, workspaceForCodeIntelligencePath } from ".
|
|
|
27
15
|
* unlike read/write/edit) -- never a value captured once at session start.
|
|
28
16
|
*/
|
|
29
17
|
export interface CodeIntelligenceOperations {
|
|
30
|
-
goToDefinition(path: string, line: number, character: number): Promise<
|
|
31
|
-
goToImplementation(path: string, line: number, character: number): Promise<
|
|
32
|
-
findReferences(path: string, line: number, character: number, includeDeclaration: boolean): Promise<
|
|
33
|
-
hover(path: string, line: number, character: number): Promise<
|
|
34
|
-
documentSymbols(path: string): Promise<
|
|
35
|
-
diagnostics(path: string): Promise<
|
|
36
|
-
prepareCallHierarchy(path: string, line: number, character: number): Promise<
|
|
37
|
-
incomingCalls(path: string, line: number, character: number): Promise<
|
|
38
|
-
outgoingCalls(path: string, line: number, character: number): Promise<
|
|
18
|
+
goToDefinition(path: string, line: number, character: number): Promise<OperationOutputs["workspace.goToDefinition"]>;
|
|
19
|
+
goToImplementation(path: string, line: number, character: number): Promise<OperationOutputs["workspace.goToImplementation"]>;
|
|
20
|
+
findReferences(path: string, line: number, character: number, includeDeclaration: boolean): Promise<OperationOutputs["workspace.findReferences"]>;
|
|
21
|
+
hover(path: string, line: number, character: number): Promise<OperationOutputs["workspace.hover"]>;
|
|
22
|
+
documentSymbols(path: string): Promise<OperationOutputs["workspace.documentSymbols"]>;
|
|
23
|
+
diagnostics(path: string): Promise<OperationOutputs["workspace.diagnostics"]>;
|
|
24
|
+
prepareCallHierarchy(path: string, line: number, character: number): Promise<OperationOutputs["workspace.prepareCallHierarchy"]>;
|
|
25
|
+
incomingCalls(path: string, line: number, character: number): Promise<OperationOutputs["workspace.incomingCalls"]>;
|
|
26
|
+
outgoingCalls(path: string, line: number, character: number): Promise<OperationOutputs["workspace.outgoingCalls"]>;
|
|
39
27
|
populateSymbolGraph(path: string, maxFiles: number, maxSymbolsPerFile: number, waitMs?: number): Promise<JobSnapshot<PopulateSymbolGraphResult>>;
|
|
40
28
|
jobStatus(jobId: string): Promise<JobSnapshot<PopulateSymbolGraphResult>>;
|
|
41
29
|
reachableFrom(path: string, line: number, character: number, maxDepth: number, kind?: SymbolEdgeKind): Promise<readonly SymbolNode[]>;
|
|
@@ -50,8 +38,7 @@ export function createLectorCodeIntelligenceOperations(): CodeIntelligenceOperat
|
|
|
50
38
|
() => workspaceForCodeIntelligencePath(path),
|
|
51
39
|
async ({ workspaceId }) => {
|
|
52
40
|
const client = await lectorClient();
|
|
53
|
-
|
|
54
|
-
return locations;
|
|
41
|
+
return client.call("workspace.goToDefinition", { workspaceId, path, line, character });
|
|
55
42
|
},
|
|
56
43
|
);
|
|
57
44
|
},
|
|
@@ -60,8 +47,7 @@ export function createLectorCodeIntelligenceOperations(): CodeIntelligenceOperat
|
|
|
60
47
|
() => workspaceForCodeIntelligencePath(path),
|
|
61
48
|
async ({ workspaceId }) => {
|
|
62
49
|
const client = await lectorClient();
|
|
63
|
-
|
|
64
|
-
return locations;
|
|
50
|
+
return client.call("workspace.goToImplementation", { workspaceId, path, line, character });
|
|
65
51
|
},
|
|
66
52
|
);
|
|
67
53
|
},
|
|
@@ -70,8 +56,7 @@ export function createLectorCodeIntelligenceOperations(): CodeIntelligenceOperat
|
|
|
70
56
|
() => workspaceForCodeIntelligencePath(path),
|
|
71
57
|
async ({ workspaceId }) => {
|
|
72
58
|
const client = await lectorClient();
|
|
73
|
-
|
|
74
|
-
return locations;
|
|
59
|
+
return client.call("workspace.findReferences", { workspaceId, path, line, character, includeDeclaration });
|
|
75
60
|
},
|
|
76
61
|
);
|
|
77
62
|
},
|
|
@@ -80,8 +65,7 @@ export function createLectorCodeIntelligenceOperations(): CodeIntelligenceOperat
|
|
|
80
65
|
() => workspaceForCodeIntelligencePath(path),
|
|
81
66
|
async ({ workspaceId }) => {
|
|
82
67
|
const client = await lectorClient();
|
|
83
|
-
|
|
84
|
-
return hover;
|
|
68
|
+
return client.call("workspace.hover", { workspaceId, path, line, character });
|
|
85
69
|
},
|
|
86
70
|
);
|
|
87
71
|
},
|
|
@@ -90,8 +74,7 @@ export function createLectorCodeIntelligenceOperations(): CodeIntelligenceOperat
|
|
|
90
74
|
() => workspaceForCodeIntelligencePath(path),
|
|
91
75
|
async ({ workspaceId }) => {
|
|
92
76
|
const client = await lectorClient();
|
|
93
|
-
|
|
94
|
-
return symbols;
|
|
77
|
+
return client.call("workspace.documentSymbols", { workspaceId, path });
|
|
95
78
|
},
|
|
96
79
|
);
|
|
97
80
|
},
|
|
@@ -100,8 +83,7 @@ export function createLectorCodeIntelligenceOperations(): CodeIntelligenceOperat
|
|
|
100
83
|
() => workspaceForCodeIntelligencePath(path),
|
|
101
84
|
async ({ workspaceId }) => {
|
|
102
85
|
const client = await lectorClient();
|
|
103
|
-
|
|
104
|
-
return diagnostics;
|
|
86
|
+
return client.call("workspace.diagnostics", { workspaceId, path });
|
|
105
87
|
},
|
|
106
88
|
);
|
|
107
89
|
},
|
|
@@ -110,8 +92,7 @@ export function createLectorCodeIntelligenceOperations(): CodeIntelligenceOperat
|
|
|
110
92
|
() => workspaceForCodeIntelligencePath(path),
|
|
111
93
|
async ({ workspaceId }) => {
|
|
112
94
|
const client = await lectorClient();
|
|
113
|
-
|
|
114
|
-
return items;
|
|
95
|
+
return client.call("workspace.prepareCallHierarchy", { workspaceId, path, line, character });
|
|
115
96
|
},
|
|
116
97
|
);
|
|
117
98
|
},
|
|
@@ -120,8 +101,7 @@ export function createLectorCodeIntelligenceOperations(): CodeIntelligenceOperat
|
|
|
120
101
|
() => workspaceForCodeIntelligencePath(path),
|
|
121
102
|
async ({ workspaceId }) => {
|
|
122
103
|
const client = await lectorClient();
|
|
123
|
-
|
|
124
|
-
return calls;
|
|
104
|
+
return client.call("workspace.incomingCalls", { workspaceId, path, line, character });
|
|
125
105
|
},
|
|
126
106
|
);
|
|
127
107
|
},
|
|
@@ -130,8 +110,7 @@ export function createLectorCodeIntelligenceOperations(): CodeIntelligenceOperat
|
|
|
130
110
|
() => workspaceForCodeIntelligencePath(path),
|
|
131
111
|
async ({ workspaceId }) => {
|
|
132
112
|
const client = await lectorClient();
|
|
133
|
-
|
|
134
|
-
return calls;
|
|
113
|
+
return client.call("workspace.outgoingCalls", { workspaceId, path, line, character });
|
|
135
114
|
},
|
|
136
115
|
);
|
|
137
116
|
},
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { TextSearchResult, WorkspaceQueryOutcome
|
|
1
|
+
import type { SymbolSearchResult, TextSearchResult, WorkspaceQueryOutcome } from "@danypops/lector";
|
|
2
2
|
import { lectorClient, workspaceForDirectory } from "./lector-client.ts";
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -10,11 +10,7 @@ import { lectorClient, workspaceForDirectory } from "./lector-client.ts";
|
|
|
10
10
|
* required, same "no implicit fallback" convention as find_symbols/search_code.
|
|
11
11
|
*/
|
|
12
12
|
export interface CrossWorkspaceSearchOperations {
|
|
13
|
-
findSymbols(
|
|
14
|
-
query: string,
|
|
15
|
-
directories: readonly string[],
|
|
16
|
-
timeoutMs?: number,
|
|
17
|
-
): Promise<readonly WorkspaceQueryOutcome<{ symbols: readonly WorkspaceSymbol[] }>[]>;
|
|
13
|
+
findSymbols(query: string, directories: readonly string[], timeoutMs?: number): Promise<readonly WorkspaceQueryOutcome<SymbolSearchResult>[]>;
|
|
18
14
|
searchText(
|
|
19
15
|
query: string,
|
|
20
16
|
directories: readonly string[],
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { TextSearchResult, WorkspaceQueryOutcome
|
|
1
|
+
import type { SymbolSearchResult, TextSearchResult, WorkspaceQueryOutcome } from "@danypops/lector";
|
|
2
2
|
import { keyHint } from "@earendil-works/pi-coding-agent";
|
|
3
3
|
import type { LectorTheme } from "./lector-tui-theme.ts";
|
|
4
4
|
|
|
@@ -17,7 +17,7 @@ function formatOutcomeHeader(outcome: WorkspaceQueryOutcome<unknown>, theme: Lec
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
export function formatFindSymbolsAcrossProjectsResult(
|
|
20
|
-
results: readonly WorkspaceQueryOutcome<
|
|
20
|
+
results: readonly WorkspaceQueryOutcome<SymbolSearchResult>[] | undefined,
|
|
21
21
|
expanded: boolean,
|
|
22
22
|
theme: LectorTheme,
|
|
23
23
|
): string {
|
|
@@ -26,6 +26,9 @@ export function formatFindSymbolsAcrossProjectsResult(
|
|
|
26
26
|
for (const outcome of results) {
|
|
27
27
|
lines.push(formatOutcomeHeader(outcome, theme));
|
|
28
28
|
if (outcome.status !== "ready") continue;
|
|
29
|
+
lines.push(
|
|
30
|
+
theme.fg("muted", ` ${outcome.result.provenance.fidelity} via ${outcome.result.provenance.backend}${outcome.result.truncated ? " (truncated)" : ""}`),
|
|
31
|
+
);
|
|
29
32
|
if (outcome.result.symbols.length === 0) {
|
|
30
33
|
lines.push(theme.fg("dim", " no symbols matched"));
|
|
31
34
|
continue;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { SymbolSearchResult } from "@danypops/lector";
|
|
2
2
|
import { lectorClient, withWorkspace, workspaceForDirectory } from "./lector-client.ts";
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -15,7 +15,7 @@ import { lectorClient, withWorkspace, workspaceForDirectory } from "./lector-cli
|
|
|
15
15
|
* is no implicit fallback anywhere in this module.
|
|
16
16
|
*/
|
|
17
17
|
export interface FindSymbolsOperations {
|
|
18
|
-
findSymbols(query: string, directory: string): Promise<
|
|
18
|
+
findSymbols(query: string, directory: string): Promise<SymbolSearchResult>;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
export function createLectorFindSymbolsOperations(): FindSymbolsOperations {
|
|
@@ -25,8 +25,7 @@ export function createLectorFindSymbolsOperations(): FindSymbolsOperations {
|
|
|
25
25
|
() => workspaceForDirectory(directory),
|
|
26
26
|
async ({ workspaceId }) => {
|
|
27
27
|
const client = await lectorClient();
|
|
28
|
-
|
|
29
|
-
return symbols;
|
|
28
|
+
return client.call("workspace.findSymbols", { workspaceId, query });
|
|
30
29
|
},
|
|
31
30
|
);
|
|
32
31
|
},
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { WorkspaceSymbol } from "@danypops/lector";
|
|
1
|
+
import type { SymbolSearchResult, WorkspaceSymbol } from "@danypops/lector";
|
|
2
2
|
import { keyHint } from "@earendil-works/pi-coding-agent";
|
|
3
3
|
import { colorForKind, formatLocation, type LectorTheme } from "./lector-tui-theme.ts";
|
|
4
4
|
|
|
@@ -32,14 +32,17 @@ function formatSymbolLine(symbol: WorkspaceSymbol, theme: FindSymbolsTheme, kind
|
|
|
32
32
|
return `${kind} ${name} ${location}`;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
export function formatFindSymbolsResult(
|
|
36
|
-
if (!
|
|
37
|
-
|
|
35
|
+
export function formatFindSymbolsResult(result: SymbolSearchResult | undefined, query: string, expanded: boolean, theme: FindSymbolsTheme): string {
|
|
36
|
+
if (!result) return theme.fg("dim", `No symbols found matching "${query}".`);
|
|
37
|
+
const { symbols, provenance, truncated } = result;
|
|
38
|
+
const source = `${provenance.fidelity} via ${provenance.backend}${truncated ? " (truncated)" : ""}`;
|
|
39
|
+
if (symbols.length === 0) {
|
|
40
|
+
return `${theme.fg("muted", source)}\n${theme.fg("dim", `No symbols found matching "${query}".`)}`;
|
|
38
41
|
}
|
|
39
42
|
|
|
40
43
|
const kindColumnWidth = Math.max(...symbols.map((symbol) => symbol.kind.length));
|
|
41
44
|
const displayCount = expanded ? symbols.length : Math.min(symbols.length, DEFAULT_VISIBLE_RESULTS);
|
|
42
|
-
const lines = [theme.fg("muted", `${symbols.length} symbol${symbols.length === 1 ? "" : "s"} matching "${query}":`)];
|
|
45
|
+
const lines = [theme.fg("muted", source), theme.fg("muted", `${symbols.length} symbol${symbols.length === 1 ? "" : "s"} matching "${query}":`)];
|
|
43
46
|
|
|
44
47
|
for (const symbol of symbols.slice(0, displayCount)) {
|
|
45
48
|
lines.push(formatSymbolLine(symbol, theme, kindColumnWidth));
|
package/extension/src/index.ts
CHANGED
|
@@ -8,15 +8,17 @@ import type {
|
|
|
8
8
|
GitStatusSummary,
|
|
9
9
|
Hover,
|
|
10
10
|
IncomingCall,
|
|
11
|
+
IntelligenceProvenance,
|
|
11
12
|
JobSnapshot,
|
|
12
13
|
OutgoingCall,
|
|
14
|
+
PackageSourceOperationResult,
|
|
13
15
|
PopulateSymbolGraphResult,
|
|
14
16
|
RepoFetchResult,
|
|
15
17
|
SymbolNode,
|
|
18
|
+
SymbolSearchResult,
|
|
16
19
|
TextSearchResult,
|
|
17
20
|
WorkspaceLocation,
|
|
18
21
|
WorkspaceQueryOutcome,
|
|
19
|
-
WorkspaceSymbol,
|
|
20
22
|
} from "@danypops/lector";
|
|
21
23
|
import { createEditToolDefinition, createReadToolDefinition, createWriteToolDefinition, type ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
22
24
|
import { Text } from "@earendil-works/pi-tui";
|
|
@@ -55,6 +57,8 @@ import { formatFindSymbolsCall, formatFindSymbolsResult } from "./find-symbols-r
|
|
|
55
57
|
import { createLectorGitOperations } from "./git-operations.ts";
|
|
56
58
|
import { formatGitDiffCall, formatGitDiffResult, formatGitLogCall, formatGitLogResult, formatGitStatusCall, formatGitStatusResult } from "./git-rendering.ts";
|
|
57
59
|
import { nearestGitRoot } from "./nearest-workspace-root.ts";
|
|
60
|
+
import { createLectorPackageSourceOperations } from "./package-source-operations.ts";
|
|
61
|
+
import { formatPackageSourceCall, formatPackageSourceResult } from "./package-source-rendering.ts";
|
|
58
62
|
import { createLectorReadOperations } from "./read-operations.ts";
|
|
59
63
|
import { createLectorRepoFetchOperations } from "./repo-fetch-operations.ts";
|
|
60
64
|
import { formatRepoFetchCall, formatRepoFetchResult } from "./repo-fetch-rendering.ts";
|
|
@@ -63,6 +67,14 @@ import { formatSearchCall, formatSearchResult } from "./search-rendering.ts";
|
|
|
63
67
|
import { type CachePresentationState, createWorkspaceCacheOperations, monitorWorkspaceCache } from "./workspace-cache-operations.ts";
|
|
64
68
|
import { createLectorWriteOperations } from "./write-operations.ts";
|
|
65
69
|
|
|
70
|
+
function describeIntelligenceSource(provenance: IntelligenceProvenance): string {
|
|
71
|
+
return `${provenance.fidelity} via ${provenance.backend}`;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function renderIntelligenceSource(body: string, provenance: IntelligenceProvenance | undefined, theme: { fg(color: "muted", text: string): string }): string {
|
|
75
|
+
return provenance ? `${theme.fg("muted", describeIntelligenceSource(provenance))}\n${body}` : body;
|
|
76
|
+
}
|
|
77
|
+
|
|
66
78
|
/**
|
|
67
79
|
* pi-lector -- the thin Pi host adapter for Lector. Overrides the built-in
|
|
68
80
|
* read/write/edit tools by name with Lector-backed Operations, so built-in
|
|
@@ -160,7 +172,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
160
172
|
"Search a workspace for functions, classes, interfaces, types, enums, and methods by name " +
|
|
161
173
|
"(case-insensitive substring match). Returns each match's kind and file location. `directory` " +
|
|
162
174
|
"selects which project to search -- pass the current working directory to search it, or any " +
|
|
163
|
-
"other project's directory to get code intelligence there without needing to be in it.",
|
|
175
|
+
"other project's directory to get code intelligence there without needing to be in it. Results identify semantic language-server authority or structural compiler/parser fallback.",
|
|
164
176
|
promptSnippet: "Search a workspace for a symbol (function, class, etc.) by name",
|
|
165
177
|
promptGuidelines: [
|
|
166
178
|
"Use find_symbols to locate where a function, class, interface, type, enum, or method is declared by name, instead of grepping for it.",
|
|
@@ -172,14 +184,16 @@ export default function (pi: ExtensionAPI) {
|
|
|
172
184
|
}),
|
|
173
185
|
async execute(_toolCallId, params) {
|
|
174
186
|
const directory = resolve(cwd, params.directory);
|
|
175
|
-
const
|
|
187
|
+
const result = await findSymbolsOperations.findSymbols(params.query, directory);
|
|
188
|
+
const { symbols, provenance, truncated } = result;
|
|
189
|
+
const source = `${provenance.fidelity} via ${provenance.backend}${truncated ? " (truncated)" : ""}`;
|
|
176
190
|
const text =
|
|
177
191
|
symbols.length === 0
|
|
178
|
-
?
|
|
179
|
-
: symbols
|
|
192
|
+
? `${source}\nNo symbols found matching "${params.query}".`
|
|
193
|
+
: `${source}\n${symbols
|
|
180
194
|
.map((symbol) => `${symbol.kind} ${symbol.name} -- ${symbol.location.path}:${symbol.location.line}:${symbol.location.character}`)
|
|
181
|
-
.join("\n")
|
|
182
|
-
return { content: [{ type: "text", text }], details:
|
|
195
|
+
.join("\n")}`;
|
|
196
|
+
return { content: [{ type: "text", text }], details: result };
|
|
183
197
|
},
|
|
184
198
|
renderCall(args, theme, context) {
|
|
185
199
|
const text = context.lastComponent instanceof Text ? context.lastComponent : new Text("", 0, 0);
|
|
@@ -197,10 +211,10 @@ export default function (pi: ExtensionAPI) {
|
|
|
197
211
|
.join("\n");
|
|
198
212
|
return new Text(theme.fg("error", errorText || "find_symbols failed"), 0, 0);
|
|
199
213
|
}
|
|
200
|
-
const details = result.details as
|
|
214
|
+
const details = result.details as SymbolSearchResult | undefined;
|
|
201
215
|
const query = typeof context.args?.query === "string" ? context.args.query : "";
|
|
202
216
|
const text = context.lastComponent instanceof Text ? context.lastComponent : new Text("", 0, 0);
|
|
203
|
-
text.setText(formatFindSymbolsResult(details
|
|
217
|
+
text.setText(formatFindSymbolsResult(details, query, expanded, theme));
|
|
204
218
|
return text;
|
|
205
219
|
},
|
|
206
220
|
});
|
|
@@ -221,9 +235,9 @@ export default function (pi: ExtensionAPI) {
|
|
|
221
235
|
parameters: Type.Object(positionParameters),
|
|
222
236
|
async execute(_toolCallId, params) {
|
|
223
237
|
const path = resolve(cwd, params.path);
|
|
224
|
-
const
|
|
225
|
-
const text = locations.length === 0 ? "No definition found." : locations.map((l) => `${l.path}:${l.line}:${l.character}`).join("\n");
|
|
226
|
-
return { content: [{ type: "text", text }], details
|
|
238
|
+
const details = await codeIntelligenceOperations.goToDefinition(path, params.line, params.character);
|
|
239
|
+
const text = details.locations.length === 0 ? "No definition found." : details.locations.map((l) => `${l.path}:${l.line}:${l.character}`).join("\n");
|
|
240
|
+
return { content: [{ type: "text", text: `${describeIntelligenceSource(details.provenance)}\n${text}` }], details };
|
|
227
241
|
},
|
|
228
242
|
renderCall(args, theme, context) {
|
|
229
243
|
const text = context.lastComponent instanceof Text ? context.lastComponent : new Text("", 0, 0);
|
|
@@ -239,9 +253,9 @@ export default function (pi: ExtensionAPI) {
|
|
|
239
253
|
.join("\n");
|
|
240
254
|
return new Text(theme.fg("error", errorText || "go_to_definition failed"), 0, 0);
|
|
241
255
|
}
|
|
242
|
-
const details = result.details as { locations?: readonly WorkspaceLocation[] } | undefined;
|
|
256
|
+
const details = result.details as { locations?: readonly WorkspaceLocation[]; provenance?: IntelligenceProvenance } | undefined;
|
|
243
257
|
const text = context.lastComponent instanceof Text ? context.lastComponent : new Text("", 0, 0);
|
|
244
|
-
text.setText(formatGoToDefinitionResult(details?.locations, expanded, theme));
|
|
258
|
+
text.setText(renderIntelligenceSource(formatGoToDefinitionResult(details?.locations, expanded, theme), details?.provenance, theme));
|
|
245
259
|
return text;
|
|
246
260
|
},
|
|
247
261
|
});
|
|
@@ -258,9 +272,10 @@ export default function (pi: ExtensionAPI) {
|
|
|
258
272
|
parameters: Type.Object(positionParameters),
|
|
259
273
|
async execute(_toolCallId, params) {
|
|
260
274
|
const path = resolve(cwd, params.path);
|
|
261
|
-
const
|
|
262
|
-
const text =
|
|
263
|
-
|
|
275
|
+
const details = await codeIntelligenceOperations.goToImplementation(path, params.line, params.character);
|
|
276
|
+
const text =
|
|
277
|
+
details.locations.length === 0 ? "No implementation found." : details.locations.map((l) => `${l.path}:${l.line}:${l.character}`).join("\n");
|
|
278
|
+
return { content: [{ type: "text", text: `${describeIntelligenceSource(details.provenance)}\n${text}` }], details };
|
|
264
279
|
},
|
|
265
280
|
renderCall(args, theme, context) {
|
|
266
281
|
const text = context.lastComponent instanceof Text ? context.lastComponent : new Text("", 0, 0);
|
|
@@ -276,9 +291,9 @@ export default function (pi: ExtensionAPI) {
|
|
|
276
291
|
.join("\n");
|
|
277
292
|
return new Text(theme.fg("error", errorText || "go_to_implementation failed"), 0, 0);
|
|
278
293
|
}
|
|
279
|
-
const details = result.details as { locations?: readonly WorkspaceLocation[] } | undefined;
|
|
294
|
+
const details = result.details as { locations?: readonly WorkspaceLocation[]; provenance?: IntelligenceProvenance } | undefined;
|
|
280
295
|
const text = context.lastComponent instanceof Text ? context.lastComponent : new Text("", 0, 0);
|
|
281
|
-
text.setText(formatGoToImplementationResult(details?.locations, expanded, theme));
|
|
296
|
+
text.setText(renderIntelligenceSource(formatGoToImplementationResult(details?.locations, expanded, theme), details?.provenance, theme));
|
|
282
297
|
return text;
|
|
283
298
|
},
|
|
284
299
|
});
|
|
@@ -298,9 +313,9 @@ export default function (pi: ExtensionAPI) {
|
|
|
298
313
|
}),
|
|
299
314
|
async execute(_toolCallId, params) {
|
|
300
315
|
const path = resolve(cwd, params.path);
|
|
301
|
-
const
|
|
302
|
-
const text = locations.length === 0 ? "No references found." : locations.map((l) => `${l.path}:${l.line}:${l.character}`).join("\n");
|
|
303
|
-
return { content: [{ type: "text", text }], details
|
|
316
|
+
const details = await codeIntelligenceOperations.findReferences(path, params.line, params.character, params.includeDeclaration);
|
|
317
|
+
const text = details.locations.length === 0 ? "No references found." : details.locations.map((l) => `${l.path}:${l.line}:${l.character}`).join("\n");
|
|
318
|
+
return { content: [{ type: "text", text: `${describeIntelligenceSource(details.provenance)}\n${text}` }], details };
|
|
304
319
|
},
|
|
305
320
|
renderCall(args, theme, context) {
|
|
306
321
|
const text = context.lastComponent instanceof Text ? context.lastComponent : new Text("", 0, 0);
|
|
@@ -316,9 +331,9 @@ export default function (pi: ExtensionAPI) {
|
|
|
316
331
|
.join("\n");
|
|
317
332
|
return new Text(theme.fg("error", errorText || "find_references failed"), 0, 0);
|
|
318
333
|
}
|
|
319
|
-
const details = result.details as { locations?: readonly WorkspaceLocation[] } | undefined;
|
|
334
|
+
const details = result.details as { locations?: readonly WorkspaceLocation[]; provenance?: IntelligenceProvenance } | undefined;
|
|
320
335
|
const text = context.lastComponent instanceof Text ? context.lastComponent : new Text("", 0, 0);
|
|
321
|
-
text.setText(formatFindReferencesResult(details?.locations, expanded, theme));
|
|
336
|
+
text.setText(renderIntelligenceSource(formatFindReferencesResult(details?.locations, expanded, theme), details?.provenance, theme));
|
|
322
337
|
return text;
|
|
323
338
|
},
|
|
324
339
|
});
|
|
@@ -334,8 +349,13 @@ export default function (pi: ExtensionAPI) {
|
|
|
334
349
|
parameters: Type.Object(positionParameters),
|
|
335
350
|
async execute(_toolCallId, params) {
|
|
336
351
|
const path = resolve(cwd, params.path);
|
|
337
|
-
const
|
|
338
|
-
return {
|
|
352
|
+
const details = await codeIntelligenceOperations.hover(path, params.line, params.character);
|
|
353
|
+
return {
|
|
354
|
+
content: [
|
|
355
|
+
{ type: "text", text: `${describeIntelligenceSource(details.provenance)}\n${details.hover?.contents ?? "No hover information available."}` },
|
|
356
|
+
],
|
|
357
|
+
details,
|
|
358
|
+
};
|
|
339
359
|
},
|
|
340
360
|
renderCall(args, theme, context) {
|
|
341
361
|
const text = context.lastComponent instanceof Text ? context.lastComponent : new Text("", 0, 0);
|
|
@@ -351,9 +371,9 @@ export default function (pi: ExtensionAPI) {
|
|
|
351
371
|
.join("\n");
|
|
352
372
|
return new Text(theme.fg("error", errorText || "hover failed"), 0, 0);
|
|
353
373
|
}
|
|
354
|
-
const details = result.details as { hover?: Hover } | undefined;
|
|
374
|
+
const details = result.details as { hover?: Hover; provenance?: IntelligenceProvenance } | undefined;
|
|
355
375
|
const text = context.lastComponent instanceof Text ? context.lastComponent : new Text("", 0, 0);
|
|
356
|
-
text.setText(formatHoverResult(details?.hover, expanded, theme));
|
|
376
|
+
text.setText(renderIntelligenceSource(formatHoverResult(details?.hover, expanded, theme), details?.provenance, theme));
|
|
357
377
|
return text;
|
|
358
378
|
},
|
|
359
379
|
});
|
|
@@ -367,9 +387,9 @@ export default function (pi: ExtensionAPI) {
|
|
|
367
387
|
parameters: Type.Object({ path: Type.String({ description: "Absolute or cwd-relative path to the file" }) }),
|
|
368
388
|
async execute(_toolCallId, params) {
|
|
369
389
|
const path = resolve(cwd, params.path);
|
|
370
|
-
const
|
|
371
|
-
const text = symbols.length === 0 ? "No symbols found." : symbols.map((s) => `${s.kind} ${s.name}`).join("\n");
|
|
372
|
-
return { content: [{ type: "text", text }], details
|
|
390
|
+
const details = await codeIntelligenceOperations.documentSymbols(path);
|
|
391
|
+
const text = details.symbols.length === 0 ? "No symbols found." : details.symbols.map((s) => `${s.kind} ${s.name}`).join("\n");
|
|
392
|
+
return { content: [{ type: "text", text: `${describeIntelligenceSource(details.provenance)}\n${text}` }], details };
|
|
373
393
|
},
|
|
374
394
|
renderCall(args, theme, context) {
|
|
375
395
|
const text = context.lastComponent instanceof Text ? context.lastComponent : new Text("", 0, 0);
|
|
@@ -385,9 +405,9 @@ export default function (pi: ExtensionAPI) {
|
|
|
385
405
|
.join("\n");
|
|
386
406
|
return new Text(theme.fg("error", errorText || "document_symbols failed"), 0, 0);
|
|
387
407
|
}
|
|
388
|
-
const details = result.details as { symbols?: readonly DocumentSymbolEntry[] } | undefined;
|
|
408
|
+
const details = result.details as { symbols?: readonly DocumentSymbolEntry[]; provenance?: IntelligenceProvenance } | undefined;
|
|
389
409
|
const text = context.lastComponent instanceof Text ? context.lastComponent : new Text("", 0, 0);
|
|
390
|
-
text.setText(formatDocumentSymbolsResult(details?.symbols, expanded, theme));
|
|
410
|
+
text.setText(renderIntelligenceSource(formatDocumentSymbolsResult(details?.symbols, expanded, theme), details?.provenance, theme));
|
|
391
411
|
return text;
|
|
392
412
|
},
|
|
393
413
|
});
|
|
@@ -401,12 +421,12 @@ export default function (pi: ExtensionAPI) {
|
|
|
401
421
|
parameters: Type.Object({ path: Type.String({ description: "Absolute or cwd-relative path to the file" }) }),
|
|
402
422
|
async execute(_toolCallId, params) {
|
|
403
423
|
const path = resolve(cwd, params.path);
|
|
404
|
-
const
|
|
424
|
+
const details = await codeIntelligenceOperations.diagnostics(path);
|
|
405
425
|
const text =
|
|
406
|
-
diagnostics.length === 0
|
|
426
|
+
details.diagnostics.length === 0
|
|
407
427
|
? "No diagnostics."
|
|
408
|
-
: diagnostics.map((d) => `${d.severity} ${d.range.path}:${d.range.start.line}:${d.range.start.character} -- ${d.message}`).join("\n");
|
|
409
|
-
return { content: [{ type: "text", text }], details
|
|
428
|
+
: details.diagnostics.map((d) => `${d.severity} ${d.range.path}:${d.range.start.line}:${d.range.start.character} -- ${d.message}`).join("\n");
|
|
429
|
+
return { content: [{ type: "text", text: `${describeIntelligenceSource(details.provenance)}\n${text}` }], details };
|
|
410
430
|
},
|
|
411
431
|
renderCall(args, theme, context) {
|
|
412
432
|
const text = context.lastComponent instanceof Text ? context.lastComponent : new Text("", 0, 0);
|
|
@@ -422,9 +442,9 @@ export default function (pi: ExtensionAPI) {
|
|
|
422
442
|
.join("\n");
|
|
423
443
|
return new Text(theme.fg("error", errorText || "diagnostics failed"), 0, 0);
|
|
424
444
|
}
|
|
425
|
-
const details = result.details as { diagnostics?: readonly Diagnostic[] } | undefined;
|
|
445
|
+
const details = result.details as { diagnostics?: readonly Diagnostic[]; provenance?: IntelligenceProvenance } | undefined;
|
|
426
446
|
const text = context.lastComponent instanceof Text ? context.lastComponent : new Text("", 0, 0);
|
|
427
|
-
text.setText(formatDiagnosticsResult(details?.diagnostics, expanded, theme));
|
|
447
|
+
text.setText(renderIntelligenceSource(formatDiagnosticsResult(details?.diagnostics, expanded, theme), details?.provenance, theme));
|
|
428
448
|
return text;
|
|
429
449
|
},
|
|
430
450
|
});
|
|
@@ -440,12 +460,12 @@ export default function (pi: ExtensionAPI) {
|
|
|
440
460
|
parameters: Type.Object(positionParameters),
|
|
441
461
|
async execute(_toolCallId, params) {
|
|
442
462
|
const path = resolve(cwd, params.path);
|
|
443
|
-
const
|
|
463
|
+
const details = await codeIntelligenceOperations.prepareCallHierarchy(path, params.line, params.character);
|
|
444
464
|
const text =
|
|
445
|
-
items.length === 0
|
|
465
|
+
details.items.length === 0
|
|
446
466
|
? "No call-hierarchy root at this position."
|
|
447
|
-
: items.map((i) => `${i.kind} ${i.name} -- ${i.location.path}:${i.location.line}:${i.location.character}`).join("\n");
|
|
448
|
-
return { content: [{ type: "text", text }], details
|
|
467
|
+
: details.items.map((i) => `${i.kind} ${i.name} -- ${i.location.path}:${i.location.line}:${i.location.character}`).join("\n");
|
|
468
|
+
return { content: [{ type: "text", text: `${describeIntelligenceSource(details.provenance)}\n${text}` }], details };
|
|
449
469
|
},
|
|
450
470
|
renderCall(args, theme, context) {
|
|
451
471
|
const text = context.lastComponent instanceof Text ? context.lastComponent : new Text("", 0, 0);
|
|
@@ -461,9 +481,9 @@ export default function (pi: ExtensionAPI) {
|
|
|
461
481
|
.join("\n");
|
|
462
482
|
return new Text(theme.fg("error", errorText || "prepare_call_hierarchy failed"), 0, 0);
|
|
463
483
|
}
|
|
464
|
-
const details = result.details as { items?: readonly CallHierarchyEntry[] } | undefined;
|
|
484
|
+
const details = result.details as { items?: readonly CallHierarchyEntry[]; provenance?: IntelligenceProvenance } | undefined;
|
|
465
485
|
const text = context.lastComponent instanceof Text ? context.lastComponent : new Text("", 0, 0);
|
|
466
|
-
text.setText(formatPrepareCallHierarchyResult(details?.items, theme));
|
|
486
|
+
text.setText(renderIntelligenceSource(formatPrepareCallHierarchyResult(details?.items, theme), details?.provenance, theme));
|
|
467
487
|
return text;
|
|
468
488
|
},
|
|
469
489
|
});
|
|
@@ -479,12 +499,14 @@ export default function (pi: ExtensionAPI) {
|
|
|
479
499
|
parameters: Type.Object(positionParameters),
|
|
480
500
|
async execute(_toolCallId, params) {
|
|
481
501
|
const path = resolve(cwd, params.path);
|
|
482
|
-
const
|
|
502
|
+
const details = await codeIntelligenceOperations.incomingCalls(path, params.line, params.character);
|
|
483
503
|
const text =
|
|
484
|
-
calls.length === 0
|
|
504
|
+
details.calls.length === 0
|
|
485
505
|
? "No incoming calls found."
|
|
486
|
-
: calls
|
|
487
|
-
|
|
506
|
+
: details.calls
|
|
507
|
+
.map((c) => `${c.from.kind} ${c.from.name} -- ${c.from.location.path}:${c.from.location.line}:${c.from.location.character}`)
|
|
508
|
+
.join("\n");
|
|
509
|
+
return { content: [{ type: "text", text: `${describeIntelligenceSource(details.provenance)}\n${text}` }], details };
|
|
488
510
|
},
|
|
489
511
|
renderCall(args, theme, context) {
|
|
490
512
|
const text = context.lastComponent instanceof Text ? context.lastComponent : new Text("", 0, 0);
|
|
@@ -500,9 +522,9 @@ export default function (pi: ExtensionAPI) {
|
|
|
500
522
|
.join("\n");
|
|
501
523
|
return new Text(theme.fg("error", errorText || "incoming_calls failed"), 0, 0);
|
|
502
524
|
}
|
|
503
|
-
const details = result.details as { calls?: readonly IncomingCall[] } | undefined;
|
|
525
|
+
const details = result.details as { calls?: readonly IncomingCall[]; provenance?: IntelligenceProvenance } | undefined;
|
|
504
526
|
const text = context.lastComponent instanceof Text ? context.lastComponent : new Text("", 0, 0);
|
|
505
|
-
text.setText(formatIncomingCallsResult(details?.calls, expanded, theme));
|
|
527
|
+
text.setText(renderIntelligenceSource(formatIncomingCallsResult(details?.calls, expanded, theme), details?.provenance, theme));
|
|
506
528
|
return text;
|
|
507
529
|
},
|
|
508
530
|
});
|
|
@@ -516,12 +538,12 @@ export default function (pi: ExtensionAPI) {
|
|
|
516
538
|
parameters: Type.Object(positionParameters),
|
|
517
539
|
async execute(_toolCallId, params) {
|
|
518
540
|
const path = resolve(cwd, params.path);
|
|
519
|
-
const
|
|
541
|
+
const details = await codeIntelligenceOperations.outgoingCalls(path, params.line, params.character);
|
|
520
542
|
const text =
|
|
521
|
-
calls.length === 0
|
|
543
|
+
details.calls.length === 0
|
|
522
544
|
? "No outgoing calls found."
|
|
523
|
-
: calls.map((c) => `${c.to.kind} ${c.to.name} -- ${c.to.location.path}:${c.to.location.line}:${c.to.location.character}`).join("\n");
|
|
524
|
-
return { content: [{ type: "text", text }], details
|
|
545
|
+
: details.calls.map((c) => `${c.to.kind} ${c.to.name} -- ${c.to.location.path}:${c.to.location.line}:${c.to.location.character}`).join("\n");
|
|
546
|
+
return { content: [{ type: "text", text: `${describeIntelligenceSource(details.provenance)}\n${text}` }], details };
|
|
525
547
|
},
|
|
526
548
|
renderCall(args, theme, context) {
|
|
527
549
|
const text = context.lastComponent instanceof Text ? context.lastComponent : new Text("", 0, 0);
|
|
@@ -537,9 +559,9 @@ export default function (pi: ExtensionAPI) {
|
|
|
537
559
|
.join("\n");
|
|
538
560
|
return new Text(theme.fg("error", errorText || "outgoing_calls failed"), 0, 0);
|
|
539
561
|
}
|
|
540
|
-
const details = result.details as { calls?: readonly OutgoingCall[] } | undefined;
|
|
562
|
+
const details = result.details as { calls?: readonly OutgoingCall[]; provenance?: IntelligenceProvenance } | undefined;
|
|
541
563
|
const text = context.lastComponent instanceof Text ? context.lastComponent : new Text("", 0, 0);
|
|
542
|
-
text.setText(formatOutgoingCallsResult(details?.calls, expanded, theme));
|
|
564
|
+
text.setText(renderIntelligenceSource(formatOutgoingCallsResult(details?.calls, expanded, theme), details?.provenance, theme));
|
|
543
565
|
return text;
|
|
544
566
|
},
|
|
545
567
|
});
|
|
@@ -825,6 +847,45 @@ export default function (pi: ExtensionAPI) {
|
|
|
825
847
|
},
|
|
826
848
|
});
|
|
827
849
|
|
|
850
|
+
const packageSourceOperations = createLectorPackageSourceOperations();
|
|
851
|
+
pi.registerTool({
|
|
852
|
+
name: "package_source",
|
|
853
|
+
label: "Package Source",
|
|
854
|
+
description:
|
|
855
|
+
"Resolve an installed npm package to verified exact repository source. Uses the project's lockfile, bounded registry metadata, and an exact Git ref/commit; registers verified source as a read-only workspace for the other Lector tools.",
|
|
856
|
+
promptSnippet: "Resolve an installed npm package to exact read-only source",
|
|
857
|
+
parameters: Type.Object({
|
|
858
|
+
directory: Type.String({ description: "Project directory containing the npm-family lockfile" }),
|
|
859
|
+
name: Type.String({ description: "Installed package name, including scope when present" }),
|
|
860
|
+
version: Type.Optional(Type.String({ description: "Exact installed version; required when the lockfile contains several versions" })),
|
|
861
|
+
registry: Type.Optional(Type.String({ description: "npm registry URL; defaults to the public npm registry" })),
|
|
862
|
+
}),
|
|
863
|
+
async execute(_toolCallId, params) {
|
|
864
|
+
const directory = resolve(cwd, params.directory);
|
|
865
|
+
const result = await packageSourceOperations.resolve(directory, params.name, params.version ?? null, params.registry ?? null);
|
|
866
|
+
return { content: [{ type: "text", text: JSON.stringify(result) }], details: { result } };
|
|
867
|
+
},
|
|
868
|
+
renderCall(args, theme, context) {
|
|
869
|
+
const text = context.lastComponent instanceof Text ? context.lastComponent : new Text("", 0, 0);
|
|
870
|
+
text.setText(formatPackageSourceCall(args as { directory?: unknown; name?: unknown; version?: unknown }, theme));
|
|
871
|
+
return text;
|
|
872
|
+
},
|
|
873
|
+
renderResult(result, { expanded, isPartial }, theme, context) {
|
|
874
|
+
if (isPartial) return new Text(theme.fg("warning", "Resolving package source..."), 0, 0);
|
|
875
|
+
if (context.isError) {
|
|
876
|
+
const errorText = result.content
|
|
877
|
+
.filter((block) => block.type === "text")
|
|
878
|
+
.map((block) => block.text)
|
|
879
|
+
.join("\n");
|
|
880
|
+
return new Text(theme.fg("error", errorText || "package_source failed"), 0, 0);
|
|
881
|
+
}
|
|
882
|
+
const details = result.details as { result?: PackageSourceOperationResult } | undefined;
|
|
883
|
+
const text = context.lastComponent instanceof Text ? context.lastComponent : new Text("", 0, 0);
|
|
884
|
+
text.setText(formatPackageSourceResult(details?.result, expanded, theme));
|
|
885
|
+
return text;
|
|
886
|
+
},
|
|
887
|
+
});
|
|
888
|
+
|
|
828
889
|
const repoFetchOperations = createLectorRepoFetchOperations();
|
|
829
890
|
pi.registerTool({
|
|
830
891
|
name: "repo_fetch",
|
|
@@ -894,7 +955,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
894
955
|
.join("\n");
|
|
895
956
|
return new Text(theme.fg("error", errorText || "find_symbols_across_projects failed"), 0, 0);
|
|
896
957
|
}
|
|
897
|
-
const details = result.details as { results?: readonly WorkspaceQueryOutcome<
|
|
958
|
+
const details = result.details as { results?: readonly WorkspaceQueryOutcome<SymbolSearchResult>[] } | undefined;
|
|
898
959
|
const text = context.lastComponent instanceof Text ? context.lastComponent : new Text("", 0, 0);
|
|
899
960
|
text.setText(formatFindSymbolsAcrossProjectsResult(details?.results, expanded, theme));
|
|
900
961
|
return text;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { DEFAULT_PACKAGE_SOURCE_BOUNDS, type PackageSourceOperationResult } from "@danypops/lector";
|
|
2
|
+
import { lectorClient } from "./lector-client.ts";
|
|
3
|
+
|
|
4
|
+
export interface PackageSourceOperations {
|
|
5
|
+
resolve(directory: string, name: string, requestedVersion: string | null, registry: string | null): Promise<PackageSourceOperationResult>;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function createLectorPackageSourceOperations(): PackageSourceOperations {
|
|
9
|
+
return {
|
|
10
|
+
async resolve(directory, name, requestedVersion, registry) {
|
|
11
|
+
const client = await lectorClient();
|
|
12
|
+
return client.call("package.resolveSource", {
|
|
13
|
+
request: {
|
|
14
|
+
projectRoot: directory,
|
|
15
|
+
coordinate: { ecosystem: "npm", registry, name, requestedVersion },
|
|
16
|
+
},
|
|
17
|
+
bounds: DEFAULT_PACKAGE_SOURCE_BOUNDS,
|
|
18
|
+
});
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { PackageSourceOperationResult } from "@danypops/lector";
|
|
2
|
+
import type { LectorTheme } from "./lector-tui-theme.ts";
|
|
3
|
+
|
|
4
|
+
const DEFAULT_VISIBLE_CANDIDATES = 5;
|
|
5
|
+
|
|
6
|
+
export function formatPackageSourceCall(args: { directory?: unknown; name?: unknown; version?: unknown }, theme: LectorTheme): string {
|
|
7
|
+
const name = typeof args.name === "string" ? args.name : "";
|
|
8
|
+
const version = typeof args.version === "string" ? `@${args.version}` : "";
|
|
9
|
+
const directory = typeof args.directory === "string" ? args.directory : "";
|
|
10
|
+
return `${theme.fg("toolTitle", theme.bold("package_source"))} ${theme.fg("accent", `${name}${version}`)} ${theme.fg("dim", directory)}`.trim();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function formatPackageSourceResult(result: PackageSourceOperationResult | undefined, expanded: boolean, theme: LectorTheme): string {
|
|
14
|
+
if (!result) return theme.fg("dim", "No package-source result.");
|
|
15
|
+
const { outcome } = result;
|
|
16
|
+
if (outcome.status === "verified") {
|
|
17
|
+
return [
|
|
18
|
+
`${theme.fg("accent", result.workspaceId ?? "unregistered")} ${theme.fg("success", `${outcome.coordinate.name}@${outcome.coordinate.resolvedVersion}`)}`,
|
|
19
|
+
`${outcome.workspace.cachePath}`,
|
|
20
|
+
`${outcome.repository.url ?? "local source"}@${outcome.repository.resolvedRef ?? "local"} ${outcome.repository.commit ?? outcome.verification.integrity}`,
|
|
21
|
+
].join("\n");
|
|
22
|
+
}
|
|
23
|
+
if (outcome.status === "ambiguous") {
|
|
24
|
+
const visible = expanded ? outcome.candidates : outcome.candidates.slice(0, DEFAULT_VISIBLE_CANDIDATES);
|
|
25
|
+
const lines = [theme.fg("warning", `Ambiguous package source (${outcome.code})`)];
|
|
26
|
+
for (const candidate of visible) lines.push(`${candidate.version} -- ${candidate.source}`);
|
|
27
|
+
const hidden = outcome.candidates.length - visible.length;
|
|
28
|
+
if (hidden > 0 || outcome.truncated) lines.push(theme.fg("dim", outcome.truncated ? "More candidates were truncated by the daemon." : `… ${hidden} more`));
|
|
29
|
+
return lines.join("\n");
|
|
30
|
+
}
|
|
31
|
+
if (outcome.status === "unauthenticated") {
|
|
32
|
+
return theme.fg("warning", `Authentication required (${outcome.code}): configure ${outcome.requiredCredentialNames.join(", ")}`);
|
|
33
|
+
}
|
|
34
|
+
if (outcome.status === "oversized") return theme.fg("warning", `Source resolution exceeded ${outcome.resource} limit ${outcome.limit}.`);
|
|
35
|
+
if (outcome.status === "mismatched") return theme.fg("error", `Source mismatch (${outcome.code}): expected ${outcome.expected}, got ${outcome.actual}.`);
|
|
36
|
+
return theme.fg("warning", `Source unavailable (${outcome.code}).`);
|
|
37
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@danypops/pi-lector",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.8",
|
|
4
4
|
"description": "Pi host adapter for Lector: overrides read/write/edit with a daemon-backed, hash-guarded filesystem",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"typebox": "*"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@danypops/lector": "^0.1.
|
|
21
|
+
"@danypops/lector": "^0.1.8"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"@earendil-works/pi-ai": "^0.81.1",
|