Skip to content
Merged
Show file tree
Hide file tree
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
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ All notable changes to this project are documented here. The format follows
its own thread and schedule, with a timeout that kills its whole process group, and a
failure keeps its last good output on screen under the error. `--no-integration NAME`
hides one integration and `--no-integrations` hides all of them.
- Configurable, case-sensitive built-in keys and per-integration actions, with private state files in watch mode,
per-section key hints, and fetch countdowns on section headings.
- `--profile NAME` selects the matching isolated omp profile for usage and cached readings;
only that profile's authenticated providers appear. Provider headings include that
provider's account email in white, for example `Codex (user@example.com)`.
Expand All @@ -25,6 +27,14 @@ All notable changes to this project are documented here. The format follows
- Pace icons on weekly and monthly usage bars: `>` to `>>>` when you use a quota faster than
time passes, `<` to `<<<` when slower, `|` on pace. Turn them off with `usage.pace = false`.

### Changed

- Built-in keys are case-sensitive; uppercase `R` and `Q` no longer trigger refresh or quit.
- Global PR and usage refreshes run in the background; input stays responsive while they fetch.
Pressing refresh during an active refresh schedules one follow-up.
- Section headings continue to show `fetching…` during a fetch. Each section lists its own keys;
the bottom row lists only quit and focus.

### Fixed

- CI badge uses the latest Actions run of each workflow, then the latest check of each
Expand Down
29 changes: 29 additions & 0 deletions GLOSSARY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# devdash

A terminal dashboard that combines its own sections with output from configured integrations.

## Language

**Integration**:
A configured command whose output forms one dashboard section.
_Avoid_: plugin, widget

**Built-in key**:
A devdash key for refresh, quit, or focus.
_Avoid_: integration binding, action

**Binding**:
A key mapped to an action in one integration.
_Avoid_: built-in key, shortcut

**Action**:
A name an integration defines and understands as a response to a binding.
_Avoid_: command, key

**Focus**:
The one integration that receives binding keys.
_Avoid_: selection, active section

**State file**:
An integration's state for one devdash process.
_Avoid_: cache, configuration file
62 changes: 53 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,11 @@ Every setting is optional. devdash reads a [TOML](https://toml.io) file from the
```toml
interval = 60 # seconds between refreshes

[keys]
refresh = "r" # "" disables a built-in; Ctrl-C always quits
quit = "q"
focus = "tab"

[github]
exclude = ["acme/monorepo", "some-org"] # "owner/repo", or "owner" for all of its repositories
review_requested = true # show the REVIEW REQUESTED section
Expand Down Expand Up @@ -184,6 +189,44 @@ env = { WTTR_LANG = "en" } # extra environment variables for the comma

Integrations that share a position appear in the order they are listed in the file.

### Keys, actions, focus, and state

In watch mode, `[keys]` configures the built-in `refresh`, `quit`, and `focus` keys. An empty
string disables a built-in. Key names are case-sensitive: one printable ASCII character other
than space, or `up`, `down`, `left`, `right`, `enter`, or `tab`. Arrow keys and Enter/Tab are
decoded by devdash; Ctrl-C always quits.

An integration can declare `keys = { j = "newer", k = "older" }`, mapping keys to action names
that the integration itself understands. Bindings are case-sensitive; they cannot use a key
assigned to an enabled built-in. Remap that built-in in `[keys]` to resolve a conflict. Two
integrations can use the same binding; only the focused one receives it. Focus starts on the
first integration with bindings in screen order, and the configured focus key cycles through
those integrations. Each section lists its own keys on its last row: an integration's bindings in
config order under its output, and the refresh key under the last PR section. The bottom row lists
the keys for the whole dashboard: quit, and the focus key when two or more integrations are
focusable. The focused section's heading starts with `▸ `.

Section headings show how long ago the last result was fetched and count down to the next fetch,
updating every second. Integrations use their own intervals; MY PRS, REVIEW REQUESTED, and USAGE
use the global interval. While a section is fetching, its heading says
`fetching…`. `--once` shows no countdown.

Global PR and usage refreshes run in the background, so input remains responsive. Pressing `r`
while one is running schedules one follow-up refresh; key presses after `r` in the same input batch
are still handled.

Each binding press runs the integration's command again with `DEVDASH_ACTION` set to its action.
Other runs have no `DEVDASH_ACTION`, even if it was inherited from the environment. Presses queue
in order (up to eight waiting actions); a run already in progress finishes first, and queued
actions run before the next interval refresh. The interval starts at the end of the last run.
`refresh` wakes all integrations, and any run satisfies that request.

In watch mode, an enabled integration that declares a `keys` table receives `DEVDASH_STATE_FILE`
pointing to an initially empty file in a private directory. The file is mode `0600`; its directory
is mode `0700`. devdash never reads the file: the integration owns its format and can use it to
preserve its view or selection between command runs. State lasts only for one devdash process.
`--once` and non-TTY runs set neither `DEVDASH_ACTION` nor `DEVDASH_STATE_FILE`.

**What a command must do.** Print the section body and exit with status 0. Any language works:
a shell script, a Python file, or an existing CLI with a one-shot mode. devdash sets `COLUMNS`
to the pane width and `LINES` to `max_rows`, so the command can fit its output to the room it has;
Expand All @@ -205,16 +248,18 @@ devdash runs the `command` list directly, without a shell; to use pipes or `&&`,
integration runs on its own thread, with no standard input and in its own process group, so a
slow or hung command cannot freeze the dashboard or read your keystrokes. A timeout kills the
command together with every process it started that stayed in its process group; a program
that starts its own session (a daemon, for example) escapes this. Quitting devdash, with `q`,
Ctrl-C, or by closing the pane, kills running commands the same way. Pressing `r` runs every
waiting integration again at once. With `--once`, devdash runs all integrations in parallel,
each within its own timeout, alongside the built-in GitHub and usage reads.
that starts its own session (a daemon, for example) escapes this. Quitting devdash with the
configured quit key, Ctrl-C, or by closing the pane kills running commands the same way. Pressing
the configured refresh key runs every waiting integration again at once. With `--once`, devdash
runs all integrations in parallel, each within its own timeout, alongside the built-in GitHub
and usage reads.

### Example: prompt-tutor

[prompt-tutor](https://github.com/shayan-ys/prompt-tutor) reviews the English of the prompts you
send to omp, and its Watcher shows the latest Review. Its one-shot mode, `prompt-tutor --once`,
prints one frame sized to `COLUMNS` and `LINES` and exits, so it works as an integration as is.
The key actions and state-file behavior below require a prompt-tutor build with devdash key support.

1. Install prompt-tutor and put its Watcher on your `PATH`. Until prompt-tutor's first build is
merged into its `main` branch, `main` holds only planning documents, so install the
Expand Down Expand Up @@ -242,15 +287,14 @@ prints one frame sized to `COLUMNS` and `LINES` and exits, so it works as an int
command = ["prompt-tutor", "--once"]
position = "bottom"
max_rows = 16
keys = { j = "newer", k = "older", s = "scope", J = "scroll-down", K = "scroll-up" }
```

If devdash shows `⚠ cannot run prompt-tutor`, the Watcher link is not on the `PATH` that
devdash sees; use its full path, for example `command = ["~/.local/bin/prompt-tutor", "--once"]`.
4. Run `devdash`. The section updates on the global interval; press `r` to refresh it after a
prompt, and use `devdash --no-integration prompt-tutor` to hide it for one run.

The frame's last row lists the Watcher's own keys (`j/k`, `s`, `q`). They work only in the
standalone `prompt-tutor` Watcher, not inside devdash.
4. Run `devdash`. The section updates on the global interval; press `j`/`k` for newer/older Prompts,
`s` to change Scope, and `J`/`K` to scroll the Watcher frame. The integration keeps its view and
selection in the state file. Press `r` to refresh the dashboard, and use `devdash --no-integration prompt-tutor` to hide it.

## Troubleshooting

Expand Down
9 changes: 9 additions & 0 deletions config.example.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@
# Seconds between refreshes.
interval = 60

# Interactive keys. Empty strings disable a built-in; Ctrl-C still quits.
[keys]
refresh = "r"
quit = "q"
focus = "tab"

[github]
# Repositories to hide from both PR sections. An entry is "owner/repo" for one repository,
# or a bare "owner" for every repository of that user or organization.
Expand Down Expand Up @@ -50,3 +56,6 @@ order = [] # e.g. ["openai-codex", "anthropic"]
# max_rows = 20 # longer output is cut
# enabled = true
# env = {} # extra environment variables, e.g. { CITY = "Toronto" }
# Integration bindings map keys to action names the command understands.
# Example for a build with prompt-tutor key support:
# keys = { j = "newer", k = "older", s = "scope", J = "scroll-down", K = "scroll-up" }
Loading
Loading