Go library for Things 3 on macOS. Provides read-only database access and full URL Scheme support for creating and updating tasks.
- Unified client API with
NewClient()as single entry point - Read-only access to Things 3 SQLite database
- Fluent query builder with type-safe filters
- Full Things URL Scheme support
- Create todos, projects, and batch operations via URL
- Update existing items with automatic authentication
go get github.com/moond4rk/things3Note: Requires CGO enabled (uses
go-sqlite3). macOS only.
package main
import (
"context"
"fmt"
"log"
"github.com/moond4rk/things3"
)
func main() {
client, err := things3.NewClient()
if err != nil {
log.Fatal(err)
}
defer client.Close()
ctx := context.Background()
// Get today's tasks
today, _ := client.Today(ctx)
for _, task := range today {
fmt.Printf("- %s\n", task.Title)
}
// Create a new todo
client.AddTodo().
Title("Buy groceries").
Notes("Milk, eggs, bread").
When(things3.Today()).
Execute(ctx)
}client.Inbox(ctx) // Tasks in Inbox
client.Today(ctx) // Today's tasks
client.Upcoming(ctx) // Scheduled future tasks
client.Anytime(ctx) // Anytime tasks
client.Someday(ctx) // Someday tasks
client.Logbook(ctx) // Completed/canceled tasks
client.Trash(ctx) // Trashed tasks
client.Todos(ctx) // All incomplete to-dos
client.Projects(ctx) // All incomplete projects
client.Deadlines(ctx) // Tasks with deadlines
client.Search(ctx, "query") // Search tasks
client.CreatedWithin(ctx, DaysAgo(7)) // Tasks from last 7 days// Type-safe status filtering
tasks, _ := client.Tasks().
Type().Todo().
Status().Incomplete().
All(ctx)
// Date filtering
tasks, _ := client.Tasks().
StartDate().Future().
Deadline().OnOrBefore(time.Now().AddDate(0, 0, 7)).
All(ctx)
// Filter by area, project, or tag
tasks, _ := client.Tasks().
InArea("area-uuid").
InTag("work").
All(ctx)
// Get single task or count
task, _ := client.Tasks().WithUUID("task-uuid").First(ctx)
count, _ := client.Tasks().Status().Completed().Count(ctx)// Get all areas with their tasks
areas, _ := client.Areas().IncludeItems(true).All(ctx)
// Get all tags
tags, _ := client.Tags().All(ctx)client.AddTodo().
Title("Task title").
Notes("Task notes").
When(things3.Today()). // today's date
Deadline(time.Date(2024, 12, 31, 0, 0, 0, 0, time.Local)).
Tags("work", "urgent").
ChecklistItems("Step 1", "Step 2").
List("Project Name"). // or ListID("project-uuid")
Reveal(true).
Execute(ctx)client.AddProject().
Title("New Project").
Notes("Project description").
Area("Work"). // or AreaID("area-uuid")
Tags("important").
Deadline(time.Date(2024, 12, 31, 0, 0, 0, 0, time.Local)).
Todos("Task 1", "Task 2", "Task 3"). // child todos
Execute(ctx)Update operations automatically manage authentication tokens.
// Update a todo
client.UpdateTodo("todo-uuid").
Title("Updated title").
Completed(true).
AddTags("done").
Execute(ctx)
// Update a project
client.UpdateProject("project-uuid").
Notes("Updated notes").
Canceled(true).
Execute(ctx)client.Show(ctx, "item-uuid") // Show specific item
client.ShowList(ctx, things3.ListToday) // Show Today view
client.ShowSearch(ctx, "urgent tasks") // Show search results
// Complex navigation
client.ShowBuilder().
List(things3.ListInbox).
Filter("work", "urgent").
Execute(ctx)// Create multiple items at once
client.Batch().
AddTodo(func(t things3.BatchTodoConfigurator) {
t.Title("Task 1").Tags("work")
}).
AddTodo(func(t things3.BatchTodoConfigurator) {
t.Title("Task 2").When(things3.Today())
}).
AddProject(func(p things3.BatchProjectConfigurator) {
p.Title("New Project").Notes("Description")
}).
Reveal(true).
Execute(ctx)// Use custom database path
client, _ := things3.NewClient(
things3.WithDatabasePath("/path/to/main.sqlite"),
)
// Enable SQL logging for debugging
client, _ := things3.NewClient(
things3.WithPrintSQL(true),
)
// Control Things app focus behavior
client, _ := things3.NewClient(
things3.WithForeground(), // Bring Things to foreground (default for show)
things3.WithBackground(), // Run in background without stealing focus
)
// Preload authentication token
client, _ := things3.NewClient(
things3.WithPreloadToken(),
)The database path is resolved in order:
- Custom path via
WithDatabasePath() THINGSDBenvironment variable- Default:
~/Library/Group Containers/JLMPQHK86H.com.culturedcode.ThingsMac/Things Database.thingsdatabase/main.sqlite
// Task types
things3.TaskTypeTodo // 0 - To-do item
things3.TaskTypeProject // 1 - Project
things3.TaskTypeHeading // 2 - Heading
// Status
things3.StatusIncomplete // 0
things3.StatusCanceled // 2
things3.StatusCompleted // 3
// Start bucket
things3.StartInbox // 0
things3.StartAnytime // 1
things3.StartSomeday // 2
// Date helper functions
things3.Today() // returns today's date at midnight
things3.Tomorrow() // returns tomorrow's date at midnight
// Scheduling methods (called on builders)
.When(time.Time) // schedule for specific date
.WhenEvening() // schedule for this evening
.WhenAnytime() // schedule for anytime (no specific date)
.WhenSomeday() // schedule for someday (indefinite future)
// List IDs for navigation
things3.ListInbox
things3.ListToday
things3.ListUpcoming
things3.ListAnytime
things3.ListSomeday
things3.ListLogbook
things3.ListTrash- Things URL Scheme Documentation
- things.py - Python library (inspiration for database access patterns)
Apache License 2.0