19 Commits
Author SHA1 Message Date
only-cli 0ea9f4cbda reddit: read the www.reddit.com Atom feeds, old.reddit.com is behind a login now
Since 30 June 2026 every logged-out request to old.reddit.com, HTML, .json,
and .rss alike, lands on /login/?reason=lor2, so every oc reddit verb rendered
an empty login page. The www.reddit.com .json views closed a month earlier and
answer 403 to anything without an OAuth token, whatever the User-Agent or TLS
fingerprint, so they are not a way back in either.

The Atom feeds on www.reddit.com still answer without a login, and the feed
converter already turns one into a page: a subreddit renders in about 480
tokens from a 21,500 token payload, a thread with 22 comments in about 1,000.
Every verb now points at a feed, and new <name> and top <name> join them,
since the feeds offer those sorts for free. The feeds carry no scores or
comment counts, and anonymous reddit.com allows roughly ten requests a minute
per address, which the changelog says plainly.

The fixture is a three-entry post feed in Reddit's shape, one entry for the
post and one per comment, so the thread rendering is covered offline.

Closes #52
2026-09-04 08:42:41 -04:00
only-cli 378d566765 feat: add Go, Rust, Java, PHP, TypeScript, and C/C++ docs shortcuts
The rest of the languages from #25, each probed for the cheapest shape
that works:

- go: pkg.go.dev renders search on the server, so 'search' is a plain
  URL and 'pkg <path>' opens a package page.
- php: manual-lookup.php sends an exact function name straight to its
  manual page, so 'fn array_map' lands on the function doc; fuzzy
  search goes through DuckDuckGo since the suggestions render behind
  the manual's own TOC.
- cpp: cppreference's new static site has clean /cpp/ and /c/ paths;
  its native search page is bare links oc refuses as unreadable, so
  'search' goes through DuckDuckGo.
- rust: std and doc page opens; rustdoc's search-index.js format is
  version-fragile, so 'search' goes through DuckDuckGo.
- java: Javadoc opens pinned to javase/26, the current GA (27
  redirects there); the typeSearchIndex file lacks the module names a
  URL needs, so 'search' goes through DuckDuckGo.
- ts: handbook page opens; the site searches via Algolia client-side,
  so DuckDuckGo again.
- learn gets a 'dotnet <api>' verb, since .NET API docs already live
  there.

README table and prose and the skill notes cover the new sites.
2026-08-24 12:27:17 -04:00
only-cli fa41abbb2e feat: rank Ruby docs search locally via the RDoc index
RDoc publishes a site's whole search index as one static JS file, the
same way Sphinx and the Node.js docs do, so 'oc ruby search' fetches
docs.ruby-lang.org's index (en/3.4, where the file exists), caches it
under the shared day cache, and ranks it with the same scorer the Node
backend uses. Class methods print as Class.name(params), instance
methods as Class#name(params), and every entry links straight to its
method anchor. 'oc ruby class Array' opens a class page directly.

cli.js now dispatches the local search backends through one map, and
the site-resolution guard keys on the query field, which only a search
shape ever carries.
2026-08-24 12:27:05 -04:00
only-cli 6a399cf448 feat: rank Node.js docs search locally from the docs' own reference
nodejs.org has no search results page: the site's search box is a
JavaScript modal asking a third-party service, so the search verb went
through DuckDuckGo. But the API docs publish their entire reference as
one static JSON file, all.json, much the way a Sphinx site publishes
its search index, so a new nodedoc backend ranks that file locally:
every module, class, method, property, and event heading becomes a
result linking to its own anchor, and oc node search prints them as a
normal numbered page.

The file is ~8MB (~1MB over the wire) and static, so the day cache the
Sphinx backend used moves to a shared cache module both backends call:
one directory per backend, one file per host, parsed before written so
a block page never poisons it.

A typical result list costs under 100 tokens and answers from disk in
under 100ms once cached.
2026-08-24 10:49:46 -04:00
only-cli 5706aab2a1 let MDN search ask MDN
MDN renders its search results client-side, so 'oc mdn search' has gone
through DuckDuckGo with a site: filter. But the page it renders gets its
results from a public JSON endpoint, /api/v1/search, which returns the
site's own ranking with a summary per result in one ~11KB response. A new
'api' shortcut shape names such an endpoint and which response fields
hold the list, title, URL, and snippet; the response becomes the same
synthetic results page a Sphinx search produces and rides the normal
render path, so 'do <n>' follows a result. First view is ~520 tokens and
works for any site whose search answers as JSON.
2026-08-24 10:29:28 -04:00
only-cli e834309363 prototype a Sphinx search backend for the Python docs
docs.python.org has no search server: its search page downloads the
whole Sphinx index (searchindex.js, ~900KB gzipped) and ranks matches
in the browser. oc now does the same ranking itself, so 'oc py search'
answers from the site's own index instead of DuckDuckGo.

The index is cached on disk for a day and never printed; what renders
is a numbered result list that rides the normal open path, so 'do <n>'
follows a result. A query that names a symbol exactly (json.dumps)
links straight to its anchor. A site definition opts in with a
'sphinx' field naming the docs root, so any Sphinx site can use the
backend later.
2026-08-24 09:59:34 -04:00
only-cli f3f5a93466 add Python, MDN, and Node.js documentation shortcuts
First three languages from #25. Each is one definition in clis/:

- oc py library json, oc py doc reference/datamodel (the .html is
  appended, matching how the aws shortcuts spell pages)
- oc mdn js Array/map, oc mdn css grid-template-columns, and a generic
  oc mdn doc <path> for everything else under /en-US/docs
- oc node api fs

Python renders search client-side (Sphinx), MDN serves its search page
as a client-rendered shell with only the nav in the HTML, and nodejs.org
has no search, so all three get the DuckDuckGo site: filter fallback the
aws and gcp shortcuts already use. Short names py, mdn, and node join
the alias table; python, mozilla, nodejs, and the full domains resolve
as before through the generic rules.
2026-08-24 09:26:41 -04:00
only-cli d5e12ab710 feat: add wikipedia site shortcuts
Article, search, and non English wiki lookups via clis/wikipedia.org.json,
reachable as oc wiki, oc wikipedia, or oc wikipedia.org.

The article and lang commands use ?action=render, which returns the article
HTML without the interlanguage sidebar and Tools menu that otherwise eat
about half of a 500 token budget before any prose. Its links stay root
relative, so oc do <n> still follows them; the Parsoid endpoints render just
as clean but emit ./Title hrefs that resolve against the API path and break
link following.

Search uses the normal results page. The api.php JSON search endpoint is
cheaper on paper but distills to nothing today, since its results sit in a
nested query.search array.

Closes #21
2026-08-23 22:58:56 -04:00
only-cli 7f09363963 feat: dispatch the site shortcuts the README already documented
The shortcuts table promised `sub <name>`, `item <id>`, `repo <owner> <name>`
and the rest for 13 sites, and clis/*.json shipped in the published `files`
list, but nothing ever read those files: `oc reddit sub ClaudeAI` answered
`unknown command 'reddit'`. The help text labels `fill` and `submit` as
planned, so an agent reading the table had every reason to treat the
shortcuts as shipped, construct one, and fall back to raw fetching when it
failed, which is the outcome this tool exists to prevent.

src/sites.js resolves `oc <site> <verb> [args]` against clis/*.json at
runtime and hands the URL to the existing open path, so a shortcut cannot
change what a page costs or how it renders, and a new definition needs no
wiring. A site answers to its domain, its bare name, and a short alias
(hn, gh, so, ddg, yt, aws, gcp, learn, finance, twitter), because an agent
that has to guess the spelling is back to guessing URLs.

Two details are worth naming. The last declared argument takes every word
after it, so `oc aws search s3 lifecycle rules` needs no quoting. And a value
filling a path segment keeps its slashes while a value in a query string does
not, so `oc learn doc azure/aks/what-is-aks` reaches that page instead of
asking the site for one impossible segment.

`oc sites` lists every site with its verbs, one line each, so discovery costs
less than a wrong guess. reddit's {sub} and github's {repo} are renamed to
{name} so the usage lines print what the README documents.

Fixes #16
2026-08-23 20:54:55 -04:00
only-cli fa79b0db53 Merge remote-tracking branch 'origin/main' into render-json-apis
# Conflicts:
#	README.md
2026-08-22 14:26:56 -04:00
only-cli 0f362708f8 feat: render JSON API responses as pages
Closes #3.

An API answer is a page: jsonToHTML turns a JSON body into one article per
item, and everything downstream (numbering, budget, do, read, next, raw)
treats it as an ordinary document. No per-site logic and no new dependency.

The compact view is the hard part, since a search response carries far more
fields than fit in 500 tokens. So the renderer scores each field by how much
it varies across items against how wide it prints, penalises fields flattened
out of a sub-object (owner.reputation describes the asker, not the answer),
and spends about 60 characters per item on the winners. What every item
shares is stated once at the bottom instead of repeated, empty fields are
named rather than printed, and what was cut says so and points at oc raw,
which keeps every field.

On the Stack Exchange search endpoint that is 30 results in ~960 tokens
against ~5,500 for the raw body, with each title a link and question_id
visible.

Also here:

- clis/stackoverflow.com.json gains search <query>, which is what #3 was
  blocking. Results carry question_id, and the question feed reads one in
  full, so search now completes without touching the challenged HTML page.
- fetch: the native-fetch path rejected anything that was not HTML or XML.
  It now accepts JSON, which also makes the two transports render one URL
  the same way, since the impers path never checked the type at all.
- raw threads the URL through so its view of an API response can be titled
  and, unlike the compact view, keeps every field.

Deliberately not done, from the notes on the issue: pagination in the
actions line, and API metadata on stderr. There is no stderr channel at the
distill seam, so response-level fields (has_more, quota_remaining) render as
one footer line instead. A columns hint in the clis specs and a --json
passthrough both looked like the wrong trade: the first needs per-site
tuning for something the scoring already handles, the second would break the
machine-stable Page contract.
2026-08-22 14:20:01 -04:00
only-cli e8f2b6172d feat: add docs search to the cloud provider CLIs
All three providers render their own docs search client-side, so
distilling the search page yields only nav chrome. Microsoft Learn is
the exception underneath: its public RSS search endpoint serves real
results as a feed, which the engine already renders (same route as
Stack Overflow). AWS and Google Cloud expose search only as JSON (#3),
so until the engine renders JSON their search goes through DuckDuckGo
HTML with a baked-in site: filter, the same endpoint the duckduckgo.com
CLI already relies on. Bing was tried first for that job and rejected:
it silently drops the site: operator on some queries.

Verified live: the Learn RSS endpoint returns titled results for
"app service deploy"; the DuckDuckGo route returns real
docs.aws.amazon.com pages for "lambda timeout" (it can answer with a
rate-limit challenge under rapid-fire automated use, noted in README).
2026-08-22 12:29:56 -04:00
only-cli 5e7f54c4bb feat: add site CLIs for AWS, GCP, and Azure documentation
Cloud provider docs are the pages agents hit most while writing
infrastructure code, and they carry the heaviest chrome: nav trees,
version pickers, feedback widgets. One config per provider gives them
tuned shortcuts instead of raw URLs.

The Google config points at docs.cloud.google.com because
cloud.google.com 301s every docs path there; skipping the redirect
saves a round trip, same trick as reddit.com going via old.reddit.com.
All six URL templates were verified against the live sites with
fetch + distill (56 to 4126 blocks, real titles, HTTP 200).

Closes #11
2026-08-22 12:16:14 -04:00
only-cli 01b607ac81 support youtube watch pages and transcripts
Watch pages need client JS to become interactive, but the title,
description, view count, and caption tracks already ship inline in
the initial HTML as ytInitialPlayerResponse, so this reads that
directly instead of waiting on the v0.3 headless fallback. Each
caption track becomes a numbered link, and oc do on it fetches the
timedtext transcript, collapsed into one block so it pages through
oc next/oc read like any other long document instead of costing one
block per caption line.

Adds youtubeToHTML and transcriptToHTML alongside feedToHTML in the
distiller, a youtube.com.json shortcut, and offline tests against a
fixture watch page.

Fixes #6
2026-08-19 13:27:52 -04:00
only-cli 9873b7f492 add the Yahoo Finance cli and measure each agent's default browsing against oc
Yahoo Finance quote, news, history, lookup, markets, gainers, losers, and
trending pages all arrive server rendered (the AAPL quote page carries the
closing price in about 456 tokens of view against 325,000 of raw HTML), so
a definition ships for all eight. The README grows the comparison people
actually ask for: the same stock price task run through Claude Code and
Codex as they ship versus with oc, one live session each, wrong answer and
all. Codex read raw HTML and reported a price that is not on the page;
WebFetch's digest was cheaper than oc this run and the README says so
plainly rather than hiding it. Also a banner, badges, and an llms.txt
pointer, so both people and models skimming the repo can tell what it is.
2026-08-19 07:59:38 -04:00
only-cli 42906ced60 spend turns, not tokens: do <n> reads text, near-budget pages print whole, timelines render readably
Three changes with one motive: a second command costs an agent more than
the lines it saves. 'oc do' on a heading or text block now prints the read
instead of refusing, since refusing spends a whole turn naming the command
that should have run. A page that would finish within about four times the
budget is printed whole rather than cut, because the cut moves tokens into
a second command instead of saving them. And social timelines stopped
rendering as one fused paragraph: linkedom splits text nodes around
apostrophes, so fragments are merged back by parent node and edge
whitespace, block elements now end lines, and repeated button labels are
trimmed sooner than links because a button label is never the content.
With that, x.com profiles and posts read without a login, so a six line
cli definition ships for the two x.com pages that work.
2026-08-19 07:59:29 -04:00
only-cli bb214dcf59 read Atom and RSS feeds: Stack Overflow answers through the open /feeds door
Sites behind hard bot challenges often leave their feeds open. distill()
now detects feed XML, converts entries (title, byline, link, escaped HTML
body) into a plain HTML document, and everything downstream is unchanged.
fetch accepts xml content types. Ships a stackoverflow.com spec mapping
question/tag/user onto the feed URLs.

Live: oc open on a Stack Overflow question feed renders ~493 tokens vs
~29k for the page HTML, HTTP 200 where the HTML page is challenged.
2026-08-18 10:36:36 -04:00
only-cli b536cc2ccf publish fix, GitHub and LinkedIn site definitions
setup-node's registry-url writes an npmrc auth-token line with a
placeholder token, which npm used instead of OIDC and got a 404 from
the registry. Dropping registry-url lets trusted publishing work.

New clis: github.com (repo, user, search, trending, issues) and
linkedin.com (profile, company, jobs; public guest views work through
the Chrome fingerprint). Both verified live before shipping.
2026-08-18 09:48:04 -04:00
only-cli dcc0531ef3 only-cli v0.1: turn websites into a compact CLI for AI agents
Generic distillation engine (no per-site adapters): fetch via impers
impersonating Chrome with a firefox-fingerprint retry, distill to an
interaction tree, render under a hard token budget with numbered action
handles. Raw mode emits markdown via turndown or cleaned HTML. Per-site
CLI definitions for HN, Reddit, Bing, DuckDuckGo. Offline test suite,
agent skill, OIDC publish workflow.
2026-08-18 09:01:41 -04:00