CLI: Tracking Time
The Keito CLI supports running timers, manual time entries, completed agent session records, time-entry listing, and active-timer checks.
Regular timer and manual commands create entries with source=cli. Agent lifecycle hooks should use keito time session-record, which creates source=agent entries by default.
Start a Timer
keito time start --project "Acme Website" --task "Development"
This creates a running time entry for today. The CLI checks for an existing running timer first, because only one timer can be active at a time.
| Flag | Description | Required |
|---|---|---|
--project <value> | Project name, code, or ID | Yes |
--task <value> | Task name or ID | Yes |
--notes <text> | Initial notes | No |
| `—billable <true | false>` | Override billable status |
JSON example:
keito time start --project "Acme Website" --task "Development" --json
{
"status": "started",
"entry_id": "time_entry_id_here",
"project": "Acme Website",
"task": "Development",
"spent_date": "2026-05-06",
"billable": true,
"source": "cli",
"started_at": "2026-05-06T09:00:00Z"
}
If another timer is already running, the command exits with code 3.
Check the Running Timer
keito time running
keito time running --json
When no timer is running, JSON output is:
{"running": false}
When a timer is running, JSON output is an array:
[
{
"running": true,
"entry_id": "time_entry_id_here",
"project": "Acme Website",
"task": "Development",
"started_at": "2026-05-06T09:00:00Z",
"spent_date": "2026-05-06",
"billable": true,
"source": "cli",
"elapsed_hours": 1.5,
"elapsed": "1:30"
}
]
Agents and scripts should call keito time running --json before starting a timer.
Stop a Timer
keito time stop --notes "Completed code review"
This stops the running timer and saves elapsed time. The production API calculates the duration server-side. If --notes is supplied, it replaces the entry notes. Without --notes, existing notes are preserved.
| Flag | Description |
|---|---|
--notes <text> | Replace notes on the entry |
--discard | Delete the running timer instead of saving it |
JSON example:
{
"status": "stopped",
"entry_id": "time_entry_id_here",
"project": "Acme Website",
"task": "Development",
"duration_hours": 1.5,
"duration": "1:30",
"spent_date": "2026-05-06",
"billable": true,
"source": "cli",
"started_at": "2026-05-06T09:00:00Z",
"stopped_at": "2026-05-06T10:30:00Z"
}
Discard a Timer
keito time stop --discard --json
Discarding deletes the running time entry and records no duration. Use this when a timer was started by mistake or an automated session failed and should not be billed.
JSON example:
{
"status": "discarded",
"entry_id": "time_entry_id_here",
"project": "Acme Website",
"task": "Development"
}
Log Completed Time
keito time log --project "Acme Website" --task "Development" \
--duration 1:30 \
--date 2026-05-06 \
--notes "Implemented OAuth flow"
| Flag | Description | Required |
|---|---|---|
--project <value> | Project name, code, or ID | Yes |
--task <value> | Task name or ID | Yes |
--duration <value> | Decimal hours or HH:MM | Yes |
--date <YYYY-MM-DD> | Date of work; defaults to today | No |
--notes <text> | Description of work | No |
| `—billable <true | false>` | Override billable status |
Duration formats:
| Input | Meaning |
|---|---|
1.5 | 1 hour 30 minutes |
1:30 | 1 hour 30 minutes |
0.25 | 15 minutes |
0:15 | 15 minutes |
Record an Agent Session
keito time session-record \
--project "project_id_here" \
--task "task_id_here" \
--session-id "codex-123" \
--duration-seconds 5400 \
--source agent \
--metadata '{"integration":"custom_agent","agent_type":"codex"}' \
--json
session-record is idempotent for a stable session ID on the same date and source. If the entry already exists, the CLI updates it instead of creating a duplicate.
List Time Entries
keito time list
keito time list --from 2026-05-01 --to 2026-05-31
keito time list --project "Acme Website" --limit 10 --json
keito time list --task "Development" --page 2 --json
| Flag | Description |
|---|---|
--from <YYYY-MM-DD> | Start date |
--to <YYYY-MM-DD> | End date |
--project <value> | Filter by project name, code, or ID |
--task <value> | Filter by task name or ID |
--limit <number> | Entries per page; default 50 |
--page <number> | Page number; default 1 |
JSON list output is an array of time-entry objects from the API.
Common Exit Codes
| Code | Meaning |
|---|---|
0 | Success |
3 | Conflict, usually a timer is already running |
4 | Project, task, or running timer not found |
For the full table, see Command Reference.