npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

jira-dashboard

v0.1.6

Published

Full-screen terminal dashboard for Jira: 14-day worklog summary and your open tickets

Readme


jira-dashboard (jdb)

A full-screen terminal dashboard for Jira Cloud, built with Ink.

┌─ last 14 days · 41.5h total ──────┬─ my open tickets (3/12) ─────────────────┐
│ Tue 28 Jul   7.5h  MR-4           │ ▸ MR-3   In Review    Fix the flaky test │
│ Wed 29 Jul   6.0h  MR-1, MR-2     │   MR-4   In Progress  Add worklog panel  │
├───────────────────────────────────┴──────────────────────────────────────────┤
│ MR-3 · 5.5h over 14d · 4h = 1 bar │ MR-3 · .../browse/MR-3                   │
│                                   │ Platform › MR-100 Q3 push › MR-3         │
│       ██       ██                 │ Fix the flaky test                       │
│    ▄▄ ██ ▄▄    ██ ██              │ type       Bug                           │
│ 16 17 18 19 20 21 22 …            │ status     In Review                     │
└───────────────────────────────────┴──────────────────────────────────────────┘
  • Top left — hours you logged on each of the last 14 days (oldest first, so today is at the bottom), plus the issues you logged against.
  • Top right — your open tickets (not done, not cancelled/closed, not in the backlog), selectable by keyboard or mouse. Each row starts with a Jira-style priority arrow: highest, high, = medium, low, lowest, critical/blocker. Priorities the site names differently are left blank.
  • Bottom left — bar chart of the hours you logged on the selected ticket, day by day over the same 14 days. The scale is fixed so bars are comparable between tickets and between runs: 2h is half a row, 4h one row, 8h two rows; anything logged below 2h gets a thin mark so it stays visible.
  • Bottom right — preview of the selected ticket: parent breadcrumbs, status, assignee, reporter, priority, labels, the hours you logged and on which dates, total logged by everyone, and the description.

Install

npm install -g jira-dashboard
jdb

Configure

Everything comes from the Jira Cloud REST API, authenticated with an API token.

export JIRA_BASE_URL="https://your-team.atlassian.net"
export JIRA_EMAIL="[email protected]"
export JIRA_API_TOKEN="…"

Or write ~/.config/jira-dashboard/config.json (environment variables win):

{
  "baseUrl": "https://your-team.atlassian.net",
  "email": "[email protected]",
  "apiToken": "…",
  "issuesJql": "assignee = currentUser() AND statusCategory != Done AND status NOT IN (\"Cancelled\", \"Closed\", \"Backlog\") ORDER BY updated DESC",
  "worklogDays": 14,
  "holidayCountry": "UA",
  "holidayRegion": null
}

issuesJql is worth customising: status names vary per site, and some boards model the backlog by sprint rather than by status — in that case append AND sprint IS NOT EMPTY.

Non-working days

holidayCountry is an ISO-3166 alpha-2 code (UA, PL, US, …). Its statutory holidays are fetched from date.nager.at and cached in ~/.config/jira-dashboard/holidays.json; changing the country invalidates that cache, and each year is refreshed after 30 days. Both keys can be overridden with JIRA_HOLIDAY_COUNTRY and JIRA_HOLIDAY_REGION. If the fetch fails the dashboard falls back to the cache and starts regardless.

Many holidays are provincial or state-level rather than nationwide — Canada's Civic Holiday on the first Monday of August, for instance. Set holidayRegion ("CA-ON", "US-CA", …) and the ones covering your region become full days off, while the rest are hidden: a Yukon holiday is not your day off in Ontario.

Leave holidayRegion unset and there is no way to tell which are yours, so they are shown rather than skipped — dimmed and marked ~ instead of *, listing the regions that observe them; those do not excuse an empty timesheet:

 Mon 03 Aug~🎉   0.0h (Civic Holiday / British Columbia Day … · CA-ON, …)   # region unset
 Mon 03 Aug*🎉   0.0h (Civic Holiday)                                       # holidayRegion CA-ON
 Mon 17 Aug      7.5h JD-14, JD-22                                          # CA-YT only: hidden

Personal leave goes in ~/.config/jira-dashboard/days-off.json, which you maintain by hand:

["2026-08-10", { "date": "2026-08-11", "label": "moving day" }]

Both kinds of day show up in magenta in the two left panels — holidays marked *🎉, days off ·, holidays observed only in other regions dimmed and marked ~🎉 when no region is set — and an empty day off is never coloured red, so what is left in red is what still needs hours.

Keys

| Key | Action | | --- | --- | | / j k | Move within the focused panel | | Enter | Open the selected ticket in your default browser (tickets panel only) | | PgUp PgDn | Page through the focused panel | | Home / End | Jump to the first / last item in the focused panel | | g / G | Jump to first / last ticket | | Tab / Shift+Tab | Cycle panels (worklogs → tickets → preview) | | Click | Select a ticket / focus a panel | | Wheel | Scroll the panel under the cursor | | t | Toggle the full ticket tree (show epics/stories that are not assigned to you) | | r | Reload both panels | | q / Esc / Ctrl+C | Quit |

Development

npm install
npm run dev          # run from TypeScript source, no build step
npm run build        # bundle to dist/cli.js with esbuild
npm run typecheck    # tsc --noEmit
npm run check        # lint + typecheck + build; run before publishing
npm run lint
npm run lint:fix
npm run lint:staged  # lint-staged, for a pre-commit hook

To run lint:staged automatically, add a pre-commit hook that calls npm run lint:staged.

Build

npm run build bundles src/cli.tsx into a single ESM file at dist/cli.js and marks it executable. The shebang from the entry file is preserved, which is what lets jdb run directly.

npm run build
node dist/cli.js --version

Two properties of the bundle worth knowing:

  • Relative imports are written without a .js extension. Node's ESM loader would reject that, but esbuild resolves the specifiers at build time and tsx does the same in dev. tsc is typecheck-only (noEmit), so it can never emit an unresolvable import.
  • Runtime dependencies stay external (--packages=external), so only this project's own source is bundled; ink and react are installed normally from dependencies.

prepublishOnly runs npm run check, so a publish rebuilds dist/ and aborts on a lint or type error rather than shipping one.

Run it locally as a global command

For day-to-day use, symlink the global jdb at your working copy:

npm link              # jdb -> <this repo>/dist/cli.js
npm run build         # after each source change; jdb picks it up immediately

To test what users actually get instead — a frozen copy built from the real tarball:

npm pack
npm install -g ./jira-dashboard-0.1.0.tgz
jdb --version

Undo either with npm uninstall -g jira-dashboard (or npm unlink -g jira-dashboard).

Debugging

Never use console.log. Stdout is the UI: writing to it corrupts the frame Ink is drawing. Log to stderr and redirect it to a file, then watch that file from another terminal:

# in the code: process.stderr.write(`selected=${key}\n`);
jdb 2> /tmp/jdb.log
tail -f /tmp/jdb.log      # in a second terminal

Readable stack traces. dist/cli.js is a bundle, so traces point at bundle offsets by default. The build emits a source map; turn it on to get original file and line numbers:

node --enable-source-maps dist/cli.js

Breakpoints. Attach a debugger and open chrome://inspect, or use your editor's Node attach config. Run from source so you are stepping through TypeScript rather than the bundle:

node --inspect-brk --import tsx src/cli.tsx

Develop without hitting real Jira. The app talks to nothing but the REST API, so pointing it at a local stub is enough to work offline and to exercise edge cases (no worklogs, a ticket with no sprint, a 401). Serve JSON for /rest/api/3/myself, /rest/api/3/field, /rest/api/3/search/jql, and /rest/api/3/issue/:key/worklog, then:

JIRA_BASE_URL=http://localhost:7311 [email protected] JIRA_API_TOKEN=t npm run dev

Inspect the rendering non-interactively. Handy in a script or when a layout bug only appears at a particular size — tmux gives you the finished frame as text:

tmux new-session -d -s jdb -x 130 -y 40 'jdb'
sleep 4
tmux capture-pane -p -t jdb     # prints the rendered screen
tmux kill-session -t jdb

Common failure modes: if the terminal is left in a broken state after a crash, run reset. If panel content wraps or the panel title disappears, a fixed-width panel is being flex-shrunk or its content is one row taller than its height — see PanelFrame and the row-count arithmetic in each panel.

Publishing to npm

The package publishes as jira-dashboard and installs the jdb command. Only dist/ and README.md ship (files in package.json); source and configs stay out of the tarball.

# 1. Authenticate (once per machine)
npm login
npm whoami

# 2. Make sure it is releasable (lint + typecheck + build)
npm run check

# 3. Inspect exactly what will be uploaded — no files are published by this
npm pack --dry-run

# 4. Bump the version (also creates a git tag when this is a git repo;
#    add --no-git-tag-version if it is not, or if you tag separately)
npm version patch      # or: minor | major

# 5. Publish. prepublishOnly rebuilds dist/ first.
npm publish

# 6. Verify the published artifact
npm view jira-dashboard version
npx --yes jira-dashboard@latest --version

Notes:

  • The name jira-dashboard was unclaimed on the public registry as of this writing, but that can change at any time. If npm publish fails with E403, the name is taken — publish under a scope instead (@yourname/jira-dashboard), which additionally requires npm publish --access public since scoped packages default to private.
  • Publishing is effectively permanent. Unpublishing is only allowed within 72 hours and a version number can never be reused. Prefer npm deprecate [email protected] "reason" for a bad release and ship a fix as a new version.
  • engines requires Node ≥ 20 (the code uses AbortSignal.timeout and built-in fetch).
  • To rehearse the whole flow without touching the public registry, publish to a local registry such as Verdaccio: npm publish --registry http://localhost:4873.