refactor!: replace snapshot with reader fallback, collapse commands

Remove the snapshot command and enhance reader to try Firefox Reader
View first, falling back to raw Turndown conversion of document.body
when Reader View fails or is skipped via --no-reader.

- reader always returns markdown by default (--format=json for structured)
- JSON output includes method: 'reader' | 'raw' to signal extraction path
- --no-reader skips Reader View (stays on loaded page, preserving JS mutations)
- Add @ts-nocheck to test/smoke.js and exclude test/ from tsconfig
- Update all tests from snapshot to reader with --no-reader for data URIs
- Update AGENTS.md and help text

BREAKING CHANGE: snapshot subcommand removed; use reader instead.
This commit is contained in:
2026-05-02 20:05:27 -04:00
parent eb1de23f4e
commit 6adb5111de
4 changed files with 136 additions and 170 deletions

View File

@@ -1,4 +1,5 @@
#!/usr/bin/env node
// @ts-nocheck
import {
mkdtempSync,
@@ -73,7 +74,7 @@ test("no args prints help", ["help", "cli"], () => {
assert.equal(result.status, 0, result.stderr || result.stdout);
assert.match(result.stdout, /Usage: glimpse <command> <url> \[options\]/);
assert.match(result.stdout, /snapshot <url>/);
assert.match(result.stdout, /reader <url>/);
assert.equal(result.stderr, "");
});
@@ -86,48 +87,50 @@ test("help flag prints help", ["help", "cli"], () => {
assert.equal(result.stderr, "");
});
test("snapshot returns page metadata and content", ["snapshot"], () => {
const output = expectSuccess([
"snapshot",
test("reader extracts page content as markdown", ["reader"], () => {
const result = runCli([
"reader",
dataHtml(
'<title>Hello</title><h1>Main</h1><a href="/x">X</a><button>Go</button>',
'<title>Hello</title><h1>Main</h1><p>Some text</p><a href="https://example.com">Link</a>',
),
"--no-reader",
]);
assert.equal(output.ok, true);
assert.equal(output.title, "Hello");
assert.equal(typeof output.elapsedMs, "number");
assert.deepEqual(output.result.headings, [{ level: 1, text: "Main" }]);
assert.deepEqual(output.result.links, [{ href: "/x", text: "X" }]);
assert.equal(output.result.buttons[0].text, "Go");
assert.match(output.result.text, /Main/);
assert.equal(result.status, 0, result.stderr || result.stdout);
const output = result.stdout.trim();
assert.match(output, /# Main/);
assert.match(output, /Some text/);
assert.match(output, /\[Link\]\(https:\/\/example\.com\/??\)/);
});
test("snapshot extracts aria headings", ["snapshot"], () => {
test("reader returns json format with method field", ["reader"], () => {
const output = expectSuccess([
"snapshot",
dataHtml(
'<title>Hello</title><div role="heading" aria-level="2">ARIA</div>',
),
"reader",
dataHtml("<title>Hello</title><h1>Main</h1><p>World</p>"),
"--no-reader",
"--format=json",
]);
assert.equal(output.ok, true);
assert.deepEqual(output.result.headings, [{ level: 2, text: "ARIA" }]);
assert.equal(output.title, "Hello");
assert.equal(output.method, "raw");
assert.equal(typeof output.markdown, "string");
assert.match(output.markdown, /# Main/);
assert.match(output.text, /Main/);
});
test(
"snapshot runs top-level javascript before extraction",
["snapshot", "js"],
"reader runs top-level javascript before extraction",
["reader", "js"],
() => {
const output = expectSuccess([
"snapshot",
const result = runCli([
"reader",
dataHtml("<title>Hello</title><h1>Old</h1>"),
"--no-reader",
"--js=document.querySelector('h1').textContent = 'New'",
]);
assert.equal(output.ok, true);
assert.deepEqual(output.result.headings, [{ level: 1, text: "New" }]);
assert.equal(output.result.text, "New");
assert.equal(result.status, 0, result.stderr || result.stdout);
assert.match(result.stdout, /# New/);
},
);
@@ -182,8 +185,9 @@ test(
writeFileSync(configPath, "not json");
const output = expectFailure([
"snapshot",
"reader",
dataHtml("<title>Hello</title>"),
"--no-reader",
`--config=${configPath}`,
]);
@@ -201,8 +205,9 @@ test(
writeFileSync(configPath, JSON.stringify({ search: { provider: 42 } }));
const output = expectFailure([
"snapshot",
"reader",
dataHtml("<title>Hello</title>"),
"--no-reader",
`--config=${configPath}`,
]);
@@ -218,17 +223,17 @@ test("empty home config is accepted", ["config"], () => {
mkdirSync(configDir, { recursive: true });
writeFileSync(join(configDir, "config.json"), "{}");
const output = expectSuccess(
["snapshot", dataHtml("<title>Hello</title><h1>Main</h1>")],
const result = runCli(
["reader", dataHtml("<title>Hello</title><h1>Main</h1>"), "--no-reader"],
{ env: { ...process.env, XDG_CONFIG_HOME: configHome } },
);
assert.equal(output.ok, true);
assert.equal(output.title, "Hello");
assert.equal(result.status, 0, result.stderr || result.stdout);
assert.match(result.stdout, /# Main/);
});
test("unknown command returns structured error", ["errors", "cli"], () => {
const output = expectFailure(["nope", dataHtml("<title>Hello</title>")]);
const output = expectFailure(["nope", dataHtml("<title>Hello</title>"), "--no-reader"]);
assert.equal(output.ok, false);
assert.equal(output.error.code, "UNKNOWN_COMMAND");
@@ -241,8 +246,9 @@ test(
["errors", "timeout"],
() => {
const output = expectFailure([
"snapshot",
"reader",
dataHtml("<title>Hello</title>"),
"--no-reader",
"--timeout=abc",
]);
@@ -255,8 +261,9 @@ test(
test("invalid wait-until returns invalid option", ["errors", "wait"], () => {
const output = expectFailure([
"snapshot",
"reader",
dataHtml("<title>Hello</title>"),
"--no-reader",
"--wait-until=loaded",
]);
@@ -266,20 +273,22 @@ test("invalid wait-until returns invalid option", ["errors", "wait"], () => {
});
test("wait-js succeeds when condition is true", ["wait"], () => {
const output = expectSuccess([
"snapshot",
dataHtml("<title>Hello</title>"),
const result = runCli([
"reader",
dataHtml("<title>Hello</title><h1>Main</h1>"),
"--no-reader",
'--wait-js=return document.title === "Hello"',
]);
assert.equal(output.ok, true);
assert.equal(output.title, "Hello");
assert.equal(result.status, 0, result.stderr || result.stdout);
assert.match(result.stdout, /# Main/);
});
test("wait-js timeout returns wait timeout", ["wait", "errors"], () => {
const output = expectFailure([
"snapshot",
"reader",
dataHtml("<title>Hello</title>"),
"--no-reader",
"--wait-js=return false",
"--timeout=1",
]);
@@ -296,8 +305,9 @@ test(
["wait", "errors", "js"],
() => {
const output = expectFailure([
"snapshot",
"reader",
dataHtml("<title>Hello</title>"),
"--no-reader",
'--wait-js=throw new Error("boom")',
]);
@@ -313,8 +323,9 @@ test(
["errors", "js"],
() => {
const output = expectFailure([
"snapshot",
"reader",
dataHtml("<title>Hello</title>"),
"--no-reader",
'--js=throw new Error("boom")',
]);