# mps

A plain-text personal productivity CLI. Append journal entries, tasks, reminders, logs, and notes to daily `.mps` files; search, list, and export them; sync via git. Single binary, no runtime dependencies.

Rust rewrite of [mps (Ruby)](https://github.com/mash-97/mps). Fully backward-compatible with all Ruby-generated files and config.

---

## Install

```bash
cargo install mps-rs
```

The installed binary is named `mps`.

### From source

```bash
git clone https://github.com/mash-97/mps-rs
cd mps-rs
cargo install --path .
```

---

## Quick start

```bash
mps                                          # open today's file in $EDITOR
mps list                                     # print today's elements
mps list --since monday                      # all elements since last Monday
mps append task "Fix the auth bug" --tags work,backend
mps append note "The edge case only appears under load"
mps append reminder "Team standup" --at 3pm
mps append log "Deep-work session" --start-time 09:00 --end-time 11:30
mps append character "Helped me think through the design" --name "Dr. Alice" --tags mentor
mps done task-1                              # mark task-1 done
mps search "auth" --since "last week"
mps stats --since monday
mps export --format csv --since "20260501" > may.csv
mps autogit                                  # stage + commit + pull + push
```

---

## Element types

| Type | Description |
|------|-------------|
| `task` | Something to do, with open/done status |
| `note` | Free-form observation or thought |
| `reminder` | Time-anchored alert |
| `log` | Timed work block (start/end time → duration) |
| `character` | Running monologue about a person |

---

## Commands

### `mps [open] [DATE]`

Open the `.mps` file for DATE in `$EDITOR` (falls back to `vim`). Creates the file if absent.

```bash
mps                   # today
mps open yesterday
mps open last friday
```

---

### `mps list [DATE]`

Print elements for DATE as an indented tree.

| Flag | Short | Description |
|------|-------|-------------|
| `--type TYPE` | `-t` | Filter by type: `task`, `note`, `log`, `reminder`, `character` |
| `--tag TAG` | `-g` | Filter by tag name |
| `--status STATUS` | `-s` | Filter tasks by `open` or `done` (hides all non-task elements) |
| `--name NAME` | `-n` | Filter character entries by person name |
| `--since DATE` | `-S` | Show elements from DATE up to the target date |
| `--refs` | `-r` | Show human-readable ref column (`task-1`, `mps-1.2`, …) |
| `--all` | `-a` | List across the entire archive, not just one date |

```bash
mps list
mps list --type task --status open
mps list --since monday --refs
mps list --all --name "Dr. Alice"
```

---

### `mps append TYPE BODY [FLAGS]`

Append one element to today's file without opening an editor. TYPE is resolved through `type_aliases` from config (e.g. `t` → `task`).

| Flag | Description |
|------|-------------|
| `--tags t1,t2` | Comma-separated tags |
| `--status open\|done` | Task status (default: `open`) |
| `--at TIME` | Time for reminders (`5pm`, `10:30`, …) |
| `--start-time HH:MM` | Start time for logs |
| `--end-time HH:MM` | End time for logs |
| `--name NAME` | `-n` Person name for character entries |

```bash
mps append task "Review the PR" --tags work,backend
mps append note "Cache invalidation approach"
mps append reminder "1:1 with manager" --at 2pm
mps append log "Debugging session" --start-time 14:00 --end-time 16:30
mps append character "Explained the system clearly" --name "Mahfuz Vai" --tags mentor,work
mps append task "Write tests" --status done --tags ci
```

---

### `mps update REFPATH [FLAGS]`

Update one element's attributes in-place. REFPATH is a human ref (`task-1`, `mps-1.2`) or an epoch ref (`20260428.1732500287.1`).

| Flag | Description |
|------|-------------|
| `--status open\|done` | Set task status |
| `--start-time HH:MM` | Set log start time |
| `--end-time HH:MM` | Set log end time |
| `--at TIME` | Set reminder time |
| `--date DATE` | `-d` Date context for human refs (default: today) |

```bash
mps update task-1 --status done
mps update mps-1.2 --end-time 17:00
mps update task-3 --status done --date yesterday
```

---

### `mps done REFPATH [--date DATE]`

Shorthand for `mps update REFPATH --status done`.

```bash
mps done task-1
mps done task-2 --date yesterday
```

---

### `mps search QUERY [FLAGS]`

Full-text search across all `.mps` files. Returns matching elements with date and ref.

| Flag | Short | Description |
|------|-------|-------------|
| `--type TYPE` | `-t` | Filter by element type |
| `--tag TAG` | `-g` | Filter by tag |
| `--name NAME` | `-n` | Filter character entries by person name |
| `--since DATE` | `-S` | Search from DATE onward |

```bash
mps search "auth"
mps search "design" --type character --name "Dr. Alice"
mps search "deploy" --since "last week" --type log
```

---

### `mps stats [DATE] [FLAGS]`

Show element counts and total log durations.

| Flag | Short | Description |
|------|-------|-------------|
| `--since DATE` | `-S` | Stats from DATE up to target date |
| `--all` | `-a` | Stats across the entire archive |

```bash
mps stats
mps stats --since monday
mps stats --all
```

---

### `mps tags [DATE] [FLAGS]`

Show tag usage as a frequency bar chart.

| Flag | Short | Description |
|------|-------|-------------|
| `--type TYPE` | `-t` | Count tags for this element type only |
| `--status STATUS` | `-s` | Restrict to tasks with this status |
| `--name NAME` | `-n` | Restrict to character entries for this person |
| `--since DATE` | `-S` | Tags from DATE up to target date |
| `--all` | `-a` | Count across the entire archive |

```bash
mps tags
mps tags --all --type task --status open
mps tags --since monday --name "Dr. Alice"
```

---

### `mps export [DATE] [FLAGS]`

Export elements to stdout as JSON or CSV.

| Flag | Short | Description |
|------|-------|-------------|
| `--format json\|csv` | `-f` | Output format (default: `json`) |
| `--type TYPE` | `-t` | Filter by element type |
| `--since DATE` | `-S` | Export from DATE up to target date |

CSV columns: `date`, `ref`, `type`, `tags`, `body`, `status`, `at`, `start`, `end`, `name`

```bash
mps export --format json
mps export --format csv --since "20260501" > may.csv
mps export --since monday --type log
```

---

### `mps config [show|edit]`

View or edit configuration.

```bash
mps config         # same as mps config show
mps config show
mps config edit    # opens config file in $EDITOR
```

---

### `mps git ARGS` / `mps autogit` / `mps cmd ARGS`

Run git or shell commands inside the storage directory.

```bash
mps git status
mps git auto          # add . + commit + pull + push
mps git autocommit    # add . + commit only
mps autogit           # same as mps git auto
mps cmd ls -la        # any shell command in storage dir
```

---

### `mps version`

Print the version string.

---

## File format

Files are named `YYYYMMDD.<epoch>.mps` (or `YYYYMMDD.mps` for files without an epoch). The format is identical to the Ruby gem — no migration needed.

```
@task[work, release, status: done]{
  Ship the API refactor
}

@note{
  The auth token expiry edge case only appears under concurrent load
}

@reminder[at: 3pm]{
  Team standup
}

@log[work, start: 09:00, end: 11:30]{
  Debugging the auth flow
}

@character[name: Dr. Alice, mentor, trusted]{
  Explained the layered caching approach in detail.
  Would consult again for architecture decisions.
}

@mps[sprint-42]{
  @task[backend]{
    Nested task inside a sprint block
  }
  @note{
    Retrospective note
  }
}
```

Brackets are optional — `@task{ body }` is valid. Tags and named attributes share the bracket: `[tag1, tag2, status: done, at: 5pm]`.

---

## Date formats

Accepted everywhere a DATE is expected:

| Input | Meaning |
|-------|---------|
| `today` | Today |
| `yesterday` | Yesterday |
| `monday` … `sunday` | Most recent occurrence of that weekday |
| `last friday` | The Friday before the most recent one |
| `3 days ago` | 3 days before today |
| `last week` | 7 days ago |
| `20260421` | Explicit YYYYMMDD |
| `2026-04-21` | Explicit YYYY-MM-DD |

---

## Configuration

Config file: `~/.mps_config.yaml`. Created automatically on first run. The same file written by the Ruby gem is accepted without changes (Ruby symbol-key YAML like `:storage_dir:` is handled transparently).

```yaml
mps_dir: /home/you/.mps
storage_dir: /home/you/.mps/mps
log_file: /home/you/.mps/mps.log
git_remote: origin
git_branch: master
default_command: list    # command run by bare `mps` invocation (open or list)

# Short-hand element type aliases (also accepts legacy key: aliases)
type_aliases:
  t: task
  n: note
  r: reminder
  l: log
  c: character

# Short-hand command aliases
command_aliases:
  a: append
  "+": append
  s: search
  l: list
```

With the above config:
- `mps a t "Fix the bug" --tags work` → appends a task (`a`→`append`, `t`→`task`)
- `mps + n "Interesting observation"` → appends a note
- `mps s "auth"` → searches for "auth"

Override the config path:

```bash
mps --config-path /path/to/other.yaml list
# or
MPS_CONFIG=/path/to/other.yaml mps list
```

---

## Architecture

```
src/
  main.rs        Entry point — alias pre-processing, clap dispatch
  cli.rs         Cli + Commands (#[derive(Parser)])
  config.rs      Config struct; YAML load/init; Ruby symbol-key normalization
  constants.rs   Filename regexes, new_file_name()
  date_parse.rs  parse_date() — natural-language + absolute formats
  error.rs       MpsError (thiserror)
  parser.rs      Position-based stack parser; mirrors Ruby Engines::Parser
  ref_resolver.rs Bidirectional epoch ↔ human ref (task-1, mps-1.2)
  store.rs       Store — all filesystem I/O; append, parse, search, rewrite
  elements/      Element enum + per-type Data structs
  commands/      One module per command + shared display helpers
```

---

## Requirements

- Rust 1.70+
- `git` (for `git` / `autogit` commands)
- `$EDITOR` or `$VISUAL` (for `open` / `config edit`; falls back to `vim`)

---

## License

MIT
