Install
$ agentstack add skill-nweii-agent-stuff-things-app ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Things 3
Drive Things 3 (tasks, projects, areas) through whichever modality fits the environment. Prefer the things CLI whenever there's command-line access to Things — it reads and writes the local database directly, so it's fast and needs no network round-trip. Where there's no CLI access, the fallbacks are: a connected Things MCP (full read + write that syncs across devices, from anywhere), the things:/// URL scheme (any device with Things, write-only), and email-to-Things (unattended, Inbox-only capture).
A Things MCP — a self-hosted server, or an official one Cultured Code may ship — plays the role the CLI plays locally, but reachable from anywhere, so it's the first fallback when there's no command-line access. Match its tools by their things_* base names; the MCP host prepends its own namespace, so the full identifier varies by client.
Pick a modality first
| Modality | Read state? | Schedule & update? | Unattended? | From phone? | |----------|:-----------:|:------------------:|:-----------:|:-----------:| | CLI (things binary) | Yes | Yes | On the Mac only | No | | URL scheme (things:///) | No | Yes (update/json need auth token) | No — someone must open the URL | Yes | | Email-to-Things | No | No (Inbox capture only) | Yes | Yes (send an email) |
The real axis is attended vs unattended, not headless. A headless session someone is watching can still use the URL scheme — the agent prints the link, the user opens it. Only a routine with no one to act on its output is limited to email.
Decision rules:
- Command-line access to Things (a terminal with the local Things database) → use the CLI. The default: reads (Inbox, Today, search, projects, areas, tags) and the full write surface, all against the local DB.
- No CLI access, but a Things MCP is connected → use it. Some setups expose Things through an MCP server (self-hosted, or a future official one); its
things_*tools give full read + write that syncs across devices, with no Mac or device interaction required. The path for headless, remote, or cloud-routine environments. - No terminal, but the user can act on the output → build a URL. You construct the
things:///...link; the user (or an iOS Shortcut) opens it on a device with Things to execute. This holds even when the agent itself is headless — being headless doesn't rule out the URL scheme; only the absence of anyone to open the link does. The mobile and attended-remote write/schedule path. - Fully unattended (a remote/NAS routine with no one watching) → email-to-Things. The only path that needs neither a Mac nor a human. Lands in the Inbox only — no scheduling, no project placement, no updates. Good for dropping raw captures that get triaged later on desktop.
Credentials
Two values are specific to the user's Things setup:
- Auth token — needed only for commands that modify existing data (
update,update-project,jsonwith anupdateop); plainadd/add-project/add-areadon't. Things → Settings → General → Enable Things URLs → Manage. - Mail-to-Things address — the capture address for the email modality. Things → Settings → Mail to Things.
Resolve each at use time: env var first (THINGS_AUTH_TOKEN, THINGS_EMAIL), then the inline values below, then ask. Never invent one; never echo either into output, commits, or logs.
In a shell, these belong in the shell profile so an update can't overwrite them; the things CLI reads THINGS_AUTH_TOKEN on its own:
export THINGSAUTHTOKEN=… export THINGS_EMAIL=…@things.email
As a hosted skill (Claude.ai, ChatGPT) there's no shell and nothing overwrites the file — paste the values here instead:
THINGSAUTHTOKEN = THINGS_EMAIL =
1. CLI (things binary) — desktop, full read + write
Default modality on a Mac. Reads the local Things SQLite DB for queries; writes go through the URL scheme under the hood. Reading Things STATE is only possible here.
Setup (skip if which things returns a path): brew install ossianhempel/tap/things3-cli. The DB lives in the Things app sandbox; the terminal may need Full Disk Access to read it. Override the DB path with THINGSDB or --db=PATH if needed.
Read (queries the local DB)
- Built-in lists:
things inbox,things today,things upcoming,things anytime,things someday,things logbook,things deadlines,things trash - Time-scoped:
things logtoday(completed today),things createdtoday,things completed,things canceled - Overview:
things all(Inbox, Today, Upcoming, Anytime, Someday, Logbook, No Area, Areas in one shot) - Structure:
things projects,things areas,things tags - Search:
things search "query"— title/notes match; flags--status=incomplete|completed|canceled|any,--project=,--area=,--tag=,--limit=,--all(include completed/canceled/trashed),--json - List todos with filters:
things tasks --project "Travel",things tasks --area "Work",things tasks --tag focus --search "draft" - Inspect one item (exact match):
things show "Project Name",things show --id= --json - Most read commands accept
--jsonand--no-header. Get an item's ID from--jsonoutput orthings search "x" --json.
Write
Preview any write without executing: prepend --dry-run (prints the URL, does not open Things). Add --foreground to bring Things to the front.
- Add todo:
things add "Buy milk" --notes "2% + bananas" --when today --deadline 2026-07-01 --tags "health,phone" - Scheduling via
--when:today,tomorrow,evening(This Evening),anytime,someday, a date (2026-07-01), or a datetime ("2026-07-01 18:00"→ adds a reminder). - Into a list/heading:
--list "Travel" --heading "Before"(or--list-id=). - Checklist: repeat
--checklist-item "Passport" --checklist-item "Tickets"(max 100). - Multiple at once:
--titles "Milk,Beer,Cheese"(other flags apply to all). - From STDIN (first line = title, rest = notes):
printf 'Title\nNote line\n' | things add - - Update todo (needs auth token):
things update --id= "New title" --when today --append-notes "..." --add-tags "urgent" - Notes:
--notesreplaces;--prepend-notes/--append-notesadd. Same pattern for--checklist-item(replace) vs--prepend-checklist-item/--append-checklist-item. - Tags:
--tagsreplaces;--add-tagsadds. - Move:
--list "Travel" --heading "Before". Complete/cancel:--completed/--canceled. --duplicateupdates a copy and leaves the original untouched.- Areas & projects:
things add-area "Health";things add-project "New Site" --area "Work" --notes "..." --todo "Task A" --todo "Task B" things update-project --id= --auth-token=$THINGS_AUTH_TOKEN "New Title" --when tomorrow --add-tags Important(same notes/tags/scheduling flags asupdate, plus--areato move,--todoto append todos).things update-area --id= --add-tags Focus(AppleScript-based; only updates tags; may prompt for automation permission).
Notes on the CLI write surface: update/update-project/update-area only modify existing items. Repeating todos/projects reject --when, --deadline, --completed, --canceled. There is no real "delete" — complete or cancel instead.
2. URL scheme (things:///...) — any device, write-only
Use when building something to open on an iPhone (or any device without terminal access). You construct the URL; it executes only when opened on a device that has Things installed. Cannot read state.
Form: things:///command?param1=value1¶m2=value2. All values are percent-encoded (space → %20, newline → %0a, comma stays literal in tag/list lists). add needs no token; update, update-project, and json-with-changes need auth-token=$THINGS_AUTH_TOKEN.
Quick examples:
- Capture to Inbox:
things:///add?title=Buy%20milk¬es=Low%20fat - Schedule with a tag:
things:///add?title=Call%20doctor&when=next%20monday&tags=Errand(natural-language dates must be English) - Into a project with a checklist:
things:///add?title=Trip%20prep&list=Travel&checklist-items=Passport%0aTickets - Several todos:
things:///add?titles=Milk%0aBeer%0aCheese&list=Shopping - Reschedule an existing todo:
things:///update?id=&auth-token=$THINGS_AUTH_TOKEN&when=today - Show a built-in list:
things:///show?id=today(ids:inbox,today,anytime,upcoming,someday,logbook,deadlines, etc.)
The json batch command builds a whole project — headings, todos, notes, checklists — in one URL. For the full per-parameter reference, the json schema, date-string formats, and show/search ids, read references/url-scheme.md before constructing anything beyond a simple add.
When the agent is on the Mac, prefer generating these URLs via things --dry-run ..., which prints a correctly-encoded URL you can hand off. Otherwise encode by hand or with a small script.
3. Email-to-Things — headless capture, Inbox only
The only modality that needs neither a Mac nor any device interaction, so it is the one a remote/NAS routine uses. Send an email to the mail-to-Things address ($THINGS_EMAIL):
- Subject → to-do title. Body → notes.
- Lands in the Inbox only. No scheduling, no tags, no project/area placement, no updates. Triage happens later on desktop.
Use it for unattended capture (a cron job dropping raw items); reach for the CLI or URL scheme whenever scheduling or placement matters.
Gotchas
- CLI is macOS-only. It needs the Things app and its local DB present.
- Reading state (Inbox/Today/search/projects/areas/tags) only works via the CLI against the local DB. URL scheme and email cannot read anything back.
update/update-project/json-with-changes require the auth token. Plainadddoes not.- A
things:///URL does nothing until opened on a device with Things. Constructing it is not executing it. - Email is Inbox-only — no scheduling, no placement, no updates.
- Preview CLI writes with
--dry-runbefore running anything destructive or unfamiliar. - Natural-language dates (
next monday,in 3 days) must be in English, regardless of device language.yyyy-mm-ddandtoday/tomorrowalways work. - No true delete — complete (
--completed) or cancel (--canceled) instead. The CLI's--when=somedayand--later(This Evening) are the scheduling aliases. - Renaming/deleting areas and deleting projects aren't in the
thingsCLI or URL scheme — butosascript(AppleScript) does all of them.update-areaexposes tags only, and there's nodelete-area/delete-project, so via those two modalities you can only create areas/projects and reassign todos (update --list "..."). The fourth path is AppleScript against the scriptable Things app — use it on the Mac for exactly these structural edits: - Rename an area:
osascript -e 'tell application "Things3" to set name of area "Hobby" to "Side"' - Delete an area:
osascript -e 'tell application "Things3" to delete area "Strata"' - Delete a project (its to-dos go with it):
osascript -e 'tell application "Things3" to delete project "Old Project"' - List/identify first:
osascript -e 'tell application "Things3" to get name of every area'
Deletes move items to Things' Trash (recoverable until emptied), not a hard purge. The first osascript call may trigger a one-time macOS automation-permission prompt. This is Mac-only and needs no auth token. (The CLI's update-project --id= --auth-token=$THINGS_AUTH_TOKEN --canceled is a softer alternative that sends a project to the Logbook instead of deleting.)
- Clearing a value via URL: include the param with an empty value, e.g.
&deadline=removes a deadline.
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: nweii
- Source: nweii/agent-stuff
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.