Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 44 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,14 @@ A Neovim plugin for automatic coding activity tracking. Works with the ShellTime
## Features

- **Automatic Time Tracking** - Passively monitors coding activity
- **Language Detection** - Auto-detects programming language from filetype
- **Language Detection** - Uses the buffer's filetype, falling back to the file extension
- **Project Analytics** - Tracks time per project/workspace
- **Git Integration** - Records activity by git branch
- **Event Debouncing** - Efficient heartbeat batching (30s cooldown per file)
- **Offline Support** - Queues heartbeats when daemon unavailable
- **Debug Detection** - Marks activity as `debugging` while an nvim-dap session is active
- **Debouncing & Batching** - At most one heartbeat per file every 30s (saves always count), sent in batches every 2 minutes
- **Offline Support** - Keeps heartbeats in memory and retries them when the daemon is unavailable
- **Flush on Exit** - Sends pending heartbeats when Neovim quits, so short sessions aren't lost
- **CLI Update Check** - Warns once per session when your ShellTime CLI is out of date

## Prerequisites

Expand Down Expand Up @@ -44,7 +47,7 @@ This will:

### 3. Enable Code Tracking

Add the following to your ShellTime config (`~/.shelltime/config.yaml`):
Make sure your ShellTime config (`~/.shelltime/config.yaml`) contains:

```yaml
codeTracking:
Expand All @@ -58,10 +61,14 @@ Or in TOML format (`~/.shelltime/config.toml`):
enabled = true
```

Configs created by `shelltime init` already include this; older configs may not. The daemon ignores editor heartbeats unless it is `true`, and it reads the setting at startup, so restart the daemon after changing it (for example with `shelltime daemon reinstall`).

## Requirements

- Neovim >= 0.10.0
- Neovim >= 0.10.0 (Neovim only; classic Vim isn't supported)
- ShellTime daemon running (see [Prerequisites](#prerequisites))
- Git (optional, for branch tracking)
- `curl` (optional, for the CLI update check)

## Installation

Expand Down Expand Up @@ -92,12 +99,14 @@ use {
Plug 'shelltime/coding-extension-vim'
```

Then add to your `init.lua`:
Then add to your `init.lua` (or `lua require("shelltime").setup()` to `init.vim`):

```lua
require("shelltime").setup()
```

Calling `setup()` is required: loading the plugin only registers its commands. With lazy.nvim, `opts = {}` calls it for you.

## Quick Start

1. **Complete the [Prerequisites](#prerequisites)** - Install CLI, authenticate, and enable code tracking
Expand All @@ -120,38 +129,48 @@ require("shelltime").setup({
})
```

`config` is the only `setup()` option. The plugin reads just this one YAML file: not `config.toml`, `config.yml` or `config.local.*`. If the file is missing, it uses the defaults below. The file is re-read whenever it changes, so most settings apply without restarting Neovim (`heartbeatInterval` needs a restart).

### Config File Options

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `socketPath` | string | `/tmp/shelltime.sock` | Unix socket path for daemon |
| `codeTracking.enabled` | boolean | `true` | Enable/disable tracking |
| `codeTracking.enabled` | boolean | `true` | Enable/disable tracking in the plugin. The daemon also requires it to be `true` (see [Prerequisites](#3-enable-code-tracking)) |
| `debug` | boolean | `false` | Enable debug logging |
| `heartbeatInterval` | number | `120000` | How often pending heartbeats are sent, in ms |
| `debounceInterval` | number | `30000` | Minimum time between heartbeats for the same file, in ms |
| `apiEndpoint` | string | - | ShellTime API URL used for the CLI update check (written by `shelltime init`) |
| `webEndpoint` | string | - | ShellTime web URL used in the update command (written by `shelltime init`) |

## Commands

| Command | Description |
|---------|-------------|
| `:ShellTimeStatus` | Show daemon connection status and pending heartbeats |
| `:ShellTimeStatus` | Show daemon connection status (version, uptime, platform) and pending heartbeats |
| `:ShellTimeFlush` | Manually flush pending heartbeats to daemon |
| `:ShellTimeEnable` | Enable tracking |
| `:ShellTimeDisable` | Disable tracking |
| `:ShellTimeEnable` | Enable tracking for this session (needs `setup()`; doesn't override `codeTracking.enabled: false`) |
| `:ShellTimeDisable` | Flush pending heartbeats and disable tracking for this session |

## How It Works

The plugin monitors these Neovim events:

| Event | Trigger |
|-------|---------|
| `BufEnter` | Opening a file |
| `BufEnter` | Opening or switching to a file |
| `TextChanged` / `TextChangedI` | Editing text |
| `BufWritePost` | Saving a file |
| `CursorMoved` / `CursorMovedI` | Moving cursor |

Only regular files are tracked. Special buffers (help, terminal, quickfix, etc.), unnamed buffers, files inside `.git/`, and non-`file://` URLs such as `oil://` or `fugitive://` are skipped.

Heartbeats are:
- **Deduplicated**: Buffer and cursor events at the same file and cursor position as the last event are skipped (edits always count)
- **Debounced**: Max 1 heartbeat per file per 30 seconds (except saves)
- **Batched**: Sent to daemon every 2 minutes
- **Queued**: Stored locally if daemon is unavailable
- **Batched**: Sent to the daemon over its Unix socket every 2 minutes
- **Flushed on exit**: Pending heartbeats are sent on `VimLeavePre` (Neovim waits up to 1.5s)
- **Queued**: Kept in memory and retried on the next flush if the daemon is unavailable (up to 5,000; oldest dropped first). They are not written to disk, so they are lost if Neovim exits while the daemon is down

### Data Tracked

Expand All @@ -161,24 +180,30 @@ Each heartbeat includes:
- **Project info**: Name, root path, git branch
- **Editor info**: Neovim version, plugin version
- **System info**: Hostname, OS, OS version
- **Activity**: Timestamp, whether it was a save event
- **Activity**: Timestamp, whether it was a save event, category (`coding` or `debugging`)

The project root is the nearest parent directory containing `.git`, `package.json`, `Cargo.toml`, `go.mod`, `pyproject.toml`, `setup.py`, `Makefile`, `CMakeLists.txt`, `.project` or `.root` (otherwise the file's directory). The project name is its last two path segments, e.g. `code/my-app`.

### CLI Update Check

On startup, if the daemon is reachable, the plugin asks `<apiEndpoint>/api/v1/cli/version-check` (via `curl`) whether the daemon's CLI version is the latest. If not, it shows a warning once per session with the update command (`curl -sSL <webEndpoint>/i | bash`) and copies that command to the `+` register when a clipboard is available. The check is skipped when `apiEndpoint` or `webEndpoint` is not set.

## Troubleshooting

### Plugin not tracking

1. Check if daemon is running:
```bash
ls -la /tmp/shelltime.sock
# If not present, run: shelltime init
shelltime daemon status # socket, running state, and whether Code Tracking is enabled
# If not running, run: shelltime init
```

2. Verify config file exists:
```bash
cat ~/.shelltime/config.yaml
```

3. Enable debug mode in config:
3. Enable debug mode in config (messages are shown via `vim.notify`):
```yaml
debug: true
```
Expand All @@ -191,8 +216,9 @@ Each heartbeat includes:
### Heartbeats not sending

Run `:ShellTimeStatus` to check:
- If "Disconnected", ensure daemon is running
- If "Disconnected", ensure daemon is running and `socketPath` matches the daemon's socket
- If pending heartbeats > 0, try `:ShellTimeFlush`
- If "Connected" and flushes succeed but no activity shows up, check that `codeTracking.enabled` is `true` for the daemon and restart it. The daemon drops heartbeats otherwise, and the plugin can't tell

## License

Expand Down
Loading