Skip to content

Releases: PsychQuant/livedocs

v0.9.0

Choose a tag to compare

@kiki830621 kiki830621 released this 04 Jul 01:54
  • 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 bare npm: and a URL scheme's npm://x are 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 published explicit-doc-lookup spec's pin scenario is also reconciled with the crates/go/rubygems pin-honoring behavior shipped earlier.

  • R runtime depth adapterintrospect kind=runtime now covers R: probes R (falling back to Rscript) --version, reads DESCRIPTION's Depends: R (>= x.y) as a constraint (lower bound, never an exact version), honors .r-version, and auto-detects R projects via DESCRIPTION+NAMESPACE together (a bare DESCRIPTION is too weak a signal on its own), or any of renv.lock / .r-version / *.Rproj. The look-up flagship example /livedocs:look-up R now anchors to the local R toolchain version when R is installed (DESCRIPTION Depends stays 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 like R and JavaScript are accepted; renv.lock is a detection signal only (its pinned R version parsing is a tracked follow-up).

  • javascriptnode alias — runtime introspection with target=javascript resolves 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/rubygemsresolve_source / latest_version now confirm a requested pin at each ecosystem's first-class per-version endpoint (crates /crates/{n}/{v}, Go @v/v{v}.info, RubyGems v2 versions/{v}.json), reporting the confirmed version. latest_version gains a machine-readable pin_honored field: true only when the resolved version exactly equals the requested pin (modulo a leading v), false for 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 reported pin_honored:false, never faked as a confirmed pin. On false, version is 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 a pin_honored signal, 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

Choose a tag to compare

@kiki830621 kiki830621 released this 01 Jul 23:46
b5e90ec

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_source docs_url, openapi/graphql) is validated at the single transport choke point: an http/https scheme allowlist plus a host classifier that rejects loopback / link-local (incl. 169.254.169.254 metadata) / RFC-1918 / ULA / .internal / .local targets, 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_docs crash fix — a negative max_bytes no longer traps prefix(_:); truncation is byte-accurate and fetched content is stripped of control/ANSI/bidi characters before returning.
  • Hardened process runner — one ProcessRunner replaces 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; parseMiseToml strips inline comments / rejects arrays and reads the canonical mise.toml; uncovered-but-safe languages fall back to the universal pin layer; introspect kind=runtime accepts an optional validated path.
  • Single version sourceLiveDocsVersion is the one place the version lives; the MCP server, User-Agent (now pointing at PsychQuant/livedocs), and the mcpb/plugin/marketplace manifests all track it.

v0.6.0

Choose a tag to compare

@kiki830621 kiki830621 released this 01 Jul 15:49

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

Choose a tag to compare

@kiki830621 kiki830621 released this 01 Jul 06:01

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

Choose a tag to compare

@kiki830621 kiki830621 released this 01 Jul 04:44

Installed-version introspection + version reconciliation (issue #1). Handles targets that have BOTH a web-latest and a locally-installed version.

  • MCP introspect gains kind:"r-pkg" — a READ-ONLY probe of a locally installed R package's version via Rscript (name-guarded, passed as an arg not -e, watchdog-timed). Returns installed_version + the resolved_env (.libPaths() entry) it came from, or an honest "not installed in the current context" — never a fabricated global version. Never installs.
  • docs-router skill gains per-question target-type classification (has-local vs web-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-only targets (e.g. Claude Code features/config, SaaS) skip reconciliation entirely.

v0.3.0

Choose a tag to compare

@kiki830621 kiki830621 released this 01 Jul 02:42

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

Choose a tag to compare

@kiki830621 kiki830621 released this 01 Jul 02:25

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 pinningversion 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

Choose a tag to compare

@kiki830621 kiki830621 released this 30 Jun 07:30

Initial release — primary-source-first, always-latest documentation engine.

Discovery chain (fidelity-first, then freshness):

  • llms.txt / llms-full.txt auto-discovery across root / /docs / full-variant paths, with a soft-404 guard (a real hit is 200 + 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).