{"ok":true,"site":{"host":"gentropic.org","url":"https://gentropic.org/weir/","type":"live","desc":"@gcu/weir","tools":[{"name":"weir_queryItems","kind":"answer","impl":"imperative","description":"Search/list weir feed items, newest first. Filters: q (substring over title/excerpt/text), feed (a source by id OR name, e.g. \"Saved Links\"), category (folder name; \"\" = ungrouped), type (article|video|release|paper|status|track|podcast|commit|issue|note|doc), view (inbox|saved|archived), unread (bool), saved (bool), limit (default 30, max 100). PROVENANCE filters (the agent footprint): addedBy = items the agent ADDED (true = any agent, or an identity string like \"claude:librarian\"); taggedBy = items the agent TAGGED. Filters combine. Paginated: returns { count, total, hasMore, items, nextCursor }; page by passing nextCursor back with the SAME filters. Items are compact (id, title, url, feed, published, tags, added_by, excerpt). Use weir_listSources first to see feed/folder names.","annotations":{"readOnlyHint":true,"idempotentHint":true,"title":"Query weir items"},"inputSchema":{"type":"object","properties":{"q":{"type":"string","description":"Substring search over title/excerpt/text"},"feed":{"type":"string","description":"Scope to one source — its feed id or display name (e.g. \"Saved Links\")"},"category":{"type":"string","description":"Scope to one folder by name (\"\" = ungrouped)"},"type":{"type":"string","description":"Item type filter"},"view":{"type":"string","enum":["inbox","saved","archived"],"description":"Which view to scope to"},"unread":{"type":"boolean","description":"Only unread items"},"saved":{"type":"boolean","description":"Only saved (true) / only unsaved (false)"},"addedBy":{"description":"Items the agent ADDED — true (any agent) or an identity string (e.g. \"claude:librarian\")"},"taggedBy":{"description":"Items the agent TAGGED — true (any agent) or an identity string"},"limit":{"type":"integer","description":"Max items per page (default 30, cap 100)"},"cursor":{"type":"string","description":"Opaque pagination cursor from a previous call’s nextCursor — reuse the same filters"}}},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/weir/"},{"name":"weir_listSources","kind":"answer","impl":"imperative","description":"Source overview (compact — safe on a 400+ feed corpus). DEFAULT returns { feedCount, folders:[{category,feeds,inbox}], health:{failing,stale,…}, troubled:[…] } — folder summaries + a health tally + ONLY the non-healthy feeds (state/lastPolled/fails/lastError), so you can spot prune candidates without dumping everything. Pass `q` to FIND a feed by name/URL/id across ALL folders (the way to locate a specific feed) → { query, count, feeds:[…incl url] }. Pass `category` for the FULL per-feed list of one folder. Pair with weir_updateFeed / weir_removeFeed to curate, weir_queryItems({feed}) to drill in.","annotations":{"readOnlyHint":true,"idempotentHint":true,"title":"List weir sources"},"inputSchema":{"type":"object","properties":{"q":{"type":"string","description":"Find feeds whose name/URL/id contains this (across all folders)"},"category":{"type":"string","description":"Full per-feed detail for one folder (\"\" = ungrouped)"}}},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/weir/"},{"name":"weir_addFeed","kind":"act","impl":"imperative","description":"Subscribe to a feed by URL — adapter auto-detected (RSS/Atom/JSON Feed, YouTube channel, GitHub repo); an initial poll fires in the app. Optional `name` + `category` (folder). When YOU (Claude) add it, it is stamped source:agent + your identity and lands in weir_reviewQueue as a `feed` proposal for the user to ratify — pass `rationale` so they see WHY. Returns the created feed.","annotations":{"title":"Add a feed"},"inputSchema":{"type":"object","properties":{"url":{"type":"string","description":"Feed or page URL to subscribe to"},"name":{"type":"string","description":"Display name (default: derived from the URL/adapter)"},"category":{"type":"string","description":"Folder to file it under"},"rationale":{"type":"string","description":"Why you are proposing this feed — shown in the review queue at ratify time"}},"required":["url"]},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/weir/"},{"name":"weir_updateFeed","kind":"act","impl":"imperative","description":"Curate a feed: ARCHIVE/unarchive it (retire a dead/dormant feed off the health warning — items kept, poller skips it, fully reversible — the never-delete alternative to removing), change its URL (e.g. fix a moved/404 feed — items stay, validators reset, it re-polls now), rename, recategorize (folder), set retention (\"forever\" or a day count), poll interval (minutes), image policy, or full-text auto-fetch. Identify it by `id` (from weir_listSources). Not destructive. Returns the applied patch (+ `repoll` result when the URL changed).","annotations":{"title":"Update a feed"},"inputSchema":{"type":"object","properties":{"id":{"type":"string","description":"Feed id (from weir_listSources)"},"archived":{"type":"boolean","description":"true = retire the feed (off the failing/stale warning, poller skips it, items kept); false = restore + re-poll. Reversible, never deletes."},"url":{"type":"string","description":"New feed URL (fixes a moved/dead feed; re-polls immediately)"},"name":{"type":"string","description":"Rename the feed"},"category":{"type":"string","description":"Move to a folder (\"\" = ungrouped)"},"retention":{"description":"\"forever\", or a number of days to keep before archiving"},"poll_interval_minutes":{"type":"integer","description":"How often to poll (min 5)"},"images_allowed":{"type":"boolean","description":"Always load images for this feed"},"fetch_full_content":{"type":"boolean","description":"Auto-fetch full article text"}},"required":["id"]},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/weir/"},{"name":"weir_removeFeed","kind":"transact","impl":"imperative","description":"Unsubscribe and DELETE a feed and its items — for pruning dead/moved sources (DNS-gone, TLS-dead, 404) during a curation sweep. Destructive + NOT reversible (no trash, unlike the stacks). Gated behind a user setting; if disabled, errors and points to the UI. Confirm with the user before calling. Returns { removed, name, items }.","annotations":{"title":"Remove a feed","destructiveHint":true},"inputSchema":{"type":"object","properties":{"id":{"type":"string","description":"Feed id (from weir_listSources)"}},"required":["id"]},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/weir/"},{"name":"weir_renameFeed","kind":"act","impl":"imperative","description":"Re-key a feed's ID (not its display name — use weir_updateFeed for the name). A feed id is load-bearing: the adapter mints item ids as `<feedid>:<guid>` every poll, and content files, tombstones, and catalog cards are addressed by it. This migrates ALL of that in lockstep (relocating content; nothing is data-deleted) and preserves read/saved/tags. Use it to clean up an id that was auto-derived from a bad name — e.g. a feed that landed on the generic host slug `bsky-app`. The newId is slugified; a collision with an existing id is rejected. Returns { renamed, from, items, tombstones }.","annotations":{"title":"Rename a feed id"},"inputSchema":{"type":"object","properties":{"id":{"type":"string","description":"Current feed id (from weir_listSources)"},"newId":{"type":"string","description":"Desired feed id (will be slugified, e.g. \"arne-androidarts\")"}},"required":["id","newId"]},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/weir/"},{"name":"weir_repoll","kind":"act","impl":"imperative","description":"Force a fresh poll of one feed right now, bypassing conditional-GET so the full body re-parses even if nothing changed — re-derives titles, picks up edits. The clean way to refresh a feed (does NOT reset validators or schedule, unlike changing the URL). Use it to heal a microblog feed reading as \"(untitled)\", or to pull a feed immediately. Returns { id, inserted, updated, skipped } or { error }.","annotations":{"title":"Force-refresh a feed"},"inputSchema":{"type":"object","properties":{"id":{"type":"string","description":"Feed id (from weir_listSources)"}},"required":["id"]},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/weir/"},{"name":"weir_recover","kind":"act","impl":"imperative","description":"Recover a dead/truncated feed's lost history from the Internet Archive (Wayback Machine): finds old snapshots of the feed URL, re-parses their items, and stores them — archived history preserved, nothing deleted. Default QUEUES feed(s) into a gentle background drip (one IA request every few minutes, resumes across restarts) — the right tool for a batch of dead feeds; `now:true` recovers a single feed immediately as a throttled burst and returns counts (good for proving one feed before queuing many). Scope by id, ids[], or category. Returns recovery counts (now) or drip status (queued). Pairs with never-delete: recover the data, keep the feed, don't prune.","annotations":{"title":"Recover feed history (Wayback)"},"inputSchema":{"type":"object","properties":{"id":{"type":"string","description":"A single feed id (from weir_listSources)"},"ids":{"type":"array","items":{"type":"string"},"description":"Multiple feed ids to queue into the drip"},"category":{"type":"string","description":"Queue every feed in this folder"},"now":{"type":"boolean","description":"Recover the single `id` immediately (foreground burst) instead of queuing the background drip"}}},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/weir/"},{"name":"weir_resolveLinks","kind":"act","impl":"imperative","description":"Kick the background resolver to process pending saved links now — resolve share.google/shortener URLs to their real destination and fetch thumbnail/title/excerpt metadata, gently over time (a couple every ~15s, so it never burst-hits the shortener). Returns { kicked, pending, running }. Imported links resolve on their own; use this to nudge it.","annotations":{"title":"Resolve saved links"},"inputSchema":{"type":"object","properties":{}},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/weir/"},{"name":"weir_resolverLog","kind":"answer","impl":"imperative","description":"Read the background link-resolver run log — { pending, running, resolved, parked, reasons, recent, startedAt, updatedAt }. `resolved` = links fully resolved+enriched; `parked` = gave up after retries; `reasons` tallies every failed try (http-429 = share.google throttling, no-redirect, network); `recent` = the last few parked links (host + reason). Use it to review an overnight run.","annotations":{"readOnlyHint":true,"idempotentHint":true,"title":"Resolver run log"},"inputSchema":{"type":"object","properties":{}},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/weir/"},{"name":"weir_reEnrich","kind":"act","impl":"imperative","description":"Rework saved links: clear their enriched flag so the background resolver re-fetches + re-applies metadata (title/thumbnail/excerpt). `weakTitles:true` re-does only links with a weak title (e.g. \"Source: Hackaday\" → real og:title); `all:true` re-does every saved link. Already-resolved urls are re-fetched directly (fast, no share.google throttle). Returns { queued, pending }.","annotations":{"title":"Re-enrich saved links"},"inputSchema":{"type":"object","properties":{"weakTitles":{"type":"boolean","description":"Only links whose title is weak"},"all":{"type":"boolean","description":"Every saved link"}}},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/weir/"},{"name":"weir_getItem","kind":"answer","impl":"imperative","description":"Get one weir item by id: glass facets + description (if cataloged), the card's `card` authorship block (cataloger=provider:model, reviewer human|agent, by=identity, needs_review), item provenance (`added_by`, plus `tag_src`/`tag_by` = who applied each tag), and its knowledge-graph edges — `links` (what its body links to via [[ref]], resolved to {ref,id,title}) and `backlinks` (items whose body links to it). Pass content:true to include the extracted article/note text (capped 8k).","annotations":{"readOnlyHint":true,"title":"Get a weir item"},"inputSchema":{"type":"object","properties":{"id":{"type":"string","description":"Item id (from weir_queryItems)"},"content":{"type":"boolean","description":"Include the extracted body text"}},"required":["id"]},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/weir/"},{"name":"weir_getItems","kind":"answer","impl":"imperative","description":"BATCH weir_getItem — fetch many items at once (reference work fans out: one search → pull a dozen cards). Pass `ids` (from weir_search / weir_queryItems / weir_queryCatalog). Returns { count, items:[…same shape as weir_getItem], missing?:[ids not found], omitted? }. With content:true each body is capped at 8k so the batch is capped tighter (~25 vs ~60). Saves a round-trip storm vs. calling weir_getItem N times.","annotations":{"readOnlyHint":true,"idempotentHint":true,"title":"Get weir items (batch)"},"inputSchema":{"type":"object","properties":{"ids":{"type":"array","items":{"type":"string"},"description":"Item ids to fetch"},"content":{"type":"boolean","description":"Include each item's extracted body text (capped 8k each)"}},"required":["ids"]},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/weir/"},{"name":"weir_search","kind":"act","impl":"imperative","description":"RANKED full-text search (the librarian BM25 index) — relevance-ordered, better than weir_queryItems's substring `q` for \"most relevant about X\" on a large corpus. CURATION-AWARE by default: results are reranked so the curated minority (books, notes, repo `doc` items, saved links) outranks the auto-ingested feed/video firehose, with a bonus when the query matches an item's facet terms — for a reference query the curated layer is the signal (pass rerank:false for raw BM25, or curated:true to hard-scope to the curated tier only). Each hit carries its `tier` (curated|neutral|firehose) when reranked. VOCAB-SYNONYM EXPANSION is also on by default: a query term that is a controlled-vocabulary prefLabel/altLabel also matches its synonyms (the seeded cross-lingual pairs like kriging↔krigagem bridge lexically, no embeddings) — the response carries `expanded:{term→[synonyms]}` when it fires; pass expand:false for the literal query. SEES THE ARCHIVE by default (the standing corpus, never-delete) — hits carry `archived:true` when archived; pass includeArchived:false to limit to active. Optional scope filters (feed/type/category/view/unread/saved) narrow it like queryItems. Returns { ranked, reranked?, count, items:[…,score,tier?,archived?] } (ranked:false = index not ready, fell back to substring). Use queryItems to LIST a whole feed/folder; use search to FIND by relevance; use weir_queryCatalog to search WITHIN a facet set.","annotations":{"readOnlyHint":true,"idempotentHint":true,"title":"Ranked search"},"inputSchema":{"type":"object","properties":{"q":{"type":"string","description":"The search query"},"feed":{"type":"string","description":"Scope to a source (id or display name)"},"category":{"type":"string","description":"Scope to a folder (\"\" = ungrouped)"},"type":{"type":"string","description":"Scope to an item type (e.g. \"doc\" for repo/project docs)"},"view":{"type":"string","enum":["inbox","saved","archived"],"description":"Scope to a view"},"unread":{"type":"boolean","description":"Only unread"},"saved":{"type":"boolean","description":"Only saved"},"curated":{"type":"boolean","description":"Hard-scope to the curated tier only (books/notes/repo docs/saved links) — excludes the feed firehose"},"rerank":{"type":"boolean","description":"Curation-aware reranking (default true); pass false for raw BM25 relevance"},"expand":{"type":"boolean","description":"Vocab-synonym query expansion (default true): a query term that is a controlled-vocabulary prefLabel/altLabel also matches its synonyms — bridges curated + cross-lingual pairs (kriging↔krigagem) lexically. Pass false for the literal query. When it fires, the response carries `expanded:{term→[synonyms]}`."},"weights":{"type":"object","description":"PER-CALL rerank weight override (ephemeral — for tuning; defaults are curated 2.5 / neutral 1.0 / firehose 0.6 / facet 0.2). Clamped 0–10. Sweep these against the eval queries, then report the winning set to bake as the default.","properties":{"curated":{"type":"number"},"neutral":{"type":"number"},"firehose":{"type":"number"},"facet":{"type":"number","description":"per-matched-facet-term bonus increment"}}},"explain":{"type":"boolean","description":"Surface the raw lexical `lex` score per hit + the `weights` used, so you can SEE why each item ranked where it did while tuning"},"includeArchived":{"type":"boolean","description":"Include archived items (default TRUE — the reference desk sees the whole archive; pass false to limit to active)"},"limit":{"type":"integer","description":"Max hits (default 20, cap 100)"}},"required":["q"]},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/weir/"},{"name":"weir_setState","kind":"act","impl":"imperative","description":"Set read / saved / archived (each a boolean; pass only the ones to change) — on ONE item (`id`), a LIST of specific items (`ids`: an array — applied in one atomic pass, the right way to flip many known items at once), or every item matching a query (pass q/feed/category/type/view/unread/saved — e.g. archive a dead feed, mark a folder read). A query bulk call REQUIRES a scoping filter (no accidental whole-corpus change). All reversible — archive never deletes (weir_unarchiveAll reverses a sweep). Returns the item (id mode) or { matched, patch, missing? } (ids/query mode).","annotations":{"title":"Set item state"},"inputSchema":{"type":"object","properties":{"id":{"type":"string","description":"A single item id; omit to bulk over `ids` or a query"},"ids":{"type":"array","items":{"type":"string"},"description":"An explicit list of item ids — flip them all in one atomic write (preferred over many single-id calls). Unknown ids come back in `missing`."},"read":{"type":"boolean","description":"Mark read/unread"},"saved":{"type":"boolean","description":"Save/unsave (star)"},"archived":{"type":"boolean","description":"Archive/unarchive (non-destructive)"},"q":{"type":"string","description":"Bulk scope: substring over title/excerpt/text"},"feed":{"type":"string","description":"Bulk scope: a source by id or name"},"category":{"type":"string","description":"Bulk scope: a folder (\"\" = ungrouped)"},"type":{"type":"string","description":"Bulk scope: item type"},"view":{"type":"string","enum":["inbox","saved","archived"],"description":"Bulk scope: a view (use view:\"saved\" to scope to saved items)"},"unread":{"type":"boolean","description":"Bulk scope: only unread"}}},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/weir/"},{"name":"weir_tag","kind":"act","impl":"imperative","description":"Add and/or remove tags — on ONE item (pass `id`) or on EVERY item matching a query (pass any of q/feed/category/type/view/unread/saved — the \"tag all these results\" verb). Tags are stamped source:'llm' (the UI shows them apart from your 'human' tags), are immediately searchable/queryable, and feed the glass `entity` facet on the next catalog. Returns the updated item (id mode) or { matched, changed } (query mode). Use weir_queryItems first to preview a bulk scope.","annotations":{"title":"Tag item(s)"},"inputSchema":{"type":"object","properties":{"id":{"type":"string","description":"Tag a single item by id (from weir_queryItems); omit to bulk-tag a query"},"q":{"type":"string","description":"Bulk scope: substring over title/excerpt/text"},"feed":{"type":"string","description":"Bulk scope: a source by id or display name"},"category":{"type":"string","description":"Bulk scope: a folder name (\"\" = ungrouped)"},"type":{"type":"string","description":"Bulk scope: item type (article|video|paper|note|…)"},"view":{"type":"string","enum":["inbox","saved","archived"],"description":"Bulk scope: a view"},"unread":{"type":"boolean","description":"Bulk scope: only unread"},"saved":{"type":"boolean","description":"Bulk scope: only saved"},"add":{"type":"array","items":{"type":"string"},"description":"Tags to add"},"remove":{"type":"array","items":{"type":"string"},"description":"Tags to remove"}}},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/weir/"},{"name":"weir_unarchiveAll","kind":"act","impl":"imperative","description":"Bring EVERY archived item back to active and clear its expiry (so retention won't re-shelve it) — the one-shot \"I keep everything\" restore that reverses an over-eager auto-archive sweep. Reversible; nothing is deleted. Returns { unarchived }.","annotations":{"title":"Unarchive everything"},"inputSchema":{"type":"object","properties":{}},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/weir/"},{"name":"weir_provenanceMigrate","kind":"act","impl":"imperative","description":"One-shot cleanup: normalize the agent's historical authorship stamps to the unified `source:agent` tier — tags previously marked 'llm' and relation edges marked 'claude' (both meant Claude) become 'agent'. Pass `backfillBooks` (an identity string, e.g. \"claude:librarian\") to ALSO repair agent-proposed book holdings whose `added_by` was stripped by the pre-fix makeItem bug — identified by their `web-proposed` tag — so they re-enter the review queue. Idempotent and safe to re-run; nothing is deleted. Returns { migrated: { tags, edges, books } }.","annotations":{"title":"Migrate / backfill provenance"},"inputSchema":{"type":"object","properties":{"backfillBooks":{"type":"string","description":"Identity to stamp on web-proposed books missing added_by (e.g. \"claude:librarian\"); omit to skip the book backfill"}}},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/weir/"},{"name":"weir_addBook","kind":"transact","impl":"imperative","description":"Add OR update owned/physical books in the holdings (the \"Books\" library) — e.g. cataloging a real shelf, or stamping series/seq onto books already there. Each book: title (required), author, isbn (→ Open Library fills cover/date/publisher), series + seq (the volume number — series+seq keep a numbered set TOGETHER and in volume order on the shelf; without seq a series scatters by year), date, tags (yours), ddc/lcc (display codes). To UPDATE an existing holding in place, pass its `id` (e.g. \"book:269049145\", from the shelf list / weir_queryItems) — read/saved/tags and the catalog card are preserved, and `structured` is MERGED, so you only pass the fields you are changing (existing isbn/ddc/lcc/series/seq are kept; a plain LibraryThing re-import will not wipe a stamped series). Without an id a new book is created (keyed by isbn||title). Batch with `books:[…]` (preferred) or pass ONE book inline. A NEW book YOU add is stamped source:agent and surfaces in weir_reviewQueue as a `book` proposal (tag it `to-buy` for a wishlist item you don't own yet); pass `rationale` so the user sees why. Returns { inserted, updated, books }.","annotations":{"title":"Add/update book(s) in holdings"},"inputSchema":{"type":"object","properties":{"books":{"type":"array","description":"The books to add/update (batch).","items":{"type":"object","properties":{"id":{"type":"string","description":"Existing holding id to UPDATE in place (e.g. \"book:269049145\"); omit to create a new book"},"title":{"type":"string","description":"Book title (required for a NEW book; omit when updating an existing holding by id, e.g. a shelved-status write)"},"author":{"type":"string","description":"Author (\"Surname, Given\" or \"Given Surname\")"},"isbn":{"type":"string","description":"ISBN-10/13 (enables Open Library enrichment)"},"series":{"type":"string","description":"Series title for a numbered set (e.g. \"YKK\")"},"seq":{"type":"number","description":"Volume number within the series"},"date":{"type":"string","description":"Publication date (year or ISO date)"},"tags":{"type":"array","items":{"type":"string"},"description":"Your tags (stamped as human; only applied when creating)"},"ddc":{"type":"string","description":"Dewey number (display metadata)"},"lcc":{"type":"string","description":"Library of Congress class (display metadata)"},"shelved":{"type":"boolean","description":"Physical-shelf status — round-trips with the shelf-list export’s \"shelved\" JSON; preloads the sheet checkboxes"},"rationale":{"type":"string","description":"Why you are proposing this book (e.g. a to-buy suggestion) — shown in the review queue; tag it \"to-buy\" for a wishlist item you don't own yet"}}}},"id":{"type":"string","description":"Shortcut: update a single existing holding by id"},"title":{"type":"string","description":"Shortcut: add/update a single book by its title (with the same sibling fields)"},"author":{"type":"string"},"isbn":{"type":"string"},"series":{"type":"string"},"seq":{"type":"number"},"date":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}},"ddc":{"type":"string"},"lcc":{"type":"string"}}},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/weir/"},{"name":"weir_addLink","kind":"act","impl":"imperative","description":"Save a link into weir's \"Saved Links\" — the same source the Telegram bot feeds, so you can save a URL directly (not only via Telegram). Pass `url` (one link) or `links:[{ url, title?, tags?, date? }]`. The background resolver follows wrapped/shortened URLs and fetches title/excerpt/thumbnail over time; it then catalogs like any item. Idempotent (keyed by URL hash) — re-saving updates in place and never resets read/saved/tags. A NEW save is stamped source:agent + your identity (its tags too); a re-import of an existing link is not relabeled. Returns { inserted, updated, links }.","annotations":{"title":"Save a link"},"inputSchema":{"type":"object","properties":{"url":{"type":"string","description":"A single link URL to save"},"title":{"type":"string","description":"Optional title (else derived from the host; the resolver may improve it)"},"tags":{"type":"array","items":{"type":"string"},"description":"Optional tags to apply (stamped as yours-via-agent)"},"date":{"type":"string","description":"Optional publish date (ISO or year)"},"links":{"type":"array","description":"Batch: [{ url, title?, tags?, date? }] (preferred over a single url for many)","items":{"type":"object","properties":{"url":{"type":"string"},"title":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}},"date":{"type":"string"}},"required":["url"]}}}},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/weir/"},{"name":"weir_ingestRepo","kind":"act","impl":"imperative","description":"Ingest a code repo’s own DOCS as a first-class source (SPEC-repos-as-source) — so the GCU constellation becomes a queryable subgraph in weir. weir never runs git; YOU (with the files + git) decide WHAT to ingest. `repo` = the repo name/path (e.g. \"auditable\"). `anchor` = the commit SHA these docs are from. TWO ways to supply content: (a) **`paths`** = [path strings, or { path, title?, url? }] — weir reads each NAMED file from the read-only repos folder you mounted in Settings (no full text through this call; the way to ingest at fidelity without a verbatim conduit); (b) **`docs`** = [{ path, title?, markdown, url?, date? }] — you pass the body inline (use for a gitignored/untracked file like CLAUDE.md, or when no folder is mounted). Mix both. README/SPEC/docs/CLAUDE.md — docs, NOT code. First call CREATES the source as a proposal (→ weir_reviewQueue, ratify like a feed); later calls REFRESH it (and can fix the name/category/rationale in place). REFRESH recipe: read the stored anchor from weir_listSources, run `git diff --name-only <anchor> HEAD -- <doc globs>` (+ `--diff-filter=D` for deletions) locally, then call this with only the changed `paths`/`docs` + the new HEAD as `anchor` + deleted paths in `removed`. Idempotent (stable ids; never resets read/saved/tags); `removed` archives (never deletes). Docs become `doc` items — searchable, quotable, relatable; relate your dive-map (a stacks note) to them with weir_relate. Returns { ok, source, inserted, updated, removed, anchor, bodyless?, bodylessReason? } — `bodyless` lists paths that landed with NO body (they can't be cataloged — no text to read), and `bodylessReason` maps each to WHY (not-found | read-error:<msg> | empty | no-path) so you can self-diagnose. Paths are repo-relative (weir prepends the repo slug → `<mount>/<repo>/<path>`); keep them repo-relative so re-ingest updates the same items in place.","annotations":{"title":"Ingest a repo as a source"},"inputSchema":{"type":"object","properties":{"repo":{"type":"string","description":"The repo name or path (e.g. \"auditable\" or \"../auditable\") — the source key + mounted-folder dir is its basename"},"name":{"type":"string","description":"Display name for the source (default: \"<repo> (repo)\"); updatable on refresh"},"anchor":{"type":"string","description":"The commit SHA these docs are from — the dive-ledger anchor; advanced on each refresh"},"paths":{"type":"array","description":"Doc paths to READ from the mounted repos folder: [\"README.md\", …] or [{ path, title?, url? }]. No verbatim conduit — weir reads the named files itself.","items":{}},"docs":{"type":"array","description":"Docs with inline bodies: [{ path, title?, markdown, url?, date? }] — for gitignored/untracked files or when no folder is mounted","items":{"type":"object","properties":{"path":{"type":"string"},"title":{"type":"string"},"markdown":{"type":"string"},"url":{"type":"string"},"date":{"type":"string"}},"required":["path"]}},"removed":{"type":"array","items":{"type":"string"},"description":"Doc paths deleted since the anchor — archived (never deleted)"},"category":{"type":"string","description":"Folder to group the source under (default \"repos\"); updatable on refresh"},"rationale":{"type":"string","description":"Why you are adding this source — shown in the review queue; updatable on refresh (capped to a short blurb)"}},"required":["repo"]},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/weir/"},{"name":"weir_ingestDocument","kind":"act","impl":"imperative","description":"Ingest PDF document(s) you have DROPPED under the read-only repos folder Arthur mounted in weir (Settings → Courier) — the way to add binary documents at fidelity without a verbatim conduit or a bridge fetch. YOU (free to download from anywhere) save the PDFs under the mounted GitHub folder, then pass their `paths` (relative to the mount root, e.g. [\"weir-desk/documents/ccg/declustering.pdf\"], optionally [{ path, title?, author?, url? }]). weir reads each file, extracts its text (+ a per-page offset map for page-anchored weir_quote), and stores it as a first-class, content-addressed `document` item: full-text searchable, quotable, catalogable, relatable. Idempotent (same bytes → same id, updates in place). Ingested LIVE + attributed (source:agent + your identity) — NOT gated in the review queue (a corpus the user asked for shouldn't flood it; see your footprint via weir_listMine). NOTE: extraction is currently naive reading order (v0) — fine for single-column; two-column docs (CCG guidebooks) shuffle until the reconstruction pass ships, after which the corpus is re-extracted in place (the binary is the source of truth). Returns { added, items:[{id,path,title,pages,chars}], failed?:{path:reason} }.","annotations":{"title":"Ingest dropped PDF documents"},"inputSchema":{"type":"object","properties":{"paths":{"type":"array","description":"PDF paths relative to the mounted folder root: [\"weir-desk/documents/x.pdf\", …] or [{ path, title?, author?, url? }]. weir reads the named files itself (no bytes through this call).","items":{}},"path":{"type":"string","description":"Convenience: a single path (use `paths` for batches)"}}},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/weir/"},{"name":"weir_catalogItem","kind":"act","impl":"imperative","description":"Catalog one item with the configured LLM right now (fills its glass facets + description) — best for items with real body text. Returns glass_id, facets, description. On a metadata-only item (too little text to read, e.g. a book with no body/abstract) it ABSTAINS rather than fabricate: returns ok:false + skipped + a note, leaving it needs_review — author it yourself via weir_reviewItem({ id, description, facets }). Needs the cataloger configured and reachable (Lemonade via the bridge).","annotations":{"title":"Catalog an item"},"inputSchema":{"type":"object","properties":{"id":{"type":"string","description":"Item id (from weir_queryItems)"}},"required":["id"]},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/weir/"},{"name":"weir_listModels","kind":"answer","impl":"imperative","description":"List the catalog provider's available models (so you can pick one). Optional `provider` overrides the configured one (lemonade|ollama|nanogpt|groq|custom). Returns { provider, count, models }.","annotations":{"readOnlyHint":true,"title":"List provider models"},"inputSchema":{"type":"object","properties":{"provider":{"type":"string","description":"Override the configured provider"}}},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/weir/"},{"name":"weir_setCatalog","kind":"act","impl":"imperative","description":"Set cataloger config — provider, model, baseUrl, paceMs (delay between calls; 0 = fastest, good for cloud), maxBodyChars (doc text sent; cost/context). NOT the API key (set that in the UI). Takes effect on the next cataloged item; a running batch picks it up. Returns the new config.","annotations":{"title":"Set cataloger config"},"inputSchema":{"type":"object","properties":{"provider":{"type":"string","description":"lemonade | ollama | nanogpt | groq | custom"},"model":{"type":"string","description":"Model id (see weir_listModels)"},"baseUrl":{"type":"string","description":"Override base URL (local/custom providers)"},"paceMs":{"type":"integer","description":"Delay between catalog calls in ms (0 = no pause)"},"maxBodyChars":{"type":"integer","description":"Max doc chars sent to the LLM (500–20000)"},"mailto":{"type":"string","description":"Contact email for the Crossref/OpenAlex polite pool (biblio enrich); sent only to those scholarly APIs"}}},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/weir/"},{"name":"weir_catalogControl","kind":"act","impl":"imperative","description":"Start / stop / clear / inspect the catalog batch. action:\"start\" catalogs un-cataloged non-archived items (paced, runs in the page) — optionally SCOPED to one feed/folder/type via feed|category|type (omit all → whole corpus); \"stop\" cancels; \"clear\" discards ALL cards + un-files every item (items/content/reading state untouched; reversible by re-cataloging) for a clean restart; \"status\" (default) reports running state, progress {total,done,failed}, and cataloged/total counts. start returns {running, todo, deferred}.","annotations":{"title":"Control cataloging","destructiveHint":true},"inputSchema":{"type":"object","properties":{"action":{"type":"string","enum":["start","stop","clear","status"],"description":"Default: status"},"feed":{"type":"string","description":"start scope: a source by id OR display name (e.g. \"Saved Links\")"},"category":{"type":"string","description":"start scope: a folder name (\"\" = ungrouped)"},"type":{"type":"string","description":"start scope: item type (article|video|paper|…)"},"recatalog":{"type":"boolean","description":"with action:start + a scope: discard that scope's cataloger-generated cards and re-catalog from scratch (re-do a batch cataloged under an old rule). SAFE-BY-DEFAULT: hand-authored / human-reviewed cards (a `reviewer` stamp) are PRESERVED — only regenerable cataloger cards are redone — so it can't silently destroy authored curation (the cataloger abstains on thin/metadata-only items like book holdings, which would have nothing to regenerate them). Returns `preserved` (count kept)."},"includeAuthored":{"type":"boolean","description":"with recatalog: ALSO redo hand-authored / human-reviewed cards in scope (default false → they're preserved). Use only when you want authored cards re-derived — e.g. body text has since arrived and the body-fed cards beat the hand facets."}}},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/weir/"},{"name":"weir_reviewQueue","kind":"act","impl":"imperative","description":"The UNIFIED review queue — everything awaiting human attention, tagged by `kind`: \"catalog\" = cataloger cards flagged low-confidence/unparseable (carry facets + confidence); \"feed\"/\"relation\"/\"book\" = things the agent ADDED (source:agent) not yet ratified — a feed, a relation edge, or a book holding (e.g. a to-buy suggestion). Each item carries the proposer `by` identity, a `rationale` (why), and `ratifyWith` — the tool to act on it: catalog → weir_reviewItem (confirm/correct), feed/relation/book → weir_ratify (bless or dismiss). Returns { counts:{catalog,feed,relation,book,total}, count, items }. Optional `kind` filters to one. This is the decides-vs-proposes gate: the agent proposes, you ratify here.","annotations":{"readOnlyHint":true,"title":"Review queue"},"inputSchema":{"type":"object","properties":{"kind":{"type":"string","enum":["catalog","feed","relation","book"],"description":"Filter to one kind (default: all)"},"limit":{"type":"integer","description":"Max items (default 30, cap 100)"}}},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/weir/"},{"name":"weir_listMine","kind":"answer","impl":"imperative","description":"Your footprint on the corpus — every contribution stamped by an agent identity, across kinds, with its ratification status. The provenance LENS for self-audit, the propose-vs-ratify ledger, and cross-channel coordination; pairs with weir_reviewQueue (which shows only the pending tray). Defaults to the CALLING channel's identity; pass identity:\"*\" for any agent (the cross-channel view), or a specific identity string. `kinds` filters to a subset of tag|note|edge|feed|book|catalog; `status` filters to one (pending|ratified|applied|authored|dismissed). Returns { identity, counts:{total, byKind, byStatus}, contributions:[{ kind, id, label, identity, status, … }] }. IMPORTANT: this is a CURRENT-STATE lens, not a history — contributions later undone or corrected by a human are NOT shown (weir's stamps are attribution; the undo paths erase them). It is the ground truth to reconcile your MEMORY against (memory says you added X but it is not here → your memory is stale). Read-only.","annotations":{"readOnlyHint":true,"idempotentHint":true,"title":"List my contributions"},"inputSchema":{"type":"object","properties":{"identity":{"type":"string","description":"Scope to an identity (default: the calling channel); \"*\" = any agent"},"kinds":{"type":"array","items":{"type":"string","enum":["tag","note","edge","feed","book","catalog"]},"description":"Limit to these contribution kinds"},"status":{"type":"string","description":"Filter to one status: pending | ratified | applied | authored | dismissed"},"limit":{"type":"integer","description":"Max contributions (default 100, cap 500)"}}},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/weir/"},{"name":"weir_ratify","kind":"act","impl":"imperative","description":"Ratify or dismiss an agent STRUCTURAL proposal from the review queue (decides-vs-proposes §2.1) — a feed the agent added, a relation edge it proposed, or a book holding it suggested. `action:\"ratify\"` blesses it (it stays, marked ratified, and leaves the queue); `action:\"dismiss\"` undoes it (feed → remove it + its just-polled items; relation → unrelate; book → archive it, non-destructive). For kind \"feed\" pass `feedId`; \"book\" pass `bookId`; \"relation\" pass `from` + `to` (+ optional `type`). Catalog cards are confirmed via weir_reviewItem instead, not here. Returns the action taken.","annotations":{"title":"Ratify / dismiss a proposal"},"inputSchema":{"type":"object","properties":{"kind":{"type":"string","enum":["feed","relation","book"],"description":"What kind of proposal"},"action":{"type":"string","enum":["ratify","dismiss"],"description":"ratify = keep + bless; dismiss = undo (default ratify)"},"feedId":{"type":"string","description":"kind \"feed\": the proposed feed id (from weir_reviewQueue)"},"bookId":{"type":"string","description":"kind \"book\": the proposed book holding id"},"from":{"type":"string","description":"kind \"relation\": the source item id or glass_id"},"to":{"type":"string","description":"kind \"relation\": the target item id or glass_id"},"type":{"type":"string","description":"kind \"relation\": optionally scope to one edge type"}},"required":["kind"]},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/weir/"},{"name":"weir_reviewItem","kind":"act","impl":"imperative","description":"Confirm, correct, or AUTHOR a catalog card. Facets-only on an existing card = confirm (clears needs_review). Pass a `description` (a precise one-sentence summary) to set the card's blurb by hand — and if the item has no card yet, this CREATES one (a deliberate \"I know this item\" add, e.g. a metadata-only book the cataloger can only hallucinate). Authored over MCP, the card is stamped source:agent + your identity. `facets` is an object of facet→string[] overwriting only the given facets (e.g. {\"entity\":[\"minecraft\"],\"scale\":[]}). Pairs with weir_addBook → weir_reviewItem to land a pointed add fully formed.","annotations":{"title":"Confirm/correct a card"},"inputSchema":{"type":"object","properties":{"id":{"type":"string","description":"Item id (from weir_reviewQueue / weir_queryItems)"},"description":{"type":"string","description":"A precise one-sentence card summary to set by hand (creates the card if none exists)"},"facets":{"type":"object","description":"Optional facet corrections, e.g. {\"scale\":[],\"entity\":[\"minecraft\"]} — overwrites only the given facets"}},"required":["id"]},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/weir/"},{"name":"weir_mergeFacetTerm","kind":"act","impl":"imperative","description":"Thesaurus normalization: rewrite a facet term across the WHOLE catalog (from → to within one facet, de-duped) so facet-browsing stops splitting one concept across spelling/synonym variants — e.g. spatial usa→united states, entity ai→artificial intelligence. Also RECORDS the merge in the controlled vocabulary: the from-term becomes a skos:altLabel (synonym) of the target, so the decision is remembered (inspect via weir_vocab), not just applied. An empty/omitted `to` DROPS the term (records nothing). Use weir_listFacets to spot variants first. Pass one {facet, from, to} or a {merges:[…]} batch (one atomic flush). Pure card edit — items/reading state untouched, reversible. Returns each merge's card-change count.","annotations":{"title":"Merge a facet term (thesaurus)"},"inputSchema":{"type":"object","properties":{"facet":{"type":"string","description":"Facet to edit (domain|entity|process|method|scale|spatial|stance|temporal|form|provenance)"},"from":{"type":"string","description":"Existing term to rewrite"},"to":{"type":"string","description":"Replacement term (empty/omitted = drop the from-term)"},"merges":{"type":"array","items":{"type":"object","properties":{"facet":{"type":"string"},"from":{"type":"string"},"to":{"type":"string"}}},"description":"Batch of {facet, from, to} merges applied atomically"}}},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/weir/"},{"name":"weir_vocab","kind":"act","impl":"imperative","description":"Inspect the controlled vocabulary / thesaurus (SKOS-shaped, GLASS §7). No args → per-facet concept-count overview. `facet` → that facet's concepts. `facet`+`term` → one concept (prefLabel + altLabels/synonyms + broader/narrower/related). `export:true` → SKOS JSON-LD (optionally for one `facet`). The vocabulary GROWS from curation: weir_mergeFacetTerm records the merged term as a synonym (altLabel); weir_relateTerm declares BT/NT/RT.","annotations":{"title":"Inspect the vocabulary (SKOS)"},"inputSchema":{"type":"object","properties":{"facet":{"type":"string","description":"Limit to one facet"},"term":{"type":"string","description":"With facet: return just this term's concept"},"export":{"type":"boolean","description":"Return SKOS JSON-LD instead"}}},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/weir/"},{"name":"weir_relateTerm","kind":"act","impl":"imperative","description":"Declare typed thesaurus relations (SKOS, GLASS §7) on a term: broader (BT), narrower (NT), related (RT), or alt (a synonym/altLabel that redirects to this term). Inverses are maintained automatically (declare broader → the target gains narrower). Each value is a string or list of terms. This is the ratified way to grow hierarchy — a similarity signal may propose, but a relation exists only once you declare it here (decides-vs-proposes). Use weir_vocab to inspect.","annotations":{"title":"Declare a thesaurus relation"},"inputSchema":{"type":"object","properties":{"facet":{"type":"string","description":"Facet (e.g. spatial, domain, entity)"},"term":{"type":"string","description":"The preferred term to relate"},"broader":{"description":"Broader term(s) — string or array (BT)"},"narrower":{"description":"Narrower term(s) — string or array (NT)"},"related":{"description":"Related term(s) — string or array (RT)"},"alt":{"description":"Synonym(s) that redirect to this term — string or array (altLabel/UF)"}},"required":["facet","term"]},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/weir/"},{"name":"weir_buildGazetteer","kind":"act","impl":"imperative","description":"Bulk-populate SPATIAL containment from the vendored gazetteer (GLASS §7 thesaurus): the CC0 CIA World Factbook backbone (country→continent, capital→country) + a curated supplement (US states, major cities). Declares the broader/narrower edges so the faceted browser + weir_queryCatalog roll up — selecting a parent (e.g. japan) catches its subtree (tokyo, osaka). Deterministic + idempotent (re-running links only what is new). Inspect with weir_vocab({facet:\"spatial\"}). Returns { linked, total }.","annotations":{"title":"Populate spatial containment (gazetteer)"},"inputSchema":{"type":"object","properties":{}},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/weir/"},{"name":"weir_works","kind":"act","impl":"imperative","description":"Inspect (and optionally rebuild) FRBR work-grouping (GLASS §4.1): items that are the same Work across manifestations — wire-syndication, re-uploads, cross-posts. `regroup:true` runs the deterministic grouping pass (identical canonical URL + SimHash near-duplicate; NOT an LLM call) then reports; default reports current grouping. Returns { stats:{items,works,manifestations,biggest}, works:[{work_id,size,members:[{title,feed}]}] } biggest-first — to eyeball precision. De-dup as GROUPING not discarding: nothing is deleted, work_id is a reversible overlay.","annotations":{"title":"Work-grouping (FRBR)"},"inputSchema":{"type":"object","properties":{"regroup":{"type":"boolean","description":"Recompute the grouping before reporting (run this first — the corpus has no work_ids until you do)"},"maxHamming":{"type":"integer","description":"SimHash near-dup threshold in bits (default 3; higher = looser grouping)"},"limit":{"type":"integer","description":"Max example clusters to return (default 20, cap 100)"}}},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/weir/"},{"name":"weir_relatedTo","kind":"answer","impl":"imperative","description":"The knowledge graph around an item OR stacks note (GLASS §10): its ratified `related` edges — outgoing + backlinks — PLUS on-demand SUGGESTIONS (set suggest:false to skip) from facet co-occurrence, each carrying the shared facet terms (the \"why\") and a score, PLUS the soft `wikilinks` layer (resolved [[name]] cross-references + who annotates this, navigable without ratification). Pass an item id (from weir_queryItems), a glass_id, or a stacks PATH (e.g. \"gcu/README.md\"). An uncataloged note has no ratified edges yet but still shows its wikilinks. Suggestions/wikilinks are NOT edges until ratified via weir_relate.","annotations":{"readOnlyHint":true,"title":"Related items + suggestions"},"inputSchema":{"type":"object","properties":{"id":{"type":"string","description":"Item id, glass_id, or stacks path"},"suggest":{"type":"boolean","description":"Include facet-overlap suggestions (default true)"},"limit":{"type":"integer","description":"Max suggestions (default 8, cap 25)"}},"required":["id"]},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/weir/"},{"name":"weir_relate","kind":"act","impl":"imperative","description":"Propose a typed `related` edge between two items or stacks notes — decides-vs-proposes (GLASS §2.1): a facet-overlap suggestion becomes a real edge ONLY when declared here. `from`/`to` are item ids, glass_ids, OR stacks paths (e.g. \"gcu/README.md\"); an uncataloged endpoint (e.g. a fresh note) is auto-carded at Stage-0 so it can join the graph — no manual catalog step needed. `type` ∈ related | same-topic | extends | contradicts | responds-to | same-work (default \"related\"). Pass `rationale` (why these relate) — it shows in weir_reviewQueue as a `relation` proposal until the user ratifies. Pass remove:true to delete the edge (optionally just one type). Stored on the from-item, stamped source:agent + your identity; reversible (weir never deletes the items).","annotations":{"title":"Relate two items"},"inputSchema":{"type":"object","properties":{"from":{"type":"string","description":"Source item id / glass_id / stacks path"},"to":{"type":"string","description":"Target item id / glass_id / stacks path"},"type":{"type":"string","description":"related | same-topic | extends | contradicts | responds-to | same-work"},"rationale":{"type":"string","description":"Why these relate — shown in the review queue at ratify time"},"remove":{"type":"boolean","description":"Remove the edge instead of creating it"}},"required":["from","to"]},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/weir/"},{"name":"weir_listFacets","kind":"answer","impl":"imperative","description":"Glass catalog facets across the non-archived corpus: each facet → { total, terms:[{term,count}], omitted }, top terms by count (entity alone can be thousands). Facets: domain, entity, process, method, scale, spatial, temporal, form, provenance. Cataloged items contribute LLM facets; the rest contribute deterministic Stage-0 facets. Use facet+limit to drill into one.","annotations":{"readOnlyHint":true,"idempotentHint":true,"title":"List weir facets"},"inputSchema":{"type":"object","properties":{"facet":{"type":"string","description":"Limit to one facet (domain|entity|process|method|scale|spatial|temporal|form|provenance)"},"limit":{"type":"integer","description":"Top terms per facet by count (default 25, max 200)"},"minCount":{"type":"integer","description":"Only terms appearing at least this many times"}}},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/weir/"},{"name":"weir_queryCatalog","kind":"answer","impl":"imperative","description":"Faceted INTERSECTION query over the catalog — the Ranganathan move: find interdisciplinary material by intersecting facets instead of one shelf or keyword. Pass `facets`, a map of facet → term(s): within a facet the terms UNION, across facets they INTERSECT — e.g. { domain:[\"geostatistics\"], entity:[\"kriging\",\"itabirite\"], process:[\"estimation\"] } = (geostatistics) AND (kriging OR itabirite) AND (estimation). Facets: domain, entity, process, method, scale, spatial, stance, form, provenance, temporal (weir_listFacets shows live terms). Terms resolve against the controlled vocabulary (a synonym → its preferred term); unknown or zero-hit terms come back in `vocabularyNotes` instead of silently missing. Optional `q` ANDs a ranked full-text constraint (composes with weir_search). Finds what excerpt/substring search cannot — e.g. entity:\"itabirite\" hits a cataloged item even when the word is not in its title. Read-only. Returns { count, total, items:[{ …, glass_id?, facets, matchedTerms }], vocabularyNotes? }.","annotations":{"readOnlyHint":true,"idempotentHint":true,"title":"Faceted catalog query"},"inputSchema":{"type":"object","properties":{"facets":{"type":"object","description":"Map of facet name → term or [terms]. e.g. { domain: [\"geostatistics\"], entity: [\"kriging\",\"itabirite\"] }. Union within a facet, intersect across facets."},"q":{"type":"string","description":"Optional free-text to AND a ranked-search constraint (weir_search) onto the intersection"},"includeArchived":{"type":"boolean","description":"Include archived items (default TRUE — sees the whole archive; pass false to limit to active)"},"limit":{"type":"integer","description":"Max items (default 20, cap 100)"}},"required":["facets"]},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/weir/"},{"name":"weir_quote","kind":"answer","impl":"imperative","description":"Verify a citation against a source — strict-grounding's self-check. Given an item `id` and a candidate `quote`, confirm the text actually appears in that source and get a stable locator + surrounding context (so you can cite it precisely, or REFUSE if it is not there). Whitespace is normalized before matching (HTML/wrapping ignored), with a case-insensitive fallback. Returns { found:true, locator:\"<glass_id|id>#start-end\", quote (the source's verbatim span), before, after, match } when present, or { found:false, note } when the quote is NOT in the source — in which case do not assert it as grounded. Read-only. Pair with weir_getItem (the body + citation chain) and weir_search/weir_queryCatalog (to find the id).","annotations":{"readOnlyHint":true,"idempotentHint":true,"title":"Verify a quote"},"inputSchema":{"type":"object","properties":{"id":{"type":"string","description":"The item id to verify against (from weir_search / weir_queryItems / weir_queryCatalog)"},"quote":{"type":"string","description":"The candidate quote/claim text to locate in the source"},"context":{"type":"integer","description":"Chars of surrounding context to return on each side (default 240, max 1000)"}},"required":["id","quote"]},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/weir/"},{"name":"weir_cite","kind":"answer","impl":"imperative","description":"Render a canonical, stable citation for an item — the companion to weir_quote (quote VERIFIES a span; cite RENDERS the reference). Pass `id` for one item, or `ids` for a batch. SINGLE mode: pass `quote` to fold verification in — you get `cited:true` + the verified verbatim span + a `glass_id#start-end` locator embedded in the reference, or `cited:false` (and NO reference) if the quote is not in the source, so a fabricated claim cannot get a citation. (Or pass a `locator` you already got from weir_quote to splice it without re-verifying.) Each citation comes in every form: `inline` \"(Author year)\", `reference` (a full reference-list line with a durable weir handle that survives a dead URL), `footnote` (markdown [^key]: …), `wikilink` ([[handle]] — a LIVE graph backlink once written into a stacks note), and `csl` (CSL-JSON for machine reuse), plus `missing` (essentials absent — cite what is known, never fabricate). BATCH mode (`ids`): returns per-item `entries`, a `keys` map (stable BibTeX-style cite-keys), and an assembled `bibliography` (markdown footnotes by default; `style:\"numbered\"|\"plain\"`). Read-only; pure render over metadata weir already holds.","annotations":{"readOnlyHint":true,"idempotentHint":true,"title":"Cite an item"},"inputSchema":{"type":"object","properties":{"id":{"type":"string","description":"One item id / glass_id to cite"},"ids":{"type":"array","items":{"type":"string"},"description":"Batch: item ids → entries + a deduped bibliography with cite-keys"},"quote":{"type":"string","description":"Single mode: a candidate quote to VERIFY + embed (refuses with cited:false if not in the source)"},"locator":{"type":"string","description":"Single mode: a weir_quote locator (glass_id#start-end) to splice in without re-verifying"},"style":{"type":"string","description":"Batch bibliography format: footnotes (default) | numbered | plain"}}},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/weir/"},{"name":"weir_stacksList","kind":"answer","impl":"imperative","description":"List the STACKS — weir’s notes/files vault (authored markdown notes + dropped files, living as real files under /stacks/). Optional `path` scopes to a folder (recursively). Returns { count, folders:[…], entries:[{ id, path, uid, type:note|file, title, tags, missing, glass_id }] }. The `id` is a normal item id (stacks:<uid>) — pass it to weir_getItem/weir_catalogItem/weir_setState. Stacks entries are also queryable via weir_queryItems({ feed: \"Stacks\" }).","annotations":{"readOnlyHint":true,"idempotentHint":true,"title":"List the stacks"},"inputSchema":{"type":"object","properties":{"path":{"type":"string","description":"Scope to a folder (recursive), e.g. \"specs/weir\""},"limit":{"type":"integer","description":"Max entries (default 200, cap 500)"}}},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/weir/"},{"name":"weir_stacksRead","kind":"answer","impl":"imperative","description":"Read one stacks entry by `path` (from weir_stacksList). For a note: returns its markdown body (capped 16k) + tags + metadata. For a file: metadata only (mime, no bytes). Returns { id, path, uid, type, title, tags, glass_id, markdown? , mime? }.","annotations":{"readOnlyHint":true,"title":"Read a stacks entry"},"inputSchema":{"type":"object","properties":{"path":{"type":"string","description":"Entry path, e.g. \"inbox/huffman.md\""}},"required":["path"]},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/weir/"},{"name":"weir_stacksWrite","kind":"act","impl":"imperative","description":"Create OR update a stacks NOTE — \"draft a note straight into the stacks\". If `path` names an existing note, its body is updated (uid/created preserved); otherwise a new note is created. Address it with `path` (e.g. \"specs/weir/idea.md\") OR `folder`+`name`. If you give NO folder (bare name / bare path), the note is filed to inbox/ as triage and the result says so (routedToInbox:true) — pass `folder` (or a path that includes a folder) to file it directly. `markdown` is the body (required); `title` and `tags` optional (tags stamped as yours-via-Claude). Link other notes/holdings with [[uid]] or [[name]] (resolved by weir_relatedTo). For a one-line change, prefer weir_stacksEdit (find/replace/append) over rewriting the whole note here. Returns { ok, id, path, uid, title, tags, folder, routedToInbox? }. Files are dropped via Telegram or the app, not here.","annotations":{"title":"Write a stacks note"},"inputSchema":{"type":"object","properties":{"path":{"type":"string","description":"Target path, e.g. \"specs/weir/idea.md\" (folder + filename)"},"folder":{"type":"string","description":"Folder (alternative to a full path); defaults to \"inbox\""},"name":{"type":"string","description":"Filename (alternative to a full path)"},"title":{"type":"string","description":"Note title (defaults to the first heading / filename)"},"markdown":{"type":"string","description":"The note body (markdown)"},"tags":{"type":"array","items":{"type":"string"},"description":"Tags to set"}},"required":["markdown"]},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/weir/"},{"name":"weir_stacksEdit","kind":"act","impl":"imperative","description":"Partial edit of a stacks NOTE — change one part without rewriting the whole note (the way the agent Edit tool works). Address it by `path`. Either replace text: `find` (the exact string to locate) + `replace` (its replacement; omit/empty to delete the text) — `find` must be UNIQUE unless you pass replaceAll:true. OR append: `append` (a block added to the end of the note). Exact-string match; errors are explicit (find-not-found / find-not-unique), never silent. Identity (uid/created) and source-stamp are preserved. Notes only (not files). Returns { ok, id, path, uid, title, tags, markdown }.","annotations":{"title":"Edit a stacks note"},"inputSchema":{"type":"object","properties":{"path":{"type":"string","description":"Entry path, e.g. \"gcu/README.md\""},"find":{"type":"string","description":"Exact text to locate (must be unique unless replaceAll)"},"replace":{"type":"string","description":"Replacement for `find` (omit or empty string to delete it)"},"replaceAll":{"type":"boolean","description":"Replace every occurrence of `find` (default false → must be unique)"},"append":{"type":"string","description":"A block to append to the end of the note (alternative to find/replace)"}},"required":["path"]},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/weir/"},{"name":"weir_stacksMove","kind":"act","impl":"imperative","description":"Move/refile a stacks entry to another folder. Identity (uid) is preserved, so its tags, read-state, catalog card and inbound [[uid]] links ride along. Returns { ok, movedFrom, id, path, uid, title }.","annotations":{"title":"Move a stacks entry"},"inputSchema":{"type":"object","properties":{"path":{"type":"string","description":"Current entry path"},"toFolder":{"type":"string","description":"Destination folder, e.g. \"papers/kriging\""}},"required":["path","toFolder"]},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/weir/"},{"name":"weir_stacksTag","kind":"act","impl":"imperative","description":"Add/remove tags on a stacks entry by `path`. Tags are stamped source:'llm' and mirrored into the note’s frontmatter / the file’s .meta.json sidecar (portable, Obsidian-readable). Returns the updated entry.","annotations":{"title":"Tag a stacks entry"},"inputSchema":{"type":"object","properties":{"path":{"type":"string","description":"Entry path"},"add":{"type":"array","items":{"type":"string"},"description":"Tags to add"},"remove":{"type":"array","items":{"type":"string"},"description":"Tags to remove"}},"required":["path"]},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/weir/"},{"name":"weir_stacksTrash","kind":"act","impl":"imperative","description":"Delete a stacks entry by `path` — but weir never really deletes: the file (+ sidecar) is moved into /stacks/.trash/ (a hidden folder the scanner ignores), so it disappears from weir while the bytes survive on disk, recoverable. Drops the index entry. Returns { ok, trashed, dest }.","annotations":{"title":"Delete a stacks entry","destructiveHint":true},"inputSchema":{"type":"object","properties":{"path":{"type":"string","description":"Entry path to delete"}},"required":["path"]},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/weir/"},{"name":"weir_mcpDiag","kind":"answer","impl":"imperative","description":"Diagnostics for weir's numen fs channels (transport debugging — for tracking down why a channel won't connect). No input. Per known channel returns: connection state + clientId, whether a token is set, the folder-handle NAME it points at, what weir reads from that folder's bridge.live (present? session, ageMs, fresh under 90s?), and the folder's top-level entries. Reading: bridgeLive absent or stale while the bridge insists it is announcing ⇒ FOLDER MISMATCH (weir and the bridge are on different directories); bridgeLive fresh but clientId null ⇒ STALE HANDSHAKE (re-dial / token). If a channel shows hasHandle:false, open weir Settings → Connections once to cache the handle, then re-run.","annotations":{"title":"Diagnose numen fs channels","readOnlyHint":true},"inputSchema":{"type":"object","properties":{}},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute","page":"/weir/"}],"category":"Media & Personal","favicon":"/favicons/gentropic.org.png","apiSurface":"spec","toolCount":49}}