# @toggl/cli

Command-line interface for [Toggl 2.0](https://focus.toggl.com).

## Installation

```bash
npm install -g @togglhq/cli
```

Or run without installing:

```bash
npx @togglhq/cli
```

## Authentication

```bash
toggl auth
```

Opens a browser for OAuth sign-in. On success, credentials are saved to `~/.toggl/focus-tools.json`. When `--profile` is omitted, the CLI saves the account under a name based on your account, such as `user-12345`, and makes it active.

**Options:**

| Flag                  | Description                                                           |
| --------------------- | --------------------------------------------------------------------- |
| `--workspace-id <id>` | Select a specific workspace (required when your account has multiple) |
| `--profile <name>`    | Save credentials under an explicit profile name                       |

Sessions refresh automatically. Re-run `toggl auth` only to switch workspaces or after 4 weeks of inactivity.

## Profiles

Use profiles to switch between saved accounts and environments:

```bash
toggl profile list
toggl profile current
toggl profile switch user-12345
toggl profile switch   # omit name in a TTY: pick from a menu or use the only profile
toggl profile remove user-12345
```

The active profile controls both the account and environment for later commands. You can still override it for one call with the global `--profile <name>` flag.

## Agent skill setup

`@togglhq/cli` ships a `toggl-cli` agent skill under `skills/toggl-cli`. Install it with:

```bash
toggl skill setup
```

This delegates to the [`skills`](https://www.npmjs.com/package/skills) CLI, listed as a **dependency** of `@togglhq/cli` (no separate install). Interactive agent/scope prompts come from that CLI.

After upgrading the CLI, refresh the skill install:

```bash
toggl skill sync
```

By default the installer **symlinks** into your agent skill dirs so updates track CLI upgrades; use `--copy` only if you need a detached snapshot (then re-run `toggl skill sync` after upgrades). Pass flags for non-interactive setup:

```bash
toggl skill setup --agent cursor --global --copy --yes
toggl skill setup --agent cursor codex --copy --yes
```

## Command shape

Generated Toggl 2.0 commands look like:

```
toggl [global flags] <group> … <subcommand> [flags]
```

Many endpoints use two segments (`toggl tasks list`). Some use nested groups, including:

- **`toggl organization …`** — organization APIs (not `toggl org …`; there is no `org` top-level command). Examples: `toggl organization invitations list`, `toggl organization members get`, `toggl organization workspaces list`.
- **`toggl shared …`** — shared workspace/org data routed through `@toggl/queries` helpers (status, holidays, working hours, time off). Example: `toggl shared status get`.

Core resource groups still include `tasks`, `projects`, `time-blocks`, `time-entries`, `statuses`, `users`, `tags`, `clients`, and others surfaced by the public command catalog. Some low-level endpoints are omitted from the CLI and MCP. Existing public command paths and operation IDs stay stable; use `toggl --help` and `toggl <group> --help` to explore what shipped.

**Typical entity commands:**

| Resource       | Actions                                                  |
| -------------- | -------------------------------------------------------- |
| `tasks`        | `list` (alias `ls`), `get`, `create`, `update`, `delete` |
| `projects`     | `list` (alias `ls`), `get`, `create`, `update`, `delete` |
| `time-blocks`  | `list` (alias `ls`), `get`, `create`, `update`, `delete` |
| `time-entries` | `list` (alias `ls`), `start`, `stop`                     |
| `statuses`     | `list` (alias `ls`)                                      |
| `users`        | `list` (alias `ls`)                                      |

Bulk mutations use `toggl <resource> bulk <verb>`; see `toggl tasks bulk --help`.

**Global flags** (placed before the command path):

| Flag                  | Description                                                   |
| --------------------- | ------------------------------------------------------------- |
| `--json`              | Pretty-printed JSON output (easier to pipe than human tables) |
| `--workspace-id <id>` | Override the active workspace for this call                   |
| `--profile <name>`    | Use a named auth profile                                      |

**Per-command flags** on generated operations always include `--data`, `--data-file`, and `--dry-run`. **`--interactive`** / **`--no-interactive`** appear only when the operation’s catalog metadata allows interactive prompting (read-only operations default to non-interactive unless marked otherwise). Interactive prompting requires a TTY and is disabled when you pass `--json`, `--data`, `--data-file`, `CI`, or `TOGGL_NON_INTERACTIVE`.

To see scalar flags for an operation, use **`toggl <group> … <command> --help`**. To print the resolved operation id and validated body without calling the API, use **`--dry-run`**.

## Providing input

There are three ways to pass the request body:

### 1. Scalar flags

Each input field maps to a `--field-name` flag. Numeric values are coerced automatically.

```bash
toggl tasks get --task-id 12345
toggl tasks list --project-id 999 --status-id 111222
toggl time-entries list --date-from 2025-01-01 --date-to 2025-01-07
toggl time-entries start --task-id 12345 --type activity
```

For `update` commands, payload fields use a `--payload-<field>` prefix:

```bash
toggl tasks update --task-id 12345 --payload-name "New name" --payload-status-id 456789
toggl projects update --project-id 999 --payload-name "Renamed"
```

### 2. `--data` inline JSON

Pass the full input as a JSON string:

```bash
toggl tasks list --data '{"project_id": 123456, "per_page": 50}'
toggl tasks create --data '{"name": "Fix login bug", "project_id": 123456}'
toggl tasks update --data '{"task_id": 12345, "payload": {"name": "Updated", "status_id": 456789}}'
```

### 3. `--data -` (stdin) or `--data-file <path>`

Read JSON from stdin or a file:

```bash
echo '{"task_id": 12345}' | toggl tasks get --data -
toggl tasks create --data-file ./new-task.json
```

## Output and errors

- Successful output goes to **stdout** as JSON (pretty-printed; `--json` keeps JSON while skipping human-oriented formatting helpers).
- Validation errors and API errors go to **stderr**.
- Exit code `0` on success, `1` on error.

## Safety flags

### `--dry-run`

Preview the resolved input without making an API call. Output includes `dry_run: true`, the operation ID, and the validated input object:

```bash
toggl tasks delete --task-id 12345 --dry-run
# {"dry_run":true,"operation":"focus.tasks.delete","input":{"task_id":12345}}
```

### `--yes` (destructive operations)

`delete` commands require `--yes` to execute. Omitting it prints an error to stderr:

```bash
toggl tasks delete --task-id 12345 --yes
toggl projects delete --project-id 999 --yes
toggl time-blocks delete --task-id 12345 --time-block-id 67890 --yes
```

## Examples

**Tasks**

```bash
# List all tasks in a project
toggl tasks list --project-id 123456

# Get a single task
toggl tasks get --task-id 12345

# Create a task
toggl tasks create --data '{"name": "Write release notes", "project_id": 123456}'

# Move task to "In Progress"
toggl tasks update --task-id 12345 --payload-status-id 456789

# Delete a task (requires --yes)
toggl tasks delete --task-id 12345 --yes
```

**Projects**

```bash
toggl projects list
toggl projects get --project-id 123456
toggl projects create --data '{"name": "Q3 Planning", "workspace_id": 789012}'
toggl projects update --project-id 123456 --payload-name "Q3 Planning (archived)"
toggl projects delete --project-id 999 --yes
```

**Time entries**

```bash
# List time entries for a week
toggl time-entries list --date-from 2025-01-01 --date-to 2025-01-07

# Start tracking (defaults type to 'activity')
toggl time-entries start --task-id 12345

# Start a break
toggl time-entries start --type break

# Stop the running timer
toggl time-entries stop
```

**Time blocks**

```bash
# List scheduled blocks for a week
toggl time-blocks list --date-from 2025-01-01 --date-to 2025-01-07

# Create a time block
toggl time-blocks create --data '{
  "task_id": 12345,
  "payload": {"start": "2025-01-06T09:00:00Z", "end": "2025-01-06T11:00:00Z"}
}'

# Delete a time block
toggl time-blocks delete --task-id 12345 --time-block-id 67890 --yes
```

**Reference data**

```bash
# List workspace statuses (useful for getting status IDs)
toggl statuses list

# List workspace members (useful for getting user IDs)
toggl users list
```

## Dates

`--date-from` and `--date-to` accept:

- `YYYY-MM-DD` — expanded to `T00:00:00Z` (from) or `T23:59:59Z` (to)
- RFC 3339 datetime — used as-is (e.g. `2025-01-01T08:00:00+02:00`)

## Troubleshooting

**Not authenticated**

```
Not authenticated. Run `toggl auth` to sign in.
```

Run `toggl auth`. Credentials are stored in `~/.toggl/focus-tools.json`. If you previously authenticated with an older version, your session at `~/.toggl-focus-mcp/config.json` is still read automatically.

**Invalid JSON from `--data`**

```
Invalid JSON from --data: Unexpected token ...
```

Verify your JSON is well-formed. Use `--data-file` with a file if quoting is difficult in your shell.

**Validation error**

```
Invalid input: date_from: must be YYYY-MM-DD or RFC3339 datetime
```

Check field names and types. Use `--dry-run` to validate input without executing.

**Debug API responses**

```bash
TOGGL_DEBUG=1 toggl tasks list
```

Prints schema validation warnings from API responses to stderr.

## License

Proprietary Toggl software. See [LICENSE](LICENSE) and [Toggl legal terms](https://toggl.com/legal/).
