Skip to content

How to write a custom source script

This guide walks you through creating your own source script so gtdhelper can surface items from any service you use.

  • gtdhelper installed and running
  • A programming language of your choice (Bash, Python, Node, Go, etc.)

A source script is any executable file that prints task data as JSON Lines to stdout. gtdhelper discovers it, runs it every 5 minutes, and displays the results in the overlay.

Terminal window
mkdir -p ~/.gtdhelper/sources

Create a file in ~/.gtdhelper/sources/. The filename stem becomes the source name (e.g. jira.py produces tasks with source jira).

Here is a minimal example in Bash:

~/.gtdhelper/sources/reminders.sh
#!/usr/bin/env bash
cat <<'JSON'
{"id":"reminder:1","title":"Review weekly report","reference":"W12","project":"Team","url":"https://example.com/reports","created_at":"2025-03-10T09:00:00Z","updated_at":"2025-03-10T09:00:00Z"}
{"id":"reminder:2","title":"Update project status","reference":"P3","project":"Team","url":"https://example.com/status","created_at":"2025-03-10T10:00:00Z","updated_at":"2025-03-10T10:00:00Z"}
JSON

And the same thing in Python:

~/.gtdhelper/sources/reminders.py
#!/usr/bin/env python3
import json, sys
tasks = [
{
"id": "reminder:1",
"title": "Review weekly report",
"reference": "W12",
"project": "Team",
"url": "https://example.com/reports",
"created_at": "2025-03-10T09:00:00Z",
"updated_at": "2025-03-10T09:00:00Z",
},
]
for task in tasks:
print(json.dumps(task))
Terminal window
chmod +x ~/.gtdhelper/sources/reminders.sh

Run your script manually and check the output:

Terminal window
~/.gtdhelper/sources/reminders.sh

Each line must be a valid JSON object with all required fields. gtdhelper silently skips lines that fail parsing or miss required fields.

gtdhelper discovers new scripts automatically on the next refresh cycle (every 5 minutes). You can also restart the app to trigger an immediate refresh.

Every JSON object must include these fields:

FieldTypeExample
idstring"jira:PROJ-42"
titlestring"Fix login timeout"
referencestring"PROJ-42"
projectstring"Backend"
urlstring"https://jira.example.com/PROJ-42"
created_atstring"2025-01-15T10:00:00Z"
updated_atstring"2025-02-20T14:30:00Z"

Optional fields: type (string), is_draft (boolean), is_bot (boolean).

  • Keep IDs stable: Use the same id for the same logical task across runs. This lets gtdhelper preserve snooze and archive state.
  • Use stderr for logging: Write diagnostics to stderr. Only stdout is parsed as task data.
  • Stay under 30 seconds: Scripts that don’t exit within 30 seconds are killed and disabled until the file is modified.
  • Non-zero exit disables the script: If your script fails, gtdhelper stops running it. Edit the file (changing its mtime) to re-enable it on the next cycle.
  • Use a config file: If your script needs configuration, you can read a TOML file from ~/.gtdhelper/ and parse it yourself.

If you want gtdhelper to set environment variables for your script, create a TOML config file with the same stem as your script (e.g. ~/.gtdhelper/jira.toml for jira.py) and add an [env] section:

[env]
JIRA_BASE_URL = "https://jira.example.com"
JIRA_PROJECT = "PROJ"

Your script can then read these as standard environment variables. This works for both user scripts and bundled scripts.