Adding a Service in 20 Lines
The service architecture is designed to be extended. A new service is an executable script in services/<category>/. The ww dispatcher discovers it by scanning for executables. No registration step. No config update. Write the script, make it executable, it’s available as ww <category>.
Here’s a minimal service in full:
#!/usr/bin/env bash
set -euo pipefail
source "$WORKWARRIOR_BASE/lib/logging.sh"
USAGE="ww myservice — one-line description
Usage: ww myservice <list|info|add>
"
case "${1:-}" in
--help|-h)
echo "$USAGE"
exit 0
;;
list)
# implementation
log_info "Listing things..."
;;
info)
[[ -z "${2:-}" ]] && { log_error "info requires a name"; exit 1; }
log_info "Info for: $2"
;;
*)
log_error "Unknown subcommand: '${1:-}'"
echo "$USAGE"
exit 1
;;
esac
That’s the contract. Five requirements:
#!/usr/bin/env bash+set -euo pipefail- Responds to
--help/-h - Exit codes: 0 success, 1 user error, 2 system error
- Logs via
lib/logging.sh - Doesn’t write to profile directories directly — calls lib functions
The dispatcher (bin/ww) routes ww myservice list to the list case branch. The help output appears in ww help. The service is available from the browser UI CMD panel immediately.
Profile-Level Services
Services at profiles/<name>/services/<category>/ shadow global services. This means you can have a per-profile version of any service. ww myservice in the work profile runs the work-profile version; in the personal profile it runs the global version.
This is useful for per-profile customizations that shouldn’t affect other contexts. A work profile might have a specialized export service. A client profile might have a service that integrates with the client’s specific tooling.
Template Tiers
docs/guides/services/service-development.md describes three service tiers:
Tier 1 — Simple — single-file script, direct TaskWarrior/TimeWarrior calls. Appropriate for services that wrap a single tool command or do simple data reads.
Tier 2 — Compound — multiple subcommands, uses lib functions for profile management. Appropriate for services with lifecycle (create/list/delete/backup patterns).
Tier 3 — Complex — state management, external APIs, background processes. Appropriate for services like github-sync that maintain persistent state across invocations.
Most new services start at Tier 1 and grow if needed. The pattern is identical across tiers — the difference is how much of the lib you reach into.
Why This Works
The architecture is a consequence of the profile model. Because everything resolves through environment variables, a service doesn’t need to know which profile it’s running against. It reads $WORKWARRIOR_BASE, calls lib functions with the right paths, and the isolation is handled automatically.
This is also why services are safe to contribute. A new service in services/my-extension/ touches nothing that already exists. It can’t break the sync engine. It can’t break the browser UI. It can’t break the CLI dispatcher (unless it introduces a naming conflict, which the dispatcher checks for at startup).
The extension surface is designed to be safe. Build services. Build weapons. Build profile-level customizations. The architecture gets out of the way.