@apmantza/greedysearch-pi 1.8.2 → 1.8.4
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 +17 -0
- package/README.md +10 -1
- package/bin/launch.mjs +366 -366
- package/bin/search.mjs +388 -388
- package/extractors/common.mjs +291 -291
- package/extractors/gemini.mjs +146 -146
- package/extractors/google-ai.mjs +125 -125
- package/extractors/perplexity.mjs +147 -145
- package/extractors/selectors.mjs +54 -54
- package/index.ts +256 -278
- package/package.json +1 -1
- package/src/github.mjs +237 -237
- package/src/reddit.mjs +210 -0
- package/src/search/chrome.mjs +222 -222
- package/src/search/constants.mjs +37 -37
- package/src/search/defaults.mjs +14 -14
- package/src/search/engines.mjs +62 -62
- package/src/search/fetch-source.mjs +35 -3
- package/src/search/output.mjs +58 -58
- package/src/search/sources.mjs +445 -445
- package/src/search/synthesis-runner.mjs +63 -63
- package/src/search/synthesis.mjs +223 -223
- package/src/tools/deep-research-handler.ts +36 -36
- package/src/tools/greedy-search-handler.ts +53 -57
- package/src/tools/shared.ts +135 -130
- package/src/types.ts +103 -103
- package/test.mjs +423 -377
package/index.ts
CHANGED
|
@@ -1,278 +1,256 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* GreedySearch Pi Extension
|
|
3
|
-
*
|
|
4
|
-
* Adds `greedy_search`, `deep_research`, and `coding_task` tools to Pi.
|
|
5
|
-
* Tool handlers are split into separate modules for maintainability.
|
|
6
|
-
*
|
|
7
|
-
* Reports streaming progress as each engine completes.
|
|
8
|
-
* Requires Chrome to be running (or it auto-launches a dedicated instance).
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import { spawn } from "node:child_process";
|
|
12
|
-
import { dirname, join } from "node:path";
|
|
13
|
-
import { fileURLToPath } from "node:url";
|
|
14
|
-
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
15
|
-
import { Type } from "@sinclair/typebox";
|
|
16
|
-
|
|
17
|
-
import { formatCodingTask } from "./src/formatters/coding.js";
|
|
18
|
-
import { DEFAULTS } from "./src/search/defaults.mjs";
|
|
19
|
-
import { registerDeepResearchTool } from "./src/tools/deep-research-handler.js";
|
|
20
|
-
import { registerGreedySearchTool } from "./src/tools/greedy-search-handler.js";
|
|
21
|
-
import { cdpAvailable, type ProgressUpdate } from "./src/tools/shared.js";
|
|
22
|
-
|
|
23
|
-
const __dir = dirname(fileURLToPath(import.meta.url));
|
|
24
|
-
|
|
25
|
-
export default function greedySearchExtension(pi: ExtensionAPI) {
|
|
26
|
-
pi.on("session_start", async (_event, ctx) => {
|
|
27
|
-
if (!cdpAvailable(__dir)) {
|
|
28
|
-
ctx.ui.notify(
|
|
29
|
-
"GreedySearch: cdp.mjs missing from package directory — try reinstalling: pi install git:github.com/apmantza/GreedySearch-pi",
|
|
30
|
-
"warning",
|
|
31
|
-
);
|
|
32
|
-
}
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
// ─── greedy_search ────────────────────────────────────────────────────────
|
|
36
|
-
registerGreedySearchTool(pi, __dir);
|
|
37
|
-
|
|
38
|
-
// ─── deep_research ────────────────────────────────────────────────────────
|
|
39
|
-
registerDeepResearchTool(pi, __dir);
|
|
40
|
-
|
|
41
|
-
// ─── coding_task ───────────────────────────────────────────────────────────
|
|
42
|
-
pi.registerTool({
|
|
43
|
-
name: "coding_task",
|
|
44
|
-
label: "Coding Task",
|
|
45
|
-
description:
|
|
46
|
-
"Delegate a coding task to Gemini and/or Copilot via browser automation. " +
|
|
47
|
-
"Returns extracted code blocks and explanations. Supports multiple modes: " +
|
|
48
|
-
"'code' (write/modify code), 'review' (senior engineer code review), " +
|
|
49
|
-
"'plan' (architect risk assessment), 'test' (edge case testing), " +
|
|
50
|
-
"'debug' (fresh-eyes root cause analysis). " +
|
|
51
|
-
"Best for getting a 'second opinion' on hard problems, debugging tricky issues, " +
|
|
52
|
-
"or risk-assessing major refactors. Use engine 'all' for both perspectives.",
|
|
53
|
-
promptSnippet: "Browser-based coding assistant with Gemini and Copilot",
|
|
54
|
-
parameters: Type.Object({
|
|
55
|
-
task: Type.String({ description: "The coding task or question" }),
|
|
56
|
-
engine: Type.
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
),
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
return
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
const
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
"
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
});
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
258
|
-
// Config helpers for /set-greedy-locale command
|
|
259
|
-
import { homedir } from "node:os";
|
|
260
|
-
|
|
261
|
-
const USER_CONFIG_DIR = join(homedir(), ".config", "greedysearch");
|
|
262
|
-
const USER_CONFIG_FILE = join(USER_CONFIG_DIR, "config.json");
|
|
263
|
-
|
|
264
|
-
function loadUserConfig(): Record<string, string> {
|
|
265
|
-
try {
|
|
266
|
-
if (existsSync(USER_CONFIG_FILE)) {
|
|
267
|
-
return JSON.parse(readFileSync(USER_CONFIG_FILE, "utf8"));
|
|
268
|
-
}
|
|
269
|
-
} catch {
|
|
270
|
-
// Ignore parse errors
|
|
271
|
-
}
|
|
272
|
-
return {};
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
function saveUserConfig(config: Record<string, string>): void {
|
|
276
|
-
mkdirSync(USER_CONFIG_DIR, { recursive: true });
|
|
277
|
-
writeFileSync(USER_CONFIG_FILE, JSON.stringify(config, null, 2), "utf8");
|
|
278
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* GreedySearch Pi Extension
|
|
3
|
+
*
|
|
4
|
+
* Adds `greedy_search`, `deep_research`, and `coding_task` tools to Pi.
|
|
5
|
+
* Tool handlers are split into separate modules for maintainability.
|
|
6
|
+
*
|
|
7
|
+
* Reports streaming progress as each engine completes.
|
|
8
|
+
* Requires Chrome to be running (or it auto-launches a dedicated instance).
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { spawn } from "node:child_process";
|
|
12
|
+
import { dirname, join } from "node:path";
|
|
13
|
+
import { fileURLToPath } from "node:url";
|
|
14
|
+
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
15
|
+
import { Type } from "@sinclair/typebox";
|
|
16
|
+
|
|
17
|
+
import { formatCodingTask } from "./src/formatters/coding.js";
|
|
18
|
+
import { DEFAULTS } from "./src/search/defaults.mjs";
|
|
19
|
+
import { registerDeepResearchTool } from "./src/tools/deep-research-handler.js";
|
|
20
|
+
import { registerGreedySearchTool } from "./src/tools/greedy-search-handler.js";
|
|
21
|
+
import { cdpAvailable, stripQuotes, type ProgressUpdate } from "./src/tools/shared.js";
|
|
22
|
+
|
|
23
|
+
const __dir = dirname(fileURLToPath(import.meta.url));
|
|
24
|
+
|
|
25
|
+
export default function greedySearchExtension(pi: ExtensionAPI) {
|
|
26
|
+
pi.on("session_start", async (_event, ctx) => {
|
|
27
|
+
if (!cdpAvailable(__dir)) {
|
|
28
|
+
ctx.ui.notify(
|
|
29
|
+
"GreedySearch: cdp.mjs missing from package directory — try reinstalling: pi install git:github.com/apmantza/GreedySearch-pi",
|
|
30
|
+
"warning",
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
// ─── greedy_search ────────────────────────────────────────────────────────
|
|
36
|
+
registerGreedySearchTool(pi, __dir);
|
|
37
|
+
|
|
38
|
+
// ─── deep_research ────────────────────────────────────────────────────────
|
|
39
|
+
registerDeepResearchTool(pi, __dir);
|
|
40
|
+
|
|
41
|
+
// ─── coding_task ───────────────────────────────────────────────────────────
|
|
42
|
+
pi.registerTool({
|
|
43
|
+
name: "coding_task",
|
|
44
|
+
label: "Coding Task",
|
|
45
|
+
description:
|
|
46
|
+
"Delegate a coding task to Gemini and/or Copilot via browser automation. " +
|
|
47
|
+
"Returns extracted code blocks and explanations. Supports multiple modes: " +
|
|
48
|
+
"'code' (write/modify code), 'review' (senior engineer code review), " +
|
|
49
|
+
"'plan' (architect risk assessment), 'test' (edge case testing), " +
|
|
50
|
+
"'debug' (fresh-eyes root cause analysis). " +
|
|
51
|
+
"Best for getting a 'second opinion' on hard problems, debugging tricky issues, " +
|
|
52
|
+
"or risk-assessing major refactors. Use engine 'all' for both perspectives.",
|
|
53
|
+
promptSnippet: "Browser-based coding assistant with Gemini and Copilot",
|
|
54
|
+
parameters: Type.Object({
|
|
55
|
+
task: Type.String({ description: "The coding task or question" }),
|
|
56
|
+
engine: Type.String({
|
|
57
|
+
description: 'Engine to use: "all" (runs both Gemini and Copilot in parallel), "gemini" (default), "copilot".',
|
|
58
|
+
default: "gemini",
|
|
59
|
+
}),
|
|
60
|
+
mode: Type.String({
|
|
61
|
+
description: 'Task mode: "code" (default), "review" (code review), "plan" (architect review), "test" (write tests), "debug" (root cause analysis).',
|
|
62
|
+
default: "code",
|
|
63
|
+
}),
|
|
64
|
+
context: Type.Optional(
|
|
65
|
+
Type.String({
|
|
66
|
+
description: "Optional code context/snippet to include with the task",
|
|
67
|
+
}),
|
|
68
|
+
),
|
|
69
|
+
}),
|
|
70
|
+
execute: async (_toolCallId, params, signal, onUpdate) => {
|
|
71
|
+
const { task, context } = params as { task: string; engine: string; mode: string; context?: string };
|
|
72
|
+
const engine = stripQuotes((params as any).engine ?? "gemini") || "gemini";
|
|
73
|
+
const mode = stripQuotes((params as any).mode ?? "code") || "code";
|
|
74
|
+
|
|
75
|
+
if (!cdpAvailable(__dir)) {
|
|
76
|
+
return {
|
|
77
|
+
content: [
|
|
78
|
+
{ type: "text", text: "cdp.mjs missing — try reinstalling." },
|
|
79
|
+
],
|
|
80
|
+
details: {} as { raw?: Record<string, unknown> },
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const flags: string[] = ["--engine", engine, "--mode", mode];
|
|
85
|
+
if (context) flags.push("--context", context);
|
|
86
|
+
|
|
87
|
+
try {
|
|
88
|
+
onUpdate?.({
|
|
89
|
+
content: [
|
|
90
|
+
{
|
|
91
|
+
type: "text",
|
|
92
|
+
text: `**Coding task...** 🔄 ${engine === "all" ? "Gemini + Copilot" : engine} (${mode} mode)`,
|
|
93
|
+
},
|
|
94
|
+
],
|
|
95
|
+
details: { _progress: true },
|
|
96
|
+
} satisfies ProgressUpdate);
|
|
97
|
+
|
|
98
|
+
const data = await new Promise<Record<string, unknown>>(
|
|
99
|
+
(resolve, reject) => {
|
|
100
|
+
const proc = spawn(
|
|
101
|
+
"node",
|
|
102
|
+
[join(__dir, "bin", "coding-task.mjs"), task, ...flags],
|
|
103
|
+
{
|
|
104
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
105
|
+
},
|
|
106
|
+
);
|
|
107
|
+
let out = "";
|
|
108
|
+
let err = "";
|
|
109
|
+
|
|
110
|
+
const onAbort = () => {
|
|
111
|
+
proc.kill("SIGTERM");
|
|
112
|
+
reject(new Error("Aborted"));
|
|
113
|
+
};
|
|
114
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
115
|
+
|
|
116
|
+
proc.stdout.on("data", (d: Buffer) => (out += d));
|
|
117
|
+
proc.stderr.on("data", (d: Buffer) => (err += d));
|
|
118
|
+
proc.on("close", (code: number) => {
|
|
119
|
+
signal?.removeEventListener("abort", onAbort);
|
|
120
|
+
if (code !== 0) {
|
|
121
|
+
reject(
|
|
122
|
+
new Error(
|
|
123
|
+
err.trim() || `coding-task.mjs exited with code ${code}`,
|
|
124
|
+
),
|
|
125
|
+
);
|
|
126
|
+
} else {
|
|
127
|
+
try {
|
|
128
|
+
resolve(JSON.parse(out.trim()));
|
|
129
|
+
} catch {
|
|
130
|
+
reject(
|
|
131
|
+
new Error(
|
|
132
|
+
`Invalid JSON from coding-task.mjs: ${out.slice(0, 200)}`,
|
|
133
|
+
),
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
// Timeout after 3 minutes
|
|
140
|
+
setTimeout(() => {
|
|
141
|
+
proc.kill("SIGTERM");
|
|
142
|
+
reject(
|
|
143
|
+
new Error(
|
|
144
|
+
`Coding task timed out after ${DEFAULTS.CODING_TASK_TIMEOUT / 1000}s`,
|
|
145
|
+
),
|
|
146
|
+
);
|
|
147
|
+
}, DEFAULTS.CODING_TASK_TIMEOUT);
|
|
148
|
+
},
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
const text = formatCodingTask(data);
|
|
152
|
+
return {
|
|
153
|
+
content: [{ type: "text", text: text || "No response." }],
|
|
154
|
+
details: { raw: data },
|
|
155
|
+
};
|
|
156
|
+
} catch (e) {
|
|
157
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
158
|
+
return {
|
|
159
|
+
content: [{ type: "text", text: `Coding task failed: ${msg}` }],
|
|
160
|
+
details: {} as { raw?: Record<string, unknown> },
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
// ─── /set-greedy-locale command ───────────────────────────────────────────
|
|
167
|
+
pi.registerCommand("set-greedy-locale", {
|
|
168
|
+
description:
|
|
169
|
+
"Set default locale for GreedySearch results (e.g., /set-greedy-locale de, /set-greedy-locale --clear, /set-greedy-locale --show)",
|
|
170
|
+
handler: async (args, ctx) => {
|
|
171
|
+
const arg = args.trim() || "--show";
|
|
172
|
+
|
|
173
|
+
if (arg === "--show") {
|
|
174
|
+
const config = loadUserConfig();
|
|
175
|
+
if (config.locale) {
|
|
176
|
+
ctx.ui.notify(`Default locale: ${config.locale}`, "info");
|
|
177
|
+
} else {
|
|
178
|
+
ctx.ui.notify("No default locale (uses: en)", "info");
|
|
179
|
+
}
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (arg === "--clear") {
|
|
184
|
+
const config = loadUserConfig();
|
|
185
|
+
delete config.locale;
|
|
186
|
+
saveUserConfig(config);
|
|
187
|
+
ctx.ui.notify("Default locale cleared (now uses: en).", "info");
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Set locale
|
|
192
|
+
const locale = arg.toLowerCase();
|
|
193
|
+
const VALID_LOCALES = [
|
|
194
|
+
"en",
|
|
195
|
+
"de",
|
|
196
|
+
"fr",
|
|
197
|
+
"es",
|
|
198
|
+
"it",
|
|
199
|
+
"pt",
|
|
200
|
+
"nl",
|
|
201
|
+
"pl",
|
|
202
|
+
"ru",
|
|
203
|
+
"ja",
|
|
204
|
+
"ko",
|
|
205
|
+
"zh",
|
|
206
|
+
"ar",
|
|
207
|
+
"hi",
|
|
208
|
+
"tr",
|
|
209
|
+
"sv",
|
|
210
|
+
"da",
|
|
211
|
+
"no",
|
|
212
|
+
"fi",
|
|
213
|
+
"cs",
|
|
214
|
+
"hu",
|
|
215
|
+
"ro",
|
|
216
|
+
"el",
|
|
217
|
+
];
|
|
218
|
+
|
|
219
|
+
if (!VALID_LOCALES.includes(locale)) {
|
|
220
|
+
ctx.ui.notify(
|
|
221
|
+
`Invalid locale "${locale}". Valid: ${VALID_LOCALES.join(", ")}`,
|
|
222
|
+
"error",
|
|
223
|
+
);
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const config = loadUserConfig();
|
|
228
|
+
config.locale = locale;
|
|
229
|
+
saveUserConfig(config);
|
|
230
|
+
ctx.ui.notify(`Default locale set to: ${locale}`, "info");
|
|
231
|
+
},
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
236
|
+
// Config helpers for /set-greedy-locale command
|
|
237
|
+
import { homedir } from "node:os";
|
|
238
|
+
|
|
239
|
+
const USER_CONFIG_DIR = join(homedir(), ".config", "greedysearch");
|
|
240
|
+
const USER_CONFIG_FILE = join(USER_CONFIG_DIR, "config.json");
|
|
241
|
+
|
|
242
|
+
function loadUserConfig(): Record<string, string> {
|
|
243
|
+
try {
|
|
244
|
+
if (existsSync(USER_CONFIG_FILE)) {
|
|
245
|
+
return JSON.parse(readFileSync(USER_CONFIG_FILE, "utf8"));
|
|
246
|
+
}
|
|
247
|
+
} catch {
|
|
248
|
+
// Ignore parse errors
|
|
249
|
+
}
|
|
250
|
+
return {};
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function saveUserConfig(config: Record<string, string>): void {
|
|
254
|
+
mkdirSync(USER_CONFIG_DIR, { recursive: true });
|
|
255
|
+
writeFileSync(USER_CONFIG_FILE, JSON.stringify(config, null, 2), "utf8");
|
|
256
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@apmantza/greedysearch-pi",
|
|
3
|
-
"version": "1.8.
|
|
3
|
+
"version": "1.8.4",
|
|
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": [
|