# 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
| `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.
| `--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`).
| `--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`).
| `--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.
| `--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.
| `--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.
| `--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.
| `--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:
| `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