Skip to content

Add custom integrations: user-defined dashboard sections - #6

Merged
shayan-ys merged 4 commits into
mainfrom
feat/custom-integrations
Oct 9, 2026
Merged

shayan-ys merged 4 commits into
mainfrom
feat/custom-integrations

Conversation

@shayan-ys

@shayan-ys shayan-ys commented Oct 9, 2026 •

Copy link
Copy Markdown
Owner

📝 Summary

This PR adds custom integrations: users can declare any number of their own dashboard sections next to USAGE, MY PRS, and REVIEW REQUESTED. Each section is a command whose output devdash shows, in a slot the user picks, so weather, service health, or a tool such as prompt-tutor can live in the same pane without devdash knowing about them.

Related Issue: Closes #5


🛠️ Type of Change

  • 🐛 Bug fix (non-breaking change which fixes an issue)
  • ✨ New feature (non-breaking change which adds functionality)
  • 💥 Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • ⚡ Performance / Refactor (code optimization without behavior changes)
  • 📚 Documentation update

🔍 Changes Made

  • Contract: an integration is a command (argv list, no shell) that prints its section body to stdout and exits 0. devdash passes the pane width in COLUMNS and the row budget in LINES. Colours (SGR) and hyperlinks (OSC 8) are kept; cursor movement, screen clears, and other escape sequences are dropped; output past max_rows ends in … N more rows.
  • Configuration: [[integrations]] tables with name, title, command, position, interval, timeout, max_rows, enabled, env, validated like the rest of the config (unknown keys, wrong types, duplicate names, and bad positions stop devdash). --no-integration NAME (repeatable) and --no-integrations mirror --no-usage / --no-review.
  • Placement: top, after-usage, after-my-prs, and bottom slots; integrations sharing a slot keep file order. The built-in sections were split into render_mine / render_review so Dashboard assembles a list of sections separated by rules; the default layout is unchanged (README screenshot regenerates byte-identical apart from the clock).
  • Safety and isolation: each integration runs on its own thread and schedule, with stdin from /dev/null and in its own session. A timeout kills the whole process group, and so does an unexpected capture error. interval and timeout are capped at one day. Output is read incrementally with selectors: at most 64 KiB of stdout (a command that prints more is stopped there) and the last 4 KiB of stderr, so a noisy command cannot grow devdash's memory. Leftover control characters are replaced with �, links whose target holds a control character are dropped, and the stderr-derived error line is plain text, so a command cannot clear or move the dashboard. A failure shows ⚠ with the reason and keeps the last good output under it; rows, time, and error are published as one tuple, so a frame never mixes two runs. Quitting with q, Ctrl-C, SIGHUP (closing the pane), or SIGTERM kills running integration commands, in live mode and --once alike. r reruns every integration immediately; a pane resize reruns them at the new width; --once runs them in parallel.
  • Docs: README "Custom integrations" section and troubleshooting entries, config.example.toml, CHANGELOG.

🧪 How to Test

  1. git checkout feat/custom-integrations
  2. Add to ~/.config/devdash/config.toml:
    [[integrations]]
    name = "clock"
    command = ["date", "+%H:%M %Z"]
    position = "top"
    
    [[integrations]]
    name = "slow"
    command = ["sh", "-c", "sleep 30"]
    timeout = 2
  3. uv run devdash --once: CLOCK is first, SLOW shows ⚠ timed out after 2s, and the frame is not delayed beyond the timeout.
  4. uv run devdash, press r (sections rerun) and q while slow is running: pgrep -f "sleep 30" finds nothing afterwards.
  5. uv run devdash --once --no-integration slow hides SLOW.

✅ Review and verification

  • Docs: README "Custom integrations" has a worked prompt-tutor example: install, a fit check, the config table, and the PATH fallback (e0b4ff2).
  • Review path: material change (runs user commands, threads, signals), agent code review; CodeRabbit is not installed on this repository (no CodeRabbit status or comment on this PR or on feat: show usage pace icons on weekly and monthly bars #1–fix: score CI from the latest Actions workflow run #4), so no CodeRabbit result exists.
  • Verification:
    • uv run ruff check . — clean; uv run pytest — 82 passed; CI green on macOS and Ubuntu, Python 3.12–3.14. New tests cover config rejection (including names with a trailing newline and out-of-range interval/timeout), slot order, colour/link retention, hostile control and link sequences, plain-text error lines, last-good output under errors, timeout killing child processes, an unexpected capture error still killing the command, stdout flood stopped at the cap, stderr tail retention, and stop() killing a running command and its children.
    • devdash --once against a real account with a hanging, a failing, an endless (yes), and a real (prompt-tutor --once) integration: each rendered as described above; the hanging one did not delay the frame and left no processes.
    • Live mode in a pty: integrations show loading… then output, r refreshes, q exits 0 with the terminal restored.
    • Exit paths while an integration is mid-run (3 processes): --once with SIGTERM, SIGHUP, and SIGINT, and live mode with q and SIGHUP, each exit 0 and leave 0 processes.
  • Accepted review findings (fixed in dd98f8c): stray control characters and hostile link targets reached the terminal; --once and early startup skipped command cleanup; unbounded interval/timeout could crash a polling thread, and an unexpected capture error could orphan a command; a frame could mix two runs' rows, time, and error; a trailing newline passed the name rule; process tests treated zombies as alive and had tight startup timing.
  • Declined findings: none.
  • Remaining non-blocking findings: none.

Each [[integrations]] table runs a command and shows its output in a chosen
slot (top, after-usage, after-my-prs, bottom). Integrations run on their own
threads and schedules, with a timeout that kills the whole process group,
bounded output capture, and last-good output kept under errors. Quitting or
closing the pane kills running integration commands.

Closes #5
@shayan-ys

Copy link
Copy Markdown
Owner Author

@coderabbitai review

- Replace leftover control characters in command output and drop links
  whose target holds one; show stderr-derived errors as plain text.
- Stop running integration commands on every exit path, --once included,
  and on SIGHUP/SIGTERM; kill a command when capture fails unexpectedly.
- Bound interval and timeout to a day; publish rows, time, and error as
  one tuple so a frame never mixes two runs; reject names with a trailing
  newline.
@shayan-ys
shayan-ys marked this pull request as ready for review October 9, 2026 04:28
@shayan-ys
shayan-ys merged commit de25afc into main Oct 9, 2026
6 checks passed
@shayan-ys
shayan-ys deleted the feat/custom-integrations branch October 12, 2026 13:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Custom integrations: let users add their own dashboard sections

1 participant