AI-first operational bridge for Things 3 on macOS. It gives an AI agent a constrained CLI over Things' existing automation surfaces: AppleScript, the official Things URL Scheme, and a narrowly scoped internal SQLite restore harness used only for restore workflows.
Independent project. Not affiliated with Cultured Code.
Documentation split:
README.md: user-facing guide for the human who asks an AI coding agent such as Codex, Claude Code, Open Code, or similar tools to manage Things through the CLI.AGENTS.md: operator contract for the AI agent that actually runs the CLI.
- Why this exists
- What it does
- Opportunity
- Project status
- Installation
- Prerequisites
- Interaction model
- Design constraints
- Using with Codex, Claude Code, Open Code, etc.
- Domain glossary
- Known limits
- Backup policy
- Common workflows
- Troubleshooting
- Security warning read before use
- CI and coverage
Things already exposes useful automation surfaces, but AI agents benefit from a narrower operational layer with explicit commands, machine-readable outputs, and safer recovery workflows than ad hoc scripts.
This project is built for the practical path:
Human intent > AI agent > things-agent CLI > Things 3
Normal reads and writes stay on top of AppleScript and the official Things URL Scheme. Internal SQLite work is reserved for restore only.
- reads Things state in text or JSON forms that an AI agent can use reliably
- creates, edits, moves, completes, and deletes areas, projects, tasks, checklist items, and child tasks
- adds backup, restore, preflight, and verification flows for higher-risk operations
- keeps direct database access out of normal agent-authored operations
Things already exposes solid official local automation surfaces through AppleScript, the Things URL Scheme, and Apple Shortcuts. This project explores what becomes possible when those surfaces are wrapped in a stricter operational layer for AI agents.
The idea is not to criticize Things or bypass the app's model. The opportunity is to show that:
- Things is already a strong local automation base for agent-driven workflows
- a constrained CLI can make those workflows more explicit, verifiable, and safer to operate
- if there were ever an official CLI with full app coverage, it could become a clean foundation for an MCP server or other agent-facing runtimes
That would keep the center of gravity where it belongs: on official app behavior, official capabilities, and a safer contract between human intent, automation, and Things itself.
- This project is primarily meant to be consumed by an AI agent, not used as a polished human-first CLI.
- It is already useful in practice for organizing Things through AI, including voice-driven workflows.
- It includes safety rails, backup/restore workflows, and verification steps, but it is not fully hardened.
- The user still takes real risk and will likely need to grant system permissions for the setup to work reliably.
Install from tags (recommended):
go install github.com/alnah/things-agent@latestInstall the unstable version (latest main):
go install github.com/alnah/things-agent@latestVersion behavior:
- builds installed from
@mainreportdev(ordev (<commit>)when VCS build info is available) - builds installed from a release tag report that tagged version
- release archives inject the tagged version at build time
Releases are built from v* tags with GoReleaser.
- macOS
- Things app installed
osascript
Agents must never touch the Things database directly.
Some native checklist operations (URL scheme update) require a Things auth token (THINGS_AUTH_TOKEN or --auth-token).
Things uses both user areas and built-in lists (Inbox, Today, Logbook, etc.); this CLI uses area for the area entity and keeps list only for generic Things list filters and official URL parameters.
For token, permissions, and list-locale errors, see Troubleshooting.
The primary model is:
Human > AI agent > things-agent CLI > Things 3
This repository is not trying to turn Things into a general-purpose shell app for direct human use. The main goal is to give an AI agent a constrained operational bridge to read and change Things state with clearer semantics and some safety controls.
In practice, this means:
- the human expresses intent in natural language
- the AI agent translates that intent into
things-agentcommands - the CLI uses AppleScript and the Things URL Scheme to operate Things 3
- backup, restore, and verification flows try to reduce risk, but do not remove it
- normal operations use Things automation surfaces rather than direct database access
- restore is the only place where the project uses a narrowly scoped internal SQLite step
- the CLI is designed to be explicit enough for an AI agent to execute and verify safely
- permissions, local machine policy, and user trust still matter
Use this checklist before asking an AI agent to manage Things for you:
git clone https://github.com/alnah/things-agent.git
cd things-agent
go install github.com/alnah/things-agent@main
# optional runtime env (example for French Things setup)
export THINGS_DEFAULT_LIST="À classer"
# required for URL update/checklist operations
export THINGS_AUTH_TOKEN="<your-things-token>"
# keep one instruction source for Codex, Claude Code, Open Code, etc.
ln -sf AGENTS.md CLAUDE.md
# quick health check
things-agent version
things-agent --helpYou usually do not need to drive the CLI manually beyond setup, debugging, or recovery.
The normal path is to let the AI agent read AGENTS.md, inspect things-agent --help, and then operate Things through the CLI.
Use these terms when talking to the agent. They match the CLI's high-level model from things-agent --help.
| Term | Meaning | Language alternatives |
|---|---|---|
area |
A user-managed Things area. High-level CRUD and move commands use area. |
domaine, aire, área, Bereich |
list |
A generic Things list name used for read filters and the official URL Scheme. This includes built-in lists such as Inbox, Today, Logbook, and Archive, plus area names where the Things API expects a generic list selector. |
liste, lista, Liste |
project |
A Things project. | projet, proyecto, projeto, Projekt, progetto |
task |
A top-level to-do. | tâche, tarea, tarefa, Aufgabe, attività |
checklist item |
A lightweight native checklist line inside a task. | élément de checklist, elemento de checklist, item de checklist, Checklistenpunkt |
child task |
A structured child to-do under a project. | sous-tâche, subtarea, subtarefa, Unteraufgabe, sottoattività |
When in doubt:
- say
area, notlist, for user-managed areas - say
listonly for built-in Things lists or generic list filters - say
checklist itemfor native checklist lines inside a task - say
child taskfor structured project children - the translated terms above are user-facing equivalents for talking to the agent, not guaranteed official Things UI labels in every locale
- Reorder support is partial and uses unstable Things internals.
reorder-area-itemscannot freely interleave projects and tasks inside an area; Things still keeps projects before tasks.- No stable public backend exists yet for checklist-item reorder, heading reorder, or sidebar area reorder.
- Headings are still manual.
- Runtime validation showed that
things:///jsonproject updates did not create visible headings, private JSON read paths did not expose headings, andmove-task --to-headingor--to-heading-idmay returnokeven when nothing changes. - Create headings in Things, then return to the CLI for everything else.
- Recurring tasks are not automated yet.
- Public AppleScript and URL Scheme docs do not expose a reliable recurrence create/update path.
- Create or edit recurring tasks manually in Things.
- Restore works when Things stays offline for the first launch after the restore.
- In practice, use the CLI's offline restore mode so the restored snapshot is not immediately overwritten by sync state.
- After restore, verify the data first. If you use Things Cloud, re-enable it manually only after verification.
The CLI uses one backup artifact format: an official Things package snapshot stored in ThingsData-*/Backups. The distinction is in the backup kind, not in the snapshot format.
session: created bythings-agent session-start; the automatic checkpoint at the start of an agent session.explicit: created bythings-agent backup; the main user-facing restore checkpoint.safety: created automatically before critical operations such asrestore; mainly for rollback.
Each snapshot also gets a small JSON index file (timestamp, kind, created_at, source_command, reason, complete, files); safety backups are for rollback/debugging, retention is shared across all kinds (50 snapshots total), and if Things was already open when a backup starts, the CLI reopens it afterward.
Ask the AI agent in natural language, for example:
- "Show me what is in Today and Inbox."
- "Create a project in area Codebases and add three tasks."
- "Back up Things, then reorganize these tasks."
- "Restore the snapshot from the last timestamp."
- "Search for tasks about invoices."
If AppleScript calls fail or the CLI cannot control Things, validate the environment first:
osascript -e 'tell application id "com.culturedcode.ThingsMac" to get name'
things-agent version
things-agent listsThen re-check macOS privacy settings for your terminal/agent app:
System Settings > Privacy & Security > Automation(allow access toThings)System Settings > Privacy & Security > Full Disk Access(if your setup requires it)
In practice, agents often get better results when their tool runner is configured with broader filesystem, shell, or bypass-style permission modes. That also gives the agent broader access to your system, so keep an eye on what it is doing and review risky actions carefully.
Native checklist updates require a valid token (add-checklist-item, url update, add-task --checklist-items).
If you see missing or invalid token errors:
export THINGS_AUTH_TOKEN="<your-things-token>"
things-agent add-task --name "Token check" --area "Inbox" --checklist-items "one, two"You can also pass --auth-token explicitly per command.
Things list names are localized (Inbox, À classer, etc.). If --list looks wrong or returns no results:
things-agent lists
export THINGS_DEFAULT_LIST="À classer"
things-agent tasks --list "À classer"Use this project at your own risk.
- To be useful, AI agents often need broad system permissions.
- Agents can bypass expectations or instructions if they are sufficiently capable.
- This repository includes safety rails, but it does not provide a full safety guarantee for end users.
- You remain fully responsible for what the agent executes on your machine.
- The user may need to grant Automation, filesystem, and related permissions before the bridge works reliably.
- Deletion remains available item by item (
delete-task,delete-project,delete-area). - The agent starts each session with
session-start, and the user can ask for extra backups before heavier or harder-to-reverse operations. - Backups are rotated and capped at 50 snapshots; on the author's machine, the full
Backups/folder uses roughly316 MB, but this varies with database size. AGENTS.mdforbids direct SQLite access.- Any bypass of CLI constraints requires an explicit user decision.
Do not expose your Things auth token to your AI provider unless strictly necessary. Prefer resolving it locally from a secret store at runtime instead of hardcoding it in shell history, scripts, or repo files.
Get the token on macOS:
- Open
Things 3. - Go to
Things > Settings > General. - In the
Things URLssection, open token management and copy the auth token. - Export it in your shell:
export THINGS_AUTH_TOKEN="<your-token>"A better approach is to keep the token in a local secret manager and resolve it only at runtime on your Mac.
Examples:
- macOS with
pass:
export THINGS_AUTH_TOKEN="$(pass show things/auth-token)"If you use zsh, you can put that line in ~/.zshrc so every new terminal session gets the token automatically from your local password store.
This is often the simplest setup for individual use on macOS:
- Store the token once in
pass:
pass insert things/auth-token- Add this line to
~/.zshrc:
export THINGS_AUTH_TOKEN="$(pass show things/auth-token)"- Reload your shell:
source ~/.zshrcThis keeps the token out of the repository and out of most day-to-day shell history, while still making it available to things-agent.
Other macOS-local secret managers such as Keychain, 1Password CLI, or Bitwarden CLI work too, as long as the token is resolved locally on the machine at runtime.
This reduces accidental exposure, but it is not a perfect guarantee. If an agent is allowed and motivated to exfiltrate secrets, it may still leak the token.
ci.ymlruns unit tests on each push/PR.- It also runs mocked integration tests (
-tags=integration) without direct DB access. - Coverage is uploaded to Codecov from CI.
