@apmantza/greedysearch-pi 1.9.2 → 2.1.2
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/CHANGELOG.md +132 -2
- package/README.md +82 -47
- package/bin/cdp.mjs +1153 -1108
- package/bin/launch.mjs +9 -0
- package/bin/search.mjs +318 -81
- package/extractors/bing-copilot.mjs +48 -18
- package/extractors/chatgpt.mjs +553 -0
- package/extractors/common.mjs +213 -22
- package/extractors/consensus.mjs +655 -0
- package/extractors/consent.mjs +182 -18
- package/extractors/gemini.mjs +350 -217
- package/extractors/google-ai.mjs +129 -128
- package/extractors/logically.mjs +629 -0
- package/extractors/perplexity.mjs +547 -217
- package/extractors/selectors.mjs +3 -2
- package/extractors/semantic-scholar.mjs +219 -0
- package/package.json +8 -4
- package/skills/greedy-search/skill.md +20 -12
- package/src/fetcher.mjs +23 -1
- package/src/formatters/results.ts +185 -128
- package/src/search/browser-lifecycle.mjs +27 -5
- package/src/search/challenge-detect.mjs +205 -0
- package/src/search/chrome.mjs +653 -590
- package/src/search/constants.mjs +155 -39
- package/src/search/engines.mjs +114 -76
- package/src/search/fetch-source.mjs +566 -451
- package/src/search/pdf.mjs +68 -0
- package/src/search/progress.mjs +145 -0
- package/src/search/recovery.mjs +73 -45
- package/src/search/research.mjs +1419 -62
- package/src/search/scale-aware.mjs +93 -0
- package/src/search/simple-research.mjs +520 -0
- package/src/search/sources.mjs +52 -22
- package/src/search/synthesis-runner.mjs +105 -26
- package/src/search/synthesis.mjs +286 -246
- package/src/tools/greedy-search-handler.ts +129 -59
- package/src/tools/shared.ts +312 -186
- package/src/types.ts +110 -104
- package/test.mjs +537 -18
|
@@ -16,8 +16,37 @@ import {
|
|
|
16
16
|
makeProgressTracker,
|
|
17
17
|
runSearch,
|
|
18
18
|
stripQuotes,
|
|
19
|
+
type ProgressUpdate,
|
|
20
|
+
type ToolResult,
|
|
19
21
|
} from "./shared.js";
|
|
20
22
|
|
|
23
|
+
type GreedySearchParams = {
|
|
24
|
+
query: string;
|
|
25
|
+
engine?: string;
|
|
26
|
+
synthesize?: boolean;
|
|
27
|
+
synthesizer?: string;
|
|
28
|
+
depth?: "fast" | "standard" | "deep" | "research" | string;
|
|
29
|
+
breadth?: number;
|
|
30
|
+
iterations?: number;
|
|
31
|
+
maxSources?: number;
|
|
32
|
+
researchOutDir?: string;
|
|
33
|
+
writeResearchBundle?: boolean;
|
|
34
|
+
fullAnswer?: boolean;
|
|
35
|
+
headless?: boolean;
|
|
36
|
+
visible?: boolean;
|
|
37
|
+
alwaysVisible?: boolean;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
type ToolTheme = {
|
|
41
|
+
fg(style: string, text: string): string;
|
|
42
|
+
bold(text: string): string;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
type RenderState = {
|
|
46
|
+
expanded: boolean;
|
|
47
|
+
isPartial?: boolean;
|
|
48
|
+
};
|
|
49
|
+
|
|
21
50
|
class Text {
|
|
22
51
|
constructor(
|
|
23
52
|
private text: string,
|
|
@@ -52,8 +81,10 @@ export function registerGreedySearchTool(pi: ExtensionAPI, baseDir: string) {
|
|
|
52
81
|
name: "greedy_search",
|
|
53
82
|
label: "Greedy Search",
|
|
54
83
|
description:
|
|
55
|
-
"WEB SEARCH ONLY — searches live web via Perplexity,
|
|
56
|
-
"
|
|
84
|
+
"WEB/RESEARCH SEARCH ONLY — searches live web via Perplexity, Google AI, ChatGPT, and Gemini, plus opt-in research through Semantic Scholar and Logically. " +
|
|
85
|
+
"Research mode reuses the configured ~/.pi/greedyconfig engines for child searches and Gemini for planning/final synthesis. " +
|
|
86
|
+
"Research mode is the centerpiece: it plans follow-up actions, fetches sources, audits citations, " +
|
|
87
|
+
"and writes a structured research bundle on disk. Scale-aware: simple queries auto-classify and use a fast single-pass path. " +
|
|
57
88
|
"Use for: library docs, recent framework changes, error messages, best practices, current events. " +
|
|
58
89
|
"Reports streaming progress as each engine completes.",
|
|
59
90
|
promptSnippet: "Multi-engine AI web search with streaming progress",
|
|
@@ -61,14 +92,28 @@ export function registerGreedySearchTool(pi: ExtensionAPI, baseDir: string) {
|
|
|
61
92
|
query: Type.String({ description: "The search query" }),
|
|
62
93
|
engine: Type.String({
|
|
63
94
|
description:
|
|
64
|
-
'Engine to use: "all" (default), "perplexity", "
|
|
95
|
+
'Engine to use: "all" (default), "perplexity", "google", "chatgpt", "gemini", "gem". Research engines: "semantic-scholar" (alias "s2") and "logically". "all" fans out to the configured engines and fetches top sources. Customize via ~/.pi/greedyconfig. Bing Copilot is still available as "bing" for signed-in users.',
|
|
65
96
|
default: "all",
|
|
66
97
|
}),
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
98
|
+
synthesize: Type.Optional(
|
|
99
|
+
Type.Boolean({
|
|
100
|
+
description:
|
|
101
|
+
'Only for engine="all": synthesize the multi-engine results and fetched sources. Default: false.',
|
|
102
|
+
default: false,
|
|
103
|
+
}),
|
|
104
|
+
),
|
|
105
|
+
synthesizer: Type.Optional(
|
|
106
|
+
Type.String({
|
|
107
|
+
description:
|
|
108
|
+
'Synthesis engine for synthesize=true. Defaults to ~/.pi/greedyconfig synthesizer (currently "gemini" by default). Supported: "gemini", "chatgpt".',
|
|
109
|
+
}),
|
|
110
|
+
),
|
|
111
|
+
depth: Type.Optional(
|
|
112
|
+
Type.String({
|
|
113
|
+
description:
|
|
114
|
+
'Deprecated except "research". Use depth="research" for the iterative research workflow. Research child searches use ~/.pi/greedyconfig engines; Gemini handles research planning/final synthesis. Legacy values: "fast" skips source fetching; "standard"/"deep" alias synthesize=true.',
|
|
115
|
+
}),
|
|
116
|
+
),
|
|
72
117
|
breadth: Type.Optional(
|
|
73
118
|
Type.Number({
|
|
74
119
|
description:
|
|
@@ -89,6 +134,19 @@ export function registerGreedySearchTool(pi: ExtensionAPI, baseDir: string) {
|
|
|
89
134
|
'Only for depth="research": maximum fetched sources for the final report, 3-12.',
|
|
90
135
|
}),
|
|
91
136
|
),
|
|
137
|
+
researchOutDir: Type.Optional(
|
|
138
|
+
Type.String({
|
|
139
|
+
description:
|
|
140
|
+
'Only for depth="research": optional directory for the structured research bundle. Defaults to .pi/greedysearch-research/<timestamp>_<query>.',
|
|
141
|
+
}),
|
|
142
|
+
),
|
|
143
|
+
writeResearchBundle: Type.Optional(
|
|
144
|
+
Type.Boolean({
|
|
145
|
+
description:
|
|
146
|
+
'Only for depth="research": write the structured research bundle to disk (default true).',
|
|
147
|
+
default: true,
|
|
148
|
+
}),
|
|
149
|
+
),
|
|
92
150
|
fullAnswer: Type.Optional(
|
|
93
151
|
Type.Boolean({
|
|
94
152
|
description:
|
|
@@ -118,27 +176,33 @@ export function registerGreedySearchTool(pi: ExtensionAPI, baseDir: string) {
|
|
|
118
176
|
}),
|
|
119
177
|
),
|
|
120
178
|
}),
|
|
121
|
-
execute: async (
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
const
|
|
179
|
+
execute: async (
|
|
180
|
+
_toolCallId: string,
|
|
181
|
+
params: GreedySearchParams,
|
|
182
|
+
signal?: AbortSignal,
|
|
183
|
+
onUpdate?: (update: ProgressUpdate) => void,
|
|
184
|
+
) => {
|
|
185
|
+
const { query, fullAnswer: fullAnswerParam } = params;
|
|
186
|
+
const engine = stripQuotes(params.engine ?? "all") || "all";
|
|
187
|
+
const depthRaw = stripQuotes(params.depth ?? "") as
|
|
188
|
+
| "fast"
|
|
189
|
+
| "standard"
|
|
190
|
+
| "deep"
|
|
191
|
+
| "research"
|
|
192
|
+
| "";
|
|
193
|
+
const researchMode = depthRaw === "research";
|
|
194
|
+
const legacyFast = depthRaw === "fast";
|
|
195
|
+
const legacySynthesisDepth =
|
|
196
|
+
depthRaw === "standard" || depthRaw === "deep";
|
|
197
|
+
const synthesize =
|
|
198
|
+
engine === "all" &&
|
|
199
|
+
!legacyFast &&
|
|
200
|
+
(params.synthesize === true || legacySynthesisDepth);
|
|
201
|
+
const effectiveEngine = researchMode ? "all" : engine;
|
|
138
202
|
const visible =
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
203
|
+
params.visible === true ||
|
|
204
|
+
params.alwaysVisible === true ||
|
|
205
|
+
params.headless === false ||
|
|
142
206
|
process.env.GREEDY_SEARCH_VISIBLE === "1" ||
|
|
143
207
|
process.env.GREEDY_SEARCH_ALWAYS_VISIBLE === "1";
|
|
144
208
|
const headless = !visible;
|
|
@@ -148,28 +212,32 @@ export function registerGreedySearchTool(pi: ExtensionAPI, baseDir: string) {
|
|
|
148
212
|
const flags: string[] = [];
|
|
149
213
|
const fullAnswer = fullAnswerParam ?? effectiveEngine !== "all";
|
|
150
214
|
if (fullAnswer) flags.push("--full");
|
|
151
|
-
if (
|
|
215
|
+
if (researchMode) {
|
|
152
216
|
flags.push("--depth", "research");
|
|
153
|
-
if (typeof
|
|
154
|
-
flags.push("--breadth", String(
|
|
155
|
-
if (typeof
|
|
156
|
-
flags.push("--iterations", String(
|
|
157
|
-
if (typeof
|
|
158
|
-
flags.push("--max-sources", String(
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
217
|
+
if (typeof params.breadth === "number")
|
|
218
|
+
flags.push("--breadth", String(params.breadth));
|
|
219
|
+
if (typeof params.iterations === "number")
|
|
220
|
+
flags.push("--iterations", String(params.iterations));
|
|
221
|
+
if (typeof params.maxSources === "number")
|
|
222
|
+
flags.push("--max-sources", String(params.maxSources));
|
|
223
|
+
if (typeof params.researchOutDir === "string")
|
|
224
|
+
flags.push("--research-out-dir", params.researchOutDir);
|
|
225
|
+
if (params.writeResearchBundle === false)
|
|
226
|
+
flags.push("--no-research-bundle");
|
|
227
|
+
} else if (legacyFast) flags.push("--fast");
|
|
228
|
+
else if (depthRaw === "deep") flags.push("--depth", "deep");
|
|
229
|
+
else if (synthesize) flags.push("--synthesize");
|
|
230
|
+
if (synthesize && typeof params.synthesizer === "string") {
|
|
231
|
+
flags.push("--synthesizer", params.synthesizer);
|
|
232
|
+
}
|
|
163
233
|
|
|
164
|
-
const onProgress =
|
|
165
|
-
effectiveEngine === "all"
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
)
|
|
172
|
-
: undefined;
|
|
234
|
+
const onProgress = makeProgressTracker(
|
|
235
|
+
effectiveEngine === "all" ? ALL_ENGINES : [effectiveEngine],
|
|
236
|
+
onUpdate,
|
|
237
|
+
researchMode ? "Researching" : "Searching",
|
|
238
|
+
synthesize && effectiveEngine === "all",
|
|
239
|
+
query,
|
|
240
|
+
);
|
|
173
241
|
|
|
174
242
|
try {
|
|
175
243
|
const data = await runSearch(
|
|
@@ -179,7 +247,7 @@ export function registerGreedySearchTool(pi: ExtensionAPI, baseDir: string) {
|
|
|
179
247
|
`${baseDir}/bin/search.mjs`,
|
|
180
248
|
signal,
|
|
181
249
|
onProgress,
|
|
182
|
-
headless,
|
|
250
|
+
{ headless },
|
|
183
251
|
);
|
|
184
252
|
const text = formatResults(effectiveEngine, data);
|
|
185
253
|
return {
|
|
@@ -191,7 +259,7 @@ export function registerGreedySearchTool(pi: ExtensionAPI, baseDir: string) {
|
|
|
191
259
|
}
|
|
192
260
|
},
|
|
193
261
|
|
|
194
|
-
renderCall(args
|
|
262
|
+
renderCall(args: Partial<GreedySearchParams>, theme: ToolTheme) {
|
|
195
263
|
const q = (args.query || "").slice(0, 60);
|
|
196
264
|
const qDisplay = q.length < (args.query || "").length ? `${q}...` : q;
|
|
197
265
|
const engineDisplay =
|
|
@@ -205,11 +273,15 @@ export function registerGreedySearchTool(pi: ExtensionAPI, baseDir: string) {
|
|
|
205
273
|
);
|
|
206
274
|
},
|
|
207
275
|
|
|
208
|
-
renderResult(
|
|
276
|
+
renderResult(
|
|
277
|
+
result: ToolResult,
|
|
278
|
+
{ expanded, isPartial }: RenderState,
|
|
279
|
+
theme: ToolTheme,
|
|
280
|
+
) {
|
|
209
281
|
if (isPartial) {
|
|
210
|
-
const progressText = (
|
|
211
|
-
|
|
212
|
-
)?.text
|
|
282
|
+
const progressText = result.content.find(
|
|
283
|
+
(c) => c.type === "text",
|
|
284
|
+
)?.text;
|
|
213
285
|
const display = progressText
|
|
214
286
|
? progressText.replace(/\*\*/g, "")
|
|
215
287
|
: "Searching...";
|
|
@@ -217,9 +289,7 @@ export function registerGreedySearchTool(pi: ExtensionAPI, baseDir: string) {
|
|
|
217
289
|
}
|
|
218
290
|
|
|
219
291
|
const textContent = result.content.find((c) => c.type === "text");
|
|
220
|
-
const raw =
|
|
221
|
-
| Record<string, unknown>
|
|
222
|
-
| undefined;
|
|
292
|
+
const raw = result.details?.raw as Record<string, unknown> | undefined;
|
|
223
293
|
|
|
224
294
|
// Collapsed: one-line summary only
|
|
225
295
|
if (!expanded) {
|
|
@@ -256,7 +326,7 @@ export function registerGreedySearchTool(pi: ExtensionAPI, baseDir: string) {
|
|
|
256
326
|
);
|
|
257
327
|
let totalSources = 0;
|
|
258
328
|
for (const key of engineKeys) {
|
|
259
|
-
const eng =
|
|
329
|
+
const eng = raw?.[key] as Record<string, unknown> | undefined;
|
|
260
330
|
const s = eng?.sources as Array<unknown> | undefined;
|
|
261
331
|
if (Array.isArray(s)) totalSources += s.length;
|
|
262
332
|
}
|
|
@@ -272,7 +342,7 @@ export function registerGreedySearchTool(pi: ExtensionAPI, baseDir: string) {
|
|
|
272
342
|
}
|
|
273
343
|
|
|
274
344
|
// No structured data — show content text as error/fallback
|
|
275
|
-
const snippet =
|
|
345
|
+
const snippet = textContent?.text;
|
|
276
346
|
if (snippet) {
|
|
277
347
|
return new Text(
|
|
278
348
|
theme.fg("warning", ` → ${snippet.slice(0, 80)}`),
|