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.