Project Files
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
LM Studio plugin that exposes four web-oriented tools to local LLMs β Web Search, Image Search, Visit Website, and View Images β built on @lmstudio/sdk. Descended from Daniel Sig's original lms-plugin-duckduckgo and lms-plugin-visit-website plugins, merged and extended by Nigel Packer.
npm run dev β run plugin in LM Studio dev mode (lms dev)npm run push β publish to LM Studio Hub (lms push)npm run lint / npm run lint:fix β ESLint on src/**/*.tsnpm run format / npm run format:check β Prettiernpm run knip β dead-code / unused-export checkA local pre-commit hook at .git/hooks/pre-commit runs lint, format:check, and knip sequentially and aborts the commit on any failure. The hook is not committed to the repo β fresh clones need to reinstall it. Bypass with git commit --no-verify when necessary.
No test suite is configured. TypeScript targets ES2023 / CommonJS. Requires Node >= 22 (fetch + AbortSignal.any).
Entry point src/index.ts registers a config schematic and a tools provider with the LM Studio SDK.
src/parsers/page/page-text.ts feeds raw HTML to @mozilla/readability to strip boilerplate (nav, sidebars, comments), then routes Readability's .content HTML through either:
Both paths share src/text/normalize-blank-lines.ts for trailing-whitespace and blank-line collapsing. Both contentFormat (plugin select field, default "markdown") and contentLimit are plugin-only β neither is exposed as a tool parameter so the model cannot override the user-set or default values. The tool still returns contentLength, the pre-truncation character count, so the model can detect truncation and refine with findInPage.
src/parsers/page/page-images.ts scrapes <img> tags in document order, resolves relative src against the page URL, deduplicates, and returns { src, alt, title } tuples (up to maxImages). View Images then downloads each via downloadImages and returns { filename, alt, title, image } on success or { filename, alt, title, error } on failure. filename is derived from the URL's last path segment via src/fs/url-filename.ts.
Three disk-backed TTL caches (via cacache) are constructed once in toolsProvider and shared across tools. They persist across plugin reloads β clearing requires removing the cacache directory at ~/.lmstudio/plugin-data/lms-plugin-duckduckgo-cache:
searchCacheTtlSeconds (default 15 min)vqdCacheTtlSeconds (default 10 min)websiteCacheTtlSeconds (default 10 min)Cache sizes and subdirs are defined in src/tools-provider.ts; the TTLCache implementation is in src/cache/ttl-cache.ts. TTL defaults live in src/config/resolve-config.ts.
Image endpoints require a VQD token scraped from the DuckDuckGo homepage. A configurable delay (vqdImageDelaySeconds, default 2s) is inserted between the VQD fetch and the image API call. The token is extracted via regex from inline script URLs of the form vqd=<token> (see src/parsers/vqd-parser.ts) β DuckDuckGo removed the original hidden-input form field. Token acquisition failures raise VqdTokenError (src/duckduckgo/vqd-token-error.ts) with a VqdTokenFailureReason of token_not_found or fetch_failed.
DuckDuckGo uses non-obvious p param values: strictβ"1", moderateβ"", offβ"-1". Centralized in src/duckduckgo/safe-search.ts.
Three error hierarchies are load-bearing:
FetchError (src/http/fetch-error.ts) β HTTP/network failures, carries url and optional .formatToolError in src/errors/tool-error.ts converts these into user-facing strings per tool kind (web-search, image-search, website, image-download), including abort-detection via DOMException.name === "AbortError".
ESLint enforces two rules on src/tools/*-tool.ts: the file must contain exactly one exported create<Name>Tool factory returning Tool, and module-level function declarations other than that factory are banned. Per-tool helpers either live in a sibling module (e.g. src/fs/url-filename.ts, src/parsers/page/) or are inlined inside the implementation arrow. Interfaces at module scope are allowed.
createWebSearchTool, createImageSearchTool, createVisitWebsiteTool, createViewImagesTool). Per-call config resolves via resolveConfig in src/config/resolve-config.ts, merging plugin UI settings from src/config/config-schematics.ts with per-call overrides (runtime override > plugin config > default).RateLimiter (src/timing/rate-limiter.ts, backed by bottleneck) enforces a requestIntervalSeconds gap (default 5s) between outbound requests.impit client (src/http/impit-client.ts) wrapped by withRetry in src/http/retry.ts (maxRetries, retryInitialBackoffSeconds, retryMaxBackoffSeconds). Do not replace impit with fetch β it applies browser TLS fingerprints and headers that DuckDuckGo's anti-bot layer requires (see commit 9e97d38).search-web.ts, search-images.ts, fetch-vqd-token.ts, and build-urls.ts. Safe-search encoding lives in src/duckduckgo/safe-search.ts..result__a), src/parsers/image-results-parser.ts (JSON), src/parsers/vqd-parser.ts (homepage input[name="vqd"]).[title, url] pairs. Image Search and View Images download files via src/images/download-images.ts into the per-chat working directory obtained from ctl.getWorkingDirectory(). That call is made inside each tool's implementation (not at toolsProvider setup), since the SDK only attaches a working directory when a tool is actually invoked from a chat. On per-image download failure the tools fall back to the remote URL rather than throwing. Visit Website (src/website/fetch-website.ts) returns only the page title, first-level headings, and a readable-content excerpt β no image download or link extraction. View Images accepts explicit URLs and/or scrapes images from a given page, returning per-image records with filename, alt, title, and a markdown reference to the downloaded file.turndown service (ATX headings, - bullets, fenced code, inline links, inline images). script/style/noscript/template are stripped before conversion.html-to-text with token-conservative options: word wrapping disabled, anchor URLs dropped (only inner text kept), <img>/<noscript>/<template> skipped, headings and table headers left in source case rather than uppercased, and list items prefixed with - .causeVqdTokenError (src/duckduckgo/vqd-token-error.ts) β token acquisition.NoResultsError base with NoWebResultsError / NoImageResultsError (src/errors/no-results-error.ts).@lmstudio/sdk β plugin/tool registrationimpit β HTTP client with TLS + header fingerprinting (required for anti-bot)jsdom β HTML parsing@mozilla/readability β readable article extraction for Visit Website (boilerplate removal, not text extraction)turndown β HTML β Markdown conversion for Visit Website's content fieldhtml-to-text β HTML β plain-text conversion for Visit Website's content field when markdown is opted outzod β tool parameter schemasbottleneck β backs the shared RateLimitercacache β disk-backed cache store for all three TTL cachesfile-type β MIME sniffing for downloaded images