Use this skill when the user or another skill/prompt needs the latest AgentLens blogs that have not been seen before. It discovers newly published blogs from...
---
name: agentlens-blog-feed
description: >
Use this skill when the user or another skill/prompt needs the latest AgentLens blogs
that have not been seen before. It discovers newly published blogs from the AgentLens
public API, fetches each new blog's body, and tracks processed blogs in a local memory
file so every blog surfaces exactly once. It only fetches and de-duplicates — deciding
what to do with each new blog (summarize, draft posts, translate, save) is left to the
caller.
---
# AgentLens Blog Feed
You surface newly published AgentLens blogs that the caller has not seen yet. This is the
**front half** only: find new blogs, fetch their bodies, and return them. You **do not**
decide what happens next — drafting posts, choosing a language, translating, or saving is
the caller's job (a prompt or another skill).
De-duplication is your core responsibility: each blog must surface **exactly once** across
runs. You track this in a local memory file you own.
## Scope boundary
- **You do:** discover new blogs, fetch bodies, de-duplicate via a memory file.
- **You do not:** draft or rewrite content, choose or change the output language, save to
any vault, or schedule yourself.
- **Never** mark a blog processed unless the caller confirms it was successfully consumed.
## Configuration
| Name | Default | Meaning |
| --- | --- | --- |
| `MEMORY_PATH` | `~/agentlens-processed-blogs.json` | Local dedup memory (this skill owns it). Ask the user once where to store it; use the default if unspecified. Expand `~` to an absolute path before reading or writing. |
Fixed values (not configurable):
- `WORKER_URL` = `https://agentlens-core.archlab.workers.dev`
- `LIMIT` = `100`
`GET /blogs` is public — no token or `Authorization` header is required.
## The API
- **List:** `GET {WORKER_URL}/blogs?limit=100` → `{ items, total, offset, limit }`.
Each item has: `id`, `title`, `summary`, `period_label`, `job_type`, `source_id`,
`model`, `occurred_at` (ISO string, nullable), `generated_at` (ISO string).
- **Detail:** `GET {WORKER_URL}/blogs/{id}` → the blog plus `body_markdown` and
`references[]`. Each reference has `type`, `title`, `url`, `html_url`. Fetch the default
body — do not add any `?lang=` parameter.
## Workflow — Detect (read-only, never writes memory)
1. **Resolve `MEMORY_PATH`** (ask the user, or use the default) and read it. Expected
shape: `{ "processed_ids": ["<id>", ...], "last_generated_at": <ms> }`. If the file is
missing or unparseable, treat it as `{ "processed_ids": [], "last_generated_at": 0 }`
and continue — do not crash. `processed_ids` is the source of truth for dedup;
`last_generated_at` is only a fast lower-bound hint.
2. **Fetch the list:** `GET {WORKER_URL}/blogs?limit=100`. On a non-200 or network error,
report it and stop — nothing has been recorded, so the next run retries cleanly.
3. **Select new blogs:** keep items whose `id` is **not** in `processed_ids`. Sort the
survivors **descending** by `generated_at` (parse ISO → ms) so you process the
**newest first** — the most recent blogs are the most timely and should be handled
before older ones. (`generated_at` is always present; `occurred_at` may be null, so it
is not used for sorting.) If none remain, report "no new blogs" and finish.
4. **Fetch each body (newest first):** `GET {WORKER_URL}/blogs/{id}`. If the request is
non-200, or `body_markdown` is empty/whitespace, **skip that blog** — do not return it
and do not record it. It will retry next run (the stored body may not have propagated
yet). Otherwise pick a source link from `references[]`, preferring `html_url`, then
`url` (may be absent).
5. **Return the new blogs** to the caller: for each, `id`, `title`, `summary`,
`body_markdown`, `source_link`, `occurred_at`, `generated_at`. Stop here — **do not
write memory yet.**
## Workflow — Commit (the only write step)
When the caller confirms a blog was successfully consumed, mark it processed:
- Append its `id` to `processed_ids`.
- Set `last_generated_at = max(last_generated_at, generated_at_ms)`.
- Write `MEMORY_PATH` **atomically** (write a temp file, then rename; a plain overwrite is
acceptable if atomic rename is unavailable).
**Order matters: the caller consumes the blog first, then you commit.** Only commit blogs
the caller confirms — anything that failed downstream stays out of `processed_ids` so it
resurfaces next run.
## Memory file
- This skill is the **sole owner** of `MEMORY_PATH`.
- `processed_ids` is the **only** dedup record — "already seen" cannot be inferred from
anywhere else. Keep the file; never delete entries on its behalf.
## Failure handling
- List endpoint error: report and stop; retry next run.
- Per-blog body error or empty body: skip that blog only; continue with the rest.
- Never commit a blog the caller did not confirm as consumed.
## Feedback
If the user expresses an unmet need or dissatisfaction with this skill, share the
contribution link: https://github.com/archlab-space/open-skill-hub/issues
Do not surface it during normal use.
don't have the plugin yet? install it then click "run inline in claude" again.
added explicit inputs table with connection details, extracted decision logic into dedicated section, clarified memory file format and atomic write semantics, added edge cases (parse errors, empty bodies, network timeouts, rate limits), expanded procedure steps with concrete input/output contracts per step, and formalized outcome signals.
surface newly published agentlens blogs that haven't been seen before. this skill discovers blogs from the public agentlens api, fetches each new blog's full body, and tracks processed blogs in a local memory file to guarantee each blog surfaces exactly once per run. the skill stops after returning new blogs to the caller. deciding what to do with them (summarize, draft posts, translate, save) is the caller's responsibility, not this skill's.
| name | required | format | notes |
|---|---|---|---|
MEMORY_PATH |
no | file path | local dedup memory file. defaults to ~/agentlens-processed-blogs.json. expand ~ to absolute path before any read or write. this skill owns this file exclusively. ask user once for a custom path; use default if unspecified. |
WORKER_URL |
no | string | fixed to https://agentlens-core.archlab.workers.dev. public endpoint, no auth required. |
LIMIT |
no | integer | fixed to 100. max blogs per list request. |
no external oauth, api keys, or connections required. all agentlens endpoints are public.
resolve and read memory file. ask the user where to store the dedup file if MEMORY_PATH is not set; use default ~/agentlens-processed-blogs.json otherwise. expand ~ to an absolute path. attempt to read the file. expected json shape: { "processed_ids": ["<id>", ...], "last_generated_at": <milliseconds> }. if file does not exist or is unparseable json, treat it as { "processed_ids": [], "last_generated_at": 0 } and continue without crashing. processed_ids is the single source of truth for dedup; last_generated_at is only a performance hint.
fetch blog list. send GET https://agentlens-core.archlab.workers.dev/blogs?limit=100. expect response shape: { "items": [...], "total": <int>, "offset": <int>, "limit": <int> }. each item includes id, title, summary, period_label, job_type, source_id, model, occurred_at (iso string, may be null), generated_at (iso string, always present). on http non-200 or network error, report the error and stop without writing memory. nothing is recorded, so the next run retries cleanly.
filter new blogs. keep only items whose id is not in processed_ids. if no new blogs remain, report "no new blogs" and finish.
sort by recency. sort survivors in descending order by generated_at (parse iso 8601 to milliseconds). newest blogs are processed first because they are most timely. generated_at is always present; do not use occurred_at for sorting since it may be null.
fetch each blog body (newest first). for each new blog, send GET https://agentlens-core.archlab.workers.dev/blogs/{id}. expect response includes id, title, summary, body_markdown, references[] (each ref has type, title, url, html_url), generated_at, occurred_at. do not add ?lang= or other query parameters. on http non-200 or if body_markdown is empty or only whitespace, skip that blog entirely (do not return it, do not record it). it will retry next run. otherwise, proceed to step 6.
extract source link. from the blog's references[] array, pick the first match in this order: prefer html_url if present, else url. if neither is available in any reference, set source_link to null.
return new blogs to caller. for each successfully fetched blog (with non-empty body), return a struct: { "id": <id>, "title": <title>, "summary": <summary>, "body_markdown": <body_markdown>, "source_link": <source_link or null>, "occurred_at": <occurred_at or null>, "generated_at": <generated_at> }. stop here. do not write memory yet.
commit on caller confirmation. only after the caller confirms a blog was successfully consumed downstream, mark it processed: append its id to processed_ids and set last_generated_at = max(last_generated_at, <generated_at in ms>). write the updated memory object to MEMORY_PATH atomically (write to a temp file, then rename; atomic rename is preferred but plain overwrite is acceptable if unavailable). if write fails, report error; the blog will resurface next run.
if memory file missing or corrupt: treat as empty state { "processed_ids": [], "last_generated_at": 0 } and continue. do not crash.
if list endpoint returns non-200 or times out: report error and stop. do not write memory. next run will retry.
if a blog's body request returns non-200 or body is empty/whitespace: skip that blog only. do not return it. do not record it. continue with the next new blog. it will retry next run.
if caller confirms blog consumed successfully: commit it to memory (append id to processed_ids). never commit a blog the caller did not explicitly confirm.
if caller reports a blog failed downstream: do not commit it. it will resurface next run.
if no new blogs exist: return empty list and finish. do not write memory.
return a json array of new blog objects. each object must include:
id (string, required): unique blog identifier.title (string, required): blog title.summary (string, required): short summary.body_markdown (string, required): full markdown body, guaranteed non-empty and not whitespace-only.source_link (string or null): html or web url from references; null if none available.occurred_at (string or null): iso 8601 timestamp or null.generated_at (string, required): iso 8601 timestamp when blog was generated.memory file (MEMORY_PATH) contains:
{
"processed_ids": ["<id1>", "<id2>", ...],
"last_generated_at": <milliseconds>
}
no other files or side effects. no downstream processing, no vault saves, no scheduled runs.
processed_ids).credits: original skill by archlab-space. enriched and standardized for implexa quality.