Repository navigation
v1.2.0
Added
-
Page.selectors()— extraction without having read the markup. Writing an
extract()call means knowing the classes on the page, and an agent connected
over MCP could not:fetch_markdownstrips exactly the classes and attributes
a selector is built from, and no tool returns HTML on purpose, since raw markup
costs more tokens than the data it is meant to locate. So this reports the
structure instead of the source — the blocks that repeat, the inner selectors
that resolve across most instances, and a sample record for each, which is a
runnableitem+fieldsrather than something to interpret.Two heuristics do the work. A repeated wrapper and the card inside it appear
equally often, so the one that offers nothing its own child does not is
dropped:article.product_podsurvives,li.col-xs-6.col-sm-4.col-md-3does
not. And a column whose element contains another column's is thrown away,
because its text is every child's text run together —div.product_price
otherwise comes back as "£51.77In stockAdd to basket".
Fixed
-
markdown()was throwing the page away on listings. Main-content
extraction is built to discard repetition — which on a listing page is the
content. Onbooks.toscrape.com, the first URL the README tells you to curl,
it returned the price column and dropped all twenty book titles: 358
characters out of 1,851 of visible text. It now checks what came back against
the page's own text and falls back to full-page conversion when the
extractor kept under a quarter of it. Measured, that floor sits far below
every good case (1.14x of visible text on quotes.toscrape.com, 1.4-1.6x on
Wikipedia) and just above the one that broke (0.19x), so an article buried in
navigation still gets the boilerplate stripped.With the content back, the saving is 4.8x on books.toscrape.com, 5.8x on
quotes and 6.6x on a Wikipedia article — the 5-10x the README claims. The
broken version scored 144x, which was not a saving.
Fixed
-
"Setup for agents" set nothing up. The button copies
Read and follow <url>, which makes an agent read the skill for exactly one turn — next
message it has none of it, and/bytecrawlnever appears. The skill showed
the install command but never told the agent to run it, so the button promised
setup and delivered a single answer. Installing itself is now the first
instruction in the file — and it branches on what the agent is, because a
skills directory is Claude Code's convention and telling Codex or Cursor to
write~/.claude/skills/produces a file nobody reads. Claude Code installs
the file and gets/bytecrawl; an MCP client with no skills directory adds
the hosted server, which is the one path that persists everywhere and hands
over the tools rather than instructions about them; an agent with an
AGENTS.mdadds one line pointing back; a sandbox with nothing to write to
skips it silently. If a task came with the URL, the task comes first. -
/docsadvertised v1.0.0 through three releases. The version was written
into the template, so nothing tied it to the package and it simply stopped
being true. It renders frombytecrawl.__version__now, and a test fails if
a literal version reappears there.
Changed
fetch_markdownreturnstokens_htmlnext totokens_estimate, the pair the
playground already showed. On its own the token count has nothing to compare
against; beside the raw HTML it is the reason to have called the tool, and an
agent can state the saving instead of asserting it.extractcalled with only a url now discovers instead of failing. It used
to raise "extract needs 'item' and 'fields', or 'select'" — refusing the one
question an agent can ask about a page it has not seen. The same call works on
every surface: the MCP tool,/api?method=extractwithoutselect, and the
playground's Extract chip with the field left empty, which lists the blocks it
found and turns each into a button that fills the selector and runs it.- The demo API no longer sorts JSON keys. The discovered fields come back ranked
with the most useful selector first and the playground offers that one as the
button; sorted alphabetically,books.toscrape.comled witha::attr(href).