Scan and report had grown into two sections saying the same thing, and
step 4 repeated step 1's "show the table". Down to four steps.
Adds what nearly went wrong on the first real run: a day already
confirmed weeks ago looks identical to a planned one in a bulk approval,
and overwriting it needs its own yes. Also records the 15-minute
rounding the app enforces, and the ${VAR:-x} expansion that prints a
token instead of hiding it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
5.1 KiB
name, description, user-invocable, argument-hint, allowed-tools
| name | description | user-invocable | argument-hint | allowed-tools | |||||
|---|---|---|---|---|---|---|---|---|---|
| hourlog | Work out how many hours each client project got on each day, by reading Claude Code and Codex session activity, then reconcile that against the company timesheet and confirm the week's hours. Use when the user asks to log hours, fill in their timesheet, check what they worked on last week, or runs the Friday hour-logging session. | true | [--week last|this | --since YYYY-MM-DD [--until YYYY-MM-DD]] |
|
Hour log
Report how long each client project was active on each day, check that against the timesheet, and submit only the hours the user gives you.
The timesheet is a company record. Nothing is written to it without the user saying go, in this session, after seeing the table. An unattended run stops at the table.
Setup (once per machine)
Check all three before anything else; stop with the missing step if not.
~/.config/hourlog/projects.json— path prefix to project mapping, copied from<skill-dir>/config.example.json. Never commit a filled config, and never put a project, client, or host name in this repo — it is public.HOURLOG_APIandHOURLOG_TOKENin~/.env.claude. The token is a personal access token from the timesheet app's profile page:profile:read,schedule:read, andschedule:writeonly if submitting. Never echo it —${VAR:-x}prints the value when the variable is set; use${VAR:+set}.- Config project names must match the app exactly. Verify with
me-api.py projectsand fix the config, not the app.
1. Scan
python3 <skill-dir>/scripts/scan-activity.py --week last
A markdown table, one row per day, one column per project. Paste it into the reply as plain markdown — never in a code fence, which shows raw pipes instead of a rendered table — and don't restate it in prose.
What the numbers mean, and their limits:
- The unit is a 5-minute slot containing at least one message, deduplicated
per project, so a 40-subagent swarm counts once. Overlapping minutes are
split evenly between the projects live in them, so the columns add up to
total, which is wall-clock presence. - Measured time is a floor, never a total. Meetings, review, reading and thinking leave no transcript. Say so; the user adds them back.
- Do not extrapolate. No fitting to an 8-hour day, no scaling a thin day up, no rounding a dominant project to the whole day. Days start at 06:00 and run past midnight, and a guessed number is worse than a small true one because the user cannot tell it was guessed.
Two flags need a judgement call:
outside HHh— time at 02:00 is usually an unattended run, not work. Name it so the user can discount it; never silently drop it.unmapped time excluded— a path with no rule, left out of that day. Either a client directory the config is missing, or personal work belonging inexclude. Ask; never guess it into a client project.
--json carries the same fields for computing against.
2. Reconcile
python3 <skill-dir>/scripts/me-api.py schedule --start YYYY-MM-DD --end YYYY-MM-DD
Each day is either planned — an allocation carrying an entry id, planned hours, and a status — or absent, which needs a new entry rather than a confirmation.
Check the status of every entry before proposing a value. planned with
actual_hours: null is untouched and safe to fill. confirmed or edited
means the user already set that number, possibly weeks ago. Overwriting one is
a separate decision: show the current value against the proposed value, say
which day it is, and get a specific yes for that entry. Do not fold it into a
bulk approval.
Show planned hours beside measured ones and let the user set the number. The measured figure is almost always lower, and that gap is work off the keyboard, not evidence the plan is wrong.
3. Ask
The table, plus one line for anything flagged. No commentary on days that were straightforward, and no totals the user did not give you.
Then ask once, plainly, whether to submit. Wait. Silence, a timeout, or "user may be away" is not approval — leave the timesheet alone and say the run is waiting.
4. Submit
# confirm a planned day
python3 <skill-dir>/scripts/me-api.py confirm --entry ID --hours 5.25 --dry-run
# log a day with no allocation
python3 <skill-dir>/scripts/me-api.py log --project ID --dates D,D --hours 4 --dry-run
Hours are decimal and the app enforces 15-minute steps, so round to a multiple of 0.25 before sending. When the user gives a day's total rather than per-project numbers, split it by the measured proportions.
Run every write with --dry-run first and show the requests. Drop the flag
only for the rows approved — not the whole table, if they approved part of it.
A 423 means the period is locked and ops has to reopen it; report it and move
on. Re-read the schedule afterwards and report what actually landed, not what
was sent.
Scope
How a day divided between projects, and confirming hours already worked. Not future allocations, not time off, not anyone else's schedule.