{"_id":"@drakulavich/uae-news-digest","_rev":"7-eee92809f1c80cc585b2636ba02d2010","name":"@drakulavich/uae-news-digest","dist-tags":{"latest":"1.0.0"},"versions":{"0.0.1":{"name":"@drakulavich/uae-news-digest","version":"0.0.1","license":"MIT","_id":"@drakulavich/uae-news-digest@0.0.1","maintainers":[{"name":"drakulavich","email":"drakulavich@gmail.com"}],"bin":{"uae-news-digest":"src/index.ts"},"dist":{"shasum":"ad2d3ee5446815710b7439ddc2013d3a64cd341c","tarball":"https://registry.npmjs.org/@drakulavich/uae-news-digest/-/uae-news-digest-0.0.1.tgz","fileCount":34,"integrity":"sha512-HEKODvszIbRWKv0Ox3YPwRS9waqcR5CSsInegeeKj8xy33oJqfUNua9MDq08RGrkILbGQEtjfKNw/Y5MTmkYDg==","signatures":[{"sig":"MEUCIQDUdZzUENdYX2uzPJpcUMdappelxeOsi28qX1JvAbU6sgIgFquws7k+Q5lRvUrvEL0ZhG/ymDP5GTDmP9mMkmeKYmQ=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":157046},"type":"module","exports":{".":"./src/index.ts","./core":"./src/core.ts"},"gitHead":"7d786c84bc2776f038304f9419617ef904c25046","scripts":{"dev":"bun src/index.ts","test":"bun test"},"_npmUser":{"name":"drakulavich","email":"drakulavich@gmail.com"},"_npmVersion":"11.12.1","description":"Daily UAE news digest from Google News RSS with optional DeepL translation","directories":{},"_nodeVersion":"25.9.0","dependencies":{"commander":"^14.0.3","fast-xml-parser":"^5.3.0"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.9.3","@types/node":"^25.3.3"},"_npmOperationalInternal":{"tmp":"tmp/uae-news-digest_0.0.1_1775886109712_0.2348522252360079","host":"s3://npm-registry-packages-npm-production"}},"0.0.2":{"name":"@drakulavich/uae-news-digest","version":"0.0.2","license":"MIT","_id":"@drakulavich/uae-news-digest@0.0.2","maintainers":[{"name":"drakulavich","email":"drakulavich@gmail.com"}],"bin":{"uae-news-digest":"src/index.ts"},"dist":{"shasum":"c3c3d6a1d6d23dc8cf1d31c05642596edc109f73","tarball":"https://registry.npmjs.org/@drakulavich/uae-news-digest/-/uae-news-digest-0.0.2.tgz","fileCount":36,"integrity":"sha512-La/qs4qQKnVswZx7eIsUlTYen7GUPLhX9pkxbK+psNbYGC4SVwOG0ox+LW9JmXi4udCZVE77DsoKmoAKDPi50w==","signatures":[{"sig":"MEYCIQC51+ESqtGcAHmR85N/97l7KZ+Sv5Z9XkNirHBEJAhM9wIhAI53NyYKcMPCssl/xWRXcx8t7di2hCm5hDE0Um/OR2gc","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":171910},"type":"module","exports":{".":"./src/index.ts","./core":"./src/core.ts"},"gitHead":"7089484198cd078a7ed3510287d6d083e6feb1fd","scripts":{"dev":"bun src/index.ts","test":"bun test"},"_npmUser":{"name":"drakulavich","email":"drakulavich@gmail.com"},"_npmVersion":"11.12.1","description":"Daily UAE news digest from Google News RSS with optional DeepL translation","directories":{},"_nodeVersion":"25.9.0","dependencies":{"commander":"^14.0.3","fast-xml-parser":"^5.3.0"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.9.3","@types/node":"^25.3.3"},"_npmOperationalInternal":{"tmp":"tmp/uae-news-digest_0.0.2_1776062416146_0.4217420673022996","host":"s3://npm-registry-packages-npm-production"}},"0.0.4":{"name":"@drakulavich/uae-news-digest","version":"0.0.4","license":"MIT","_id":"@drakulavich/uae-news-digest@0.0.4","maintainers":[{"name":"drakulavich","email":"drakulavich@gmail.com"}],"homepage":"https://github.com/drakulavich/uae-news-digest#readme","bugs":{"url":"https://github.com/drakulavich/uae-news-digest/issues"},"bin":{"uae-news-digest":"src/index.ts"},"dist":{"shasum":"ecc22975385229429c68d8798393d156c25c5a59","tarball":"https://registry.npmjs.org/@drakulavich/uae-news-digest/-/uae-news-digest-0.0.4.tgz","fileCount":16,"integrity":"sha512-snOeqElgpmFKQwp6afwSD9e5eOSHzSHWzlsrQuE1TvF5bgsALiu///kNAbRcNEXHo3beoMng/F6mKvOhYe4KGw==","signatures":[{"sig":"MEUCIQDCiojyY7hKyFF04lJOMPCn5l3TcKC3Hb3koNf7NtHbPgIgdmh6VOBzsXeD5KD38skn2awDggolQStha7uX2pAcHbk=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@drakulavich%2fuae-news-digest@0.0.4","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":29270},"type":"module","exports":{".":"./src/index.ts","./core":"./src/core.ts"},"gitHead":"d52c5c42c66b4f41b999e9a59f11a86b5cd524dc","scripts":{"dev":"bun src/index.ts","test":"bun test --preload ./test/fetch-guard.ts","build":"tsc -p tsconfig.build.json","typecheck":"tsc --noEmit","smoke:pack":"bun scripts/smoke-pack.ts"},"_npmUser":{"name":"drakulavich","email":"drakulavich@gmail.com"},"repository":{"url":"git+https://github.com/drakulavich/uae-news-digest.git","type":"git"},"_npmVersion":"10.8.2","description":"Daily UAE news digest from Google News RSS with optional DeepL translation","directories":{},"_nodeVersion":"20.20.2","dependencies":{"commander":"^14.0.3","fast-xml-parser":"^5.3.0"},"_hasShrinkwrap":false,"devDependencies":{"@types/bun":"^1.3.14","typescript":"^5.9.3","@types/node":"^25.3.3"},"_npmOperationalInternal":{"tmp":"tmp/uae-news-digest_0.0.4_1779452384149_0.223817566590776","host":"s3://npm-registry-packages-npm-production"}},"0.1.0":{"name":"@drakulavich/uae-news-digest","version":"0.1.0","license":"MIT","_id":"@drakulavich/uae-news-digest@0.1.0","maintainers":[{"name":"drakulavich","email":"drakulavich@gmail.com"}],"homepage":"https://github.com/drakulavich/uae-news-digest#readme","bugs":{"url":"https://github.com/drakulavich/uae-news-digest/issues"},"bin":{"uae-news-digest":"src/index.ts"},"dist":{"shasum":"8bb54fb35e7c33b220a28c8b1fc854e97df81529","tarball":"https://registry.npmjs.org/@drakulavich/uae-news-digest/-/uae-news-digest-0.1.0.tgz","fileCount":17,"integrity":"sha512-TfR0C/ywVs7l/oGpGffYztApDF15QhYQ3DSPaMBHwMLeG7t++8le+72gLOwlMgy+jRJf4uNG9BW9SmLLyIqhmw==","signatures":[{"sig":"MEUCIBNf+W+nsUyOqvhYz+yHQl3mRloB0scvWupCF9Kduf3+AiEAnXgEsxb4bakR3Z0yYrNRhFqs5Z401f9wQ8x1lKc0iJU=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@drakulavich%2fuae-news-digest@0.1.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":46671},"type":"module","exports":{".":"./src/index.ts","./core":"./src/core.ts"},"gitHead":"38e95be08076ebd48d47650fdbaaa2c9bbf53c18","scripts":{"dev":"bun src/index.ts","test":"bun test --preload ./test/fetch-guard.ts","build":"tsc -p tsconfig.build.json","typecheck":"tsc --noEmit","smoke:pack":"bun scripts/smoke-pack.ts"},"_npmUser":{"name":"drakulavich","email":"drakulavich@gmail.com"},"repository":{"url":"git+https://github.com/drakulavich/uae-news-digest.git","type":"git"},"_npmVersion":"10.8.2","description":"Daily UAE news digest from Google News RSS with optional DeepL translation","directories":{},"_nodeVersion":"20.20.2","dependencies":{"commander":"^14.0.3","fast-xml-parser":"^5.3.0"},"_hasShrinkwrap":false,"devDependencies":{"@types/bun":"^1.3.14","typescript":"^6.0.3"},"_npmOperationalInternal":{"tmp":"tmp/uae-news-digest_0.1.0_1779458122206_0.0016755104138848065","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@drakulavich/uae-news-digest","version":"0.1.1","license":"MIT","_id":"@drakulavich/uae-news-digest@0.1.1","maintainers":[{"name":"drakulavich","email":"drakulavich@gmail.com"}],"homepage":"https://github.com/drakulavich/uae-news-digest#readme","bugs":{"url":"https://github.com/drakulavich/uae-news-digest/issues"},"bin":{"uae-news-digest":"src/index.ts"},"dist":{"shasum":"d14ca5c838dc54f9ceae0df9e06768849def3295","tarball":"https://registry.npmjs.org/@drakulavich/uae-news-digest/-/uae-news-digest-0.1.1.tgz","fileCount":17,"integrity":"sha512-2lGJTors4lWBkIDvg8B7hzlYZXY5HLISuBpLowMVuOywrF9JN6CYMra68Xc5IsjIr4/jgAY3DMkHcX99Otol2Q==","signatures":[{"sig":"MEYCIQDj8vaaGOfexi/NrznCzpNlJyhsBHuqAu0usx95Pnot7gIhAPzzNNL85kEMMwQM5OajExpZEvhDHoCu4YbspH4b0xHN","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@drakulavich%2fuae-news-digest@0.1.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":46897},"type":"module","exports":{".":"./src/index.ts","./core":"./src/core.ts"},"gitHead":"69c6e66c4dfc7b2f18104bb30f6fcf5b5e09e0d4","scripts":{"dev":"bun src/index.ts","test":"bun test --preload ./test/fetch-guard.ts","build":"tsc -p tsconfig.build.json","typecheck":"tsc --noEmit","smoke:pack":"bun scripts/smoke-pack.ts"},"_npmUser":{"name":"drakulavich","email":"drakulavich@gmail.com"},"repository":{"url":"git+https://github.com/drakulavich/uae-news-digest.git","type":"git"},"_npmVersion":"10.8.2","description":"Daily UAE news digest from Google News RSS with optional DeepL translation","directories":{},"_nodeVersion":"20.20.2","dependencies":{"commander":"^14.0.3","fast-xml-parser":"^5.3.0"},"_hasShrinkwrap":false,"devDependencies":{"@types/bun":"^1.3.14","typescript":"^6.0.3"},"_npmOperationalInternal":{"tmp":"tmp/uae-news-digest_0.1.1_1779561169897_0.8813213974386109","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@drakulavich/uae-news-digest","version":"0.2.0","license":"MIT","_id":"@drakulavich/uae-news-digest@0.2.0","maintainers":[{"name":"drakulavich","email":"drakulavich@gmail.com"}],"homepage":"https://github.com/drakulavich/uae-news-digest#readme","bugs":{"url":"https://github.com/drakulavich/uae-news-digest/issues"},"bin":{"uae-news-digest":"src/index.ts"},"dist":{"shasum":"73f3ec1c4313024cdf913ad5b76c3e2a848c1761","tarball":"https://registry.npmjs.org/@drakulavich/uae-news-digest/-/uae-news-digest-0.2.0.tgz","fileCount":18,"integrity":"sha512-N8v4prXaBRhsBVkgLVqYekTo1WNevrMLzIEj7F054SrOl+xJgxjZvfExB6GWOJRabhgQD8swzOnGeRpieTfYdA==","signatures":[{"sig":"MEQCIDG6nf46/7clZ7loKlC5g3+6bXClKBALq8DrQDHuXNLyAiBHViaGemBlPcu6ymJsfcrdJnukTx786QTwJ7afODBirA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@drakulavich%2fuae-news-digest@0.2.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":63173},"type":"module","exports":{".":"./src/index.ts","./core":"./src/core.ts"},"gitHead":"5586053e8f94209a10dd6aabbcd11da514e5a2d0","scripts":{"dev":"bun src/index.ts","test":"bun test --preload ./test/fetch-guard.ts","build":"tsc -p tsconfig.build.json","typecheck":"tsc --noEmit","smoke:pack":"bun scripts/smoke-pack.ts"},"_npmUser":{"name":"drakulavich","email":"drakulavich@gmail.com"},"repository":{"url":"git+https://github.com/drakulavich/uae-news-digest.git","type":"git"},"_npmVersion":"10.8.2","description":"Daily UAE news digest from Google News RSS with optional DeepL translation","directories":{},"_nodeVersion":"20.20.2","dependencies":{"commander":"^15.0.0","fast-xml-parser":"^5.3.0"},"_hasShrinkwrap":false,"devDependencies":{"@types/bun":"^1.3.14","typescript":"^7.0.2"},"_npmOperationalInternal":{"tmp":"tmp/uae-news-digest_0.2.0_1784741635784_0.4884220528809522","host":"s3://npm-registry-packages-npm-production"}},"1.0.0":{"_id":"@drakulavich/uae-news-digest@1.0.0","bin":{"uae-news-digest":"src/index.ts"},"bugs":{"url":"https://github.com/drakulavich/uae-news-digest/issues"},"dist":{"shasum":"56400664e0ecd4873733ac6c798d4fdd3ec96d2b","tarball":"https://registry.npmjs.org/@drakulavich/uae-news-digest/-/uae-news-digest-1.0.0.tgz","fileCount":30,"integrity":"sha512-QexOH+4767xJb+m/MubWPXsxOrjvVU6qEDIDGOTmdK1iYuF60OZ8vno7WyrshZ6gc/6uhfXAhQFo50+CqDqOBQ==","signatures":[{"sig":"MEQCIFF1rak9KsV8vXCutjtB27XTBnKD7+PMcpodZr1WtUiOAiBt8rVNYZfME1cLTjEL7lK6FyG1+UYQNvXARfCdlm5OHA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQD9nake2MRjg63VNvnSHPRN08XkZGb8nvCc1DxWjmFRaAIgYlFmiwhvzJ1qtLakm2RMsD6/+8SoPmuUPbXjqZH56tg="}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@drakulavich%2fuae-news-digest@1.0.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":70203},"name":"@drakulavich/uae-news-digest","type":"module","exports":{".":"./src/index.ts","./core":"./src/core.ts"},"gitHead":"7da5554123a9a0ffe3155f17185429a8025d9485","license":"MIT","scripts":{"dev":"bun src/index.ts","test":"bun test","build":"tsc -p tsconfig.build.json","typecheck":"tsc --noEmit","smoke:pack":"bun scripts/smoke-pack.ts"},"version":"1.0.0","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:5657a05e-3641-4b13-85d5-074ffbb911c3"}},"homepage":"https://github.com/drakulavich/uae-news-digest#readme","repository":{"url":"git+https://github.com/drakulavich/uae-news-digest.git","type":"git"},"_npmVersion":"11.19.0","description":"Daily UAE news digest from Google News RSS with optional DeepL translation","directories":{},"maintainers":[{"name":"drakulavich","email":"drakulavich@gmail.com"}],"_nodeVersion":"24.20.0","dependencies":{"zod":"^4.5.4","commander":"^15.0.0","fast-xml-parser":"^5.3.0"},"_hasShrinkwrap":false,"devDependencies":{"@types/bun":"^1.3.14","typescript":"^7.0.2"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/uae-news-digest_1.0.0_1788888250981_0.5931281859052455"}}},"time":{"created":"2026-04-11T05:41:49.587Z","modified":"2026-09-08T17:24:11.405Z","0.0.1":"2026-04-11T05:41:49.847Z","0.0.2":"2026-04-13T06:40:16.279Z","0.0.4":"2026-05-22T12:19:44.340Z","0.1.0":"2026-05-22T13:55:22.349Z","0.1.1":"2026-05-23T18:32:50.056Z","0.2.0":"2026-07-22T17:33:55.955Z","1.0.0":"2026-09-08T17:24:11.089Z"},"bugs":{"url":"https://github.com/drakulavich/uae-news-digest/issues"},"license":"MIT","homepage":"https://github.com/drakulavich/uae-news-digest#readme","repository":{"url":"git+https://github.com/drakulavich/uae-news-digest.git","type":"git"},"description":"Daily UAE news digest from Google News RSS with optional DeepL translation","maintainers":[{"name":"drakulavich","email":"drakulavich@gmail.com"}],"readme":"<p align=\"center\">\n  <img src=\"https://raw.githubusercontent.com/drakulavich/uae-news-digest/main/docs/logo.png\" alt=\"uae-news-digest logo\" width=\"200\" />\n</p>\n\n<h1 align=\"center\">uae-news-digest</h1>\n\n<p align=\"center\">\n  <a href=\"https://github.com/drakulavich/uae-news-digest/actions/workflows/ci.yml\"><img src=\"https://github.com/drakulavich/uae-news-digest/actions/workflows/ci.yml/badge.svg\" alt=\"CI\"></a>\n  <a href=\"https://www.npmjs.com/package/@drakulavich/uae-news-digest\"><img src=\"https://img.shields.io/npm/v/@drakulavich/uae-news-digest\" alt=\"npm version\"></a>\n  <a href=\"https://opensource.org/licenses/MIT\"><img src=\"https://img.shields.io/badge/License-MIT-blue.svg\" alt=\"License: MIT\"></a>\n  <a href=\"https://bun.sh\"><img src=\"https://img.shields.io/badge/runtime-Bun-f9f1e1?logo=bun\" alt=\"Bun\"></a>\n</p>\n\n<p align=\"center\"><b>Wake up to the UAE without opening ten news tabs.</b><br>\n<code>uae-news-digest</code> turns Google News RSS into a ranked, deduplicated terminal briefing with clean JSON for scripts and agents.</p>\n\n<p align=\"center\">\n  <img src=\"https://github.com/drakulavich/uae-news-digest/raw/main/assets/demo.gif\" alt=\"uae-news-digest demo: healthcheck, text digest, and JSON output\" width=\"720\">\n</p>\n\n- **Signal first.** Preferred sources and UAE-specific keywords push important stories up.\n- **No repeat sludge.** Exact keys plus fuzzy title matching collapse syndicated duplicates.\n- **Human or machine.** Read the emoji digest in your terminal, or pipe stable JSON into agents, cron, Slack, or your own scripts.\n- **Bring your own topics.** Use the built-in UAE set, or drop in a config file to define your own topics, queries, and feeds.\n- **Translate only when you ask.** Optional DeepL support keeps the default path simple and dependency-light.\n\n## Install\n\n```bash\nbun add -g @drakulavich/uae-news-digest\n```\n\nOr run from source:\n\n```bash\ngit clone https://github.com/drakulavich/uae-news-digest.git\ncd uae-news-digest\nbun install\nbun link\n```\n\n## Usage\n\n```bash\nuae-news-digest                                      # fetch + print the UAE digest\nuae-news-digest --hours 12 --limit 10                # tighter briefing window\nuae-news-digest --json                               # stable envelope for automation\nuae-news-digest --config ./digest.config.json        # your own topics\nuae-news-digest healthcheck                          # JSON liveness probe\nuae-news-digest config print-default > digest.config.json  # start a config from the built-in set\nuae-news-digest config validate                      # check the auto-detected config\nDEEPL_AUTH_KEY=xxx uae-news-digest --target-lang DE  # optional DeepL translation\n```\n\n| Flag | Default | Description |\n|------|---------|-------------|\n| `--config <path>` | | Path to a digest config JSON (overrides auto-detect; see [Topics Mode](#topics-mode)) |\n| `--hours <n>` | `36` | Lookback window in hours |\n| `--limit <n>` | | Max items per topic (overrides each topic's own limit); must be a positive integer |\n| `--target-lang <code>` | | DeepL target language (e.g. `DE`, `FR`, `JA`). Requires `DEEPL_AUTH_KEY` |\n| `--state-file <path>` | `./seen_titles.txt` | Seen-items state file |\n| `--timeout-ms <n>` | `15000` | RSS fetch timeout |\n| `--dry-run` | `false` | Preview without updating state |\n| `--json` | `false` | Output as JSON (agent-friendly envelope) |\n\n### Subcommands\n\n| Command | Description |\n|---------|-------------|\n| `manifest` | Print a machine-readable tool descriptor as JSON |\n| `healthcheck [--rss-url <url>]` | Smoke-test the first topic's feed (or `--rss-url`); prints `{ok, version, latencyMs}` |\n| `config print-default` | Print the built-in config as JSON, ready to copy into `digest.config.json` |\n| `config validate [path]` | Validate a config file (default: the auto-detected one); prints `ok` or every issue with its JSON path |\n\n### Exit codes\n\n`0` success (a topic may still have failed — see warnings). `1` a usage, config, network, or timeout error, or no topic could be fetched.\n\n## What You Get\n\n```\n🇦🇪 UAE digest — 2026-09-05\n\n📰 UAE\n  🛡️ UAE intercepts 79 Iranian strike assets (The National, 2h ago)\n  📉 Dubai property sales drop more than 30% (Anadolu Ajansı, 5h ago)\n  ⛴️ Container ship incident at Khor Fakkan (Reuters, 3h ago)\n  ✈️ Abu Dhabi airport reopens after rain (Khaleej Times, 1h ago)\n  🌧️ Unstable weather hits some emirates (Gulf News, 4h ago)\n  🛢️ Oil prices: OPEC+ mulls output increase (CNBC, 6h ago)\n```\n\nAnd when you need structured output:\n\n```bash\nuae-news-digest --json | jq '.items[].title'\n```\n\n## Signal filter\n\nEvery run automatically surfaces headlines that materially affect an expat family in the UAE across four areas:\n\n- **Safety / threats** — missiles, drones, airspace closures, evacuation alerts, storms\n- **Money / daily life** — rent, fees, fuel prices, salary, fines, subsidies\n- **Rules / visas / documents** — visa changes, new laws, permit and licence updates\n- **Logistics / infrastructure** — flight disruptions, road closures, metro outages\n\nItems that score above the threshold are pulled into a `🚨 Important` block printed **at the top** of the digest, deduplicated from the regular listing below. Each important line shows a `[signals]` marker that explains why it surfaced. PR puff (launches, awards, \"world's first/tallest/largest\", festivals, and similar) is penalised and pushed down.\n\nThis is a heuristic — no API key required, always on.\n\n**Example output:**\n\n```\n🇦🇪 UAE digest — 2025-11-14\n\n🚨 Important\n  🛡️ UAE intercepts Iranian missile barrage (The National, 1h ago) [missile, airspace] — UAE Security\n  ✈️ Dubai airport closes Terminal 2 for maintenance (Gulf News, 3h ago) [flight] — Travel\n\n💰 UAE economy\n  📉 OPEC+ weighs output cut for Q1 (Reuters, 4h ago)\n  ...\n```\n\n### Topic `match` / `matchMode`\n\nGoogle News sometimes returns loosely-matched articles for a query. Add optional `match` and `matchMode` keys to any topic to require real keyword matches in the article title:\n\n```jsonc\n{\n  \"topics\": [\n    {\n      \"slug\": \"schools\",\n      \"name\": \"Schools\",\n      \"query\": \"school fees Dubai\",\n      \"match\": [\"school\", \"fees\"],\n      \"matchMode\": \"all\"\n    }\n  ]\n}\n```\n\n| Key | Type | Default | Description |\n|-----|------|---------|-------------|\n| `match` | `string[]` | — | Keywords that must appear in the article title. Omit to keep existing behaviour. |\n| `matchMode` | `\"all\"` \\| `\"any\"` \\| `<N>` | `\"all\"` | `\"all\"` — every term must match; `\"any\"` — at least one; a positive integer `N` — at least N terms. |\n\nWhen articles are dropped for failing the keyword filter, a warning reports how many were dropped (visible in non-JSON mode on stderr).\n\n### Agent workflow (key-free smart pass)\n\n`--json` enriches every item with `importance`, `tier` (`breaking` | `impact` | `neutral` | `fluff`), `signals`, and `matchedTerms`. Pipe that to an LLM with the ready-made filter criterion:\n\n```bash\nuae-news-digest --prompt                                    # print the filter criterion\nuae-news-digest --json --dry-run | claude \"$(uae-news-digest --prompt)\"\n```\n\n`--dry-run` keeps the seen-items state file untouched, so an ad-hoc agent-filter pass doesn't mark those articles as seen and hide them from the next real digest. Drop it when this pipe _is_ the scheduled run.\n\nThe agent can drop noise reproducibly based solely on the structured metadata — no extra API key, no custom prompt engineering required.\n\n`--prompt` output:\n\n> You are a news filter for an expat family in the UAE. Keep only what materially affects safety, money, rules/visas, or logistics. Drop PR, launches, awards, rankings, and 'world's first/tallest/largest'.\n\n## Topics Mode\n\nWithout a config, the built-in UAE config runs (one topic). For per-topic digests (e.g. economy, real estate, regional politics), create a `digest.config.json` file:\n\n```json\n{\n  \"locale\": { \"hl\": \"en\", \"gl\": \"AE\", \"ceid\": \"AE:en\" },\n  \"topics\": [\n    {\n      \"slug\": \"economy\",\n      \"name\": \"UAE economy\",\n      \"emoji\": \"💰\",\n      \"query\": \"(UAE OR Emirates) AND (economy OR GDP OR inflation OR ADNOC OR non-oil)\",\n      \"limit\": 5\n    },\n    {\n      \"slug\": \"realty\",\n      \"name\": \"Real estate\",\n      \"emoji\": \"🏠\",\n      \"query\": \"(Dubai OR \\\"Abu Dhabi\\\") AND (\\\"real estate\\\" OR property OR Emaar OR Aldar)\",\n      \"limit\": 4\n    },\n    {\n      \"slug\": \"chocolate\",\n      \"name\": \"Dubai chocolate\",\n      \"emoji\": \"🍫\",\n      \"query\": \"\\\"Dubai chocolate\\\" OR pistachio OR kunafa\",\n      \"limit\": 3\n    }\n  ]\n}\n```\n\nHeuristics (skip list, source tiers, keyword boosts, dedupe synonyms, importance markers, emoji rules) also live in this file. A config without those sections runs neutral. Start from the built-in UAE set with `uae-news-digest config print-default > digest.config.json`, then edit it; run `uae-news-digest config validate` to check it against the schema before using it.\n\nEach topic builds its Google News RSS URL from `query` and `locale`. Add `feedUrl` to a topic to fetch that exact RSS URL instead — useful for non-Google feeds or deterministic tests.\n\nThe CLI looks for the config in this order:\n\n1. `--config <path>` (explicit override)\n2. `./digest.config.json` (current working directory)\n3. `$XDG_CONFIG_HOME/uae-news-digest/topics.json` (falls back to `~/.config/uae-news-digest/topics.json`)\n\nWithout a config the built-in UAE config runs. The CLI fetches each topic in parallel and renders one section per topic (a single-topic config still prints its heading). A story already selected by an earlier topic is not repeated in a later one (exact-key match; fuzzy near-duplicate detection runs within each topic), so the topic order in the config sets priority.\n\n`--target-lang` translates all section titles in a single DeepL batch. `--limit`, when given, must be a positive integer and overrides every topic's per-topic limit for the run.\n\nThe example `query` strings above are starting points, not optimal — iterate them against real Google News output.\n\n### JSON output\n\n```json\n{\n  \"tool\": \"uae-news-digest\",\n  \"version\": \"...\",\n  \"generatedAt\": \"2026-09-05T06:00:00.000Z\",\n  \"query\": { \"hours\": 36, \"limit\": null, \"targetLang\": null },\n  \"topics\": [\n    { \"slug\": \"economy\", \"name\": \"UAE economy\", \"count\": 5 },\n    { \"slug\": \"realty\",  \"name\": \"Real estate\", \"count\": 4 },\n    { \"slug\": \"chocolate\", \"name\": \"Dubai chocolate\", \"count\": 1 }\n  ],\n  \"count\": 10,\n  \"warnings\": [],\n  \"items\": [\n    {\n      \"topic\": \"economy\",\n      \"title\": \"...\",\n      \"translatedTitle\": null,\n      \"source\": \"...\",\n      \"url\": \"...\",\n      \"publishedAt\": \"...\",\n      \"hoursAgo\": 4,\n      \"score\": 0,\n      \"importance\": 0,\n      \"tier\": \"neutral\",\n      \"signals\": [],\n      \"matchedTerms\": []\n    }\n  ]\n}\n```\n\nItems are emitted as a flat list in section order; each carries its `topic` slug so consumers can group. `query.limit` is `null` unless `--limit` was passed.\n\n## Programmatic API\n\n```typescript\nimport { loadConfig, DEFAULT_CONFIG, runDigest, renderText, toJson } from \"@drakulavich/uae-news-digest/core\";\n\nconst config = await loadConfig(\"./digest.config.json\"); // or DEFAULT_CONFIG for the built-in UAE set\nconst now = new Date();\nconst result = await runDigest({\n  config,\n  seenKeys: new Set(),\n  hours: 36,\n  now,\n  fetchText: (url) => fetch(url).then((r) => r.text()),\n});\nconsole.log(renderText(result, config, now));\n```\n\nThe `/core` entry point exports `runDigest`, `renderText`, `toJson`, `loadConfig`, `resolveConfigPath`, `parseConfig`, `DEFAULT_CONFIG`, `parseRss`, plus the Seen-item state helpers (`readSeenKeys`, `writeSeenKeys`, `DEFAULT_STATE_FILE`) and DeepL helpers (`translateDeepL`, `DEEPL_API_URL`) — the same filtering, scoring, deduplication, translation fallback, and rendering logic the CLI uses, without spawning a process.\n\nAlready holding the RSS XML? Pass `fetchText: async () => xml` and `runDigest` never touches the network — the packed-package smoke test runs exactly that way.\n\n## How It Works\n\n```\nuae-news-digest --hours 12 --limit 10\n  │\n  ├── Config ──────── topics (query + locale) → one Google News RSS URL each (or feedUrl)\n  │\n  ├── Fetch RSS ───── Google News RSS feed per topic\n  │\n  ├── Filter ──────── skip: opinion, tabloids, sports, travel\n  │\n  ├── Score ───────── +3 preferred source, +2 UAE mention, +2 priority topic\n  │\n  ├── Deduplicate ─── exact key match + Jaccard fuzzy (threshold 0.45)\n  │\n  ├── Translate ───── DeepL API (optional, when --target-lang set)\n  │\n  └── Render ──────── display flag/name header + per-item emoji + title + source + hours ago\n```\n\nState file (`seen_titles.txt`) tracks seen articles so scheduled runs do not repeat the same briefing forever.\n\n`healthcheck` probes the first topic's feed from the resolved config (`--config`, `./digest.config.json`, XDG, or the built-in default), honouring `--timeout-ms` (default `15000`). For deterministic smoke tests against a fixed feed, pass `healthcheck --rss-url <url>`.\n\n## Requirements\n\n- [Bun](https://bun.sh) >= 1.3 (CI pins 1.3.14).\n- CI validates Linux and macOS. Windows is not a supported target yet.\n\n## License\n\nMade with 💛🩵 Published under MIT License.\n","readmeFilename":"README.md"}