@danypops/pi-lector 0.5.0 → 0.6.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.
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { GithubRepoSearchResult, NpmPackageCandidate, SourcegraphCodeCandidate } from "@danypops/lector";
|
|
2
|
+
import { lectorClient } from "./lector-client.ts";
|
|
3
|
+
|
|
4
|
+
/** Thin wrapper over search.githubRepos/search.npmPackages/search.sourcegraphCode -- explicit-query discovery inputs shaped for repo_cache/package_source, never open-ended discovery/trending. */
|
|
5
|
+
export interface ExternalSearchOperations {
|
|
6
|
+
githubRepos(query: string, maxResults: number): Promise<GithubRepoSearchResult>;
|
|
7
|
+
npmPackages(query: string, maxResults: number): Promise<{ candidates: readonly NpmPackageCandidate[] }>;
|
|
8
|
+
sourcegraphCode(query: string, maxResults: number): Promise<{ candidates: readonly SourcegraphCodeCandidate[] }>;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function createExternalSearchOperations(): ExternalSearchOperations {
|
|
12
|
+
return {
|
|
13
|
+
async githubRepos(query, maxResults) {
|
|
14
|
+
const client = await lectorClient();
|
|
15
|
+
return client.call("search.githubRepos", { query, maxResults });
|
|
16
|
+
},
|
|
17
|
+
async npmPackages(query, maxResults) {
|
|
18
|
+
const client = await lectorClient();
|
|
19
|
+
return client.call("search.npmPackages", { query, maxResults });
|
|
20
|
+
},
|
|
21
|
+
async sourcegraphCode(query, maxResults) {
|
|
22
|
+
const client = await lectorClient();
|
|
23
|
+
return client.call("search.sourcegraphCode", { query, maxResults });
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { GithubRepoSearchResult, NpmPackageCandidate, SourcegraphCodeCandidate } from "@danypops/lector";
|
|
2
|
+
import type { LectorTheme } from "./lector-tui-theme.ts";
|
|
3
|
+
|
|
4
|
+
type ExternalSearchAction = "github_repos" | "npm_packages" | "sourcegraph_code";
|
|
5
|
+
|
|
6
|
+
export function formatExternalSearchCall(action: ExternalSearchAction, args: { query?: unknown }, theme: LectorTheme): string {
|
|
7
|
+
const label = theme.fg("toolTitle", theme.bold("external_search"));
|
|
8
|
+
const query = typeof args.query === "string" ? args.query : "";
|
|
9
|
+
return `${label} ${theme.fg("accent", action)} ${theme.fg("dim", query)}`;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function formatGithubRepoSearchResult(result: GithubRepoSearchResult | undefined, theme: LectorTheme): string {
|
|
13
|
+
if (!result) return theme.fg("dim", "No result.");
|
|
14
|
+
const count = result.candidates.length;
|
|
15
|
+
const summary = count === 0 ? theme.fg("dim", "no repositories matched") : theme.fg("success", `${count} repositor${count === 1 ? "y" : "ies"}`);
|
|
16
|
+
return result.authenticated ? summary : `${summary} ${theme.fg("warning", "(unauthenticated -- lower rate limit)")}`;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function formatNpmPackageSearchResult(result: { candidates: readonly NpmPackageCandidate[] } | undefined, theme: LectorTheme): string {
|
|
20
|
+
const count = result?.candidates.length ?? 0;
|
|
21
|
+
return count === 0 ? theme.fg("dim", "no packages matched") : theme.fg("success", `${count} package${count === 1 ? "" : "s"}`);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function formatSourcegraphCodeSearchResult(result: { candidates: readonly SourcegraphCodeCandidate[] } | undefined, theme: LectorTheme): string {
|
|
25
|
+
const count = result?.candidates.length ?? 0;
|
|
26
|
+
return count === 0 ? theme.fg("dim", "no code matches") : theme.fg("success", `${count} match${count === 1 ? "" : "es"}`);
|
|
27
|
+
}
|
package/extension/src/index.ts
CHANGED
|
@@ -6,16 +6,19 @@ import type {
|
|
|
6
6
|
DocumentSymbolEntry,
|
|
7
7
|
EditOutcome,
|
|
8
8
|
FindFilesResult,
|
|
9
|
+
GithubRepoSearchResult,
|
|
9
10
|
Hover,
|
|
10
11
|
IntelligenceProvenance,
|
|
11
12
|
JobSnapshot,
|
|
12
13
|
LineEdit,
|
|
13
14
|
LineEditOutcome,
|
|
14
15
|
MutationHistoryEntry,
|
|
16
|
+
NpmPackageCandidate,
|
|
15
17
|
OperationOutputs,
|
|
16
18
|
PackageSourceOperationResult,
|
|
17
19
|
PopulateSymbolGraphResult,
|
|
18
20
|
RepoFetchResult,
|
|
21
|
+
SourcegraphCodeCandidate,
|
|
19
22
|
SymbolAnnotation,
|
|
20
23
|
SymbolNode,
|
|
21
24
|
SymbolSearchResult,
|
|
@@ -63,6 +66,13 @@ import {
|
|
|
63
66
|
import { createLectorCrossWorkspaceSearchOperations } from "./cross-workspace-search-operations.ts";
|
|
64
67
|
import { formatCrossWorkspaceCall, formatFindSymbolsAcrossProjectsResult, formatSearchTextAcrossProjectsResult } from "./cross-workspace-search-rendering.ts";
|
|
65
68
|
import { createLectorEditOperations } from "./edit-operations.ts";
|
|
69
|
+
import { createExternalSearchOperations } from "./external-search-operations.ts";
|
|
70
|
+
import {
|
|
71
|
+
formatExternalSearchCall,
|
|
72
|
+
formatGithubRepoSearchResult,
|
|
73
|
+
formatNpmPackageSearchResult,
|
|
74
|
+
formatSourcegraphCodeSearchResult,
|
|
75
|
+
} from "./external-search-rendering.ts";
|
|
66
76
|
import { createLectorFindFilesOperations } from "./find-files-operations.ts";
|
|
67
77
|
import { formatFindFilesCall, formatFindFilesResult } from "./find-files-rendering.ts";
|
|
68
78
|
import { createLectorFindSymbolsOperations } from "./find-symbols-operations.ts";
|
|
@@ -1520,6 +1530,62 @@ export default function (pi: ExtensionAPI) {
|
|
|
1520
1530
|
},
|
|
1521
1531
|
});
|
|
1522
1532
|
|
|
1533
|
+
type ExternalSearchToolDetails =
|
|
1534
|
+
| { readonly action: "github_repos"; readonly result: GithubRepoSearchResult }
|
|
1535
|
+
| { readonly action: "npm_packages"; readonly result: { candidates: readonly NpmPackageCandidate[] } }
|
|
1536
|
+
| { readonly action: "sourcegraph_code"; readonly result: { candidates: readonly SourcegraphCodeCandidate[] } };
|
|
1537
|
+
|
|
1538
|
+
const externalSearchOperations = createExternalSearchOperations();
|
|
1539
|
+
pi.registerTool({
|
|
1540
|
+
name: "external_search",
|
|
1541
|
+
label: "External Search",
|
|
1542
|
+
description:
|
|
1543
|
+
"Finds real prior art before writing new code -- explicit-query search only, never open-ended discovery/trending. action=github_repos searches GitHub repositories by name/description/topic (GitHub's own relevance ranking); candidates are shaped as direct repo_cache(action=fetch) inputs (host/owner/repo). Unauthenticated is rate-limited to 10 req/min; configure GITHUB_TOKEN on the daemon host for 30/min. action=npm_packages searches the public npm registry; candidates are shaped as direct package_source inputs (name, plus the version already returned). action=sourcegraph_code searches code content across public GitHub via sourcegraph.com -- \"which repos actually contain code matching X\", a genuinely different mode than the other two (which search names/metadata, not file contents); each candidate's repository field feeds repo_cache(action=fetch) once split on '/'. Every source is cached for a short TTL -- an identical query moments apart is served from cache, not re-fetched.",
|
|
1544
|
+
promptSnippet: "Search GitHub repos, npm packages, or code content for prior art",
|
|
1545
|
+
parameters: Type.Object({
|
|
1546
|
+
action: Type.Union([Type.Literal("github_repos"), Type.Literal("npm_packages"), Type.Literal("sourcegraph_code")]),
|
|
1547
|
+
query: Type.String({
|
|
1548
|
+
description: "The search query -- repo/package name or description keywords for github_repos/npm_packages, code content for sourcegraph_code",
|
|
1549
|
+
}),
|
|
1550
|
+
maxResults: Type.Optional(Type.Number({ description: "Maximum candidates to return (default 20)" })),
|
|
1551
|
+
}),
|
|
1552
|
+
async execute(_toolCallId, params): Promise<AgentToolResult<ExternalSearchToolDetails>> {
|
|
1553
|
+
const maxResults = params.maxResults ?? 20;
|
|
1554
|
+
if (params.action === "github_repos") {
|
|
1555
|
+
const result = await externalSearchOperations.githubRepos(params.query, maxResults);
|
|
1556
|
+
return { content: [{ type: "text", text: JSON.stringify(result) }], details: { action: "github_repos", result } };
|
|
1557
|
+
}
|
|
1558
|
+
if (params.action === "npm_packages") {
|
|
1559
|
+
const result = await externalSearchOperations.npmPackages(params.query, maxResults);
|
|
1560
|
+
return { content: [{ type: "text", text: JSON.stringify(result) }], details: { action: "npm_packages", result } };
|
|
1561
|
+
}
|
|
1562
|
+
const result = await externalSearchOperations.sourcegraphCode(params.query, maxResults);
|
|
1563
|
+
return { content: [{ type: "text", text: JSON.stringify(result) }], details: { action: "sourcegraph_code", result } };
|
|
1564
|
+
},
|
|
1565
|
+
renderCall(args, theme, context) {
|
|
1566
|
+
const action = args.action === "npm_packages" || args.action === "sourcegraph_code" ? args.action : "github_repos";
|
|
1567
|
+
const text = context.lastComponent instanceof Text ? context.lastComponent : new Text("", 0, 0);
|
|
1568
|
+
text.setText(formatExternalSearchCall(action, args, theme));
|
|
1569
|
+
return text;
|
|
1570
|
+
},
|
|
1571
|
+
renderResult(result, { isPartial }, theme, context) {
|
|
1572
|
+
if (isPartial) return new Text(theme.fg("warning", "Searching..."), 0, 0);
|
|
1573
|
+
if (context.isError) {
|
|
1574
|
+
const errorText = result.content
|
|
1575
|
+
.filter((block) => block.type === "text")
|
|
1576
|
+
.map((block) => block.text)
|
|
1577
|
+
.join("\n");
|
|
1578
|
+
return new Text(theme.fg("error", errorText || "external_search failed"), 0, 0);
|
|
1579
|
+
}
|
|
1580
|
+
const details = result.details as ExternalSearchToolDetails | undefined;
|
|
1581
|
+
const text = context.lastComponent instanceof Text ? context.lastComponent : new Text("", 0, 0);
|
|
1582
|
+
if (details?.action === "npm_packages") text.setText(formatNpmPackageSearchResult(details.result, theme));
|
|
1583
|
+
else if (details?.action === "sourcegraph_code") text.setText(formatSourcegraphCodeSearchResult(details.result, theme));
|
|
1584
|
+
else text.setText(formatGithubRepoSearchResult(details?.action === "github_repos" ? details.result : undefined, theme));
|
|
1585
|
+
return text;
|
|
1586
|
+
},
|
|
1587
|
+
});
|
|
1588
|
+
|
|
1523
1589
|
const crossWorkspaceSearchOperations = createLectorCrossWorkspaceSearchOperations();
|
|
1524
1590
|
pi.registerTool({
|
|
1525
1591
|
name: "find_symbols_across_projects",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@danypops/pi-lector",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
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",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
21
|
"@danypops/daemon-kit": "^0.22.1",
|
|
22
|
-
"@danypops/lector": "^0.
|
|
22
|
+
"@danypops/lector": "^0.7.0"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
25
|
"@earendil-works/pi-ai": "^0.81.1",
|