---
title: "Organize your daily.dev bookmarks"
url: https://docs.daily.dev/organize-your-bookmarks/
description: "Add local bookmark folders to the daily.dev CLI: group saved posts by name in a store you own, and swap the JSON file for a database whenever you outgrow it."
lastUpdated: "2026-09-14T09:00:00+03:00"
llmsTxt: https://docs.daily.dev/llms.txt
---

Be honest: how many bookmarks do you have right now that you will never read?

Bookmarking is the easiest promise in software. One click, zero commitment. Which is why every developer's reading list turns into a graveyard: dozens of saved posts, half of them about a framework you no longer use, sorted by nothing except the order you were procrastinating in.

Saving liberally is fine. Future you might need it. What's missing is structure. The four posts you kept because you're finally going to learn async Rust belong together, and a flat list has no way to say so.

So here we teach the [CLI](https://docs.daily.dev/daily-dev-from-your-terminal/) to group things: a `local-bookmarks` command that keeps folders of daily.dev posts on your own machine. It follows the [setup](https://docs.daily.dev/welcome-to-the-daily-dev-api/), [terminal CLI](https://docs.daily.dev/daily-dev-from-your-terminal/) and [morning briefing](https://docs.daily.dev/build-your-own-morning-briefing/) pages, and the finished code is in [dailydotdev/dd-cli-example](https://github.com/dailydotdev/dd-cli-example).

## A folder is a name and some ids

Every command we've built prints a post id next to its title, so `feed`, `popular`, and `discussed` already give you the only identifier this feature needs. A group is a name and a list of ids, which means the whole thing fits in a JSON file:

```json
{
  "Rust async deep-dive": ["abc123", "def456"],
  "Postgres 18 migration": ["ghi789"]
}
```

That's a `Record<string, string[]>`, which zod describes in one line. Add it to `src/schema.ts` next to the others:

```ts
export const localBookmarksSchema = z.record(
  z.string(),
  z.array(z.string()),
);

export type LocalBookmarks = z.infer<typeof localBookmarksSchema>;
```

Then a new `src/store.ts`, the only file that knows where local bookmarks are kept. They go in your home directory rather than the working directory, so your groups don't disappear when you run the CLI from a different folder:

```ts
const STORE =
  process.env.DAILYDEV_LOCAL_BOOKMARKS ??
  join(homedir(), '.dailydev', 'local-bookmarks.json');

export const readLocalBookmarks = async (): Promise<LocalBookmarks> => {
  const raw = await readFile(STORE, 'utf8').catch(
    (error: NodeJS.ErrnoException) => {
      if (error.code === 'ENOENT') {
        return null;
      }

      throw error;
    },
  );

  return raw === null ? {} : localBookmarksSchema.parse(JSON.parse(raw));
};

export const writeLocalBookmarks = async (folders: LocalBookmarks) => {
  await mkdir(dirname(STORE), { recursive: true });
  await writeFile(STORE, `${JSON.stringify(folders, null, 2)}\n`);
};
```

A missing file is not an error. It means you haven't filed anything yet, so you get an empty set of groups. Anything else, like a permissions problem or a corrupt file, still throws, because starting from scratch without telling you would lose your groups.

Filing is a read, a check, and a write:

```ts
export const fileBookmark = async (postId: string, folder: string) => {
  const folders = await readLocalBookmarks();
  const current = folders[folder] ?? [];

  if (current.includes(postId)) {
    return false;
  }

  await writeLocalBookmarks({ ...folders, [folder]: [...current, postId] });

  return true;
};
```

The `false` lets the command say "already filed" instead of pretending it did something. Unfiling is the mirror image, with one extra decision: with no folder given it removes the id from every group, and it drops any group left empty rather than keeping a dead name around.

## Wiring up the commands

This is the first command with more than one verb, which needs one addition to the pattern from the [CLI page](https://docs.daily.dev/daily-dev-from-your-terminal/): a second positional. `local-bookmarks` is the command, and `list`, `folders`, `add`, `move`, or `remove` is the action after it. Leave it out and you get `list`.

The args union then validates the combinations, because `add` without a post id and `add` without a folder should both fail before anything touches the store:

```bash
pnpm dailydev local-bookmarks add abc123 -f "Rust async deep-dive"
pnpm dailydev local-bookmarks folders     # groups and their sizes
pnpm dailydev local-bookmarks             # groups with their post ids
pnpm dailydev local-bookmarks move abc123 -f "Postgres 18 migration"
pnpm dailydev local-bookmarks remove abc123   # -f to leave one group only
```

```
$ pnpm dailydev local-bookmarks folders
Rust async deep-dive	4 saved
Postgres 18 migration	3 saved
```

Add `--json` to any of them and you get the store as it sits on disk, which is what your agent wants when you ask it to reorganize things for you.

Because the file holds daily.dev post ids rather than copies, it never competes with your actual library. Delete it and you lose the grouping, not the bookmarks.

## The store is swappable

Everything goes through `readLocalBookmarks` and `writeLocalBookmarks`, so a JSON file in `~/.dailydev` is only the default. Point those two functions at Firebase or Supabase and your groups follow you between laptop and phone. Point them at SQLite or Postgres if you'd rather query them, or at a KV store on the edge if the CLI runs there too. The commands don't change, only those two functions do.

Keep that boundary in place as you extend this. The `local-bookmarks` actions know that a group is a name and some ids. They don't need to know it's a file.

## If you have Plus

A file on your laptop has one obvious limit: it's on your laptop. The groups you build in the terminal aren't there when you're reading on your phone on the train, or in the browser extension on your work machine.

daily.dev Plus includes bookmark folders, which solve exactly that. Your folders live with your account, so they show up everywhere you read daily.dev.

If you have Plus, you don't need the local store at all. The CLI has a `bookmarks` command that takes the same actions against your account, so the difference is one word:

```bash
pnpm dailydev bookmarks add abc123 -f "Rust async deep-dive"
```

Same actions, same flags, and the folders are no longer tied to one machine.

## What's next

Last in this group: we stop treating daily.dev as your reading list and start treating it as your agent's knowledge source, replacing generic web search with articles developers have read and argued about.

Next: **[Point your agent at daily.dev instead of web search](https://docs.daily.dev/stop-web-searching/)**, pointing your agent at community-vetted articles instead of whatever won the SEO game this week.

- **Previous**: [Build your own morning briefing](https://docs.daily.dev/build-your-own-morning-briefing/)
- **API reference**: [api.daily.dev/public/v1/docs/json](https://api.daily.dev/public/v1/docs/json)
- **Example repo**: [github.com/dailydotdev/dd-cli-example](https://github.com/dailydotdev/dd-cli-example)