Skip to content

Source Script Protocol

This document defines the contract between gtdhelper and external source scripts.

Overview

Source scripts are executable files in ~/.gtdhelper/sources/ that produce task data. gtdhelper discovers and runs them on each refresh cycle, parsing their stdout as JSON Lines.

Discovery

Any executable file in ~/.gtdhelper/sources/ is treated as a source script. The source name is derived from the filename stem (e.g. github.py -> github, jira.sh -> jira, email-checker -> email-checker).

Subdirectories and non-executable files are ignored.

Execution Mode

One-off: gtdhelper spawns the script, reads stdout until EOF, then collects the exit code. The script runs once per refresh cycle (every 5 minutes, or on manual refresh).

Scripts have a 30-second timeout. If a script doesn't exit within 30 seconds, it is killed (SIGKILL) and treated as a failure.

Output Format

Scripts write JSON Lines to stdout — one JSON object per line:

{"id":"github:org/repo/#42","title":"Fix login flow","reference":"#42","project":"org/repo","url":"https://github.com/org/repo/pull/42","type":"pull_request","created_at":"2025-01-15T10:00:00Z","updated_at":"2025-02-20T14:30:00Z"}

Required Fields

Field Type Description
id string Globally unique task identifier. Must be stable across runs for the same logical task (e.g. github:org/repo/#42, trello:abc123).
title string Human-readable task title displayed in the overlay.
reference string Short reference shown alongside the title (e.g. #42, JIRA-123).
project string Project or group name used for grouping in the overlay (e.g. repository name, board name).
url string URL opened in the default browser when the user clicks the task.
created_at string ISO 8601 timestamp of when the task was created.
updated_at string ISO 8601 timestamp of the last activity on the task. Used for sorting.

Optional Fields

Field Type Description
type string Task type for categorization (e.g. pull_request, issue, email)
is_draft boolean Whether the task is a draft (e.g. draft PR). Affects icon styling.
is_bot boolean Whether the task author is a bot. Used for filtering.

Notes

  • The source field is set by gtdhelper to the script's filename stem. Any source value in the JSON is ignored.
  • Lines that fail JSON parsing or lack required fields are silently skipped (with a warning in gtdhelper logs).
  • Empty lines are skipped.
  • The id must be stable across runs for the same logical task, so gtdhelper can track snooze/archive state.

stdio Contract

Stream Behavior
stdin Piped from parent but unused. Reserved for future commands (see long-running mode).
stdout Captured by gtdhelper. Write JSON Lines here.
stderr Inherited from parent process. Use for logging/diagnostics.

Signals

Signal Behavior
SIGINT Graceful shutdown. Sent on app exit. Scripts should clean up and exit promptly.

Error Handling

  • Non-zero exit code: Script is removed from the active pool (disabled). Its tasks from the previous run remain in the task list until the next successful run.
  • Reintegration: If a disabled script's file is modified on disk (mtime change), gtdhelper retries it on the next refresh cycle.
  • Timeout: Treated the same as a non-zero exit (disabled, can be reintegrated).

Two-tier Script Discovery

gtdhelper combines scripts from two locations:

  1. User scripts (~/.gtdhelper/sources/): Always included. Any executable file here runs on every refresh.
  2. Bundled scripts (app resource scripts/sources/): Only activated when a matching config file <stem>.toml exists in ~/.gtdhelper/. For example, the bundled github.py runs only if ~/.gtdhelper/github.toml exists.

If a user script has the same stem as a bundled script (e.g. github.sh vs github.py), the user script takes priority and the bundled one is skipped.

No automatic bootstrap — the user must create the config file to activate a bundled script.

Bundled Script Runtime Environment

Bundled Python (.py) scripts run inside an auto-managed virtualenv. The venv is transparent to the script — gtdhelper handles creation and dependency installation automatically.

  • Venv location: ~/.gtdhelper/env/
  • Creation: uv venv --python >=3.12 on first refresh (requires uv)
  • Dependencies: Synced from scripts/requirements.lock via uv pip sync. This lock file is generated from scripts/requirements.txt (the human-edited input) using uv pip compile --generate-hashes and contains pinned versions with SHA256 hashes. Only re-synced when the lock file's SHA256 hash changes.
  • Interpreter injection: Bundled .py scripts are executed as <venv>/bin/python <script> instead of running the script directly.
  • User scripts are NOT affected: Scripts in ~/.gtdhelper/sources/ always run directly (as executable files). They manage their own interpreters and dependencies.
  • Graceful degradation: If uv is not installed, bundled Python scripts run without a venv. They will likely fail on import if they depend on packages from requirements.lock.

Bundled Script: github.py

The default github.py script ships with gtdhelper at scripts/sources/github.py.

Configuration

github.py reads its repo list from ~/.gtdhelper/github.toml:

[[repos]]
name = "org/repo"
types = ["pull_request", "issue"]

[[repos]]
name = "other/repo"
types = ["pull_request"]
  • name: GitHub repository in owner/repo format
  • types: list of "pull_request" and/or "issue"
  • If types is omitted, defaults to ["pull_request"]

Bundled Script: trello.py

The trello.py script ships with gtdhelper at scripts/sources/trello.py.

Configuration

trello.py reads credentials from ~/.gtdhelper/trello.toml:

api_key = "your-api-key"
token = "your-token"
member = "your-trello-username"
boards = ["Product Board", "Engineering"]
  • api_key: Trello API key (get one at https://trello.com/power-ups/admin)
  • token: Trello API token (generated alongside the API key)
  • member: Trello username or member ID — cards assigned to this member are fetched
  • boards: list of board names to include (exact match, case-sensitive). If omitted or empty, no cards are shown — you must opt in per board.

Output

Each Trello card produces a task with:

  • id: trello:<card-id>
  • title: card name
  • reference: #<card-short-id>
  • project: board name (fetched and cached per unique board)
  • url: card short URL
  • type: card
  • created_at: derived from the card's ObjectId (embedded creation timestamp)
  • updated_at: dateLastActivity from the Trello API