skills/timewarrior/SKILL.md
TimeWarrior time tracking: start/stop intervals, query durations by tag or issue, compute totals for issue tracker time reporting
npx skillsauth add jcsaaddupuy/badrobots timewarriorInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
The ~/.task/hooks/on-modify.timewarrior hook is active. When you run task ID start, TimeWarrior automatically starts tracking with all the task's tags. When you run task ID stop or task ID done, tracking stops. You do not need to call timew start/stop manually for TaskWarrior tasks.
task 42 start → timew start "task title..." project tag1 tag2
task 42 done → timew stop
timew start "tag1" "tag2" # start tracking with free-form tags
timew stop # stop current interval
timew summary # summary of all tracked time
timew summary :week # this week only
timew summary :day # today only
timew summary :ids :week # include interval IDs (@N) in output
timew tags # list all known tags
timew export # JSON export of all intervals
timew export :week # JSON export filtered to this week
[
{
"id": 6,
"start": "20260507T130107Z",
"end": "20260507T130203Z",
"tags": ["my-project", "issue:#42", "some task description"]
}
]
start / end: compact ISO-8601 UTC (YYYYMMDDTHHMMSSz)end is absent when the interval is currently runningtags: array of strings; a TaskWarrior task injects all its tags + full description as separate tag entriesWhen a task is started via task ID start, the hook injects:
Issue references land inside the description tag as issue:#NN or issue:NN.
import json, re, subprocess
from datetime import datetime, timezone
def seconds_for_issue(issue_num: int) -> int:
"""Sum all tracked seconds for intervals tagged with issue:#N or issue:N."""
raw = subprocess.check_output(["timew", "export"])
records = json.loads(raw)
total = 0
pattern = re.compile(r'issue:#?' + str(issue_num) + r'\b')
for r in records:
if not any(pattern.search(t) for t in r.get("tags", [])):
continue
start = datetime.strptime(r["start"], "%Y%m%dT%H%M%SZ").replace(tzinfo=timezone.utc)
end = (
datetime.strptime(r["end"], "%Y%m%dT%H%M%SZ").replace(tzinfo=timezone.utc)
if "end" in r
else datetime.now(timezone.utc) # interval still running
)
total += int((end - start).total_seconds())
return total
def to_duration_string(secs: int) -> str:
"""Convert seconds to a human duration string, e.g. '1h 30m'."""
h = secs // 3600
m = (secs % 3600) // 60
s = secs % 60
parts = []
if h: parts.append(f"{h}h")
if m: parts.append(f"{m}m")
if s and not parts: parts.append(f"{s}s") # only show seconds if < 1 min
return " ".join(parts) or "0m"
Replace 43 with the issue number:
timew export | python3 -c "
import sys, json, re
from datetime import datetime, timezone
records = json.load(sys.stdin)
total = 0
pat = re.compile(r'issue:#?43\b')
for r in records:
if not any(pat.search(t) for t in r.get('tags',[])): continue
s = datetime.strptime(r['start'],'%Y%m%dT%H%M%SZ').replace(tzinfo=timezone.utc)
e = datetime.strptime(r['end'],'%Y%m%dT%H%M%SZ').replace(tzinfo=timezone.utc) if 'end' in r else datetime.now(timezone.utc)
total += int((e-s).total_seconds())
h,m = divmod(total//60, 60)
print(f'{h}h {m}m ({total}s)')
"
timew summary TAG filters only if TAG exactly matches one of the interval's tags. Issue refs are embedded inside long description strings — always use timew export + Python regex, never timew summary issue:#42.end is still running — always treat it as now in calculations.Nh, Nm, Ns, NhNm, 1h 30m, 90m. Avoid 1.5h — parsed inconsistently by some tools.development
DuckDB patterns for JSON/JSONL analysis, array unnesting, and common gotchas. Use when querying JSON files, nested data, or encountering "UNNEST not supported here" errors.
development
Mealie recipe manager API: recipes, shopping lists, meal plans. Requires MEALIE_BASE_URL and MEALIE_API_KEY.
development
Bookmark manager for saving, searching, and annotating web content. Use when: (1) saving a webpage for later reference, (2) searching previously saved bookmarks, (3) adding highlights/annotations to saved content, (4) user asks to 'bookmark this' or 'save this article'. Requires READECK_BASE_URL and READECK_API_KEY environment variables.
data-ai
YAML parsing and manipulation with yq