Skip to content
Script Hooks

Script Hooks

kleidi-task supports project-level script hooks that run automatically on task lifecycle events. Hooks are shell commands executed asynchronously — they never block the task operation.

Configuration

Hooks are stored in .tasks/hooks.json per project:

{
  "hooks": [
    {
      "id": 1,
      "event": "task.complete",
      "command": "echo \"Task #$KVT_TASK_ID done: $KVT_TASK_TITLE\" >> ~/completed.log",
      "description": "Log completed tasks"
    },
    {
      "id": 2,
      "event": "task.create",
      "command": "./scripts/notify-slack.sh",
      "description": "Slack notification"
    }
  ]
}

You can edit this file directly, or manage hooks via the Settings UI.

Events

EventFires when
task.createA new task is created (CLI, MCP, UI, or API)
task.updateA task is modified (title, status, type, priority, etc.)
task.completeA task is marked as done
task.deleteA task is permanently deleted
task.archiveA completed task is archived

Multiple hooks can be registered for the same event. All matching hooks run in parallel.

Environment variables

Every hook receives task data as environment variables:

VariableExampleDescription
KVT_EVENTtask.completeThe event that triggered the hook
KVT_TASK_ID42Task ID
KVT_TASK_TITLEFix login bugTask title
KVT_TASK_TYPEbugTask type (task, bug, feature, hotfix, or custom)
KVT_TASK_STATUSdoneTask status (todo, doing, done)
KVT_TASK_PRIORITY5Task priority (0 = default)

Additionally, the full task JSON is piped to stdin for scripts that need more detail (description, metadata, timestamps, etc.):

#!/bin/bash
# Read full task JSON from stdin
TASK_JSON=$(cat)
DESCRIPTION=$(echo "$TASK_JSON" | jq -r '.description')

Behavior

  • Hooks run asynchronously in background goroutines — the task operation completes immediately regardless of hook execution
  • Each hook has a 30 second timeout — if the command doesn’t finish in time, it’s killed
  • Hook failures are logged but never prevent the task operation from succeeding
  • Hooks run with the project directory as working directory
  • Hooks inherit the parent process environment plus the KVT_* variables

Managing hooks

Via Settings UI

Navigate to your project’s Settings page and scroll to the Hooks section. You can:

  • Add a hook by selecting an event, entering a shell command, and clicking +
  • Remove a hook by clicking the trash icon

Via hooks.json

Edit .tasks/hooks.json directly. Assign a unique id to each hook. The file is read on every event, so changes take effect immediately — no restart needed.

Examples

Log all task completions

{
  "event": "task.complete",
  "command": "echo \"$(date -Iseconds) DONE #$KVT_TASK_ID $KVT_TASK_TITLE\" >> .tasks/activity.log"
}

Run tests when a bug is created

{
  "event": "task.create",
  "command": "[ \"$KVT_TASK_TYPE\" = \"bug\" ] && go test ./... > /dev/null 2>&1 || true"
}

Send a desktop notification

{
  "event": "task.complete",
  "command": "notify-send 'klt' \"Task #$KVT_TASK_ID completed: $KVT_TASK_TITLE\""
}

Post to a webhook

{
  "event": "task.create",
  "command": "cat | curl -s -X POST -H 'Content-Type: application/json' -d @- https://hooks.example.com/tasks"
}

This pipes the full task JSON from stdin directly to the webhook endpoint.