Skip to content
Back to skills

Dashboard

ASecurity

Render an application statusboard from a reconciled YAML tracker and (on Claude Code) drive an interactive navigate-and-act loop over the application pipeline

  • 25 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added June 2, 2026
databasesrustgotestinggit

Works with

  • claude code

Security analysis

A100/100

Scanned June 2, 2026

npx -y skills add reggiechan74/JobOps --skill dashboard --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Dashboard?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Dashboard
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/reggiechan74-dashboard/badge)](https://www.skillsdirectory.com/skills/reggiechan74-dashboard)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: dashboard
description: Render an application statusboard from a reconciled YAML tracker and (on Claude Code) drive an interactive navigate-and-act loop over the application pipeline
disable-model-invocation: true
argument-hint: "[--board-only] [--fast]"
---

# JobOps Application Dashboard

Reconciles `tracker.yaml` from the filesystem, renders a statusboard, and — on
Claude Code — drives a two-level menu loop (pick an application, then pick an
action). On Codex it renders a read-only board with suggested next actions.

**Flags:**
- `--board-only` — reconcile and render the board, then stop (no menu loop) on any platform.
- `--fast` — skip the reconcile entirely: render the board from the tracker **exactly as
  stored**, then stop (no scan, no tracker write, no menu loop). Use for a quick read-only
  glance; artifact flags and `next_action` reflect the last full reconcile, not the current
  filesystem. Takes precedence over `--board-only` (both stop after the board). If `--fast`
  and `--board-only` are passed together, `--fast` wins.

---

## Configuration

Read `.jobops/config.json`. If missing, stop with:

> JOBOPS NOT CONFIGURED
> Run /jobops:setup to initialize your workspace.

Use `config.directories.<key>` for all file paths in this skill.

**Tracker path resolution (self-heal):** read `config.directories.application_tracker`.
If that key is absent, set it to `{config.directories.applications_root}/tracker.yaml`,
load the full existing config object, add only `directories.application_tracker` (leaving
every other key — `directories.*`, `preferences`, `candidate`, `templates`, `migration` —
untouched), and write the whole object back atomically to `.jobops/config.json.tmp` in the
same directory, then `mv` over `.jobops/config.json`, and use that path. This lets
workspaces created before the dashboard existed work with no migration step.

Under `--fast`, resolve the path **read-only**: if the key is absent, use the default
`{config.directories.applications_root}/tracker.yaml` without writing config back (a fast
glance never mutates state). If no tracker file exists at the resolved path, print
`No tracker yet — run /jobops:dashboard (without --fast) to build it.` and exit.

---

## Reconcile

Run this every invocation, before rendering — **unless `--fast` was passed**, in which case
skip this entire section and render the board directly from the stored tracker (no scan, no
recompute, no write).

1. **Load** the existing tracker YAML at the resolved path, or start with
   `{version: 1, applications: []}` if the file does not exist.
2. **Scan** `config.directories.applications_root` for immediate sub-directories whose
   name is not dot-prefixed. Each such folder is an application keyed by its name (`slug`).
   For each slug, set the `artifacts` map using the resolution order below — **first match
   wins**. The canonical path is checked first; the `output_type` YAML front-matter key is the
   **durable** fallback (trust it over the filename); a filename glob is the last-resort safety
   net for files written before producers were standardized. Scan the **whole app folder**
   (recursively), not just the expected sub-folder, so flat-layout legacy apps still register.
   When testing `output_type`, read the first front-matter block (the file's first ~30 lines).
   | Flag | True when (first match wins) |
   |---|---|
   | `assessment` | `assessment/assessment.md` exists, **OR** any `.md` in the app has `output_type: assessment` |
   | `resume_draft` | `resume/step1_draft.md` or `resume/step2_provenance.md` exists, **OR** any `.md` has `output_type ∈ {resume_step1, resume_provenance}`, **OR** glob `resume/*step1*.md`, `resume/*draft*.md`, `resume/*provenance*.md` matches |
   | `resume_final` | `resume/step3_final.md` exists, **OR** any `.md` has `output_type: resume_final`, **OR** glob `resume/*step3*final*.md` / `resume/*final*.md` matches (excluding `*provenance*`) |
   | `cover_letter` | `cover-letter/cover_letter.md` exists, **OR** any `.md` has `output_type: cover_letter`, **OR** glob `cover-letter/*cover*letter*.md` matches |
   | `osint` | the detected company folder (below) exists under `company_intelligence` |
   | `briefing` | glob `interview/briefing*.md` matches, **OR** any `.md` has `output_type: briefing` |
   | `interview_prep` | glob `interview/interview_prep*.md` matches, **OR** any `.md` has `output_type: interview_prep` |

   If `applications_root` does not exist or contains no application folders, treat the scan
   as yielding zero apps (do not error) and continue — existing tracker entries are still
   archived per step 5 and an empty board is rendered.
3. **Detect company / role** for each slug:
   - Drop a trailing 8-digit date token (`_YYYYMMDD`) from the slug. The remainder,
     with `_` → space, is the **humanized title**. A folder whose name has no trailing
     8-digit date is still a valid app; its full humanized name becomes the title.
   - List sub-folders of `company_intelligence`. Slugify each folder name (spaces, `&`,
     `-`, `,` → `_`; collapse repeats; trim `_`) and lowercase. If a slugified folder name
     `C` satisfies `lower(slug) == C` or `lower(slug)` starts with `C + "_"`, the company is
     that folder's name and `osint` is true. Otherwise `company` is `""`.
   - `role`: if a company was detected, the humanized title with the company's leading
     tokens removed; otherwise the full humanized title.
4. **Merge** each scanned slug into the tracker:
   - **Existing slug:** keep every human-editable field verbatim — `company`, `role`,
     `stage`, `applied_date`, `next_deadline`, `contact`, `outcome`, `notes`. (Role
     humanized from a slug is lossy, e.g. `SeniorPM`, so the user's refined `company`/`role`
     are preserved once set.) Overwrite only `artifacts` and `next_action` from the scan.
   - **New slug:** append `{slug, company, role, stage: lead, applied_date: null,
     next_deadline: null, contact: null, outcome: null, notes: null, artifacts, next_action}`
     using the derived `company`/`role`. `lead` is the default stage in the absence of any
     other information — the user advances it via **Update status** as the application
     progresses.
5. **Archive** tracker entries whose slug no longer has a folder: set `stage: archived`,
   all `artifacts` false, `next_action: none`. Keep all human-status fields.
6. **Compute `next_action`** (first false wins; skip for archived → `none`):
   ```
   assessment    == false → assessjob
   resume_final  == false → buildresume
   cover_letter  == false → coverletter
   osint         == false → osint
   briefing      == false → briefing
   interview_prep== false → interviewprep
   else                   → record-outcome
   ```
7. **Sort** `applications` by `slug` ascending. Set top-level `generated` to the current
   UTC time in ISO-8601 (`YYYY-MM-DDTHH:MM:SSZ`).
8. **Write** the YAML back atomically: write `tracker.yaml.tmp` in the same directory as
   `tracker.yaml`, then `mv` it over `tracker.yaml` so the rename is atomic. Emit the two zones in
   this field order per entry: `slug, company, role, stage, applied_date, next_deadline,
   contact, outcome, notes, artifacts, next_action`; the `artifacts` map in this order:
   `assessment, resume_draft, resume_final, cover_letter, osint, briefing, interview_prep`.

---

## Board render

After reconcile (or, under `--fast`, directly from the stored tracker — the header's
`reconciled {generated}` then reflects the *last* full reconcile, signalling staleness),
print a summary line then a table sorted by stage rank
(`interviewing, offer, applied, preparing, lead, on_hold, withdrawn, rejected, archived`),
then by `next_deadline` ascending (nulls last). Count of non-archived apps in the header.

```
JobOps Application Tracker · {N} active · reconciled {generated}

Stage          Company / Role             Pipeline       Next             Deadline
─────────────  ─────────────────────────  ─────────────  ───────────────  ──────────
{stage}        {company / role}           {dots}         {next_action}    {deadline}{warn}

Pipeline dots: assess▸resume▸cover▸osint▸briefing▸prep
```

- **Company / Role cell:** `"{company} / {role}"` when company is non-empty, else the
  humanized title alone.
- **Dots** — six glyphs in order `assess · resume · cover · osint · briefing · prep`:
  `●` when that flag is true; for resume use `◐` when `resume_draft && !resume_final`;
  `○` otherwise. Archived rows show `——`.
- **Deadline:** `next_deadline` as `Mon D`, or `—` if null. Append ` ⚠` when the deadline
  is within 3 days of `generated`.

---

## Interaction

If `--board-only` or `--fast` was passed, stop after the board (no menu loop).

**Claude Code (AskUserQuestion available):** loop until the user backs out.
1. Render the board.
2. **Navigate** — ask one `AskUserQuestion` whose options are the up-to-3 apps with a
   pending `next_action` (i.e. not `record-outcome`/`none`), sorted by `next_deadline`
   ascending. Label `"{company-or-title} — {role}"`; description `"{stage} · next: {next_action}"`.
   The tool's auto "Other" lets the user type a slug or table row number for any other app.
   Resolve the selection to one application. Apps already at `record-outcome` are reachable
   only via the auto "Other" option (the navigate picker excludes them); selecting one jumps
   straight to the outcome fields.
3. **Act** — ask a second `AskUserQuestion` with ≤4 options:
   - `Run next: {next_action}` — invoke that skill (`/jobops:{next_action}`, or for
     `record-outcome` go straight to the outcome update below), passing the app folder
     `{applications_root}/{slug}` as context. On return, re-run **Reconcile** and loop.
   - `Update status` — ask follow-up `AskUserQuestion`(s) for the field to change
     (`stage`, `next_deadline`, `contact`, `outcome`, `notes`), write the new value into
     that app's human-status zone, re-write the YAML atomically, and loop. For `stage`, the
     choices are the pipeline stages in lifecycle order — `lead → preparing → applied →
     interviewing → offer` — plus the side states `on_hold`, `withdrawn`, and `rejected`
     (`archived` is set automatically when the app folder disappears, not chosen here).
   - `Open folder` — print `{applications_root}/{slug}` and list its contents.
   - `Back to board` — redraw the board; if chosen again at the top level, exit.

**Codex (no AskUserQuestion):** render the board, then a numbered list of the apps with a
pending `next_action` and the suggested skill for each, and stop with:

> Invoke the suggested skill manually, e.g. `/jobops:buildresume`.

---

## Notes

- This skill is the only reader/writer of `tracker.yaml`. Other skills never touch it;
  their outputs are picked up on the next reconcile.
- Never delete tracker entries — archive them, preserving human-status fields.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.