--- name: hourlog description: 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. user-invocable: true argument-hint: "[--week last|this | --since YYYY-MM-DD [--until YYYY-MM-DD]]" allowed-tools: - Read - Grep - Glob - Bash - Edit --- # 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. 1. `~/.config/hourlog/projects.json` — path prefix to project mapping, copied from `/config.example.json`. **Never commit a filled config, and never put a project, client, or host name in this repo** — it is public. 2. `HOURLOG_API` and `HOURLOG_TOKEN` in `~/.env.claude`. The token is a personal access token from the timesheet app's profile page: `profile:read`, `schedule:read`, and `schedule:write` only if submitting. Never echo it — `${VAR:-x}` prints the value when the variable is set; use `${VAR:+set}`. 3. Config project names must match the app exactly. Verify with `me-api.py projects` and fix the config, not the app. ## 1. Scan ```sh python3 /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 in `exclude`. Ask; never guess it into a client project. `--json` carries the same fields for computing against. ## 2. Reconcile ```sh python3 /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 ```sh # confirm a planned day python3 /scripts/me-api.py confirm --entry ID --hours 5.25 --dry-run # log a day with no allocation python3 /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.