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). Fully backward-compatible with all Ruby-generated files and config.
Install
The installed binary is named mps.
From source
Quick start
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.
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 |
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 |
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) |
mps done REFPATH [--date DATE]
Shorthand for mps update REFPATH --status done.
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 |
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 |
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 |
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
mps config [show|edit]
View or edit configuration.
mps git ARGS / mps autogit / mps cmd ARGS
Run git or shell commands inside the storage directory.
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).
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 notemps s "auth"→ searches for "auth"
Override the config path:
# or
MPS_CONFIG=/path/to/other.yaml
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(forgit/autogitcommands)$EDITORor$VISUAL(foropen/config edit; falls back tovim)
License
MIT