{"id":19329802,"url":"https://github.com/marmelab/curator-ai","last_synced_at":"2025-09-05T07:41:00.098Z","repository":{"id":228832505,"uuid":"748083924","full_name":"marmelab/curator-ai","owner":"marmelab","description":"An AI-powered news curator. It reads a list of articles, selects the best ones depending on a list of interests, and summarizes them into an easy-to-read news feed. Powered by the OpenAI API.","archived":false,"fork":false,"pushed_at":"2025-02-06T16:48:26.000Z","size":988,"stargazers_count":31,"open_issues_count":2,"forks_count":7,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-08-29T22:27:42.019Z","etag":null,"topics":["ai","cli","feed-aggregator","nodejs","openai","rss-reader"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/curator-ai","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/marmelab.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-01-25T08:38:06.000Z","updated_at":"2025-08-23T12:46:46.000Z","dependencies_parsed_at":"2025-02-06T17:32:27.784Z","dependency_job_id":"54c9bb74-32b2-4f27-876c-b07c177bccd4","html_url":"https://github.com/marmelab/curator-ai","commit_stats":null,"previous_names":["marmelab/curator-ai"],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/marmelab/curator-ai","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marmelab%2Fcurator-ai","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marmelab%2Fcurator-ai/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marmelab%2Fcurator-ai/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marmelab%2Fcurator-ai/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/marmelab","download_url":"https://codeload.github.com/marmelab/curator-ai/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marmelab%2Fcurator-ai/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":273728023,"owners_count":25157135,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-05T02:00:09.113Z","response_time":402,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai","cli","feed-aggregator","nodejs","openai","rss-reader"],"created_at":"2024-11-10T02:30:06.460Z","updated_at":"2025-09-05T07:40:54.811Z","avatar_url":"https://github.com/marmelab.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Curator AI\n\n[![Common Changelog](https://common-changelog.org/badge.svg)](https://common-changelog.org)\n\nAn AI-powered news curator. It reads a list of articles, selects the best ones depending on a list of interests, and summarizes them into an easy-to-read news feed. Powered by the OpenAI API.\n\n## Requirements\n\n- Node.js \u003e= 18\n- an [OpenAI API](https://platform.openai.com/) key\n- a [Postmark](https://postmarkapp.com/) server\n\n## Initialize the project\n\nAfter cloning the repository, you can copy the `.env.sample` file as `.env`, and fill it with your info:\n\n- `OPENAI_API_KEY`: your Open API key.\n- `SUPABASE_URL`: the url of your Supabase DB.\n- `SUPABASE_ANON_KEY`: the anon key of your Supabase BD.\n- `POSTMARK_API_KEY`: your Postmark API key.\n- `DEFAULT_POSTMARK_MAIL`: the default email you are using to communicate with the service.\n- `NGROK_AUTH_TOKEN`: your Ngrok auth token.\n\nThen run the following command :\n\n```sh\nmake init\n```\n\nIt will install every dependencies, build the project and migrate the db.\n\n## Start the webpage\n\nTo run the webpage locally:\n\n```sh\nmake webpage\n```\n\nTo start Next in dev mode:\n\n```sh\nmake dev\n```\n\nThis will reload automatically the webpage if you dev on the front. If you dev on the server side, you must restart it.\n\n## Test the interest scrapper (without the mail)\n\nIf you want to test the service and how the request sent by email works, without sending actual email, you can use that. Otherwise, you can go to the next part.\n\n```sh\nmake conv_agent_test\n```\n\nThis will return the service answer. If you want to see and change the request, go to the `./conversational_agent/src/test/myMessage.txt`.\n\n## Send an email an get your extracted preferences !\n\n```sh\nmake conv_agent\n```\n\nThis will start the server at `http://localhost:3000`.\nThis server is local, but we need to make it online.\nNow, in an other terminal :\n\n```sh\nmake start_ngrok\n```\n\nThis, will show a bunch of line. Note the one like :\n\n```sh\nForwarding                    \u003cYOUR_WEBHOOK_URI\u003e -\u003e http://localhost:3000\n```\n\nGo to your [Postmark](https://postmarkapp.com/) server, and :\n\n- Create an Inbound Message Stream if not already existing.\n- In the settings of this Inbound Stream, write `\u003cYOUR_WEBHOOK_URI\u003e/webhook` in the Webhook section.\n- Be sure that the email you have entered in the `.env` file as `DEFAULT_POSTMARK_MAIL` is in `Sender Signatures`. This will be the email you are going to use after.\n\nNow you can send an email to the inbound address (in the inbound settings).\nThis will return a list of preferences.\n\n## CLI Usage\n\n```sh\n# Install the package globally\nnpm install -g curator-ai\n\n# Summarize a list of articles based on aggregator URLs\nOPENAI_API_KEY=XXX curate -a https://news.ycombinator.com/ https://lobste.rs/\n```\n\nExample output:\n\n![Example output](./assets/capture.png)\n\nMore options:\n\n```sh\n# Get usage information\ncurate\n\n# Summarize a list of articles based on URLs passed directly as parameters\nOPENAI_API_KEY=XXX curate -u https://example.com/article1 https://example.com/article2\n\n# Summarize a list of articles based on a file containing URLs\nOPENAI_API_KEY=XXX curate -uf myFile.txt\n\n# Return at most 5 articles\nOPENAI_API_KEY=XXX curate -a https://news.ycombinator.com/ -m 5\n\n# Return the articles about AI and React\nOPENAI_API_KEY=XXX curate -a https://news.ycombinator.com/ -i AI React\n\n# Summarize a list of articles based on RSS feed\nOPENAI_API_KEY=XXX curate -r https://afup.org/rss.xml -i event\n```\n\nYou can also put the API key in a `.env` file:\n\n```txt\nOPENAI_API_KEY=XXX\n```\n\nThen you don't need to pass it as a parameter.\n\n```sh\ncurate -f myFile.txt\n```\n\n## Programmatic Usage\n\n```sh\n# Install the package locally\nnpm install curator-ai\n```\n\nPut the API key in a `.env` file:\n\n```txt\nOPENAI_API_KEY=XXX\n```\n\nUse the `curate` function:\n\n```js\nconst { curate } = require('curator-ai');\n\nconst links = [\n    'https://stability.ai/news/stable-code-2024-llm-code-completion-release',\n    'https://www.fromjason.xyz/p/notebook/where-have-all-the-websites-gone/',\n    'https://www.joelonsoftware.com/2000/04/06/things-you-should-never-do-part-i/',\n    'https://biomejs.dev/blog/biome-v1-5/',\n    'https://birtles.blog/2024/01/06/weird-things-engineers-believe-about-development/',\n    'https://julesblom.com/writing/flushsync',\n];\n\ncurate({\n    links,\n    interests: ['react', 'ai'],\n    max: 5,\n}).then(curatedLinks =\u003e {\n    console.log(curatedLinks);\n});\n\n// [\n//  {\n//    title: 'More Than You Need to Know About ReactDOM.flushSync',\n//    author: 'Jules Blom',\n//    summary: 'This article dives into the rarely used ReactDOM.flushSync function in React and explains what it does and when it is useful. It discusses how flushSync flushes state updates synchronously to the DOM, and why this is important. The article also explains the concept of batching in React updates and how flushSync can bypass the update queue.',\n//    relevancy_score: 8,\n//    link: 'https://julesblom.com/writing/flushsync'\n//  },\n//  ...\n// ]\n```\n\n## Development\n\n```sh\nnpm install\n\n# Get usage information\nnpm start\n\n# Summarize a list of articles based on aggregator URLs (notice the --):\nnpm start -- -a https://news.ycombinator.com/\n```\n\nDon't forget to pass a valid OpenAI key, either as an environment variable or in a `.env` file.\n\n## Build\n\n```sh\nnpm run build\nnpm publish\n```\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmarmelab%2Fcurator-ai","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmarmelab%2Fcurator-ai","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmarmelab%2Fcurator-ai/lists"}