@apmantza/greedysearch-pi 1.7.3 → 1.7.5

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 CHANGED
@@ -1,5 +1,23 @@
1
1
  # Changelog
2
2
 
3
+ ## v1.7.5 (2026-04-10)
4
+
5
+ ### Plugin
6
+ - **Claude Code plugin** — added `.claude-plugin/plugin.json` and `marketplace.json` so GreedySearch can be installed directly as a Claude Code plugin via `claude plugin install`.
7
+ - **Auto-mirror GH Action** — every push to `GreedySearch-pi/master` automatically syncs to `GreedySearch-claude/main`, keeping the Claude plugin up to date.
8
+ - **Tightened `skill.md`** — removed verbose guidance sections; kept parameters, depth table, and coding_task reference. -72 lines.
9
+
10
+ ## v1.7.4 (2026-04-10)
11
+
12
+ ### Refactor
13
+ - **Shared `waitForCopyButton()`** — consolidated duplicate copy-button polling loops from `bing-copilot`, `gemini`, and `coding-task` into a single `waitForCopyButton(tab, selector, { timeout, onPoll })` in `common.mjs`. Gemini's scroll-to-bottom logic passed as `onPoll` callback.
14
+ - **Shared `TIMING` constants** — replaced 30+ scattered `setTimeout` magic numbers with named constants (`postNav`, `postNavSlow`, `postClick`, `postType`, `inputPoll`, `copyPoll`, `afterVerify`) in `common.mjs`.
15
+ - **`waitForStreamComplete` improvements** — added `minLength` option and graceful last-value fallback; `google-ai` now uses the shared implementation instead of its own copy.
16
+ - **Removed dead code** — deleted unused `_getOrReuseBlankTab` and `_getOrOpenEngineTab` from `bin/search.mjs`; removed unused `STREAM_POLL_INTERVAL` and `STREAM_STABLE_ROUNDS` from `coding-task`.
17
+
18
+ ### Fixes
19
+ - **Synthesis tab regression** — `getOrOpenEngineTab("gemini")` call during synthesis was broken by the dead-code removal; replaced with `openNewTab()`.
20
+
3
21
  ## v1.7.3 (2026-04-10)
4
22
 
5
23
  ### Fixes
@@ -12,7 +12,7 @@
12
12
  import { existsSync, readFileSync, writeFileSync } from "node:fs";
13
13
  import { tmpdir } from "node:os";
14
14
  import { fileURLToPath } from "node:url";
15
- import { cdp, injectClipboardInterceptor } from "../extractors/common.mjs";
15
+ import { cdp, injectClipboardInterceptor, waitForCopyButton } from "../extractors/common.mjs";
16
16
  import { dismissConsent, handleVerification } from "../extractors/consent.mjs";
17
17
 
18
18
  const __dir = fileURLToPath(new URL(".", import.meta.url));
@@ -31,9 +31,7 @@ const MODE_PROMPTS = {
31
31
  debug: `You are a senior engineer debugging someone else's code. You have fresh eyes — no prior assumptions about what should work. Given the bug description and relevant code: (1) identify the most likely root cause, being specific about the exact line or condition, (2) explain why it manifests the way it does, (3) suggest the minimal fix, (4) flag any other latent bugs you notice while reading. Do not guess vaguely — reason from the code. If you need information that isn't provided, say exactly what you'd add to narrow it down.`,
32
32
  };
33
33
 
34
- const STREAM_POLL_INTERVAL = 800;
35
- const STREAM_STABLE_ROUNDS = 4;
36
- const STREAM_TIMEOUT = 120000; // coding tasks take longer
34
+ const STREAM_TIMEOUT = 120000; // coding tasks take longer — passed to waitForCopyButton
37
35
  const MIN_RESPONSE_LENGTH = 50;
38
36
 
39
37
  // ---------------------------------------------------------------------------
@@ -104,17 +102,7 @@ const ENGINES = {
104
102
  },
105
103
 
106
104
  async waitForCopyButton(tab) {
107
- const deadline = Date.now() + STREAM_TIMEOUT;
108
- while (Date.now() < deadline) {
109
- await new Promise((r) => setTimeout(r, STREAM_POLL_INTERVAL));
110
- const found = await cdp([
111
- "eval",
112
- tab,
113
- `!!document.querySelector('button[aria-label="Copy"]')`,
114
- ]).catch(() => "false");
115
- if (found === "true") return;
116
- }
117
- throw new Error("Gemini copy button did not appear within timeout");
105
+ await waitForCopyButton(tab, 'button[aria-label="Copy"]', { timeout: STREAM_TIMEOUT });
118
106
  },
119
107
 
120
108
  async extract(tab) {
@@ -162,17 +150,7 @@ const ENGINES = {
162
150
  },
163
151
 
164
152
  async waitForCopyButton(tab) {
165
- const deadline = Date.now() + STREAM_TIMEOUT;
166
- while (Date.now() < deadline) {
167
- await new Promise((r) => setTimeout(r, STREAM_POLL_INTERVAL));
168
- const found = await cdp([
169
- "eval",
170
- tab,
171
- `!!document.querySelector('button[data-testid="copy-ai-message-button"]')`,
172
- ]).catch(() => "false");
173
- if (found === "true") return;
174
- }
175
- throw new Error("Copilot copy button did not appear within timeout");
153
+ await waitForCopyButton(tab, 'button[data-testid="copy-ai-message-button"]', { timeout: STREAM_TIMEOUT });
176
154
  },
177
155
 
178
156
  async extract(tab) {
package/bin/search.mjs CHANGED
@@ -752,27 +752,6 @@ async function getAnyTab() {
752
752
  return first.slice(0, 8);
753
753
  }
754
754
 
755
- async function _getOrReuseBlankTab() {
756
- // Reuse an existing about:blank tab rather than always creating a new one
757
- const listOut = await cdp(["list"]);
758
- const lines = listOut.split("\n").filter(Boolean);
759
- for (const line of lines) {
760
- if (line.includes("about:blank")) {
761
- return line.slice(0, 8); // prefix of the blank tab's targetId
762
- }
763
- }
764
- // No blank tab — open a new one
765
- const anchor = await getAnyTab();
766
- const raw = await cdp([
767
- "evalraw",
768
- anchor,
769
- "Target.createTarget",
770
- '{"url":"about:blank"}',
771
- ]);
772
- const { targetId } = JSON.parse(raw);
773
- return targetId;
774
- }
775
-
776
755
  async function openNewTab() {
777
756
  const anchor = await getAnyTab();
778
757
  const raw = await cdp([
@@ -785,11 +764,6 @@ async function openNewTab() {
785
764
  return targetId;
786
765
  }
787
766
 
788
- async function _getOrOpenEngineTab(engine) {
789
- await cdp(["list"]);
790
- return getFullTabFromCache(engine) || openNewTab();
791
- }
792
-
793
767
  async function activateTab(targetId) {
794
768
  try {
795
769
  const anchor = await getAnyTab();
@@ -1487,7 +1461,7 @@ async function main() {
1487
1461
  "[greedysearch] Synthesizing results with Gemini...\n",
1488
1462
  );
1489
1463
  try {
1490
- const geminiTab = await getOrOpenEngineTab("gemini");
1464
+ const geminiTab = await openNewTab();
1491
1465
  await activateTab(geminiTab);
1492
1466
  const synthesis = await synthesizeWithGemini(query, out, {
1493
1467
  grounded: depth === "deep",
@@ -18,7 +18,9 @@ import {
18
18
  outputJson,
19
19
  parseArgs,
20
20
  parseSourcesFromMarkdown,
21
+ TIMING,
21
22
  validateQuery,
23
+ waitForCopyButton,
22
24
  } from "./common.mjs";
23
25
  import { dismissConsent, handleVerification } from "./consent.mjs";
24
26
  import { SELECTORS } from "./selectors.mjs";
@@ -30,20 +32,6 @@ const GLOBAL_VAR = "__bingClipboard";
30
32
  // Bing Copilot-specific helpers
31
33
  // ============================================================================
32
34
 
33
- async function waitForCopyButton(tab, timeout = 60000) {
34
- const deadline = Date.now() + timeout;
35
- while (Date.now() < deadline) {
36
- await new Promise((r) => setTimeout(r, 700));
37
- const found = await cdp([
38
- "eval",
39
- tab,
40
- `!!document.querySelector('${S.copyButton}')`,
41
- ]).catch(() => "false");
42
- if (found === "true") return;
43
- }
44
- throw new Error(`Copilot copy button did not appear within ${timeout}ms`);
45
- }
46
-
47
35
  async function extractAnswer(tab) {
48
36
  await cdp([
49
37
  "eval",
@@ -78,7 +66,7 @@ async function main() {
78
66
 
79
67
  // Navigate to Copilot homepage and use the chat input
80
68
  await cdp(["nav", tab, "https://copilot.microsoft.com/"], 35000);
81
- await new Promise((r) => setTimeout(r, 2000));
69
+ await new Promise((r) => setTimeout(r, TIMING.postNavSlow));
82
70
  await dismissConsent(tab, cdp);
83
71
 
84
72
  // Handle verification challenges (Cloudflare Turnstile, Microsoft auth, etc.)
@@ -91,7 +79,7 @@ async function main() {
91
79
 
92
80
  // After verification, page may have redirected or reloaded — wait for it to settle
93
81
  if (verifyResult === "clicked") {
94
- await new Promise((r) => setTimeout(r, 3000));
82
+ await new Promise((r) => setTimeout(r, TIMING.afterVerify));
95
83
 
96
84
  // Re-navigate if we got redirected
97
85
  const currentUrl = await cdp([
@@ -101,7 +89,7 @@ async function main() {
101
89
  ]).catch(() => "");
102
90
  if (!currentUrl.includes("copilot.microsoft.com")) {
103
91
  await cdp(["nav", tab, "https://copilot.microsoft.com/"], 35000);
104
- await new Promise((r) => setTimeout(r, 2000));
92
+ await new Promise((r) => setTimeout(r, TIMING.postNavSlow));
105
93
  await dismissConsent(tab, cdp);
106
94
  }
107
95
  }
@@ -133,9 +121,9 @@ async function main() {
133
121
 
134
122
  await injectClipboardInterceptor(tab, GLOBAL_VAR);
135
123
  await cdp(["click", tab, S.input]);
136
- await new Promise((r) => setTimeout(r, 400));
124
+ await new Promise((r) => setTimeout(r, TIMING.postClick));
137
125
  await cdp(["type", tab, query]);
138
- await new Promise((r) => setTimeout(r, 400));
126
+ await new Promise((r) => setTimeout(r, TIMING.postType));
139
127
 
140
128
  // Submit with Enter (most reliable across locales and Chrome instances)
141
129
  await cdp([
@@ -144,7 +132,7 @@ async function main() {
144
132
  `document.querySelector('${S.input}')?.dispatchEvent(new KeyboardEvent('keydown',{key:'Enter',bubbles:true,keyCode:13})), 'ok'`,
145
133
  ]);
146
134
 
147
- await waitForCopyButton(tab);
135
+ await waitForCopyButton(tab, S.copyButton, { timeout: 60000 });
148
136
 
149
137
  const { answer, sources } = await extractAnswer(tab);
150
138
  if (!answer)
@@ -119,6 +119,46 @@ export function parseSourcesFromMarkdown(text) {
119
119
  .slice(0, 10);
120
120
  }
121
121
 
122
+ // ============================================================================
123
+ // Timing constants
124
+ // ============================================================================
125
+
126
+ export const TIMING = {
127
+ postNav: 1500, // settle after navigation
128
+ postNavSlow: 2000, // settle after slower navigations (Bing, Gemini)
129
+ postClick: 400, // settle after a UI click
130
+ postType: 400, // settle after typing
131
+ inputPoll: 400, // polling interval when waiting for input to appear
132
+ copyPoll: 600, // polling interval when waiting for copy button
133
+ afterVerify: 3000, // settle after a verification challenge completes
134
+ };
135
+
136
+ // ============================================================================
137
+ // Copy button polling
138
+ // ============================================================================
139
+
140
+ /**
141
+ * Wait for a copy button to appear in the DOM.
142
+ * @param {string} tab - Tab identifier
143
+ * @param {string} selector - CSS selector for the copy button
144
+ * @param {object} [options]
145
+ * @param {number} [options.timeout=60000] - Max wait in ms
146
+ * @param {Function} [options.onPoll] - Optional async callback on each poll tick (e.g. scroll)
147
+ * @returns {Promise<void>}
148
+ */
149
+ export async function waitForCopyButton(tab, selector, options = {}) {
150
+ const { timeout = 60000, onPoll } = options;
151
+ const deadline = Date.now() + timeout;
152
+ let tick = 0;
153
+ while (Date.now() < deadline) {
154
+ await new Promise((r) => setTimeout(r, TIMING.copyPoll));
155
+ if (onPoll) await onPoll(++tick).catch(() => null);
156
+ const found = await cdp(["eval", tab, `!!document.querySelector('${selector}')`]).catch(() => "false");
157
+ if (found === "true") return;
158
+ }
159
+ throw new Error(`Copy button ('${selector}') did not appear within ${timeout}ms`);
160
+ }
161
+
122
162
  // ============================================================================
123
163
  // Stream completion detection
124
164
  // ============================================================================
@@ -139,6 +179,7 @@ export async function waitForStreamComplete(tab, options = {}) {
139
179
  interval = 600,
140
180
  stableRounds = 3,
141
181
  selector = "document.body",
182
+ minLength = 0,
142
183
  } = options;
143
184
 
144
185
  const deadline = Date.now() + timeout;
@@ -154,7 +195,7 @@ export async function waitForStreamComplete(tab, options = {}) {
154
195
  ]).catch(() => "0");
155
196
  const currentLen = parseInt(lenStr, 10) || 0;
156
197
 
157
- if (currentLen > 0) {
198
+ if (currentLen >= minLength) {
158
199
  if (currentLen === lastLen) {
159
200
  stableCount++;
160
201
  if (stableCount >= stableRounds) return currentLen;
@@ -165,6 +206,7 @@ export async function waitForStreamComplete(tab, options = {}) {
165
206
  }
166
207
  }
167
208
 
209
+ if (lastLen >= minLength) return lastLen;
168
210
  throw new Error(`Generation did not stabilise within ${timeout}ms`);
169
211
  }
170
212
 
@@ -18,7 +18,9 @@ import {
18
18
  outputJson,
19
19
  parseArgs,
20
20
  parseSourcesFromMarkdown,
21
+ TIMING,
21
22
  validateQuery,
23
+ waitForCopyButton,
22
24
  } from "./common.mjs";
23
25
  import { dismissConsent, handleVerification } from "./consent.mjs";
24
26
  import { SELECTORS } from "./selectors.mjs";
@@ -46,36 +48,15 @@ async function typeIntoGemini(tab, text) {
46
48
  ]);
47
49
  }
48
50
 
49
- async function waitForCopyButton(tab, timeout = 120000) {
50
- const deadline = Date.now() + timeout;
51
- let scrollCount = 0;
52
- while (Date.now() < deadline) {
53
- await new Promise((r) => setTimeout(r, 600));
54
-
55
- // Scroll to bottom every ~6 seconds to trigger lazy loading and ensure copy button appears
56
- if (++scrollCount % 10 === 0) {
57
- await cdp([
58
- "eval",
59
- tab,
60
- `
61
- (function() {
62
- const chat = document.querySelector('chat-window, [role="main"], main') || document.body;
63
- const scrollHeight = chat.scrollHeight || document.body.scrollHeight || 0;
64
- // Scroll to bottom to ensure all content is loaded
65
- chat.scrollTo ? chat.scrollTo({ top: scrollHeight, behavior: 'smooth' }) : window.scrollTo(0, scrollHeight);
66
- })()
67
- `,
68
- ]).catch(() => null);
69
- }
70
-
71
- const found = await cdp([
72
- "eval",
73
- tab,
74
- `!!document.querySelector('${S.copyButton}')`,
75
- ]).catch(() => "false");
76
- if (found === "true") return;
77
- }
78
- throw new Error(`Gemini copy button did not appear within ${timeout}ms`);
51
+ async function scrollToBottom(tab) {
52
+ await cdp([
53
+ "eval",
54
+ tab,
55
+ `(function() {
56
+ const chat = document.querySelector('chat-window, [role="main"], main') || document.body;
57
+ chat.scrollTo ? chat.scrollTo({ top: chat.scrollHeight, behavior: 'smooth' }) : window.scrollTo(0, document.body.scrollHeight);
58
+ })()`,
59
+ ]);
79
60
  }
80
61
 
81
62
  async function extractAnswer(tab) {
@@ -111,7 +92,7 @@ async function main() {
111
92
 
112
93
  // Each search = fresh conversation
113
94
  await cdp(["nav", tab, "https://gemini.google.com/app"], 35000);
114
- await new Promise((r) => setTimeout(r, 2000));
95
+ await new Promise((r) => setTimeout(r, TIMING.postNavSlow));
115
96
  await dismissConsent(tab, cdp);
116
97
  await handleVerification(tab, cdp, 60000);
117
98
 
@@ -124,13 +105,13 @@ async function main() {
124
105
  `!!document.querySelector('${S.input}')`,
125
106
  ]).catch(() => "false");
126
107
  if (ready === "true") break;
127
- await new Promise((r) => setTimeout(r, 400));
108
+ await new Promise((r) => setTimeout(r, TIMING.inputPoll));
128
109
  }
129
- await new Promise((r) => setTimeout(r, 300));
110
+ await new Promise((r) => setTimeout(r, TIMING.postClick));
130
111
 
131
112
  await injectClipboardInterceptor(tab, GLOBAL_VAR);
132
113
  await typeIntoGemini(tab, query);
133
- await new Promise((r) => setTimeout(r, 400));
114
+ await new Promise((r) => setTimeout(r, TIMING.postType));
134
115
 
135
116
  await cdp([
136
117
  "eval",
@@ -138,7 +119,11 @@ async function main() {
138
119
  `document.querySelector('${S.sendButton}')?.click()`,
139
120
  ]);
140
121
 
141
- await waitForCopyButton(tab);
122
+ // Scroll to bottom every ~6s while waiting to trigger lazy-loaded content
123
+ await waitForCopyButton(tab, S.copyButton, {
124
+ timeout: 120000,
125
+ onPoll: (tick) => tick % 10 === 0 ? scrollToBottom(tab) : Promise.resolve(),
126
+ });
142
127
 
143
128
  const { answer, sources } = await extractAnswer(tab);
144
129
  if (!answer) throw new Error("No answer captured from Gemini clipboard");
@@ -16,53 +16,17 @@ import {
16
16
  handleError,
17
17
  outputJson,
18
18
  parseArgs,
19
+ TIMING,
19
20
  validateQuery,
21
+ waitForStreamComplete,
20
22
  } from "./common.mjs";
21
23
  import { dismissConsent, handleVerification } from "./consent.mjs";
22
24
  import { SELECTORS } from "./selectors.mjs";
23
25
 
24
26
  const S = SELECTORS.google;
25
27
 
26
- const STREAM_POLL_INTERVAL = 600;
27
- const STREAM_STABLE_ROUNDS = 3;
28
- const STREAM_TIMEOUT = 45000;
29
28
  const MIN_ANSWER_LENGTH = 50;
30
29
 
31
- // ============================================================================
32
- // Google AI-specific helpers
33
- // ============================================================================
34
-
35
- async function waitForGoogleStreamComplete(tab) {
36
- const deadline = Date.now() + STREAM_TIMEOUT;
37
- let stableCount = 0;
38
- let lastLen = -1;
39
-
40
- while (Date.now() < deadline) {
41
- await new Promise((r) => setTimeout(r, STREAM_POLL_INTERVAL));
42
-
43
- const lenStr = await cdp([
44
- "eval",
45
- tab,
46
- `(document.querySelector('${S.answerContainer}')?.innerText?.length || 0) + ''`,
47
- ]).catch(() => "0");
48
-
49
- const len = parseInt(lenStr, 10) || 0;
50
-
51
- if (len >= MIN_ANSWER_LENGTH && len === lastLen) {
52
- stableCount++;
53
- if (stableCount >= STREAM_STABLE_ROUNDS) return len;
54
- } else {
55
- stableCount = 0;
56
- lastLen = len;
57
- }
58
- }
59
-
60
- if (lastLen >= MIN_ANSWER_LENGTH) return lastLen;
61
- throw new Error(
62
- `Google AI answer did not stabilise within ${STREAM_TIMEOUT}ms`,
63
- );
64
- }
65
-
66
30
  async function extractAnswer(tab) {
67
31
  const excludeFilter = S.sourceExclude
68
32
  .map((e) => `!a.href.includes('${e}')`)
@@ -107,7 +71,7 @@ async function main() {
107
71
 
108
72
  const url = `https://www.google.com/search?q=${encodeURIComponent(query)}&udm=50&hl=en`;
109
73
  await cdp(["nav", tab, url], 35000);
110
- await new Promise((r) => setTimeout(r, 1500));
74
+ await new Promise((r) => setTimeout(r, TIMING.postNav));
111
75
  await dismissConsent(tab, cdp);
112
76
 
113
77
  // If consent redirected us away, navigate back
@@ -116,7 +80,7 @@ async function main() {
116
80
  );
117
81
  if (!currentUrl.includes("google.com/search")) {
118
82
  await cdp(["nav", tab, url], 35000);
119
- await new Promise((r) => setTimeout(r, 1500));
83
+ await new Promise((r) => setTimeout(r, TIMING.postNav));
120
84
  }
121
85
 
122
86
  // Handle "verify you're human" — auto-click simple buttons, wait for user on hard CAPTCHA
@@ -128,10 +92,14 @@ async function main() {
128
92
  if (verifyResult === "clicked" || verifyResult === "cleared-by-user") {
129
93
  // Re-navigate to the search URL after verification
130
94
  await cdp(["nav", tab, url], 35000);
131
- await new Promise((r) => setTimeout(r, 1500));
95
+ await new Promise((r) => setTimeout(r, TIMING.postNav));
132
96
  }
133
97
 
134
- await waitForGoogleStreamComplete(tab);
98
+ await waitForStreamComplete(tab, {
99
+ timeout: 45000,
100
+ selector: `document.querySelector('${S.answerContainer}')`,
101
+ minLength: MIN_ANSWER_LENGTH,
102
+ });
135
103
 
136
104
  const { answer, sources } = await extractAnswer(tab);
137
105
  if (!answer)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@apmantza/greedysearch-pi",
3
- "version": "1.7.3",
3
+ "version": "1.7.5",
4
4
  "description": "Pi extension: multi-engine AI search (Perplexity, Bing Copilot, Google AI) via browser automation -- NO API KEYS needed. Extracts answers with sources, optional Gemini synthesis. Grounded AI answers from real browser interactions.",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -0,0 +1,44 @@
1
+ ---
2
+ name: greedy-search
3
+ description: Live web search via Perplexity, Bing, and Google AI in parallel. Use for library docs, recent framework changes, error messages, dependency selection, or anything where training data may be stale. NOT for codebase search.
4
+ ---
5
+
6
+ # GreedySearch — Live Web Search
7
+
8
+ Runs Perplexity, Bing Copilot, and Google AI in parallel. Gemini synthesizes results.
9
+
10
+ ## greedy_search
11
+
12
+ ```
13
+ greedy_search({ query: "React 19 changes", depth: "standard" })
14
+ ```
15
+
16
+ | Parameter | Type | Default | Description |
17
+ |-----------|------|---------|-------------|
18
+ | `query` | string | required | Search question |
19
+ | `engine` | string | `"all"` | `all`, `perplexity`, `bing`, `google`, `gemini` |
20
+ | `depth` | string | `"standard"` | `fast`, `standard`, `deep` |
21
+ | `fullAnswer` | boolean | `false` | Full answer vs ~300 char summary |
22
+
23
+ | Depth | Engines | Synthesis | Source Fetch | Time |
24
+ |-------|---------|-----------|--------------|------|
25
+ | `fast` | 1 | — | — | 15-30s |
26
+ | `standard` | 3 | Gemini | — | 30-90s |
27
+ | `deep` | 3 | Gemini | top 5 | 60-180s |
28
+
29
+ **When engines agree** → high confidence. **When they diverge** → note both perspectives.
30
+
31
+ ## coding_task
32
+
33
+ Second opinion from Gemini/Copilot on hard problems.
34
+
35
+ ```
36
+ coding_task({ task: "debug race condition", mode: "debug", engine: "gemini" })
37
+ ```
38
+
39
+ | Parameter | Type | Default | Options |
40
+ |-----------|------|---------|---------|
41
+ | `task` | string | required | — |
42
+ | `engine` | string | `"gemini"` | `gemini`, `copilot`, `all` |
43
+ | `mode` | string | `"code"` | `debug`, `plan`, `review`, `test`, `code` |
44
+ | `context` | string | — | Code snippet |
@@ -1,117 +0,0 @@
1
- ---
2
- name: greedy-search
3
- description: Multi-engine AI **WEB SEARCH** tool — NOT for codebase search. Use greedy_search for high-quality web research where training data may be stale or single-engine results are insufficient. Searches Perplexity, Bing, Google via browser automation. NO API KEYS needed.
4
- ---
5
-
6
- # ⚠️ WEB SEARCH ONLY — NOT CODEBASE SEARCH
7
-
8
- **`greedy_search` searches the live web**, not your local codebase.
9
-
10
- | Tool | Searches |
11
- |------|----------|
12
- | `greedy_search` | **Live web** (Perplexity, Bing, Google) |
13
- | `ast_grep_search` | **Local codebase** — use this for code patterns |
14
- | `bash` with `grep/rg` | **Local codebase** — use this for text search |
15
-
16
- **DO NOT use `greedy_search` for:**
17
- - Finding functions in your codebase
18
- - Searching local files
19
- - Code review of your project
20
- - Understanding project structure
21
-
22
- **DO use `greedy_search` for:**
23
- - Library documentation
24
- - Recent framework changes
25
- - Error message explanations
26
- - Best practices research
27
- - Current events/news
28
-
29
- # GreedySearch Tools
30
-
31
- | Tool | Speed | Use For |
32
- |------|-------|---------|
33
- | `greedy_search` | 15-180s | Multi-engine search with depth levels |
34
- | `coding_task` | 60-180s | Debug, review, plan modes for hard problems |
35
-
36
- ## greedy_search
37
-
38
- Multi-engine AI search (Perplexity, Bing, Google) with three depth levels.
39
-
40
- ```greedy_search({ query: "React 19 changes", depth: "standard" })```
41
-
42
- | Parameter | Type | Default | Description |
43
- |-----------|------|---------|-------------|
44
- | `query` | string | required | Search question |
45
- | `engine` | string | `"all"` | `all`, `perplexity`, `bing`, `google`, `gemini` |
46
- | `depth` | string | `"standard"` | `fast`, `standard`, `deep` — see below |
47
- | `fullAnswer` | boolean | `false` | Complete vs ~300 char summary |
48
-
49
- ### Depth Levels
50
-
51
- | Depth | Engines | Synthesis | Source Fetch | Time | Use When |
52
- |-------|---------|-----------|--------------|------|----------|
53
- | `fast` | 1 | ❌ | ❌ | 15-30s | Quick lookup, single perspective |
54
- | `standard` | 3 | ✅ | ❌ | 30-90s | Default — balanced speed/quality |
55
- | `deep` | 3 | ✅ | ✅ (top 5) | 60-180s | Research that matters — architecture decisions |
56
-
57
- **Standard** (default): Runs 3 engines, deduplicates sources, synthesizes via Gemini.
58
- **Deep**: Same + fetches content from top sources for grounded synthesis + confidence scores.
59
-
60
- ### Engine Selection (for fast mode)
61
-
62
- ```greedy_search({ query: "...", engine: "perplexity", depth: "fast" })```
63
-
64
- - `perplexity`: Technical Q&A, citations
65
- - `bing`: Recent news, Microsoft ecosystem
66
- - `google`: Broad coverage
67
- - `gemini`: Different training data
68
-
69
- ### Examples — Web Research Only
70
-
71
- **✅ GOOD — Web research:**
72
- ```greedy_search({ query: "what changed in React 19", depth: "fast" })```
73
- ```greedy_search({ query: "best auth patterns for SaaS", depth: "deep" })```
74
- ```greedy_search({ query: "Prisma vs Drizzle 2026", depth: "standard", fullAnswer: true })```
75
-
76
- **❌ WRONG — Don't use for codebase search:**
77
- ```javascript
78
- // DON'T: Searching your own codebase
79
- // greedy_search({ query: "find UserService class" }) // ❌ Won't find it!
80
-
81
- // DO: Use these instead for codebase search:
82
- // ast_grep_search({ pattern: "class UserService", lang: "typescript" })
83
- // bash({ command: "rg 'class UserService' --type ts" })
84
- ```
85
-
86
- ### Legacy
87
-
88
- `deep_research` tool still works — aliases to `greedy_search` with `depth: "deep"`.
89
-
90
- ## coding_task
91
-
92
- Browser-based coding assistant via Gemini/Copilot.
93
-
94
- ```coding_task({ task: "debug race condition", mode: "debug", engine: "gemini" })```
95
-
96
- | Parameter | Type | Default | Description |
97
- |-----------|------|---------|-------------|
98
- | `task` | string | required | Coding task/question |
99
- | `engine` | string | `"gemini"` | `gemini`, `copilot`, `all` |
100
- | `mode` | string | `"code"` | `debug`, `plan`, `review`, `test`, `code` |
101
- | `context` | string | — | Code snippet to include |
102
-
103
- **Modes:**
104
- - `debug`: Tricky bugs — fresh eyes catch different failure modes
105
- - `plan`: Big refactor — plays devil's advocate on risks
106
- - `review`: High-stakes code review before merge
107
- - `test`: Edge cases the author missed
108
- - `code`: Simple generation (but you're probably faster)
109
-
110
- **When to use:** Second opinions on hard problems. Skip for simple code.
111
-
112
- ## Result Interpretation
113
-
114
- - **All 3 engines agree** → High confidence, present as fact
115
- - **2 agree, 1 differs** → Likely correct, note dissent
116
- - **Sources [3/3] or [2/3]** → Multiple engines cite, higher confidence
117
- - **Deep research confidence scores** → Structured confidence metadata