Create Jira Dashboard
This skill turns the current user's assigned Jira issues (status To Do or In Progress) into a
self-contained HTML dashboard and publishes it as a nano-brain application. The published app is
upserted: the first run creates it, every later run updates the same application (new version,
same id / subdomain / URL) — it never creates a second app.
What gets extracted (per ticket)
key(e.g.AS-8721),url(browse link),summarystatus(To Do / In Progress),type,priority,project(AS / ARO)assignee,reporter,created,updatedlabels,componentsdescription(ADF → plain text)comments(author, created, body) — used to synthesize a "current status" line- A derived
statusHtml/statusPreviewso the dashboard shows where each ticket stands without
opening Jira.
Pipeline (all under scripts/)
| Script | Purpose |
|---|---|
fetch_tickets.py | Calls the Jira REST API (/rest/api/3/search) for assignee = currentUser() AND status in ("To Do","In Progress"), normalizes each issue, and writes tickets.json. |
build_spa.py | Injects tickets.json into spa_shell.html (replacing __TICKETS_JSON__) and writes the dashboard index.html. |
publish_app.py | Upserts the nano-brain app: reads ~/.config/create-jira-dashboard/state.json to find the existing app id; if present it PUT apps/save (new version), otherwise POST apps/create. Then deploys and refreshes the public share. |
run_all.py | Runs fetch → build → publish end to end. This is the entry point. |
How to run it
From the skill's scripts/ directory (or pass --workdir):
# Full pipeline: fetch → build → upsert app
python3 run_all.py
# Only refresh tickets + rebuild, do not publish
python3 run_all.py --build-only
# Only re-fetch tickets (writes tickets.json)
python3 run_all.py --tickets-only
# Custom JQL / skip a ticket
python3 run_all.py --jql 'assignee = currentUser() AND status = "In Progress"' --skip-keys AS-5257
The skill can also be invoked step by step:
python3 fetch_tickets.py -o tickets.json --skip-keys AS-5257
python3 build_spa.py tickets.json -o index.html
python3 publish_app.py index.html --state ~/.config/create-jira-dashboard/state.json -o app_publish.json
Credentials / environment
Jira (fetch_tickets.py):
JIRA_BASE_URL(defaulthttps://2befound.atlassian.net)JIRA_EMAIL/ATLASSIAN_EMAIL(defaultdpr@alpinresorts.com)JIRA_API_TOKEN/ATLASSIAN_API_TOKEN, or the token stored in the macOS keychain
under account <email> service jira (or Atlassian).
nano-brain (publish_app.py):
NANO_BRAIN_BASE_URL(defaulthttps://nano-brain.aopsi.com)NANO_BRAIN_TOKEN/NANO_BRAIN_API_TOKEN, or a token file at
~/.config/create-jira-dashboard/token.
The upsert guarantee (most important)
publish_app.py persists an application id to ~/.config/create-jira-dashboard/state.json on the
first successful publish. On every subsequent run it:
- Looks up the saved
application_idviaPOST apps/get. - If found →
PUT apps/savewith the newindex.html+main.py(bumps the version, same app). - If not found (or state missing) →
POST apps/create, then sets visibilitypublic, assigns the
subdomain jira-tickets, and creates a public share.
So re-running the skill always updates the existing app and keeps the same URL
(https://jira-tickets.aopsi.com/) and share link. It only creates a new app when no state exists.
Output
run_all.py writes app_publish.json (also echoed to stdout) containing:
{
"action": "updated", // or "created" on first run
"application_id": "019f6b38-…",
"name": "My Jira Tickets",
"version": 2,
"deploy_status": "running",
"app_url": "https://jira-tickets.aopsi.com/",
"domain": "jira-tickets.aopsi.com",
"share_url": "https://nano-brain.aopsi.com/api/admin/public/plugin-routes/artifact/appview?token=…"
}
Hand the app_url (or share_url) back to the user as the live dashboard link.
Dashboard behavior notes
- The SPA uses in-memory navigation (
showDetail/showListtoggling#view-detail/
#view-list). It does not call history.pushState — this is required so the page works inside
sandboxed srcdoc / iframe viewers where pushState throws a SecurityError. publish_app.py
warns if the built HTML contains history.pushState.
- Cards are clickable → detail view with a breadcrumb (
My Tickets / AS-8721) and a Back button. - Each ticket links out to its real Jira issue (
<a data-jira>), which opens in a new tab. AS-5257is excluded by default via--skip-keys(override with--skip-keys "").
Troubleshooting
Missing Jira credentials→ setJIRA_EMAIL+JIRA_API_TOKEN(or keychain).Missing nano-brain token→ setNANO_BRAIN_TOKENor write it to
~/.config/create-jira-dashboard/token.
- App stuck
deploying→publish_app.pywaits up to 180s; pass--no-waitto return early and
check app_url later.
- Want a fresh app → delete
~/.config/create-jira-dashboard/state.json(next run creates new).