@apmantza/greedysearch-pi 1.0.24 → 1.1.1

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.
@@ -108,15 +108,49 @@ async function extractAnswer(tab) {
108
108
  const answer = await cdp(['eval', tab, `window.__geminiClipboard || ''`]);
109
109
  if (!answer) throw new Error('Clipboard interceptor returned empty text');
110
110
 
111
- // Sources: links rendered in the page (best-effort; Shadow DOM may hide some)
111
+ // Click "Sources" button to open the sidebar with proper source cards
112
+ await cdp(['eval', tab, `
113
+ (function() {
114
+ var btn = document.querySelector('button.legacy-sources-sidebar-button, button.mdс-button--outline');
115
+ if (!btn) btn = Array.from(document.querySelectorAll('button')).find(b => b.innerText?.trim() === 'Sources');
116
+ if (btn) { btn.click(); return 'clicked'; }
117
+ return 'not-found';
118
+ })()
119
+ `]).catch(() => 'not-found');
120
+
121
+ // Wait for the sources sidebar to populate
122
+ await new Promise(r => setTimeout(r, 1500));
123
+
124
+ // Extract sources from the sidebar panel (has proper URLs + titles)
112
125
  const raw = await cdp(['eval', tab, `
113
126
  (function() {
114
- var sources = Array.from(document.querySelectorAll('a[href^="http"]'))
127
+ // Find the Sources sidebar container by heading
128
+ var headings = Array.from(document.querySelectorAll('h1, h2, h3, [class*="header"]'));
129
+ var sourceHeading = headings.find(h => h.innerText?.trim() === 'Sources');
130
+ if (sourceHeading) {
131
+ var container = sourceHeading.closest('.container') || sourceHeading.parentElement;
132
+ var links = Array.from(container.querySelectorAll('a[href^="http"]'))
133
+ .map(a => ({ url: a.href.split('#')[0], title: a.innerText?.trim().split('\\n')[0] || '' }))
134
+ .filter(s => s.url && !s.url.includes('gemini.google') && !s.url.includes('gstatic') && !s.url.includes('google.com/search'))
135
+ .filter((v, i, arr) => arr.findIndex(x => x.url === v.url) === i)
136
+ .slice(0, 8);
137
+ return JSON.stringify(links);
138
+ }
139
+ // Fallback: inline source cards with aria-labels
140
+ var cards = Array.from(document.querySelectorAll('button[aria-label*="citation from"]'));
141
+ if (cards.length) {
142
+ return JSON.stringify(cards.map(b => {
143
+ var label = b.getAttribute('aria-label') || '';
144
+ var name = label.match(/from\\s+(.+?)\\.\\s/)?.[1] || label;
145
+ return { url: '', title: name };
146
+ }));
147
+ }
148
+ // Last resort: page-wide links (may include footer junk)
149
+ return JSON.stringify(Array.from(document.querySelectorAll('a[href^="http"]'))
115
150
  .map(a => ({ url: a.href.split('#')[0], title: a.innerText?.trim().split('\\n')[0] || '' }))
116
151
  .filter(s => s.url && !s.url.includes('gemini.google') && !s.url.includes('gstatic') && !s.url.includes('google.com/search'))
117
152
  .filter((v, i, arr) => arr.findIndex(x => x.url === v.url) === i)
118
- .slice(0, 8);
119
- return JSON.stringify(sources);
153
+ .slice(0, 8));
120
154
  })()
121
155
  `]).catch(() => '[]');
122
156
  const sources = JSON.parse(raw);
package/index.ts CHANGED
@@ -22,7 +22,7 @@ function cdpAvailable(): boolean {
22
22
 
23
23
  function runSearch(engine: string, query: string, flags: string[] = []): Promise<Record<string, unknown>> {
24
24
  return new Promise((resolve, reject) => {
25
- const proc = spawn("node", [__dir + "/search.mjs", engine, ...flags, query], {
25
+ const proc = spawn("node", [__dir + "/search.mjs", engine, "--inline", ...flags, query], {
26
26
  stdio: ["ignore", "pipe", "pipe"],
27
27
  });
28
28
  let out = "";
@@ -137,9 +137,13 @@ export default function greedySearchExtension(pi: ExtensionAPI) {
137
137
  description: 'When true and engine is "all", deduplicates sources across engines and feeds them to Gemini for a single grounded synthesis. Adds ~30s but saves tokens and improves answer quality.',
138
138
  default: false,
139
139
  })),
140
+ fullAnswer: Type.Optional(Type.Boolean({
141
+ description: 'When true, returns the complete answer instead of a truncated preview (default: false, answers are shortened to ~300 chars to save tokens).',
142
+ default: false,
143
+ })),
140
144
  }),
141
145
  execute: async (_toolCallId, params) => {
142
- const { query, engine = "all", synthesize = false } = params as { query: string; engine: string; synthesize?: boolean };
146
+ const { query, engine = "all", synthesize = false, fullAnswer = false } = params as { query: string; engine: string; synthesize?: boolean; fullAnswer?: boolean };
143
147
 
144
148
  if (!cdpAvailable()) {
145
149
  return {
@@ -150,6 +154,7 @@ export default function greedySearchExtension(pi: ExtensionAPI) {
150
154
 
151
155
  const flags: string[] = [];
152
156
  if (synthesize && engine === "all") flags.push("--synthesize");
157
+ if (fullAnswer) flags.push("--full");
153
158
 
154
159
  let data: Record<string, unknown>;
155
160
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@apmantza/greedysearch-pi",
3
- "version": "1.0.24",
3
+ "version": "1.1.1",
4
4
  "description": "Pi extension: search Perplexity, Bing Copilot, and Google AI in parallel with optional Gemini synthesis — grounded AI answers, not just links",
5
5
  "type": "module",
6
6
  "keywords": [
package/search.mjs CHANGED
@@ -41,6 +41,8 @@ const ENGINES = {
41
41
  b: 'bing-copilot.mjs',
42
42
  google: 'google-ai.mjs',
43
43
  g: 'google-ai.mjs',
44
+ gemini: 'gemini.mjs',
45
+ gem: 'gemini.mjs',
44
46
  stackoverflow: 'stackoverflow-ai.mjs',
45
47
  so: 'stackoverflow-ai.mjs',
46
48
  stack: 'stackoverflow-ai.mjs',
@@ -52,6 +54,7 @@ const ENGINE_DOMAINS = {
52
54
  perplexity: 'perplexity.ai',
53
55
  bing: 'copilot.microsoft.com',
54
56
  google: 'google.com',
57
+ gemini: 'gemini.google.com',
55
58
  stackoverflow: 'stackoverflow.com',
56
59
  };
57
60
 
@@ -1,38 +1,72 @@
1
- ---
2
- name: greedy-search
3
- description: Multi-engine AI web search — Perplexity, Bing Copilot, Google AI in parallel. Use for current library docs, error messages, version diffs, and any research where training data may be stale.
4
- ---
5
-
6
- # Greedy Search Skill
7
-
8
- Use the `greedy_search` tool when you need current information from the web.
9
-
10
- ## When to Use
11
-
12
- - Questions about libraries, APIs, or frameworks — especially version-specific
13
- - User pastes an error message or stack trace
14
- - Question contains "latest", "current", "2025", "2026", "deprecated", "still recommended"
15
- - Choosing between dependencies or tools
16
- - Architecture validation or best-practice confirmation
17
- - Any research question where training data may be stale
18
-
19
- ## How to Use
20
-
21
- Call `greedy_search` with the user's question as `query`. Use `engine: "all"` (default) to fan out to all three engines in parallel for the highest-confidence answer.
22
-
23
- ```
24
- greedy_search({ query: "how to use X in Y version", engine: "all" })
25
- ```
26
-
27
- For quick lookups where one source is sufficient:
28
- - `engine: "perplexity"`best for technical Q&A
29
- - `engine: "bing"` — best for recent news and Microsoft ecosystem
30
- - `engine: "google"` — best for broad coverage
31
-
32
- ## Interpreting Results
33
-
34
- Each engine returns an AI-synthesized answer plus sources. Where all three agree, confidence is high. Where they diverge, present both perspectives to the user.
35
-
36
- ## Requirements
37
-
38
- Chrome must be running. The extension auto-launches a dedicated GreedySearch Chrome instance if needed (via `launch.mjs`).
1
+ ---
2
+ name: greedy-search
3
+ description: Multi-engine AI web search — Perplexity, Bing Copilot, Google AI in parallel with optional Gemini synthesis. Use for high-quality research where training data may be stale or single-engine results are insufficient.
4
+ ---
5
+
6
+ # Greedy Search
7
+
8
+ Use `greedy_search` when you need high-quality, multi-perspective answers from the web.
9
+
10
+ ## Greedy Search vs Built-in Web Search
11
+
12
+ | | `web_search` | `greedy_search` |
13
+ |---|---|---|
14
+ | Speed | Instant (~2s) | 15-60s (one engine) / 30-90s (all engines) |
15
+ | Quality | Good for simple lookups | Higher — 3 AI engines cross-verify |
16
+ | Synthesis | Single engine answer | Optional Gemini synthesis (cleanest answer) |
17
+ | Use for | Quick facts, simple questions | Research, decisions, complex topics |
18
+
19
+ **Rule of thumb:** Use `web_search` for quick facts. Use `greedy_search` when the answer matters — architecture decisions, comparing libraries, understanding new releases, debugging tricky errors.
20
+
21
+ ## When to Use
22
+
23
+ - **Version-specific changes** — "What changed in React 19?" / "Breaking changes in FastAPI 0.100"
24
+ - **Choosing between tools** — "Prisma vs Drizzle in 2026" / "Best auth library for Next.js 15"
25
+ - **Debugging** — User pastes an error message or stack trace
26
+ - **Research tasks** — When you need to synthesize information from multiple sources
27
+ - **Best practices** "How to structure a monorepo" / "Auth patterns for SaaS"
28
+ - **Anything where training data might be stale** 2025+, 2026+, "latest", "current", "still maintained"
29
+
30
+ ## Engine Selection
31
+
32
+ ```greedy_search({ query: "what changed in React 19", engine: "all" })```
33
+
34
+ | Engine | Latency | Best for |
35
+ |---|---|---|
36
+ | `all` (default) | 30-90s | Highest confidence — all 3 engines in parallel |
37
+ | `perplexity` | 15-30s | Technical Q&A, code explanations, documentation |
38
+ | `bing` | 15-30s | Recent news, Microsoft ecosystem |
39
+ | `google` | 15-30s | Broad coverage, multiple perspectives |
40
+ | `gemini` | 15-30s | Google's perspective, different training data |
41
+
42
+ Use a single engine when speed matters and the question isn't contentious.
43
+
44
+ ## Synthesis Mode
45
+
46
+ For complex research questions, use `synthesize: true` with `engine: "all"`:
47
+
48
+ ```greedy_search({ query: "best auth patterns for SaaS in 2026", engine: "all", synthesize: true })```
49
+
50
+ This deduplicates sources across engines and feeds them to Gemini for one clean, synthesized answer. Adds ~30s but produces the highest quality output — ideal for research tasks where you'd otherwise need to parse 3 separate answers.
51
+
52
+ Use synthesis when:
53
+ - You need one definitive answer, not multiple perspectives
54
+ - You're researching a topic to write about or make a decision
55
+ - The question has a lot of noise and you want the signal
56
+
57
+ Skip synthesis when:
58
+ - You want to see where engines disagree (useful for controversial topics)
59
+ - Speed matters
60
+
61
+ ## Full vs Short Answers
62
+
63
+ Default mode returns ~300 char summaries to save tokens. Use `fullAnswer: true` when you need the complete response:
64
+
65
+ ```greedy_search({ query: "explain the React compiler", engine: "perplexity", fullAnswer: true })```
66
+
67
+ ## Interpreting Results
68
+
69
+ - **All 3 agree** → High confidence, present as fact
70
+ - **2 agree, 1 differs** → Likely correct but note the dissent
71
+ - **All differ** → Present the different perspectives to the user
72
+ - **Sources with `[3/3]` or `[2/3]`** → Cited by multiple engines, higher confidence