@staticn0va/wigolo 0.6.4 → 0.6.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.
Files changed (175) hide show
  1. package/README.md +43 -19
  2. package/assets/blocks/claude-code/CLAUDE.md.block +20 -0
  3. package/assets/blocks/claude-code/wigolo-command.md +40 -0
  4. package/assets/blocks/cursor/wigolo.mdc +46 -0
  5. package/assets/blocks/gemini-cli/GEMINI.md.block +18 -0
  6. package/assets/blocks/vscode/copilot-instructions.md.block +18 -0
  7. package/assets/skills/wigolo/SKILL.md +50 -0
  8. package/assets/skills/wigolo/rules/cache-first.md +30 -0
  9. package/assets/skills/wigolo/rules/synthesis.md +43 -0
  10. package/assets/skills/wigolo-agent/SKILL.md +73 -0
  11. package/assets/skills/wigolo-crawl/SKILL.md +60 -0
  12. package/assets/skills/wigolo-extract/SKILL.md +59 -0
  13. package/assets/skills/wigolo-fetch/SKILL.md +65 -0
  14. package/assets/skills/wigolo-find-similar/SKILL.md +72 -0
  15. package/assets/skills/wigolo-research/SKILL.md +77 -0
  16. package/assets/skills/wigolo-search/SKILL.md +78 -0
  17. package/dist/agent/pipeline.js +3 -3
  18. package/dist/agent/pipeline.js.map +1 -1
  19. package/dist/cache/store.d.ts.map +1 -1
  20. package/dist/cache/store.js +44 -33
  21. package/dist/cache/store.js.map +1 -1
  22. package/dist/cli/agents/antigravity.d.ts +20 -0
  23. package/dist/cli/agents/antigravity.d.ts.map +1 -0
  24. package/dist/cli/agents/antigravity.js +56 -0
  25. package/dist/cli/agents/antigravity.js.map +1 -0
  26. package/dist/cli/agents/claude-code.d.ts +25 -0
  27. package/dist/cli/agents/claude-code.d.ts.map +1 -0
  28. package/dist/cli/agents/claude-code.js +117 -0
  29. package/dist/cli/agents/claude-code.js.map +1 -0
  30. package/dist/cli/agents/cursor.d.ts +21 -0
  31. package/dist/cli/agents/cursor.d.ts.map +1 -0
  32. package/dist/cli/agents/cursor.js +57 -0
  33. package/dist/cli/agents/cursor.js.map +1 -0
  34. package/dist/cli/agents/gemini-cli.d.ts +21 -0
  35. package/dist/cli/agents/gemini-cli.d.ts.map +1 -0
  36. package/dist/cli/agents/gemini-cli.js +55 -0
  37. package/dist/cli/agents/gemini-cli.js.map +1 -0
  38. package/dist/cli/agents/registry.d.ts +21 -0
  39. package/dist/cli/agents/registry.d.ts.map +1 -0
  40. package/dist/cli/agents/registry.js +20 -0
  41. package/dist/cli/agents/registry.js.map +1 -0
  42. package/dist/cli/agents/utils.d.ts +26 -0
  43. package/dist/cli/agents/utils.d.ts.map +1 -0
  44. package/dist/cli/agents/utils.js +151 -0
  45. package/dist/cli/agents/utils.js.map +1 -0
  46. package/dist/cli/agents/vscode.d.ts +21 -0
  47. package/dist/cli/agents/vscode.d.ts.map +1 -0
  48. package/dist/cli/agents/vscode.js +58 -0
  49. package/dist/cli/agents/vscode.js.map +1 -0
  50. package/dist/cli/index.d.ts +1 -1
  51. package/dist/cli/index.d.ts.map +1 -1
  52. package/dist/cli/index.js +1 -0
  53. package/dist/cli/index.js.map +1 -1
  54. package/dist/cli/init.d.ts.map +1 -1
  55. package/dist/cli/init.js +92 -54
  56. package/dist/cli/init.js.map +1 -1
  57. package/dist/cli/tui/components/AgentSelect.d.ts +13 -0
  58. package/dist/cli/tui/components/AgentSelect.d.ts.map +1 -0
  59. package/dist/cli/tui/components/AgentSelect.js +88 -0
  60. package/dist/cli/tui/components/AgentSelect.js.map +1 -0
  61. package/dist/cli/tui/components/Banner.d.ts +6 -0
  62. package/dist/cli/tui/components/Banner.d.ts.map +1 -0
  63. package/dist/cli/tui/components/Banner.js +15 -0
  64. package/dist/cli/tui/components/Banner.js.map +1 -0
  65. package/dist/cli/tui/components/BrowserSelect.d.ts +7 -0
  66. package/dist/cli/tui/components/BrowserSelect.d.ts.map +1 -0
  67. package/dist/cli/tui/components/BrowserSelect.js +12 -0
  68. package/dist/cli/tui/components/BrowserSelect.js.map +1 -0
  69. package/dist/cli/tui/components/InstallProgress.d.ts +9 -0
  70. package/dist/cli/tui/components/InstallProgress.d.ts.map +1 -0
  71. package/dist/cli/tui/components/InstallProgress.js +34 -0
  72. package/dist/cli/tui/components/InstallProgress.js.map +1 -0
  73. package/dist/cli/tui/components/SkillInstall.d.ts +14 -0
  74. package/dist/cli/tui/components/SkillInstall.d.ts.map +1 -0
  75. package/dist/cli/tui/components/SkillInstall.js +80 -0
  76. package/dist/cli/tui/components/SkillInstall.js.map +1 -0
  77. package/dist/cli/tui/components/Summary.d.ts +22 -0
  78. package/dist/cli/tui/components/Summary.d.ts.map +1 -0
  79. package/dist/cli/tui/components/Summary.js +19 -0
  80. package/dist/cli/tui/components/Summary.js.map +1 -0
  81. package/dist/cli/tui/components/SystemCheck.d.ts +8 -0
  82. package/dist/cli/tui/components/SystemCheck.d.ts.map +1 -0
  83. package/dist/cli/tui/components/SystemCheck.js +36 -0
  84. package/dist/cli/tui/components/SystemCheck.js.map +1 -0
  85. package/dist/cli/tui/components/Verification.d.ts +8 -0
  86. package/dist/cli/tui/components/Verification.d.ts.map +1 -0
  87. package/dist/cli/tui/components/Verification.js +31 -0
  88. package/dist/cli/tui/components/Verification.js.map +1 -0
  89. package/dist/cli/tui/hooks/useAgentDetect.d.ts +6 -0
  90. package/dist/cli/tui/hooks/useAgentDetect.d.ts.map +1 -0
  91. package/dist/cli/tui/hooks/useAgentDetect.js +18 -0
  92. package/dist/cli/tui/hooks/useAgentDetect.js.map +1 -0
  93. package/dist/cli/tui/hooks/useInstall.d.ts +14 -0
  94. package/dist/cli/tui/hooks/useInstall.d.ts.map +1 -0
  95. package/dist/cli/tui/hooks/useInstall.js +70 -0
  96. package/dist/cli/tui/hooks/useInstall.js.map +1 -0
  97. package/dist/cli/tui/hooks/useSystemCheck.d.ts +13 -0
  98. package/dist/cli/tui/hooks/useSystemCheck.d.ts.map +1 -0
  99. package/dist/cli/tui/hooks/useSystemCheck.js +97 -0
  100. package/dist/cli/tui/hooks/useSystemCheck.js.map +1 -0
  101. package/dist/cli/tui/hooks/useVerify.d.ts +14 -0
  102. package/dist/cli/tui/hooks/useVerify.d.ts.map +1 -0
  103. package/dist/cli/tui/hooks/useVerify.js +52 -0
  104. package/dist/cli/tui/hooks/useVerify.js.map +1 -0
  105. package/dist/cli/tui/ink-init.d.ts +2 -0
  106. package/dist/cli/tui/ink-init.d.ts.map +1 -0
  107. package/dist/cli/tui/ink-init.js +125 -0
  108. package/dist/cli/tui/ink-init.js.map +1 -0
  109. package/dist/cli/tui/status-python.js +1 -1
  110. package/dist/cli/tui/status-python.js.map +1 -1
  111. package/dist/cli/tui/utils/config-writer.d.ts +3 -0
  112. package/dist/cli/tui/utils/config-writer.d.ts.map +1 -0
  113. package/dist/cli/tui/utils/config-writer.js +20 -0
  114. package/dist/cli/tui/utils/config-writer.js.map +1 -0
  115. package/dist/cli/tui/utils/suppress-logs.d.ts +3 -0
  116. package/dist/cli/tui/utils/suppress-logs.d.ts.map +1 -0
  117. package/dist/cli/tui/utils/suppress-logs.js +7 -0
  118. package/dist/cli/tui/utils/suppress-logs.js.map +1 -0
  119. package/dist/cli/tui/verify-suggestions.d.ts +1 -1
  120. package/dist/cli/tui/verify-suggestions.d.ts.map +1 -1
  121. package/dist/cli/tui/verify-suggestions.js +0 -3
  122. package/dist/cli/tui/verify-suggestions.js.map +1 -1
  123. package/dist/cli/tui/verify.d.ts +0 -3
  124. package/dist/cli/tui/verify.d.ts.map +1 -1
  125. package/dist/cli/tui/verify.js +0 -26
  126. package/dist/cli/tui/verify.js.map +1 -1
  127. package/dist/cli/uninstall.d.ts +2 -0
  128. package/dist/cli/uninstall.d.ts.map +1 -0
  129. package/dist/cli/uninstall.js +50 -0
  130. package/dist/cli/uninstall.js.map +1 -0
  131. package/dist/embedding/embed.d.ts +2 -0
  132. package/dist/embedding/embed.d.ts.map +1 -1
  133. package/dist/embedding/embed.js +18 -0
  134. package/dist/embedding/embed.js.map +1 -1
  135. package/dist/index.js +6 -0
  136. package/dist/index.js.map +1 -1
  137. package/dist/instructions.d.ts +2 -2
  138. package/dist/instructions.d.ts.map +1 -1
  139. package/dist/instructions.js +8 -7
  140. package/dist/instructions.js.map +1 -1
  141. package/dist/logger.d.ts.map +1 -1
  142. package/dist/logger.js +29 -1
  143. package/dist/logger.js.map +1 -1
  144. package/dist/research/brief.d.ts +4 -2
  145. package/dist/research/brief.d.ts.map +1 -1
  146. package/dist/research/brief.js +127 -1
  147. package/dist/research/brief.js.map +1 -1
  148. package/dist/research/decompose.d.ts +7 -0
  149. package/dist/research/decompose.d.ts.map +1 -1
  150. package/dist/research/decompose.js +126 -2
  151. package/dist/research/decompose.js.map +1 -1
  152. package/dist/research/pipeline.d.ts +1 -1
  153. package/dist/research/pipeline.d.ts.map +1 -1
  154. package/dist/research/pipeline.js +12 -7
  155. package/dist/research/pipeline.js.map +1 -1
  156. package/dist/search/engines/bing.d.ts.map +1 -1
  157. package/dist/search/engines/bing.js +40 -0
  158. package/dist/search/engines/bing.js.map +1 -1
  159. package/dist/search/engines/duckduckgo.d.ts.map +1 -1
  160. package/dist/search/engines/duckduckgo.js +13 -1
  161. package/dist/search/engines/duckduckgo.js.map +1 -1
  162. package/dist/search/engines/startpage.d.ts.map +1 -1
  163. package/dist/search/engines/startpage.js +21 -1
  164. package/dist/search/engines/startpage.js.map +1 -1
  165. package/dist/search/find-similar.d.ts.map +1 -1
  166. package/dist/search/find-similar.js +28 -8
  167. package/dist/search/find-similar.js.map +1 -1
  168. package/dist/tools/fetch.d.ts.map +1 -1
  169. package/dist/tools/fetch.js +6 -1
  170. package/dist/tools/fetch.js.map +1 -1
  171. package/dist/tools/search.js +1 -1
  172. package/dist/tools/search.js.map +1 -1
  173. package/dist/types.d.ts +17 -0
  174. package/dist/types.d.ts.map +1 -1
  175. package/package.json +9 -1
@@ -14,7 +14,7 @@
14
14
  * Parameter schemas (types, enums, required/optional) belong on the JSON
15
15
  * Schema, not here. Installation/configuration is for humans, not LLMs.
16
16
  */
17
- export declare const WIGOLO_INSTRUCTIONS = "Wigolo is a local-first web access layer: search the open web, fetch pages, crawl sites, extract structured data, find related content, run multi-step research, and execute agent-driven data gathering. All results land in a local SQLite cache that persists across sessions.\n\n## Host-LLM synthesis pattern (read this first)\n\nWigolo has no internal LLM. It returns *structured evidence* so YOU (the host LLM) write the final answer. Fold structure into your reply:\n\n- `search` `format: \"highlights\"` \u2192 FlashRank-scored passages + `citations`. Quote [N].\n- `research` \u2192 `brief` with `topics`, `highlights`, `key_findings` when sampling unavailable. Use as report scaffold.\n- `find_similar` \u2192 `cold_start` string when local signals are weak. Pass to user verbatim.\n- `extract` `mode: \"structured\"` \u2192 tables + definitions + jsonld + chart_hints + key_value_pairs in one call.\n- `fetch` metadata \u2192 `og_type`, `canonical_url`, `og_image` when present.\n\n## When to use which tool\n\n- `search` -- you need information on a topic but do not have a URL yet. Pass a query string or an array of 3-5 semantically varied keyword forms for broader coverage.\n- `fetch` -- you already have a specific URL to read.\n- `crawl` -- you need multiple pages from the same site (docs, wikis, references).\n- `cache` -- you want to know if the content is already on disk from an earlier read.\n- `extract` -- you need specific data points (tables, metadata, schema-shaped fields) rather than a whole page as markdown.\n- `find_similar` -- you have a URL or concept and want related content from the cache or web. Useful for \"more like this\" discovery.\n- `research` -- you have a complex question that needs multi-step investigation: question decomposition, parallel search, source synthesis into a report. Set `depth` to control thoroughness.\n- `agent` -- you need to gather structured or unstructured data from multiple sources based on a natural-language prompt. Provides full step transparency.\n\n## Routing by intent\n\n| Intent | Tool | Key parameters |\n|--------|------|----------------|\n| Documentation lookup | `search` | `include_domains: [\"react.dev\", \"nextjs.org\"]` -- scope to the project's official site, do not rely on `category: \"docs\"` alone |\n| Error debugging | `search` | exact error string as query, `category: \"code\"` (no domain scoping -- errors appear everywhere) |\n| Library research | `crawl` | seed URL of docs site, `strategy: \"sitemap\"`, then `cache` for later queries |\n| Related content | `find_similar` | `url` of a known good page, or `concept` as free text |\n| Direct quote | `search` | `format: \"highlights\"` returns FlashRank-scored passages with citations; cite [N] in your reply |\n| Direct answer | `search` | `format: \"answer\"` if client supports sampling, else falls back to `highlights` (not plain context) |\n| Comprehensive research | `research` | `depth: \"comprehensive\"`, optional `include_domains` to scope |\n| Data gathering | `agent` | natural-language `prompt`, optional `schema` for structured output |\n| Structured extraction | `extract` | `mode: \"structured\"` (tables + dl + JSON-LD + chart hints + kv pairs), or `mode: \"schema\"` with a JSON Schema |\n| Site inventory | `crawl` | `strategy: \"map\"` for URL-only discovery, no content fetched |\n\n## Rapidly changing content\n\nFor news, prices, status pages, or release notes, bypass the cache with `force_refresh: true`:\n\n search({ query: \"...\", force_refresh: true })\n fetch({ url: \"...\", force_refresh: true })\n\nFor docs, tutorials, and reference pages, let the cache work -- much faster.\n\n## Check the cache before going to the network\n\nBefore every `search` or `fetch`, consider a `cache` call. Pages read this session or earlier return instantly with full markdown -- no network. `research` and `agent` check the cache internally.\n\n## Multi-query search strategy\n\nFor broad queries, pass an array of 3-5 semantically varied keyword forms rather than one natural-language question. Example: instead of \"how does React handle state management\", pass `[\"react state management\", \"useState useReducer\", \"react hooks state\", \"react context vs redux\"]`. Sub-queries are deduplicated automatically.\n\n## Pick the right strategy\n\n- For docs sites, prefer `crawl` with `strategy: \"sitemap\"` -- faster and more complete than BFS.\n- For URL discovery only, use `crawl` with `strategy: \"map\"` -- URLs only, no content. Follow with targeted `fetch` calls.\n- For structured data (prices, specs, table rows), use `extract` with `mode: \"schema\"` or `mode: \"tables\"`. Use `fetch` only when you want the whole page as markdown.\n- For multi-source synthesis, use `research` instead of chaining `search` + `fetch` manually.\n- For natural-language data gathering, use `agent` with optional `schema`.\n- `crawl` accepts regex `include_patterns` and `exclude_patterns` to stay inside a section of a large site.\n\n## Scope searches by domain\n\nFor library/framework/SDK queries, **always pass `include_domains`** with official sites. Unscoped queries return generic noise. `category: \"docs\"` alone returns generic portals -- pair with `include_domains` or omit. Skip domain scoping for error strings, broad exploration, and news.\n\n## Performance\n\n- `max_results: 3` for focused lookups; `5` default; `10+` only for broad research.\n- `max_content_chars: 3000` on `search` or `fetch` smart-truncates each result's markdown at a paragraph/heading boundary with a `[... content truncated]` marker. Keeps context compact for AI agents. Prefer this over raw `max_chars` slicing.\n- `fetch` with `section: \"Heading Name\"` returns content under that heading -- cheaper than the whole page.\n- Repeated fetches of the same URL are free (SQLite cache).\n- `research` with `depth: \"quick\"` (~15s) suits most factual questions; reserve `\"comprehensive\"` for deep investigation.\n- `agent` respects `max_pages` (default 10) and `max_time_ms` (default 60s).\n\n## Extras\n\n- Localhost URLs (`localhost:3000`, `127.0.0.1:8080`) work for local dev servers.\n- `use_auth: true` on `fetch`/`crawl` reuses browser session for logged-in pages.\n- `cache` supports FTS5 syntax (`AND`, `OR`, `NOT`, `\"phrase\"`).\n- `research`/`agent` use MCP sampling when supported; fall back to structured data for host-LLM synthesis.";
17
+ export declare const WIGOLO_INSTRUCTIONS = "Wigolo is a local-first web access layer: search the open web, fetch pages, crawl sites, extract structured data, find related content, run multi-step research, and execute agent-driven data gathering. All results land in a local SQLite cache that persists across sessions.\n\n## Host-LLM synthesis pattern (read this first)\n\nWigolo has no internal LLM. It returns *structured evidence* so YOU (the host LLM) write the final answer. Fold structure into your reply:\n\n- `search` `format: \"highlights\"` \u2192 FlashRank-scored passages + `citations`. Quote [N].\n- `research` \u2192 `brief` with `topics`, `highlights`, `key_findings`, `sections` when sampling unavailable. Use `sections.overview.cross_references` for corroborated findings, `sections.gaps` for coverage limits, `sections.comparison` for entity-vs-entity analysis. `query_type` indicates decomposition strategy used.\n- `find_similar` \u2192 `cold_start` string when local signals are weak. Pass to user verbatim.\n- `extract` `mode: \"structured\"` \u2192 tables + definitions + jsonld + chart_hints + key_value_pairs in one call.\n- `fetch` metadata \u2192 `og_type`, `canonical_url`, `og_image` when present.\n\n## When to use which tool\n\n- `search` -- you need information on a topic but do not have a URL yet. Pass a query string or an array of 3-5 semantically varied keyword forms for broader coverage.\n- `fetch` -- you already have a specific URL to read.\n- `crawl` -- you need multiple pages from the same site (docs, wikis, references).\n- `cache` -- you want to know if the content is already on disk from an earlier read.\n- `extract` -- you need specific data points (tables, metadata, schema-shaped fields) rather than a whole page as markdown.\n- `find_similar` -- you have a URL or concept and want related content from the cache or web. Useful for \"more like this\" discovery.\n- `research` -- you have a complex question that needs multi-step investigation: question decomposition, parallel search, source synthesis into a report. Set `depth` to control thoroughness.\n- `agent` -- you need to gather structured or unstructured data from multiple sources based on a natural-language prompt. Provides full step transparency.\n\n## Routing by intent\n\n| Intent | Tool | Key parameters |\n|--------|------|----------------|\n| Documentation lookup | `search` | `include_domains: [\"react.dev\", \"nextjs.org\"]` -- scope to the project's official site, do not rely on `category: \"docs\"` alone |\n| Error debugging | `search` | exact error string as query, `category: \"code\"` (no domain scoping -- errors appear everywhere) |\n| Library research | `crawl` | seed URL of docs site, `strategy: \"sitemap\"`, then `cache` for later queries |\n| Related content | `find_similar` | `url` of a known good page, or `concept` as free text |\n| Direct quote | `search` | `format: \"highlights\"` returns FlashRank-scored passages with citations; cite [N] in your reply |\n| Direct answer | `search` | `format: \"answer\"` if client supports sampling, else falls back to `highlights` (not plain context) |\n| Comprehensive research | `research` | `depth: \"comprehensive\"`, optional `include_domains` to scope |\n| Data gathering | `agent` | natural-language `prompt`, optional `schema` for structured output |\n| Structured extraction | `extract` | `mode: \"structured\"` (tables + dl + JSON-LD + chart hints + kv pairs), or `mode: \"schema\"` with a JSON Schema |\n| Site inventory | `crawl` | `strategy: \"map\"` for URL-only discovery, no content fetched |\n\n## Rapidly changing content\n\nFor news, prices, status pages, or release notes, bypass the cache with `force_refresh: true`:\n\n search({ query: \"...\", force_refresh: true })\n fetch({ url: \"...\", force_refresh: true })\n\nFor docs, tutorials, and reference pages, let the cache work -- much faster.\n\n## Check the cache before going to the network\n\nBefore every `search` or `fetch`, consider a `cache` call. Pages read this session or earlier return instantly with full markdown -- no network. `research` and `agent` check the cache internally.\n\n## Multi-query search strategy\n\nFor broad queries, pass an array of 3-5 semantically varied keyword forms rather than one natural-language question. Example: instead of \"how does React handle state management\", pass `[\"react state management\", \"useState useReducer\", \"react hooks state\", \"react context vs redux\"]`. Sub-queries are deduplicated automatically.\n\n## Pick the right strategy\n\n- For docs sites, prefer `crawl` with `strategy: \"sitemap\"` -- faster and more complete than BFS.\n- For URL discovery only, use `crawl` with `strategy: \"map\"` -- URLs only, no content. Follow with targeted `fetch` calls.\n- For structured data (prices, specs, table rows), use `extract` with `mode: \"schema\"` or `mode: \"tables\"`. Use `fetch` only when you want the whole page as markdown.\n- For multi-source synthesis, use `research` instead of chaining `search` + `fetch` manually.\n- For natural-language data gathering, use `agent` with optional `schema`.\n- `crawl` accepts regex `include_patterns` and `exclude_patterns` to stay inside a section of a large site.\n\n## Scope searches by domain\n\nFor library/framework/SDK queries, **always pass `include_domains`** with official sites. Unscoped queries return generic noise. `category: \"docs\"` alone returns generic portals -- pair with `include_domains` or omit. Skip domain scoping for error strings, broad exploration, and news.\n\n## Performance\n\n- `max_results: 3` for focused lookups; `5` default; `10+` only for broad research.\n- `max_content_chars: 3000` on `search` or `fetch` smart-truncates each result's markdown at a paragraph/heading boundary with a `[... content truncated]` marker. Keeps context compact for AI agents. Prefer this over raw `max_chars` slicing.\n- `fetch` with `section: \"Heading Name\"` returns content under that heading -- cheaper than the whole page.\n- Repeated fetches of the same URL are free (SQLite cache).\n- `research` with `depth: \"quick\"` (~15s) suits most factual questions; reserve `\"comprehensive\"` for deep investigation.\n- `agent` respects `max_pages` (default 10) and `max_time_ms` (default 60s).\n\n## Extras\n\n- Localhost URLs (`localhost:3000`, `127.0.0.1:8080`) work for local dev servers.\n- `use_auth: true` on `fetch`/`crawl` reuses browser session for logged-in pages.\n- `cache` supports FTS5 syntax (`AND`, `OR`, `NOT`, `\"phrase\"`).\n- `research`/`agent` use MCP sampling when supported; fall back to structured data for host-LLM synthesis.";
18
18
  export declare const TOOL_DESCRIPTIONS: {
19
19
  readonly fetch: "Fetch a single URL and return clean markdown. Use when you have a specific URL to read. Automatically detects if JavaScript rendering is needed.\n\nKey parameters:\n- section: extract content under a specific heading (e.g., section: \"API Reference\") -- faster than reading the whole page\n- max_content_chars: smart-truncate markdown at a paragraph/heading boundary with a `[... content truncated]` marker (e.g., 3000 for compact context). Preferred over max_chars for AI agents.\n- use_auth: true to use stored browser session for authenticated/private pages\n- render_js: \"auto\" (default, detects JS need), \"always\" (force browser), \"never\" (HTTP only, fastest)\n- headers: custom HTTP headers if needed\n- force_refresh: true to bypass cache and fetch fresh content from the network\n\nReturns title, markdown, links, images, metadata (og_image, og_type, canonical_url, keywords). Decorative images filtered, relative URLs resolved. Cached locally; repeat fetches are instant. Localhost URLs work.\n\nUse force_refresh: true for frequently changing content. Default serves from cache.";
20
20
  readonly search: "Search the web and return full markdown content from top results. Returns extracted page content, not just snippets.\n\nKey parameters:\n- query: string or string[] array (3-5 keyword variants for broader coverage; deduplicated automatically)\n- include_domains/exclude_domains: scope to specific sites. ALWAYS scope library/framework queries.\n- category: \"general\" | \"news\" | \"code\" | \"docs\" | \"papers\" — coarse filter, pair with include_domains.\n- from_date/to_date: ISO YYYY-MM-DD for time-bounded queries\n- max_results: default 5; use 3 for focused, 10+ for research\n- format: \"full\" (default), \"context\", \"highlights\" (FlashRank-scored passages + [N] citations), \"answer\" (sampling synthesis; falls back to highlights), \"stream_answer\"\n- max_highlights: cap highlights count (default 10)\n- max_content_chars: smart-truncate markdown at paragraph boundary (e.g., 3000)\n- force_refresh: true to bypass all caches\n\n\"answer\" falls back to \"highlights\" when sampling unsupported (most clients). Results include title, URL, relevance_score, and full markdown_content. Cache serves previously fetched pages instantly.";
@@ -22,7 +22,7 @@ export declare const TOOL_DESCRIPTIONS: {
22
22
  readonly cache: "Search previously fetched content without hitting the network. Use before searching the web -- if relevant content was already fetched or crawled, this returns it instantly.\n\nKey parameters:\n- query: full-text search over cached markdown and titles (supports FTS5 syntax: AND, OR, NOT, \"phrase match\")\n- url_pattern: glob filter on URLs (e.g., \"*example.com*\")\n- since: ISO date -- only results cached after this date\n- stats: true to get cache size, entry count, oldest/newest dates\n- clear: true to delete matching entries\n\nReturns matching cached pages with full markdown content. Cache persists across sessions in local SQLite.";
23
23
  readonly extract: "Extract structured data from a URL or raw HTML. Use when you need specific data points, tables, or metadata rather than full page markdown.\n\nKey parameters:\n- mode: \"selector\" (CSS selector -> text), \"tables\" (HTML tables only), \"metadata\" (title/author/date/description/og_* + JSON-LD), \"schema\" (JSON Schema -> heuristic field extraction), \"structured\" (ONE-SHOT: tables + <dl> definitions + JSON-LD + chart hints from SVG/figure + microdata/data-attr/grid key-value pairs)\n- css_selector: required for mode=\"selector\" -- any valid CSS selector\n- schema: for mode=\"schema\", a JSON Schema object describing the fields to extract\n- multiple: true to return array of all matches (mode=\"selector\" only)\n\nPrefer mode=\"structured\" over chaining multiple extract calls — it returns every structured pattern on the page in one response:\n { tables, definitions, jsonld, chart_hints, key_value_pairs }\n\nchart_hints surfaces SVG titles, aria-labels, and figcaptions — host LLMs use these to describe data visualizations even when the underlying data is rendered by JavaScript.\n\nFor mode=\"tables\", returns array of table objects with headers and row data. For mode=\"schema\", pass { price: \"string\", name: \"string\" } and get structured fields extracted from the page.";
24
24
  readonly find_similar: "Find content related to a URL or concept. Use when you have a known-good page or topic and want to discover similar resources from the cache or web.\n\nKey parameters:\n- url: a URL to find content similar to. The page's content and embeddings are used for similarity matching.\n- concept: free-text description of what you want similar content for. Use when you do not have a specific URL.\n- max_results: number of similar items to return (default 5)\n- include_cached: true (default) to search the local cache first, false to skip cache and search the web only\n- threshold: minimum similarity score (0-1, default 0.5) -- higher values return fewer, more relevant results\n\nProvide either url or concept (not both). Results are fused from three signals via 3-way RRF: FTS5 keyword match, sentence-transformer embeddings, and (if local hits are sparse) a live web search. Each result carries `match_signals` with `embedding_rank`, `fts5_rank`, and `fused_score` so you can explain ranking to the user.\n\nThe response may include a `cold_start` string when local signals are weak (empty cache, embeddings unavailable, < 20 cached pages). Pass this verbatim to the user — it explains why results came from web search and how to warm the cache.\n\nReturns results array, method used (\"hybrid\" | \"embedding\" | \"fts5\" | \"search\"), cache_hits, search_hits, embedding_available, and total_time_ms.";
25
- readonly research: "Run multi-step research on a complex question. Decomposes the question into sub-queries, searches in parallel, fetches top sources, and synthesizes a report with citations.\n\nKey parameters:\n- question: the research question to investigate\n- depth: \"quick\" (~15s, 2 sub-queries, 5-8 sources), \"standard\" (~40s, 4 sub-queries, 10-15 sources, default), \"comprehensive\" (~80s, 7 sub-queries, 20-25 sources)\n- max_sources: override the default source count for the chosen depth\n- include_domains/exclude_domains: scope research to specific sites\n- schema: optional JSON Schema -- if provided, the report is structured to extract fields matching the schema\n- stream: true to receive progress notifications as each research phase completes\n\nThe pipeline: (1) decompose question into sub-queries, (2) parallel search across sub-queries, (3) fetch and extract top unique sources, (4) synthesize report with citations from all sources, (5) optionally structure report fields if schema is provided.\n\nUses MCP requestSampling for intelligent decomposition and synthesis when available. Without sampling support (the common case), the output includes a `brief` field with:\n - `topics`: sub-query labels or distilled source-title topics\n - `highlights`: FlashRank-scored passages with source_index, source_url, relevance_score\n - `key_findings`: first substantive paragraph per source, ordered by relevance\n - `per_source_char_cap` / `total_sources_char_cap`: budget hints so you know what was trimmed\n\nThe host LLM (you) should build its final report from the brief rather than re-reading every source the brief is the structured scaffold this tool produces when it cannot synthesize internally.\n\nReturns report (markdown), citations array, sources with full content, sub_queries used, depth level, total_time_ms, sampling_supported flag, and optional brief.";
25
+ readonly research: "Run multi-step research on a complex question. Decomposes the question into sub-queries, searches in parallel, fetches top sources, and synthesizes a report with citations.\n\nKey parameters:\n- question: the research question to investigate\n- depth: \"quick\" (~15s, 2 sub-queries, 5-8 sources), \"standard\" (~40s, 4 sub-queries, 10-15 sources, default), \"comprehensive\" (~80s, 7 sub-queries, 20-25 sources)\n- max_sources: override the default source count for the chosen depth\n- include_domains/exclude_domains: scope research to specific sites\n- schema: optional JSON Schema -- if provided, the report is structured to extract fields matching the schema\n- stream: true to receive progress notifications as each research phase completes\n\nThe pipeline: (1) decompose question into sub-queries, (2) parallel search across sub-queries, (3) fetch and extract top unique sources, (4) synthesize report with citations from all sources, (5) optionally structure report fields if schema is provided.\n\nUses MCP requestSampling for intelligent decomposition and synthesis when available. Without sampling support (the common case), the output includes a `brief` with:\n - `topics`, `highlights` (FlashRank-scored), `key_findings` (per-source, by relevance)\n - `query_type`: \"comparison\" | \"how-to\" | \"concept\" | \"general\"\n - `sections.overview`: top findings + cross_references (corroborated by 2+ sources)\n - `sections.comparison`: entities + comparison_points (comparison queries only)\n - `sections.gaps`: sub-queries with limited source coverage\n\nBuild your report: overview from key_findings, cross-referenced findings first (most reliable), per-topic sections, comparison table if present, then gaps and sources.\n\nReturns report (markdown), citations array, sources with full content, sub_queries used, depth level, total_time_ms, sampling_supported flag, and optional brief.";
26
26
  readonly agent: "Execute a natural-language data gathering task. Plans search queries and URLs from a prompt, executes them in parallel, and synthesizes results. Full step transparency.\n\nKey parameters:\n- prompt: natural-language description of what data to gather (e.g., \"find pricing for the top 5 CRM tools\")\n- urls: optional array of specific URLs to include in the gathering\n- schema: optional JSON Schema -- if provided, extracts structured data matching the schema from each page and merges results\n- max_pages: maximum pages to fetch (default 10)\n- max_time_ms: maximum execution time in milliseconds (default 60000)\n- stream: true to receive progress notifications as each step completes\n\nThe pipeline: (1) plan -- interpret prompt to determine search queries and URLs to visit, (2) execute -- run searches and fetch URLs in parallel within budget, (3) extract -- if schema provided, apply schema extraction to each page and merge, (4) synthesize -- produce natural-language or structured result.\n\nThe steps array in the output provides full transparency into every action taken (plan, search, fetch, extract, synthesize) with timing. This differentiates from black-box alternatives.\n\nUses MCP requestSampling for planning and synthesis. Without sampling support, uses keyword extraction for planning and returns raw content.\n\nReturns result (string or structured object), sources array, pages_fetched count, steps array with action/detail/time_ms, total_time_ms, and sampling_supported flag.";
27
27
  };
28
28
  export type ToolName = keyof typeof TOOL_DESCRIPTIONS;
@@ -1 +1 @@
1
- {"version":3,"file":"instructions.d.ts","sourceRoot":"","sources":["../src/instructions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,eAAO,MAAM,mBAAmB,iwMAkF+E,CAAC;AAEhH,eAAO,MAAM,iBAAiB;;;;;;;;;CAwHpB,CAAC;AAEX,MAAM,MAAM,QAAQ,GAAG,MAAM,OAAO,iBAAiB,CAAC"}
1
+ {"version":3,"file":"instructions.d.ts","sourceRoot":"","sources":["../src/instructions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,eAAO,MAAM,mBAAmB,u8MAkF+E,CAAC;AAEhH,eAAO,MAAM,iBAAiB;;;;;;;;;CAyHpB,CAAC;AAEX,MAAM,MAAM,QAAQ,GAAG,MAAM,OAAO,iBAAiB,CAAC"}
@@ -21,7 +21,7 @@ export const WIGOLO_INSTRUCTIONS = `Wigolo is a local-first web access layer: se
21
21
  Wigolo has no internal LLM. It returns *structured evidence* so YOU (the host LLM) write the final answer. Fold structure into your reply:
22
22
 
23
23
  - \`search\` \`format: "highlights"\` → FlashRank-scored passages + \`citations\`. Quote [N].
24
- - \`research\` → \`brief\` with \`topics\`, \`highlights\`, \`key_findings\` when sampling unavailable. Use as report scaffold.
24
+ - \`research\` → \`brief\` with \`topics\`, \`highlights\`, \`key_findings\`, \`sections\` when sampling unavailable. Use \`sections.overview.cross_references\` for corroborated findings, \`sections.gaps\` for coverage limits, \`sections.comparison\` for entity-vs-entity analysis. \`query_type\` indicates decomposition strategy used.
25
25
  - \`find_similar\` → \`cold_start\` string when local signals are weak. Pass to user verbatim.
26
26
  - \`extract\` \`mode: "structured"\` → tables + definitions + jsonld + chart_hints + key_value_pairs in one call.
27
27
  - \`fetch\` metadata → \`og_type\`, \`canonical_url\`, \`og_image\` when present.
@@ -184,13 +184,14 @@ Key parameters:
184
184
 
185
185
  The pipeline: (1) decompose question into sub-queries, (2) parallel search across sub-queries, (3) fetch and extract top unique sources, (4) synthesize report with citations from all sources, (5) optionally structure report fields if schema is provided.
186
186
 
187
- Uses MCP requestSampling for intelligent decomposition and synthesis when available. Without sampling support (the common case), the output includes a \`brief\` field with:
188
- - \`topics\`: sub-query labels or distilled source-title topics
189
- - \`highlights\`: FlashRank-scored passages with source_index, source_url, relevance_score
190
- - \`key_findings\`: first substantive paragraph per source, ordered by relevance
191
- - \`per_source_char_cap\` / \`total_sources_char_cap\`: budget hints so you know what was trimmed
187
+ Uses MCP requestSampling for intelligent decomposition and synthesis when available. Without sampling support (the common case), the output includes a \`brief\` with:
188
+ - \`topics\`, \`highlights\` (FlashRank-scored), \`key_findings\` (per-source, by relevance)
189
+ - \`query_type\`: "comparison" | "how-to" | "concept" | "general"
190
+ - \`sections.overview\`: top findings + cross_references (corroborated by 2+ sources)
191
+ - \`sections.comparison\`: entities + comparison_points (comparison queries only)
192
+ - \`sections.gaps\`: sub-queries with limited source coverage
192
193
 
193
- The host LLM (you) should build its final report from the brief rather than re-reading every source the brief is the structured scaffold this tool produces when it cannot synthesize internally.
194
+ Build your report: overview from key_findings, cross-referenced findings first (most reliable), per-topic sections, comparison table if present, then gaps and sources.
194
195
 
195
196
  Returns report (markdown), citations array, sources with full content, sub_queries used, depth level, total_time_ms, sampling_supported flag, and optional brief.`,
196
197
  agent: `Execute a natural-language data gathering task. Plans search queries and URLs from a prompt, executes them in parallel, and synthesizes results. Full step transparency.
@@ -1 +1 @@
1
- {"version":3,"file":"instructions.js","sourceRoot":"","sources":["../src/instructions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,MAAM,CAAC,MAAM,mBAAmB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+GAkF4E,CAAC;AAEhH,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAC/B,KAAK,EAAE;;;;;;;;;;;;oFAY2E;IAElF,MAAM,EAAE;;;;;;;;;;;;;uMAa6L;IAErM,KAAK,EAAE;;;;;;;;uLAQ8K;IAErL,KAAK,EAAE;;;;;;;;;0GASiG;IAExG,OAAO,EAAE;;;;;;;;;;;;;4LAaiL;IAE1L,YAAY,EAAE;;;;;;;;;;;;;kJAakI;IAEhJ,QAAQ,EAAE;;;;;;;;;;;;;;;;;;;;kKAoBsJ;IAEhK,KAAK,EAAE;;;;;;;;;;;;;;;;sKAgB6J;CAC5J,CAAC"}
1
+ {"version":3,"file":"instructions.js","sourceRoot":"","sources":["../src/instructions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,MAAM,CAAC,MAAM,mBAAmB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+GAkF4E,CAAC;AAEhH,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAC/B,KAAK,EAAE;;;;;;;;;;;;oFAY2E;IAElF,MAAM,EAAE;;;;;;;;;;;;;uMAa6L;IAErM,KAAK,EAAE;;;;;;;;uLAQ8K;IAErL,KAAK,EAAE;;;;;;;;;0GASiG;IAExG,OAAO,EAAE;;;;;;;;;;;;;4LAaiL;IAE1L,YAAY,EAAE;;;;;;;;;;;;;kJAakI;IAEhJ,QAAQ,EAAE;;;;;;;;;;;;;;;;;;;;;kKAqBsJ;IAEhK,KAAK,EAAE;;;;;;;;;;;;;;;;sKAgB6J;CAC5J,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAGA,KAAK,MAAM,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,GAAG,OAAO,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,WAAW,GAAG,UAAU,GAAG,OAAO,CAAC;AASzJ,MAAM,WAAW,MAAM;IACrB,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACzD,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACxD,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACxD,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CAC1D;AAmBD,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAiBnD"}
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAKA,KAAK,MAAM,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,GAAG,OAAO,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,WAAW,GAAG,UAAU,GAAG,OAAO,CAAC;AASzJ,MAAM,WAAW,MAAM;IACrB,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACzD,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACxD,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACxD,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CAC1D;AA+CD,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAiBnD"}
package/dist/logger.js CHANGED
@@ -1,3 +1,5 @@
1
+ import { appendFileSync, mkdirSync } from 'node:fs';
2
+ import { join } from 'node:path';
1
3
  import { getConfig } from './config.js';
2
4
  const LEVEL_PRIORITY = {
3
5
  debug: 0,
@@ -5,6 +7,23 @@ const LEVEL_PRIORITY = {
5
7
  warn: 2,
6
8
  error: 3,
7
9
  };
10
+ let tuiLogPath = null;
11
+ function getTuiLogPath() {
12
+ if (!tuiLogPath) {
13
+ const dataDir = getConfig().dataDir;
14
+ mkdirSync(dataDir, { recursive: true });
15
+ tuiLogPath = join(dataDir, 'init.log');
16
+ }
17
+ return tuiLogPath;
18
+ }
19
+ function appendToLogFile(line) {
20
+ try {
21
+ appendFileSync(getTuiLogPath(), line + '\n');
22
+ }
23
+ catch {
24
+ // best effort — don't crash the TUI
25
+ }
26
+ }
8
27
  function writeJson(level, module, msg, data) {
9
28
  const line = JSON.stringify({
10
29
  ts: new Date().toISOString(),
@@ -13,12 +32,21 @@ function writeJson(level, module, msg, data) {
13
32
  module,
14
33
  ...(data ? { data } : {}),
15
34
  });
35
+ if (process.env.WIGOLO_TUI_MODE === 'true') {
36
+ appendToLogFile(line);
37
+ return;
38
+ }
16
39
  process.stderr.write(line + '\n');
17
40
  }
18
41
  function writeText(level, module, msg, data) {
19
42
  const ts = new Date().toISOString();
20
43
  const dataStr = data ? ' ' + Object.entries(data).map(([k, v]) => `${k}=${v}`).join(' ') : '';
21
- process.stderr.write(`[${ts}] ${level.toUpperCase().padEnd(5)} [${module}] ${msg}${dataStr}\n`);
44
+ const line = `[${ts}] ${level.toUpperCase().padEnd(5)} [${module}] ${msg}${dataStr}`;
45
+ if (process.env.WIGOLO_TUI_MODE === 'true') {
46
+ appendToLogFile(line);
47
+ return;
48
+ }
49
+ process.stderr.write(line + '\n');
22
50
  }
23
51
  export function createLogger(module) {
24
52
  const config = getConfig();
@@ -1 +1 @@
1
- {"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAKxC,MAAM,cAAc,GAA6B;IAC/C,KAAK,EAAE,CAAC;IACR,IAAI,EAAE,CAAC;IACP,IAAI,EAAE,CAAC;IACP,KAAK,EAAE,CAAC;CACT,CAAC;AASF,SAAS,SAAS,CAAC,KAAe,EAAE,MAAc,EAAE,GAAW,EAAE,IAA8B;IAC7F,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC;QAC1B,EAAE,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QAC5B,KAAK;QACL,GAAG;QACH,MAAM;QACN,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC1B,CAAC,CAAC;IACH,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;AACpC,CAAC;AAED,SAAS,SAAS,CAAC,KAAe,EAAE,MAAc,EAAE,GAAW,EAAE,IAA8B;IAC7F,MAAM,EAAE,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACpC,MAAM,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC9F,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,KAAK,CAAC,WAAW,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,MAAM,KAAK,GAAG,GAAG,OAAO,IAAI,CAAC,CAAC;AAClG,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,MAAc;IACzC,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAC3B,MAAM,WAAW,GAAG,cAAc,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IACpD,MAAM,KAAK,GAAG,MAAM,CAAC,SAAS,KAAK,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;IAElE,SAAS,GAAG,CAAC,KAAe,EAAE,GAAW,EAAE,IAA8B;QACvE,IAAI,cAAc,CAAC,KAAK,CAAC,IAAI,WAAW,EAAE,CAAC;YACzC,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;QAClC,CAAC;IACH,CAAC;IAED,OAAO;QACL,KAAK,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC;QAC7C,IAAI,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,CAAC;QAC3C,IAAI,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,CAAC;QAC3C,KAAK,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC;KAC9C,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,5 @@
1
- import type { ResearchBrief, ResearchSource } from '../types.js';
2
- export declare function buildResearchBrief(question: string, sources: ResearchSource[], subQueries: string[], perSourceCharCap: number, totalSourcesCharCap: number): Promise<ResearchBrief>;
1
+ import type { ResearchBrief, ResearchSource, CrossReference } from '../types.js';
2
+ import type { QueryType } from './decompose.js';
3
+ export declare function buildResearchBrief(question: string, sources: ResearchSource[], subQueries: string[], perSourceCharCap: number, totalSourcesCharCap: number, queryType?: QueryType, comparisonEntities?: string[]): Promise<ResearchBrief>;
4
+ export declare function detectCrossReferences(sources: ResearchSource[]): CrossReference[];
3
5
  //# sourceMappingURL=brief.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"brief.d.ts","sourceRoot":"","sources":["../../src/research/brief.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAoB,MAAM,aAAa,CAAC;AAUnF,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,cAAc,EAAE,EACzB,UAAU,EAAE,MAAM,EAAE,EACpB,gBAAgB,EAAE,MAAM,EACxB,mBAAmB,EAAE,MAAM,GAC1B,OAAO,CAAC,aAAa,CAAC,CAyBxB"}
1
+ {"version":3,"file":"brief.d.ts","sourceRoot":"","sources":["../../src/research/brief.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAoB,cAAc,EAAE,MAAM,aAAa,CAAC;AACnG,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAYhD,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,cAAc,EAAE,EACzB,UAAU,EAAE,MAAM,EAAE,EACpB,gBAAgB,EAAE,MAAM,EACxB,mBAAmB,EAAE,MAAM,EAC3B,SAAS,GAAE,SAAqB,EAChC,kBAAkB,GAAE,MAAM,EAAO,GAChC,OAAO,CAAC,aAAa,CAAC,CAwCxB;AA6BD,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,cAAc,EAAE,GAAG,cAAc,EAAE,CAuCjF"}
@@ -2,10 +2,12 @@ import { extractHighlights } from '../search/highlights.js';
2
2
  const MAX_HIGHLIGHTS = 12;
3
3
  const MAX_KEY_FINDING_LEN = 280;
4
4
  const MAX_TOPICS = 8;
5
+ const MAX_CROSS_REFS = 10;
6
+ const MIN_PHRASE_LEN = 4;
5
7
  // Build a host-LLM-friendly structured brief when internal sampling is
6
8
  // unavailable. The host model (Claude Code / Cursor / etc.) consumes this
7
9
  // shape to produce the final report without needing to re-read raw sources.
8
- export async function buildResearchBrief(question, sources, subQueries, perSourceCharCap, totalSourcesCharCap) {
10
+ export async function buildResearchBrief(question, sources, subQueries, perSourceCharCap, totalSourcesCharCap, queryType = 'general', comparisonEntities = []) {
9
11
  const fetched = sources.filter((s) => s.fetched && s.markdown_content.length > 0);
10
12
  // Highlights reuse the FlashRank-or-paragraph scorer so briefs align with
11
13
  // whatever format='highlights' produces for single-query searches.
@@ -19,12 +21,26 @@ export async function buildResearchBrief(question, sources, subQueries, perSourc
19
21
  const { highlights } = await extractHighlights(question, searchItems, MAX_HIGHLIGHTS);
20
22
  const topics = buildTopics(subQueries, fetched);
21
23
  const keyFindings = buildKeyFindings(fetched);
24
+ const crossReferences = detectCrossReferences(fetched);
25
+ const gaps = detectGaps(subQueries, fetched);
26
+ const comparison = queryType === 'comparison' && comparisonEntities.length >= 2
27
+ ? buildComparisonSection(comparisonEntities, fetched)
28
+ : undefined;
22
29
  return {
23
30
  topics,
24
31
  highlights,
25
32
  key_findings: keyFindings,
26
33
  per_source_char_cap: perSourceCharCap,
27
34
  total_sources_char_cap: totalSourcesCharCap,
35
+ sections: {
36
+ overview: {
37
+ key_findings: keyFindings.slice(0, 5),
38
+ cross_references: crossReferences,
39
+ },
40
+ ...(comparison ? { comparison } : {}),
41
+ gaps,
42
+ },
43
+ query_type: queryType,
28
44
  };
29
45
  }
30
46
  // Prefer sub-queries (planner's view of the topic space) when available;
@@ -53,6 +69,106 @@ function buildKeyFindings(sources) {
53
69
  }
54
70
  return dedupe(out);
55
71
  }
72
+ export function detectCrossReferences(sources) {
73
+ if (sources.length < 2)
74
+ return [];
75
+ // Extract significant phrases from each source's content
76
+ const phraseMap = new Map();
77
+ for (let idx = 0; idx < sources.length; idx++) {
78
+ const content = sources[idx].markdown_content.toLowerCase();
79
+ const words = content
80
+ .replace(/[^a-z0-9\s]/g, ' ')
81
+ .split(/\s+/)
82
+ .filter((w) => w.length >= MIN_PHRASE_LEN && !STOP_WORDS.has(w));
83
+ const seenForSource = new Set();
84
+ for (let i = 0; i < words.length - 2; i++) {
85
+ const phrase = words.slice(i, i + 3).join(' ');
86
+ if (seenForSource.has(phrase))
87
+ continue;
88
+ seenForSource.add(phrase);
89
+ if (!phraseMap.has(phrase))
90
+ phraseMap.set(phrase, new Set());
91
+ phraseMap.get(phrase).add(idx);
92
+ }
93
+ }
94
+ // Phrases found in 2+ sources are cross-references
95
+ const candidates = [];
96
+ for (const [phrase, sourceIndices] of phraseMap) {
97
+ if (sourceIndices.size >= 2) {
98
+ candidates.push({
99
+ finding: phrase,
100
+ source_indices: [...sourceIndices].sort(),
101
+ confidence: sourceIndices.size >= 3 ? 'high' : 'medium',
102
+ });
103
+ }
104
+ }
105
+ // Sort by number of sources (desc), then deduplicate overlapping phrases
106
+ candidates.sort((a, b) => b.source_indices.length - a.source_indices.length);
107
+ return deduplicateOverlapping(candidates).slice(0, MAX_CROSS_REFS);
108
+ }
109
+ function deduplicateOverlapping(refs) {
110
+ const kept = [];
111
+ const usedWords = new Set();
112
+ for (const ref of refs) {
113
+ const words = ref.finding.split(' ');
114
+ // Skip if most words already covered by a higher-ranked cross-reference
115
+ const overlapCount = words.filter((w) => usedWords.has(w)).length;
116
+ if (overlapCount >= words.length - 1 && kept.length > 0)
117
+ continue;
118
+ kept.push(ref);
119
+ for (const w of words)
120
+ usedWords.add(w);
121
+ }
122
+ return kept;
123
+ }
124
+ function detectGaps(subQueries, sources) {
125
+ if (subQueries.length === 0)
126
+ return [];
127
+ const gaps = [];
128
+ const contentLower = sources.map((s) => s.markdown_content.toLowerCase()).join(' ');
129
+ for (const query of subQueries) {
130
+ // Extract significant words from sub-query
131
+ const words = query.toLowerCase()
132
+ .replace(/[^a-z0-9\s]/g, ' ')
133
+ .split(/\s+/)
134
+ .filter((w) => w.length >= MIN_PHRASE_LEN && !STOP_WORDS.has(w));
135
+ if (words.length === 0)
136
+ continue;
137
+ // Count how many significant words appear in any source
138
+ const found = words.filter((w) => contentLower.includes(w)).length;
139
+ const coverage = found / words.length;
140
+ if (coverage < 0.5) {
141
+ gaps.push(`Limited coverage for: "${query}"`);
142
+ }
143
+ }
144
+ return gaps;
145
+ }
146
+ function buildComparisonSection(entities, sources) {
147
+ const comparisonPoints = [];
148
+ const contentLower = sources.map((s) => s.markdown_content.toLowerCase()).join('\n');
149
+ // Look for comparison keywords near entity mentions
150
+ const comparisonTerms = ['faster', 'slower', 'better', 'worse', 'more', 'less',
151
+ 'easier', 'harder', 'simpler', 'complex', 'lightweight', 'heavy',
152
+ 'performance', 'scalability', 'ecosystem', 'community', 'support'];
153
+ for (const term of comparisonTerms) {
154
+ if (!contentLower.includes(term))
155
+ continue;
156
+ // Check if term appears near any entity
157
+ const nearEntity = entities.some((e) => {
158
+ const entityLower = e.toLowerCase();
159
+ const idx = contentLower.indexOf(entityLower);
160
+ if (idx === -1)
161
+ return false;
162
+ // Check within 200 chars of entity mention
163
+ const neighborhood = contentLower.slice(Math.max(0, idx - 200), idx + e.length + 200);
164
+ return neighborhood.includes(term);
165
+ });
166
+ if (nearEntity) {
167
+ comparisonPoints.push(term);
168
+ }
169
+ }
170
+ return { entities, comparison_points: [...new Set(comparisonPoints)] };
171
+ }
56
172
  function firstSubstantiveParagraph(markdown) {
57
173
  const paragraphs = markdown.split(/\n\n+/).map((p) => p.trim());
58
174
  for (const p of paragraphs) {
@@ -76,4 +192,14 @@ function dedupe(list) {
76
192
  }
77
193
  return out;
78
194
  }
195
+ const STOP_WORDS = new Set([
196
+ 'about', 'after', 'also', 'been', 'before', 'being', 'between',
197
+ 'both', 'could', 'does', 'doing', 'done', 'each', 'even', 'every',
198
+ 'from', 'have', 'here', 'into', 'just', 'like', 'made', 'make',
199
+ 'many', 'more', 'most', 'much', 'must', 'need', 'only', 'other',
200
+ 'over', 'same', 'should', 'some', 'such', 'than', 'that', 'their',
201
+ 'them', 'then', 'there', 'these', 'they', 'this', 'those', 'through',
202
+ 'very', 'want', 'well', 'were', 'what', 'when', 'where', 'which',
203
+ 'while', 'will', 'with', 'would', 'your',
204
+ ]);
79
205
  //# sourceMappingURL=brief.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"brief.js","sourceRoot":"","sources":["../../src/research/brief.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"brief.js","sourceRoot":"","sources":["../../src/research/brief.ts"],"names":[],"mappings":"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"}
@@ -1,7 +1,14 @@
1
1
  import { type SamplingCapableServer } from '../search/sampling.js';
2
+ export type QueryType = 'comparison' | 'how-to' | 'concept' | 'general';
2
3
  export interface DecomposeResult {
3
4
  subQueries: string[];
4
5
  samplingUsed: boolean;
6
+ queryType: QueryType;
5
7
  }
6
8
  export declare function decomposeQuestion(question: string, depth: 'quick' | 'standard' | 'comprehensive', server?: SamplingCapableServer): Promise<DecomposeResult>;
9
+ export declare function detectQueryType(question: string): QueryType;
10
+ export declare function extractComparisonEntities(question: string): {
11
+ entities: string[];
12
+ context: string;
13
+ };
7
14
  //# sourceMappingURL=decompose.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"decompose.d.ts","sourceRoot":"","sources":["../../src/research/decompose.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,qBAAqB,EAG3B,MAAM,uBAAuB,CAAC;AAU/B,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,YAAY,EAAE,OAAO,CAAC;CACvB;AAED,wBAAsB,iBAAiB,CACrC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,OAAO,GAAG,UAAU,GAAG,eAAe,EAC7C,MAAM,CAAC,EAAE,qBAAqB,GAC7B,OAAO,CAAC,eAAe,CAAC,CAkB1B"}
1
+ {"version":3,"file":"decompose.d.ts","sourceRoot":"","sources":["../../src/research/decompose.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,qBAAqB,EAG3B,MAAM,uBAAuB,CAAC;AAU/B,MAAM,MAAM,SAAS,GAAG,YAAY,GAAG,QAAQ,GAAG,SAAS,GAAG,SAAS,CAAC;AAExE,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,YAAY,EAAE,OAAO,CAAC;IACtB,SAAS,EAAE,SAAS,CAAC;CACtB;AAED,wBAAsB,iBAAiB,CACrC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,OAAO,GAAG,UAAU,GAAG,eAAe,EAC7C,MAAM,CAAC,EAAE,qBAAqB,GAC7B,OAAO,CAAC,eAAe,CAAC,CAuC1B;AA8ED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,CA0B3D;AAED,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,GAAG;IAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CA6BnG"}
@@ -8,11 +8,12 @@ const DEPTH_SUB_QUERY_COUNT = {
8
8
  };
9
9
  export async function decomposeQuestion(question, depth, server) {
10
10
  const targetCount = DEPTH_SUB_QUERY_COUNT[depth] ?? 4;
11
+ const queryType = detectQueryType(question);
11
12
  if (server) {
12
13
  try {
13
14
  const result = await decomposeWithSampling(question, targetCount, server);
14
15
  if (result) {
15
- return { subQueries: result, samplingUsed: true };
16
+ return { subQueries: result, samplingUsed: true, queryType };
16
17
  }
17
18
  }
18
19
  catch (err) {
@@ -21,8 +22,27 @@ export async function decomposeQuestion(question, depth, server) {
21
22
  });
22
23
  }
23
24
  }
25
+ const templateQueries = decomposeWithTemplate(question, queryType, targetCount);
26
+ if (templateQueries && templateQueries.length >= targetCount) {
27
+ return { subQueries: templateQueries, samplingUsed: false, queryType };
28
+ }
29
+ // Template produced some queries but not enough — supplement with fallback
30
+ if (templateQueries && templateQueries.length > 0) {
31
+ const fallback = decomposeWithFallback(question, targetCount);
32
+ const merged = [...templateQueries];
33
+ const seen = new Set(merged.map(q => q.toLowerCase()));
34
+ for (const q of fallback) {
35
+ if (merged.length >= targetCount)
36
+ break;
37
+ if (!seen.has(q.toLowerCase())) {
38
+ merged.push(q);
39
+ seen.add(q.toLowerCase());
40
+ }
41
+ }
42
+ return { subQueries: merged.slice(0, targetCount), samplingUsed: false, queryType };
43
+ }
24
44
  const fallback = decomposeWithFallback(question, targetCount);
25
- return { subQueries: fallback, samplingUsed: false };
45
+ return { subQueries: fallback, samplingUsed: false, queryType };
26
46
  }
27
47
  async function decomposeWithSampling(question, targetCount, server) {
28
48
  try {
@@ -85,6 +105,109 @@ Respond with ONLY valid JSON: {"subQueries": ["query1", "query2", ...]}`;
85
105
  return null;
86
106
  }
87
107
  }
108
+ export function detectQueryType(question) {
109
+ const q = question.trim().toLowerCase();
110
+ // Comparison: "X vs Y", "X versus Y", "compare X and Y", "X or Y for Z"
111
+ if (/\bvs\.?\s/i.test(q) || /\bversus\b/i.test(q) || /^compare\b/i.test(q)) {
112
+ return 'comparison';
113
+ }
114
+ if (/\b(?:difference|differences)\s+between\b/i.test(q)) {
115
+ return 'comparison';
116
+ }
117
+ // "X or Y for Z" pattern (choice between alternatives)
118
+ if (/\b\w+\s+or\s+\w+\s+for\b/i.test(q)) {
119
+ return 'comparison';
120
+ }
121
+ // How-to: "how to ...", "how do I ...", "how can I ...", "steps to ..."
122
+ if (/^how\s+(?:to|do|does|can|should)\b/i.test(q) || /^steps\s+to\b/i.test(q)) {
123
+ return 'how-to';
124
+ }
125
+ // Concept: "what is ...", "explain ...", "overview of ..."
126
+ if (/^(?:what\s+(?:is|are)|explain|overview\s+of|describe)\b/i.test(q)) {
127
+ return 'concept';
128
+ }
129
+ return 'general';
130
+ }
131
+ export function extractComparisonEntities(question) {
132
+ // Strip common question prefixes so "how does X vs Y" still matches
133
+ const cleaned = question
134
+ .replace(/[?!.]/g, '')
135
+ .replace(/^(?:how\s+(?:does|do|is|are|about)|what\s+(?:is|are)\s+(?:the\s+)?(?:difference|differences)\s+(?:between)?|should\s+I\s+(?:use|choose)|which\s+is\s+better)\s*/i, '')
136
+ .trim();
137
+ // "X vs Y vs Z for/in/with context"
138
+ const vsMatch = cleaned.match(/^(.+?)\s+(?:vs\.?|versus)\s+(.+?)(?:\s+(?:vs\.?|versus)\s+(.+?))?(?:\s+(?:for|in|with|when)\s+(.+))?$/i);
139
+ if (vsMatch) {
140
+ const entities = [vsMatch[1], vsMatch[2], vsMatch[3]].filter(Boolean).map(e => e.trim());
141
+ return { entities, context: vsMatch[4]?.trim() || '' };
142
+ }
143
+ // "compare X and Y for context" / "compare X, Y, and Z"
144
+ const compareMatch = cleaned.match(/^compare\s+(.+?)(?:\s+(?:for|in|with|when)\s+(.+))?$/i);
145
+ if (compareMatch) {
146
+ const entityPart = compareMatch[1];
147
+ const entities = entityPart.split(/(?:,\s*|\s+and\s+)/).map(e => e.trim()).filter(Boolean);
148
+ return { entities, context: compareMatch[2]?.trim() || '' };
149
+ }
150
+ // "differences between X and Y"
151
+ const diffMatch = cleaned.match(/differences?\s+between\s+(.+?)\s+and\s+(.+?)(?:\s+(?:for|in|with)\s+(.+))?$/i);
152
+ if (diffMatch) {
153
+ return { entities: [diffMatch[1].trim(), diffMatch[2].trim()], context: diffMatch[3]?.trim() || '' };
154
+ }
155
+ return { entities: [], context: '' };
156
+ }
157
+ function decomposeWithTemplate(question, queryType, targetCount) {
158
+ if (queryType === 'comparison') {
159
+ const { entities, context } = extractComparisonEntities(question);
160
+ if (entities.length < 2)
161
+ return null;
162
+ const queries = [];
163
+ // Per-entity queries
164
+ for (const entity of entities) {
165
+ queries.push(`${entity} ${context} features performance`.trim());
166
+ }
167
+ // Cross-comparison queries
168
+ for (let i = 0; i < entities.length; i++) {
169
+ for (let j = i + 1; j < entities.length; j++) {
170
+ queries.push(`${entities[i]} vs ${entities[j]} ${context} comparison`.trim());
171
+ }
172
+ }
173
+ // Ecosystem/adoption query
174
+ queries.push(`${entities.join(' vs ')} which to choose ${context}`.trim());
175
+ return [...new Set(queries)].slice(0, targetCount);
176
+ }
177
+ if (queryType === 'how-to') {
178
+ const cleaned = question.replace(/[?!.]/g, '').trim();
179
+ const task = cleaned.replace(/^(?:how\s+(?:to|do\s+I|does\s+one|can\s+I|should\s+I)|steps\s+to)\s+/i, '').trim();
180
+ if (task.length < 5)
181
+ return null;
182
+ const queries = [
183
+ `${task} tutorial guide`,
184
+ `${task} best practices`,
185
+ `${task} common mistakes pitfalls`,
186
+ `${task} examples production`,
187
+ `${task} step by step`,
188
+ `${task} tools libraries`,
189
+ `${task} troubleshooting`,
190
+ ];
191
+ return [...new Set(queries)].slice(0, targetCount);
192
+ }
193
+ if (queryType === 'concept') {
194
+ const cleaned = question.replace(/[?!.]/g, '').trim();
195
+ const concept = cleaned.replace(/^(?:what\s+(?:is|are)|explain|overview\s+of|describe)\s+/i, '').trim();
196
+ if (concept.length < 3)
197
+ return null;
198
+ const queries = [
199
+ `${concept} definition overview`,
200
+ `${concept} how it works architecture`,
201
+ `${concept} use cases applications`,
202
+ `${concept} alternatives comparison`,
203
+ `${concept} advantages disadvantages`,
204
+ `${concept} examples real world`,
205
+ `${concept} best practices`,
206
+ ];
207
+ return [...new Set(queries)].slice(0, targetCount);
208
+ }
209
+ return null;
210
+ }
88
211
  function decomposeWithFallback(question, targetCount) {
89
212
  const cleaned = question.trim();
90
213
  if (!cleaned) {
@@ -146,6 +269,7 @@ function extractNounPhrases(text) {
146
269
  'this', 'that', 'these', 'those', 'i', 'me', 'my', 'we', 'our',
147
270
  'you', 'your', 'he', 'him', 'his', 'she', 'her', 'it', 'its',
148
271
  'they', 'them', 'their',
272
+ 'vs', 'versus', 'compare', 'compared', 'comparison',
149
273
  ]);
150
274
  const words = text.replace(/[?!.,;:'"()\[\]{}]/g, ' ').split(/\s+/).filter(Boolean);
151
275
  const contentWords = words.filter((w) => !stopWords.has(w.toLowerCase()));