skip to content

tnotes for AI agents

Use tnotes as a notes backend for AI agents: a headless CLI with JSON output, stable note ids, links and backlinks, no daemon and no API. Recipes for Claude Code, Codex, cron and shell scripts.

Why a CLI and not an MCP server

Every coding agent can already run shell commands. A CLI that prints JSON is the smallest possible integration surface: nothing to install on the agent side, nothing to keep running, nothing to authenticate. The vault is a folder of Markdown files, so an agent can also just cat and grep it — tnotes adds the index: titles, tags, links, backlinks, timestamps and fuzzy search.

The five commands an agent needs

$ tnotes ls --json                                  # every note: id, path, title, folder, tags, links, backlinks, created, modified, preview$ tnotes search "standup" --json | jq '.[0].path'   # best match first$ tnotes cat vault/weekly-plan                      # full text (--json adds "text")$ echo "- [ ] follow up with Ann" | tnotes new "Meeting notes" --tag work --stdin$ tnotes append meeting-notes "- [ ] send the slides"

A note is addressed by its id (root/sub/stem, as printed by ls), a path, or a unique file stem. The id is the same in every command. --dir pins the vault for one invocation, so an agent never depends on the user's config.

Walking the note graph

links lists the [[titles]] a note references; backlinks lists the notes that reference it. A "what's related to X" prompt is one ls --json plus a jq filter:

$ tnotes ls --json | jq '.[] | select(.links[]? == "Weekly plan" or .backlinks[]? == "Weekly plan") | .title'"tnotes roadmap""Meeting notes"

What the agent cannot break

Claude Code / Codex: a snippet for AGENTS.md

Drop this into the project's AGENTS.md or CLAUDE.md (adjust the path):

## NotesMy notes are a tnotes vault at ~/Documents/notes. Use the CLI, never edit files directly:- list:   tnotes ls --json [--tag t] [--folder f] [--limit n]- search: tnotes search "<query>" --json- read:   tnotes cat <id>- create: tnotes new "<Title>" [--tag t] [--folder f] --stdin   (body on stdin)- append: tnotes append <id> "<line>"   or   --stdin- rewrite: tnotes write <id> --stdinIds look like vault/sub/stem and come from ls/search. Link notes with [[Title]]; tag with #tag in the text.

Daily note from cron

# crontab: create today's note at 08:00 if it does not exist0 8 * * * tnotes new "$(date +%F)" --tag daily --folder journal --stdin <<< "- [ ] " 2>/dev/null || true

Handy jq one-liners

$ tnotes ls --json | jq -r '.[] | select(.tags | index("work")) | .id'      # ids tagged #work$ tnotes ls --json | jq -r 'sort_by(.modified) | reverse | .[0:5][] | .title'   # five most recent titles$ tnotes ls --json | jq -r '.[] | select(.preview | test("\\[ \\]")) | .title'   # notes with open tasks$ tnotes ls --json | jq '[.[] | .tags[]] | group_by(.) | map({(.[0]): length}) | add'   # tag counts

See the CLI reference for every flag, and sync and encryption for running agents against a synced vault.