wiki-dashboard

>

INSTALLATION
npx skills add https://github.com/ar9av/obsidian-wiki --skill wiki-dashboard
Run in your project or agent environment. Adjust flags if your CLI version differs.

SKILL.md

$27

Top-level keys:

filters:      # Global filter applied to all views (expression strings under and/or/not)

formulas:     # Named computed properties — referenced as formula.<name>

properties:   # Display config per property — sets displayName for column headers

summaries:    # Aggregation formulas (e.g. mean, sum)

views:        # Array of view definitions (required)

Each item in views::

views:

  - type: table          # table | list | cards | map

    name: "View Name"    # display label

    limit: 50            # optional max rows

    order:               # column display order (list of property/formula names)

      - file.name

      - note.updated

    groupBy:             # grouping — goes INSIDE the view, NOT at top level

      property: note.tags

      direction: ASC     # ASC | DESC

    filters:             # view-specific filter (merges with global filters)

      and:

        - 'note.status != "done"'

    summaries:

      formula.myFormula: Average

Filter syntax — CRITICAL

Filters use expression strings, not typed objects. Always wrap in and:, or:, or not: — a bare list causes a "may only have one of and/or/not keys" parse error.

# CORRECT

filters:

  and:

    - file.inFolder("concepts")

# WRONG — typed objects (parse error)

filters:

  - type: folder

    folder: concepts

Filters support nesting:

filters:

  or:

    - file.hasTag("book")

    - and:

        - file.inFolder("concepts")

        - file.hasTag("research")

    - not:

        - file.hasTag("archived")

Property name conventions

Different contexts use different naming — confirmed from Obsidian's auto-reformat behaviour:

Context

Frontmatter field tags

File name

Formula

properties: keys

note.tags

file.name

formula.<name>

order: values

tags (bare)

file.name

formula.<name>

groupBy.property:

tags (bare)

file.name

filters: expressions

file.hasTag(...) / note.tags

file.name

formula.<name>

formulas: expressions

note.tags, note.updated

file.name

Basic table — folder filter

filters:

  and:

    - file.inFolder("concepts")

properties:

  file.name:

    displayName: Page

  note.tags:

    displayName: Tags

  note.summary:

    displayName: Summary

  note.updated:

    displayName: Updated

views:

  - type: table

    name: Table

    order:

      - file.name

      - tags

      - summary

      - updated

Cards view — folder filter

filters:

  and:

    - file.inFolder("entities")

properties:

  file.name:

    displayName: Entity

  note.title:

    displayName: Full Name

  note.tags:

    displayName: Tags

  note.summary:

    displayName: Summary

views:

  - type: cards

    name: Cards

    order:

      - file.name

      - title

      - tags

      - summary

Group by property — groupBy goes INSIDE the view

When groupBy is set, **omit that property from order:** — it becomes the group header row and adding it as a column too causes duplication.

filters:

  and:

    - file.inFolder("concepts")

properties:

  file.name:

    displayName: Concept

  note.summary:

    displayName: Summary

  note.updated:

    displayName: Updated

views:

  - type: table

    name: By Domain

    groupBy:

      property: tags        # bare property name, no note. prefix

      direction: ASC

    order:

      - file.name           # do NOT include tags here — already the group header

      - summary

      - updated

Tag filter

filters:

  and:

    - file.hasTag("machine-learning")

properties:

  file.name:

    displayName: Page

  note.category:

    displayName: Category

  note.summary:

    displayName: Summary

views:

  - type: table

    name: Table

    order:

      - file.name

      - category

      - summary

Multi-filter (folder AND tag)

filters:

  and:

    - file.inFolder("projects")

    - file.hasTag("active")

properties:

  file.name:

    displayName: Project

  note.summary:

    displayName: Summary

  note.updated:

    displayName: Last Updated

views:

  - type: cards

    name: Cards

    order:

      - file.name

      - summary

      - updated

OR filter (two folders)

filters:

  or:

    - file.inFolder("concepts")

    - file.inFolder("entities")

properties:

  file.name:

    displayName: Page

  note.category:

    displayName: Category

  note.updated:

    displayName: Updated

views:

  - type: table

    name: Table

    order:

      - file.name

      - category

      - updated

Computed column via formulas

filters:

  and:

    - file.inFolder("concepts")

formulas:

  days_stale: "floor((now() - note.updated) / 86400000)"

properties:

  file.name:

    displayName: Page

  note.updated:

    displayName: Updated

  formula.days_stale:

    displayName: Days Stale

views:

  - type: table

    name: Stale

    order:

      - file.name

      - updated

      - formula.days_stale

Filter expression reference

Expression

What it does

file.inFolder("path")

Pages in that folder

file.hasTag("tag")

Pages with that tag (no # prefix)

file.hasLink("Note Name")

Pages linking to a note

file.name == "note-name"

Exact filename match

file.ext == "md"

Filter by extension

note.propertyName

Any frontmatter property

formula.formulaName

A named formula result

now()

Current timestamp in ms

On Obsidian UI-generated format: When Obsidian's GUI writes or reformats a .base file it may output a simplified shorthand with top-level columns:, sort:, and view: keys instead of the canonical schema. That format also works — Obsidian accepts both. Manually authored files should use the canonical schema above.

Option B — Dataview (community plugin)

Dataview uses a SQL-like query language inside dataview code blocks in any note. More powerful than Bases for computed columns, GROUP BY, and cross-folder queries.

Basic table — folder

TABLE

tags AS "Tags",

summary AS "Summary",

file.mtime AS "Last Modified"

FROM "concepts"

SORT file.mtime DESC

Table with clickable links (TABLE WITHOUT ID)

TABLE WITHOUT ID

file.link AS "Entity",

tags AS "Tags",

summary AS "Summary"

FROM "entities"

SORT file.name ASC

GROUP BY — use rows. prefix after grouping

After GROUP BY, individual file properties must be prefixed with rows. — otherwise the column is empty or errors.

TABLE WITHOUT ID

rows.file.link AS "Concept",

rows.summary AS "Summary"

FROM "concepts"

GROUP BY tags[0] AS "Domain"

Stale pages — use file.mtime for date math

Avoid choice(updated, date(updated), file.mtime) — mixed date formats in updated frontmatter cause arithmetic errors. file.mtime is always a valid DateTime.

TABLE WITHOUT ID

file.link AS "Page",

category AS "Type",

file.mtime AS "Last Modified",

(date(today) - file.mtime).days + " days" AS "Age"

FROM "concepts" OR "entities" OR "projects"

WHERE file.name != file.folder

WHERE (date(today) - file.mtime).days > 30

SORT (date(today) - file.mtime).days DESC

Multi-folder query

TABLE

summary AS "Summary",

file.mtime AS "Last Modified"

FROM "projects"

WHERE file.name != file.folder

SORT file.mtime DESC

Dataview reference

Clause

Usage

FROM "folder"

All notes in folder

FROM #tag

All notes with tag

FROM "a" OR "b"

Union of two folders

WHERE file.name != file.folder

Exclude folder index pages

GROUP BY field AS "Label"

Group rows — use rows. for properties after this

SORT field DESC

Sort direction

file.link

Clickable wikilink

file.mtime

Last modified time (always valid DateTime)

(date(today) - file.mtime).days

Days since last modification

Step 3: Write the File

Bases: Target path $OBSIDIAN_VAULT_PATH/_meta/<dashboard-name>.base

Dataview: Write queries directly into any .md note. A dedicated dashboard note at $OBSIDIAN_VAULT_PATH/_meta/dashboard.md works well for multi-section views.

Slug examples:

  • "All concepts" → _meta/concepts-index.base
  • "Recent ingests" → _meta/recent-ingests.base
  • "Project overview" → _meta/projects-overview.base
  • "Stale pages" → _meta/stale-pages.base
  • "Full dashboard" → _meta/dashboard.md

Create _meta/ if it doesn't exist yet.

Step 4: Embed Bases (optional)

To embed a .base inside a note:

## Entities

![[_meta/entities-tracker.base]]

Ask before modifying an existing note.

Step 5: Update Tracking

Append to $OBSIDIAN_VAULT_PATH/log.md:

- [TIMESTAMP] WIKI_DASHBOARD name="<slug>" tool=bases|dataview view=<type> filter="<description>"

No manifest or index update needed — dashboards are live queries, not static pages.

Common Dashboard Recipes

Dashboard

Best tool

What it shows

Content index

Bases or Dataview

All pages grouped by category, sorted by updated

Entity tracker

Bases (cards)

Entity pages as a visual card gallery

Concepts by domain

Dataview

Concepts grouped by first tag using GROUP BY

Ingestion log

Either

Pages sorted by created date

Stale content

Dataview

Pages not touched in 30+ days with day count

Project overview

Either

Project pages with last-sync date

Research tracker

Dataview

Synthesis pages tagged research

Quality Checklist

  • Bases: filters use expression strings under and:/or:/not:, never typed objects
  • Bases: groupBy goes inside the view definition — not as a top-level key
  • Bases: column headers set via properties: <name>: displayName: "...", not columns: [{title}]
  • Bases: formulas: used for computed columns, referenced as formula.<name> in order/properties
  • Dataview: GROUP BY queries use rows.property not bare property
  • Dataview: date arithmetic uses file.mtime, not choice(updated, ...)
  • File written to _meta/ with a descriptive slug
  • log.md updated
  • User told how to embed Bases (![[_meta/<name>.base]]) or open the dashboard note

QMD Refresh After Vault Writes

QMD is a search index, not the source of truth. If $QMD_WIKI_COLLECTION is empty or unset, skip this step. Run it only after this skill has written or rewritten vault markdown. If QMD refresh fails, do not roll back the vault changes; report the QMD status separately.

Use $QMD_CLI if set; otherwise use qmd.

${QMD_CLI:-qmd} update

If the output says vectors are needed or embeddings may be stale, run:

${QMD_CLI:-qmd} embed

Verify the collection with either:

${QMD_CLI:-qmd} ls "$QMD_WIKI_COLLECTION"

or, when a specific page path is known:

${QMD_CLI:-qmd} get "qmd://$QMD_WIKI_COLLECTION/<page>.md" -l 5

Record one of:

  • QMD refreshed: update + embed + verified
  • QMD refreshed: update only + verified
  • QMD skipped: QMD_WIKI_COLLECTION unset
  • QMD skipped: qmd CLI unavailable
  • QMD failed: <short error summary>
BrowserAct

Let your agent run on any real-world website

Bypass CAPTCHA & anti-bot for free. Start local, scale to cloud.

Explore BrowserAct Skills →

Stop writing automation&scrapers

Install the CLI. Run your first Skill in 30 seconds. Scale when you're ready.

Start free
free · no credit card