Releases: PsychQuant/livedocs
Release list
v0.9.0
-
Explicit ecosystem hint syntax for
look-up— a<ecosystem>:<name>prefix (npm:go,cran:dplyr,crates:serde@1.0.100) forces a specific ecosystem, as an escape hatch for (1) package names shadowed by the fixed language set (go/swift/r/…, which otherwise route to runtime introspection) and (2) named-only registries (cran/crates/maven/…) that previously relied on the agent inferring the ecosystem. Classification precedence is URL → ecosystem-hint → Language → Package; splitting once at the first:, a hint is recognized only when the prefix (case-insensitively) is one of the nine known ecosystem ids, the remainder is non-empty, and the remainder's first character is not/(so a barenpm:and a URL scheme'snpm://xare both NOT hints and fall through). Version pins compose (crates:serde@1.0.100). This is a plugin-shell (skill contract) change — the MCP binary is unchanged. The publishedexplicit-doc-lookupspec's pin scenario is also reconciled with the crates/go/rubygems pin-honoring behavior shipped earlier. -
R runtime depth adapter —
introspect kind=runtimenow covers R: probesR(falling back toRscript)--version, readsDESCRIPTION'sDepends: R (>= x.y)as a constraint (lower bound, never an exact version), honors.r-version, and auto-detects R projects viaDESCRIPTION+NAMESPACEtogether (a bareDESCRIPTIONis too weak a signal on its own), or any ofrenv.lock/.r-version/*.Rproj. Thelook-upflagship example/livedocs:look-up Rnow anchors to the local R toolchain version when R is installed (DESCRIPTIONDependsstays a lower-bound cross-check, never the effective version; without a toolchain the honest not-resolved degrade still applies). Runtime target ids are canonicalized before the safety gate, so natural spellings likeRandJavaScriptare accepted;renv.lockis a detection signal only (its pinned R version parsing is a tracked follow-up). -
javascript→nodealias — runtime introspection withtarget=javascriptresolves through the Node adapter; the result keeps the requested identifier and the source field discloses the node probe. No response-schema change. -
Version pins honored for crates/go/rubygems —
resolve_source/latest_versionnow confirm a requested pin at each ecosystem's first-class per-version endpoint (crates/crates/{n}/{v}, Go@v/v{v}.info, RubyGems v2versions/{v}.json), reporting the confirmed version.latest_versiongains a machine-readablepin_honoredfield:trueonly when the resolved version exactly equals the requested pin (modulo a leadingv),falsefor a latest-only ecosystem (jsr/packagist/maven/cran), a version that wasn't found, or a non-exact token — an npm dist-tag (beta/next) or a partial pin (18) resolves to a moving/other concrete version and is reportedpin_honored:false, never faked as a confirmed pin. Onfalse,versionis the registry's current resolution — the latest, or the concrete version a tag/partial resolved to — never presented as a confirmed pin or guaranteed-latest-stable. A requested pin always gets apin_honoredsignal, even when nothing resolved. During ecosystem auto-detect, an npm pinned-version miss falls through to PyPI's exact pin instead of short-circuiting to npm's latest. Docs/repo URLs remain latest (default-branch) and stay labeled not-pinned. maven/packagist/jsr per-version support is a tracked follow-up. -
Binary release ships the R adapter (#35) and this version-pin work (#36) together in one signed release train.
v0.7.0
Security and robustness hardening across the fetch and introspection surfaces (addresses the multi-agent review, issues #3–#17).
- SSRF guard — every outbound fetch (
fetch_docs,resolve_sourcedocs_url, openapi/graphql) is validated at the single transport choke point: anhttp/httpsscheme allowlist plus a host classifier that rejects loopback / link-local (incl.169.254.169.254metadata) / RFC-1918 / ULA /.internal/.localtargets, with a DNS-resolution check for rebinding and a redirect delegate that re-validates every hop. - Response-size ceiling — bodies are streamed and aborted past a byte limit, bounding the decompressed size so a gzip bomb can't OOM the server. The ETag cache is now LRU-bounded (total-byte budget + entry cap) and refuses to store oversized bodies.
fetch_docscrash fix — a negativemax_bytesno longer trapsprefix(_:); truncation is byte-accurate and fetched content is stripped of control/ANSI/bidi characters before returning.- Hardened process runner — one
ProcessRunnerreplaces three copies: pipes are drained concurrently (no >64 KB deadlock), the watchdog escalates SIGTERM → SIGKILL, a timed-out probe surfaces as an error instead of silent truncation, and executable resolution is PATH-first so pyenv/mise/asdf shims are honored. - Runtime introspection accuracy — version files are refused if they are symlinks (closes a secret-exfil channel) and only version-shaped first lines are accepted; the toolchain probe requires a clean exit and labels the source with the command that actually ran;
parseMiseTomlstrips inline comments / rejects arrays and reads the canonicalmise.toml; uncovered-but-safe languages fall back to the universal pin layer;introspect kind=runtimeaccepts an optional validatedpath. - Single version source —
LiveDocsVersionis the one place the version lives; the MCP server, User-Agent (now pointing atPsychQuant/livedocs), and the mcpb/plugin/marketplace manifests all track it.
v0.6.0
Proactive language-runtime version detection. introspect gains a read-only runtime kind that resolves the effective language-runtime version for the current project across Python, Node/TypeScript, Go, Rust, Java, C#/.NET, and Swift. Resolution is two-layer: a universal pin parser reads cross-language declaration files (asdf .tool-versions, mise, idiomatic .<lang>-version files), and per-language depth adapters probe the active toolchain and read the manifest. The active toolchain is authoritative; declared sources are interpreted by their semantics — a constraint (requires-python >=3.9) or a language-mode declaration (swift-tools-version) is never reported as an exact version, and an unresolvable runtime returns not-resolved rather than a guessed global version. The docs-router skill splits version reconciliation into an eager, per-cwd-cached, silent detect phase and a lazy, only-when-relevant offer phase; defer-to-local and confirmed-install are unchanged.
v0.5.0
ETag conditional-revalidation cache — the only thesis-safe way to be faster without becoming a stale index. An ETagCachingHTTPClient decorator wraps the network layer: when a URL is cached it always re-requests with If-None-Match, so the server decides freshness. On 304 Not Modified it serves the cached body (no re-download/re-parse of an unchanged, often large doc like an llms.txt); on 200 with a new ETag it refreshes. It deliberately ignores max-age — "latest" stays latest, it's just cheaper when nothing changed. Sources without an ETag (e.g. some registry endpoints) are simply not cached; POSTs (GraphQL introspection) are never cached. In-memory, per session.
v0.4.0
Installed-version introspection + version reconciliation (issue #1). Handles targets that have BOTH a web-latest and a locally-installed version.
- MCP
introspectgainskind:"r-pkg"— a READ-ONLY probe of a locally installed R package's version viaRscript(name-guarded, passed as an arg not-e, watchdog-timed). Returnsinstalled_version+ theresolved_env(.libPaths()entry) it came from, or an honest "not installed in the current context" — never a fabricated global version. Never installs. docs-routerskill gains per-question target-type classification (has-localvsweb-only) and a version-reconciliation state machine: for a has-local query, introspect the installed version + fetch web-latest, then always defer to local (web only gates the upgrade decision); a chosen upgrade is run by the skill after explicit confirmation, and the MCP stays read-only.web-onlytargets (e.g. Claude Code features/config, SaaS) skip reconciliation entirely.
v0.3.0
CRAN (R) registry adapter — 9th ecosystem. ecosystem:"cran" resolves an R package's latest version + repo (from the CRAN URL/BugReports fields) via the crandb JSON API, feeding the same chain (e.g. dplyr → github.com/tidyverse/dplyr → dplyr.tidyverse.org/llms.txt). Turns the mechanical half of a hand-curated R docs guide into a live lookup.
No other behavior change.
v0.2.0
Broader dynamic coverage + version pinning. context7 stays a purely external reference — never integrated as a fallback leg (LiveDocs only ever serves live primary sources).
More registry adapters (all keyless, single live GET, ranked into the same chain): crates.io (Rust), Go modules (proxy.golang.org; module path used as repo when Origin is absent), RubyGems, JSR (Deno; two-call for the repo), Packagist (PHP), Maven Central (version-only). npm/pypi still auto-detect; the rest need an explicit ecosystem.
Version pinning — version param on resolve_source/latest_version (e.g. React 18.3.1 vs latest). Honored deterministically by the npm/PyPI registry legs; the repo/changelog/llms.txt sources are labeled "content NOT pinned" since their URLs serve the default branch / latest (llms.txt has no per-version hosting convention). A pin ignored by a latest-only ecosystem is reported, not silently dropped.
Security — boundary validation (isSafePackageName/isSafeVersion) before any name/version is interpolated into a registry URL; Maven group:artifact gets a stricter charset so it can't inject extra Solr clauses.
Reviewed by a 4-lens adversarial workflow; 7 findings fixed, 40 unit tests green.
v0.1.0
Initial release — primary-source-first, always-latest documentation engine.
Discovery chain (fidelity-first, then freshness):
llms.txt/llms-full.txtauto-discovery across root //docs/ full-variant paths, with a soft-404 guard (a real hit is200+text/plain+ non-trivial size) and index→full upgrade.- Package registry resolution: npm (
/latest) and PyPI (/pypi/<pkg>/json) — the exact latest version + changelog/repo/docs URLs, deterministically. - GitHub repo as raw source.
- Introspection: OpenAPI/Swagger JSON, GraphQL schema, and installed-CLI
--help/--version(with command-injection guard). - context7/web reserved as labeled low-fidelity fallback (engine returns empty when no primary source exists).
Tools: resolve_source, fetch_docs, latest_version, introspect.
Quality: 26 unit tests (pure logic + engine via injected HTTP fakes); all 4 tools verified live end-to-end. Premise validated by probing 25 popular docs hosts (~88% ship llms.txt).