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.
Prerequisites
Section titled “Prerequisites”- gtdhelper installed and running
- A programming language of your choice (Bash, Python, Node, Go, etc.)
What is a source script?
Section titled “What is a source script?”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.
1. Create the sources directory
Section titled “1. Create the sources directory”mkdir -p ~/.gtdhelper/sources2. Write your script
Section titled “2. Write your script”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:
#!/usr/bin/env bashcat <<'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"}JSONAnd the same thing in Python:
#!/usr/bin/env python3import 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))3. Make it executable
Section titled “3. Make it executable”chmod +x ~/.gtdhelper/sources/reminders.sh4. Verify the output
Section titled “4. Verify the output”Run your script manually and check the output:
~/.gtdhelper/sources/reminders.shEach line must be a valid JSON object with all required fields. gtdhelper silently skips lines that fail parsing or miss required fields.
5. Wait for the next refresh
Section titled “5. Wait for the next refresh”gtdhelper discovers new scripts automatically on the next refresh cycle (every 5 minutes). You can also restart the app to trigger an immediate refresh.
Required fields
Section titled “Required fields”Every JSON object must include these fields:
| Field | Type | Example |
|---|---|---|
id | string | "jira:PROJ-42" |
title | string | "Fix login timeout" |
reference | string | "PROJ-42" |
project | string | "Backend" |
url | string | "https://jira.example.com/PROJ-42" |
created_at | string | "2025-01-15T10:00:00Z" |
updated_at | string | "2025-02-20T14:30:00Z" |
Optional fields: type (string), is_draft (boolean), is_bot (boolean).
- Keep IDs stable: Use the same
idfor 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.
Using environment variables from config
Section titled “Using environment variables from config”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.
See also
Section titled “See also”- Source script protocol — full technical specification
- Configure the GitHub source — example of a bundled source