@duckmind/dm-windows-x64 0.62.3 → 0.63.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.
Files changed (93) hide show
  1. package/dm.exe +0 -0
  2. package/extensions/.dm-extensions.json +43 -183
  3. package/extensions/dm-context/src/context.js +1 -2
  4. package/extensions/dm-context/src/index.js +1 -2
  5. package/extensions/{greedysearch-dm/bin/cdp.mjs → dm-cua/bin/browser-cdp.mjs} +3 -3
  6. package/extensions/dm-cua/bin/browser-cua.mjs +1 -1
  7. package/extensions/{greedysearch-dm/bin/launch.mjs → dm-cua/bin/browser-launch.mjs} +12 -11
  8. package/extensions/dm-cua/index.js +1 -1
  9. package/extensions/dm-cua/src/browser-cua-lib.mjs +17 -12
  10. package/extensions/{greedysearch-dm/src/search → dm-cua/src/browser-helpers}/constants.mjs +11 -11
  11. package/extensions/{greedysearch-dm/src/utils → dm-cua/src/browser-helpers}/node-runtime.mjs +1 -1
  12. package/extensions/{greedysearch-dm/src/search → dm-cua/src/browser-helpers}/port-pid.mjs +1 -1
  13. package/extensions/dm-ponytail/extensions/ponytail.mjs +186 -0
  14. package/extensions/dm-ponytail/hooks/claude-codex-hooks.json +41 -0
  15. package/extensions/dm-ponytail/hooks/copilot-hooks.json +21 -0
  16. package/extensions/dm-ponytail/hooks/ponytail-activate.js +59 -0
  17. package/extensions/dm-ponytail/hooks/ponytail-config.js +116 -0
  18. package/extensions/dm-ponytail/hooks/ponytail-instructions.js +81 -0
  19. package/extensions/dm-ponytail/hooks/ponytail-mode-tracker.js +96 -0
  20. package/extensions/dm-ponytail/hooks/ponytail-runtime.js +79 -0
  21. package/extensions/dm-ponytail/hooks/ponytail-statusline.ps1 +24 -0
  22. package/extensions/dm-ponytail/hooks/ponytail-statusline.sh +18 -0
  23. package/extensions/dm-ponytail/hooks/ponytail-subagent.js +50 -0
  24. package/extensions/dm-ponytail/hooks/qoder-hooks.json +26 -0
  25. package/extensions/dm-ponytail/package.json +19 -0
  26. package/extensions/dm-ponytail/pi-extension/index.js +186 -0
  27. package/extensions/dm-ponytail/pi-extension/package.json +8 -0
  28. package/extensions/dm-ponytail/skills/ponytail/SKILL.md +120 -0
  29. package/extensions/dm-ponytail/skills/ponytail-audit/SKILL.md +41 -0
  30. package/extensions/dm-ponytail/skills/ponytail-debt/SKILL.md +44 -0
  31. package/extensions/dm-ponytail/skills/ponytail-gain/SKILL.md +50 -0
  32. package/extensions/dm-ponytail/skills/ponytail-help/SKILL.md +71 -0
  33. package/extensions/dm-ponytail/skills/ponytail-review/SKILL.md +57 -0
  34. package/package.json +2 -6
  35. package/extensions/dm-caveman/extensions/caveman.js +0 -300
  36. package/extensions/dm-caveman/package.json +0 -20
  37. package/extensions/dm-goal/package.json +0 -23
  38. package/extensions/dm-goal/src/goal.js +0 -914
  39. package/extensions/greedysearch-dm/bin/cdp-greedy.mjs +0 -40
  40. package/extensions/greedysearch-dm/bin/cdp-headless.mjs +0 -5
  41. package/extensions/greedysearch-dm/bin/cdp-visible.mjs +0 -5
  42. package/extensions/greedysearch-dm/bin/gschrome.mjs +0 -30
  43. package/extensions/greedysearch-dm/bin/kill-visible.mjs +0 -7
  44. package/extensions/greedysearch-dm/bin/launch-visible.mjs +0 -13
  45. package/extensions/greedysearch-dm/bin/mcp.mjs +0 -400
  46. package/extensions/greedysearch-dm/bin/search.mjs +0 -635
  47. package/extensions/greedysearch-dm/bin/visible.mjs +0 -22
  48. package/extensions/greedysearch-dm/extractors/bing-copilot.mjs +0 -344
  49. package/extensions/greedysearch-dm/extractors/chatgpt.mjs +0 -354
  50. package/extensions/greedysearch-dm/extractors/common.mjs +0 -794
  51. package/extensions/greedysearch-dm/extractors/consensus.mjs +0 -527
  52. package/extensions/greedysearch-dm/extractors/consent.mjs +0 -449
  53. package/extensions/greedysearch-dm/extractors/gemini.mjs +0 -291
  54. package/extensions/greedysearch-dm/extractors/google-ai.mjs +0 -86
  55. package/extensions/greedysearch-dm/extractors/logically.mjs +0 -589
  56. package/extensions/greedysearch-dm/extractors/perplexity.mjs +0 -370
  57. package/extensions/greedysearch-dm/extractors/selectors.mjs +0 -32
  58. package/extensions/greedysearch-dm/extractors/semantic-scholar.mjs +0 -199
  59. package/extensions/greedysearch-dm/index.js +0 -123
  60. package/extensions/greedysearch-dm/package.json +0 -27
  61. package/extensions/greedysearch-dm/skills/greedy-search/skill.md +0 -18
  62. package/extensions/greedysearch-dm/src/fetcher.mjs +0 -577
  63. package/extensions/greedysearch-dm/src/formatters/results.js +0 -95
  64. package/extensions/greedysearch-dm/src/formatters/sources.js +0 -57
  65. package/extensions/greedysearch-dm/src/formatters/synthesis.js +0 -49
  66. package/extensions/greedysearch-dm/src/github.mjs +0 -228
  67. package/extensions/greedysearch-dm/src/reddit.mjs +0 -154
  68. package/extensions/greedysearch-dm/src/search/browser-lifecycle.mjs +0 -340
  69. package/extensions/greedysearch-dm/src/search/challenge-detect.mjs +0 -156
  70. package/extensions/greedysearch-dm/src/search/chrome.mjs +0 -510
  71. package/extensions/greedysearch-dm/src/search/defaults.mjs +0 -10
  72. package/extensions/greedysearch-dm/src/search/engines.mjs +0 -79
  73. package/extensions/greedysearch-dm/src/search/fetch-source.mjs +0 -470
  74. package/extensions/greedysearch-dm/src/search/file-sources.mjs +0 -31
  75. package/extensions/greedysearch-dm/src/search/output.mjs +0 -51
  76. package/extensions/greedysearch-dm/src/search/paths.mjs +0 -48
  77. package/extensions/greedysearch-dm/src/search/pdf.mjs +0 -64
  78. package/extensions/greedysearch-dm/src/search/progress.mjs +0 -109
  79. package/extensions/greedysearch-dm/src/search/query.mjs +0 -21
  80. package/extensions/greedysearch-dm/src/search/recovery.mjs +0 -49
  81. package/extensions/greedysearch-dm/src/search/research.mjs +0 -2255
  82. package/extensions/greedysearch-dm/src/search/scale-aware.mjs +0 -61
  83. package/extensions/greedysearch-dm/src/search/simple-research.mjs +0 -418
  84. package/extensions/greedysearch-dm/src/search/sources.mjs +0 -412
  85. package/extensions/greedysearch-dm/src/search/synthesis-runner.mjs +0 -127
  86. package/extensions/greedysearch-dm/src/search/synthesis.mjs +0 -202
  87. package/extensions/greedysearch-dm/src/tools/greedy-search-handler.js +0 -209
  88. package/extensions/greedysearch-dm/src/tools/shared.js +0 -226
  89. package/extensions/greedysearch-dm/src/types.js +0 -0
  90. package/extensions/greedysearch-dm/src/utils/content.mjs +0 -38
  91. package/extensions/greedysearch-dm/src/utils/helpers.js +0 -22
  92. /package/extensions/{greedysearch-dm/src/search → dm-cua/src/browser-helpers}/minimize.mjs +0 -0
  93. /package/extensions/{greedysearch-dm/src/utils → dm-cua/src/browser-helpers}/system-cmds.mjs +0 -0
@@ -1,40 +0,0 @@
1
- import { spawn } from "node:child_process";
2
- import { nodeRuntimeCommand } from "../src/utils/node-runtime.mjs";
3
- import {
4
- ACTIVE_PORT_FILE,
5
- CHROME_MODE_FILE,
6
- GREEDY_PROFILE_DIR
7
- } from "../src/search/constants.mjs";
8
- import { existsSync, readFileSync } from "node:fs";
9
- import { tmpdir } from "node:os";
10
- const PROFILE_DIR = GREEDY_PROFILE_DIR;
11
- const ACTIVE_PORT = ACTIVE_PORT_FILE;
12
- const MODE_FILE = CHROME_MODE_FILE;
13
- const args = process.argv.slice(2);
14
- let desiredMode = null;
15
- const modeIdx = args.indexOf("--mode");
16
- if (modeIdx !== -1) {
17
- desiredMode = args[modeIdx + 1] || null;
18
- args.splice(modeIdx, 2);
19
- }
20
- if (desiredMode && !["visible", "headless"].includes(desiredMode)) {
21
- console.error(`Invalid --mode ${desiredMode}. Use visible or headless.`);
22
- process.exit(2);
23
- }
24
- if (!existsSync(ACTIVE_PORT)) {
25
- console.error(`GreedySearch Chrome is not running (missing ${ACTIVE_PORT}). Launch with bin/visible.mjs or bin/launch.mjs.`);
26
- process.exit(1);
27
- }
28
- if (desiredMode) {
29
- const actualMode = existsSync(MODE_FILE) ? readFileSync(MODE_FILE, "utf8").trim() : "unknown";
30
- if (actualMode !== desiredMode) {
31
- console.error(`GreedySearch Chrome is ${actualMode}, not ${desiredMode}. Refusing to attach.`);
32
- process.exit(1);
33
- }
34
- }
35
- const cdpBin = new URL("./cdp.mjs", import.meta.url).pathname.replace(/^\/([A-Z]:)/, "$1");
36
- const proc = spawn(nodeRuntimeCommand(), [cdpBin, ...args], {
37
- stdio: "inherit",
38
- env: { ...process.env, CDP_PROFILE_DIR: PROFILE_DIR }
39
- });
40
- proc.on("close", (code) => process.exit(code ?? 0));
@@ -1,5 +0,0 @@
1
- import { spawn } from "node:child_process";
2
- import { nodeRuntimeCommand } from "../src/utils/node-runtime.mjs";
3
- const cdpGreedyBin = new URL("./cdp-greedy.mjs", import.meta.url).pathname.replace(/^\/([A-Z]:)/, "$1");
4
- const proc = spawn(nodeRuntimeCommand(), [cdpGreedyBin, "--mode", "headless", ...process.argv.slice(2)], { stdio: "inherit" });
5
- proc.on("close", (code) => process.exit(code ?? 0));
@@ -1,5 +0,0 @@
1
- import { spawn } from "node:child_process";
2
- import { nodeRuntimeCommand } from "../src/utils/node-runtime.mjs";
3
- const cdpGreedyBin = new URL("./cdp-greedy.mjs", import.meta.url).pathname.replace(/^\/([A-Z]:)/, "$1");
4
- const proc = spawn(nodeRuntimeCommand(), [cdpGreedyBin, "--mode", "visible", ...process.argv.slice(2)], { stdio: "inherit" });
5
- proc.on("close", (code) => process.exit(code ?? 0));
@@ -1,30 +0,0 @@
1
- import { spawn } from "node:child_process";
2
- import { fileURLToPath } from "node:url";
3
- import { nodeRuntimeCommand } from "../src/utils/node-runtime.mjs";
4
- const command = process.argv[2] || "status";
5
- const launch = fileURLToPath(new URL("./launch.mjs", import.meta.url));
6
- const map = {
7
- "launch-headless": { args: [], visible: false },
8
- "launch-visible": { args: [], visible: true },
9
- kill: { args: ["--kill"], visible: null },
10
- status: { args: ["--status"], visible: null }
11
- };
12
- const selected = map[command];
13
- if (!selected) {
14
- console.error("Usage: gschrome.mjs launch-headless|launch-visible|kill|status");
15
- process.exit(2);
16
- }
17
- const env = { ...process.env };
18
- if (selected.visible === true)
19
- env.GREEDY_SEARCH_VISIBLE = "1";
20
- if (selected.visible === false)
21
- delete env.GREEDY_SEARCH_VISIBLE;
22
- const proc = spawn(nodeRuntimeCommand(), [launch, ...selected.args], {
23
- stdio: "inherit",
24
- env
25
- });
26
- proc.on("exit", (code, signal) => {
27
- if (signal)
28
- process.kill(process.pid, signal);
29
- process.exit(code ?? 1);
30
- });
@@ -1,7 +0,0 @@
1
- import { spawn } from "node:child_process";
2
- import { nodeRuntimeCommand } from "../src/utils/node-runtime.mjs";
3
- const launchVisibleBin = new URL("./launch-visible.mjs", import.meta.url).pathname.replace(/^\/([A-Z]:)/, "$1");
4
- const proc = spawn(nodeRuntimeCommand(), [launchVisibleBin, "--kill"], {
5
- stdio: "inherit"
6
- });
7
- proc.on("close", (code) => process.exit(code ?? 0));
@@ -1,13 +0,0 @@
1
- import { spawn } from "node:child_process";
2
- import { fileURLToPath } from "node:url";
3
- import { nodeRuntimeCommand } from "../src/utils/node-runtime.mjs";
4
- const launch = fileURLToPath(new URL("./launch.mjs", import.meta.url));
5
- const proc = spawn(nodeRuntimeCommand(), [launch, ...process.argv.slice(2)], {
6
- stdio: "inherit",
7
- env: { ...process.env, GREEDY_SEARCH_VISIBLE: "1" }
8
- });
9
- proc.on("exit", (code, signal) => {
10
- if (signal)
11
- process.kill(process.pid, signal);
12
- process.exit(code ?? 1);
13
- });
@@ -1,400 +0,0 @@
1
- import { spawn } from "node:child_process";
2
- import { nodeRuntimeCommand } from "../src/utils/node-runtime.mjs";
3
- import { createInterface } from "node:readline";
4
- import { fileURLToPath } from "node:url";
5
- import { dirname, join } from "node:path";
6
- const __dirname = dirname(fileURLToPath(import.meta.url));
7
- const SEARCH_BIN = join(__dirname, "search.mjs");
8
- const PROTOCOL_VERSION = "2025-06-18";
9
- const SERVER_INFO = { name: "greedysearch", version: "1.0.0" };
10
- const DEFAULT_TIMEOUT_MS = 6 * 60 * 1000;
11
- function log(...args) {
12
- process.stderr.write(`[greedysearch-mcp] ${args.join(" ")}
13
- `);
14
- }
15
- function send(message) {
16
- process.stdout.write(`${JSON.stringify(message)}
17
- `);
18
- }
19
- function sendResult(id, result) {
20
- if (id === undefined || id === null)
21
- return;
22
- send({ jsonrpc: "2.0", id, result });
23
- }
24
- function sendError(id, code, message, data) {
25
- if (id === undefined || id === null)
26
- return;
27
- send({ jsonrpc: "2.0", id, error: { code, message, ...data ? { data } : {} } });
28
- }
29
- const ENGINE_DESCRIPTION = 'Engine to use: "all" (default, fans out to configured engines and fetches top sources), ' + '"perplexity" (p), "google" (g), "chatgpt" (gpt), "gemini" (gem), "bing" (b). ' + 'Research engines: "semantic-scholar" (s2) and "logically" (log).';
30
- const GREEDY_SEARCH_SCHEMA = {
31
- type: "object",
32
- properties: {
33
- query: { type: "string", description: "The search query" },
34
- engine: {
35
- type: "string",
36
- default: "all",
37
- description: ENGINE_DESCRIPTION
38
- },
39
- synthesize: {
40
- type: "boolean",
41
- default: false,
42
- description: 'Only for engine="all": synthesize the multi-engine results and fetched sources.'
43
- },
44
- synthesizer: {
45
- type: "string",
46
- description: 'Synthesis engine for synthesize=true. Defaults to ~/.dm/greedyconfig synthesizer ("gemini" by default). Supported: "gemini", "chatgpt".'
47
- },
48
- depth: {
49
- type: "string",
50
- description: 'Use "research" for the iterative research workflow. Legacy values: "fast" skips source fetching; "standard"/"deep" alias synthesize=true.'
51
- },
52
- breadth: {
53
- type: "number",
54
- default: 3,
55
- description: 'Only for depth="research": number of parallel research directions per round, 1-5.'
56
- },
57
- iterations: {
58
- type: "number",
59
- default: 2,
60
- description: 'Only for depth="research": number of iterative research rounds, 1-3.'
61
- },
62
- maxSources: {
63
- type: "number",
64
- description: 'Only for depth="research": maximum fetched sources for the final report, 3-12.'
65
- },
66
- researchOutDir: {
67
- type: "string",
68
- description: 'Only for depth="research": optional directory for the structured research bundle.'
69
- },
70
- writeResearchBundle: {
71
- type: "boolean",
72
- default: true,
73
- description: 'Only for depth="research": write the structured research bundle to disk.'
74
- },
75
- fullAnswer: {
76
- type: "boolean",
77
- default: false,
78
- description: "When true, returns the complete answer instead of a truncated preview."
79
- },
80
- locale: {
81
- type: "string",
82
- description: 'Force results language (e.g. "en", "de", "fr"). Defaults to GREEDY_SEARCH_LOCALE or "en".'
83
- },
84
- visible: {
85
- type: "boolean",
86
- default: false,
87
- description: "Set to true to always use visible Chrome for this search (e.g. to solve a login/captcha challenge)."
88
- }
89
- },
90
- required: ["query"]
91
- };
92
- const GREEDY_FETCH_SCHEMA = {
93
- type: "object",
94
- properties: {
95
- url: { type: "string", description: "URL to fetch and extract content from" },
96
- maxChars: {
97
- type: "number",
98
- default: 8000,
99
- description: "Maximum characters of extracted content to return."
100
- }
101
- },
102
- required: ["url"]
103
- };
104
- const TOOLS = [
105
- {
106
- name: "greedy_search",
107
- description: "WEB/RESEARCH SEARCH ONLY — searches live web via Perplexity, Google AI, ChatGPT, and Gemini, " + "plus opt-in research through Semantic Scholar and Logically, using local headless Chrome automation " + '(no API keys needed). Research mode (depth: "research") plans follow-up actions, fetches sources, ' + "audits citations, and writes a structured research bundle on disk. Searches take roughly 1-5 minutes.",
108
- inputSchema: GREEDY_SEARCH_SCHEMA
109
- },
110
- {
111
- name: "greedy_fetch",
112
- description: "Fetch a single URL and extract its readable content (title, byline, markdown body). " + "Uses HTTP first, falling back to Chrome/browser rendering for JS-heavy or blocked pages.",
113
- inputSchema: GREEDY_FETCH_SCHEMA
114
- }
115
- ];
116
- function buildSearchFlags(args) {
117
- const engine = String(args.engine ?? "all").trim() || "all";
118
- const depthRaw = String(args.depth ?? "").trim();
119
- const researchMode = depthRaw === "research";
120
- const legacyFast = depthRaw === "fast";
121
- const legacySynthesisDepth = depthRaw === "standard" || depthRaw === "deep";
122
- const synthesize = engine === "all" && !legacyFast && (args.synthesize === true || legacySynthesisDepth);
123
- const effectiveEngine = researchMode ? "all" : engine;
124
- const visible = args.visible === true || process.env.GREEDY_SEARCH_VISIBLE === "1";
125
- const flags = [];
126
- const fullAnswer = args.fullAnswer ?? effectiveEngine !== "all";
127
- if (fullAnswer)
128
- flags.push("--full");
129
- if (researchMode) {
130
- flags.push("--depth", "research");
131
- if (typeof args.breadth === "number")
132
- flags.push("--breadth", String(args.breadth));
133
- if (typeof args.iterations === "number")
134
- flags.push("--iterations", String(args.iterations));
135
- if (typeof args.maxSources === "number")
136
- flags.push("--max-sources", String(args.maxSources));
137
- if (typeof args.researchOutDir === "string")
138
- flags.push("--research-out-dir", args.researchOutDir);
139
- if (args.writeResearchBundle === false)
140
- flags.push("--no-research-bundle");
141
- } else if (legacyFast) {
142
- flags.push("--fast");
143
- } else if (depthRaw === "deep") {
144
- flags.push("--depth", "deep");
145
- } else if (synthesize) {
146
- flags.push("--synthesize");
147
- }
148
- if (synthesize && typeof args.synthesizer === "string") {
149
- flags.push("--synthesizer", args.synthesizer);
150
- }
151
- if (typeof args.locale === "string" && args.locale) {
152
- flags.push("--locale", args.locale);
153
- }
154
- if (visible)
155
- flags.push("--always-visible");
156
- else
157
- flags.push("--headless");
158
- return { engine: effectiveEngine, flags, visible };
159
- }
160
- function runSearch(query, args) {
161
- return new Promise((resolve, reject) => {
162
- const { engine, flags, visible } = buildSearchFlags(args);
163
- const procEnv = { ...process.env };
164
- if (visible) {
165
- procEnv.GREEDY_SEARCH_VISIBLE = "1";
166
- procEnv.GREEDY_SEARCH_ALWAYS_VISIBLE = "1";
167
- }
168
- const proc = spawn(nodeRuntimeCommand(), [SEARCH_BIN, engine, "--inline", "--stdin", ...flags], { stdio: ["pipe", "pipe", "pipe"], env: procEnv });
169
- proc.stdin.write(query);
170
- proc.stdin.end();
171
- let out = "";
172
- let err = "";
173
- const MAX_ERR = 50 * 1024;
174
- let settled = false;
175
- const parsedTimeout = parseInt(process.env.GREEDY_SEARCH_TIMEOUT_MS || "", 10);
176
- const timeoutMs = Number.isNaN(parsedTimeout) ? DEFAULT_TIMEOUT_MS : parsedTimeout;
177
- const timer = setTimeout(() => {
178
- if (settled)
179
- return;
180
- settled = true;
181
- if (process.platform === "win32")
182
- proc.kill();
183
- else
184
- proc.kill("SIGTERM");
185
- reject(new Error(`greedy_search timed out after ${timeoutMs}ms`));
186
- }, timeoutMs);
187
- timer.unref();
188
- proc.stderr.on("data", (d) => {
189
- err += d;
190
- if (err.length > MAX_ERR)
191
- err = err.slice(-MAX_ERR);
192
- log(d.toString().trimEnd());
193
- });
194
- proc.stdout.on("data", (d) => out += d);
195
- proc.on("error", (e) => {
196
- if (settled)
197
- return;
198
- settled = true;
199
- clearTimeout(timer);
200
- reject(e);
201
- });
202
- proc.on("close", (code) => {
203
- if (settled)
204
- return;
205
- settled = true;
206
- clearTimeout(timer);
207
- if (code !== 0) {
208
- reject(new Error(err.trim() || `search.mjs exited with code ${code}`));
209
- return;
210
- }
211
- try {
212
- resolve(JSON.parse(out.trim()));
213
- } catch {
214
- reject(new Error(`Invalid JSON from search.mjs: ${out.slice(0, 200)}`));
215
- }
216
- });
217
- });
218
- }
219
- function formatSummary(engine, data) {
220
- const lines = [];
221
- const needsHuman = data._needsHumanVerification;
222
- if (needsHuman) {
223
- lines.push("## Manual verification required");
224
- lines.push(String(needsHuman.message || "Visible Chrome is open. Solve the verification challenge, then rerun the same search."));
225
- }
226
- const synthesis = data._synthesis;
227
- if (synthesis?.answer) {
228
- lines.push(String(synthesis.answer));
229
- const sources = Array.isArray(data._sources) ? data._sources : [];
230
- if (sources.length > 0) {
231
- lines.push(`
232
- Sources:`);
233
- for (const s of sources.slice(0, 8)) {
234
- lines.push(`- [${s.title || s.url}](${s.url})`);
235
- }
236
- }
237
- return lines.join(`
238
- `).trim();
239
- }
240
- if (typeof data.answer === "string") {
241
- lines.push(data.answer);
242
- const sources = Array.isArray(data.sources) ? data.sources : [];
243
- if (sources.length > 0) {
244
- lines.push(`
245
- Sources:`);
246
- for (const s of sources.slice(0, 8)) {
247
- lines.push(`- [${s.title || s.url}](${s.url})`);
248
- }
249
- }
250
- return lines.join(`
251
- `).trim();
252
- }
253
- const engineKeys = Object.keys(data).filter((k) => !k.startsWith("_") && data[k] && typeof data[k] === "object");
254
- for (const key of engineKeys) {
255
- const eng = data[key];
256
- if (engineKeys.length > 1) {
257
- lines.push(`
258
- ## ${key}`);
259
- }
260
- if (eng.error) {
261
- lines.push(`Error: ${eng.error}`);
262
- continue;
263
- }
264
- if (eng.answer)
265
- lines.push(String(eng.answer));
266
- const sources = Array.isArray(eng.sources) ? eng.sources : [];
267
- if (sources.length > 0) {
268
- lines.push(`
269
- Sources:`);
270
- for (const s of sources.slice(0, 5)) {
271
- lines.push(`- [${s.title || s.url}](${s.url})`);
272
- }
273
- }
274
- }
275
- return lines.join(`
276
- `).trim() || "No results returned.";
277
- }
278
- async function callGreedySearch(args) {
279
- if (!args || typeof args.query !== "string" || !args.query.trim()) {
280
- throw new Error("query is required");
281
- }
282
- const data = await runSearch(args.query, args);
283
- const summary = formatSummary(args.engine ?? "all", data);
284
- return {
285
- content: [
286
- { type: "text", text: summary || "No results returned." },
287
- { type: "text", text: JSON.stringify(data) }
288
- ]
289
- };
290
- }
291
- let fetchSourceContentPromise;
292
- function getFetchSourceContent() {
293
- if (!fetchSourceContentPromise) {
294
- fetchSourceContentPromise = import("../src/search/fetch-source.mjs").then((m) => m.fetchSourceContent);
295
- }
296
- return fetchSourceContentPromise;
297
- }
298
- async function callGreedyFetch(args) {
299
- if (!args || typeof args.url !== "string" || !args.url.trim()) {
300
- throw new Error("url is required");
301
- }
302
- const maxChars = typeof args.maxChars === "number" ? args.maxChars : 8000;
303
- const fetchSourceContent = await getFetchSourceContent();
304
- const result = await fetchSourceContent(args.url, maxChars);
305
- if (result?.error && !result.content) {
306
- return {
307
- content: [{ type: "text", text: `Fetch failed: ${result.error}` }],
308
- isError: true
309
- };
310
- }
311
- const summary = [
312
- `# ${result.title || args.url}`,
313
- "",
314
- result.byline ? `By ${result.byline}` : null,
315
- result.siteName ? `Source: ${result.siteName}` : null,
316
- "",
317
- result.content || "(no content extracted)"
318
- ].filter((l) => l !== null).join(`
319
- `);
320
- return {
321
- content: [
322
- { type: "text", text: summary },
323
- { type: "text", text: JSON.stringify(result) }
324
- ]
325
- };
326
- }
327
- async function handleRequest(msg) {
328
- const { id, method, params } = msg;
329
- switch (method) {
330
- case "initialize": {
331
- const clientVersion = params?.protocolVersion;
332
- sendResult(id, {
333
- protocolVersion: typeof clientVersion === "string" ? clientVersion : PROTOCOL_VERSION,
334
- capabilities: { tools: {} },
335
- serverInfo: SERVER_INFO
336
- });
337
- return;
338
- }
339
- case "notifications/initialized":
340
- case "initialized":
341
- return;
342
- case "ping":
343
- sendResult(id, {});
344
- return;
345
- case "tools/list":
346
- sendResult(id, { tools: TOOLS });
347
- return;
348
- case "tools/call": {
349
- const name = params?.name;
350
- const toolArgs = params?.arguments ?? {};
351
- try {
352
- let result;
353
- if (name === "greedy_search")
354
- result = await callGreedySearch(toolArgs);
355
- else if (name === "greedy_fetch")
356
- result = await callGreedyFetch(toolArgs);
357
- else {
358
- sendError(id, -32602, `Unknown tool: ${name}`);
359
- return;
360
- }
361
- sendResult(id, result);
362
- } catch (e) {
363
- sendResult(id, {
364
- content: [{ type: "text", text: `Error: ${e.message || String(e)}` }],
365
- isError: true
366
- });
367
- }
368
- return;
369
- }
370
- default:
371
- if (method && method.startsWith("notifications/"))
372
- return;
373
- sendError(id, -32601, `Method not found: ${method}`);
374
- }
375
- }
376
- function main() {
377
- const rl = createInterface({ input: process.stdin, terminal: false });
378
- rl.on("line", (line) => {
379
- const trimmed = line.trim();
380
- if (!trimmed)
381
- return;
382
- let msg;
383
- try {
384
- msg = JSON.parse(trimmed);
385
- } catch {
386
- sendError(null, -32700, "Parse error");
387
- return;
388
- }
389
- handleRequest(msg).catch((e) => {
390
- log("Unhandled error:", e?.stack || String(e));
391
- sendError(msg?.id, -32603, "Internal error", String(e?.message || e));
392
- });
393
- });
394
- rl.on("close", () => process.exit(0));
395
- process.on("uncaughtException", (e) => {
396
- log("Uncaught exception:", e?.stack || String(e));
397
- });
398
- log(`GreedySearch MCP server ready (${SEARCH_BIN})`);
399
- }
400
- main();