Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 109 additions & 0 deletions .claude/skills/migrate-dashboard/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
---
name: migrate-dashboard
description: Migrate an exported platform dashboard JSON to plugin format. Use when the user pastes a dashboard JSON to convert, says "migrate this dashboard", or wants to add an exported dashboard to a plugin as default content.
model: sonnet
metadata:
author: SquaredUp
version: "0.0.1"
---

# Migrate Dashboard to Plugin Format

Convert a platform-exported dashboard JSON to plugin format by normalizing hardcoded IDs to template variables, then saving as plugin default content.

**Announce at start:** "I'm using the migrate-dashboard skill."

---

## Steps

### 1. Locate plugin

Identify the plugin and version from context, or ask the user.

Working folder: `plugins/<Name>/<version>/`

If only one version exists, use it without asking.

**Done when:** working folder is set.

---

### 2. Receive dashboard JSON

Ask the user to paste the exported dashboard JSON if not already provided.

**Done when:** valid JSON with `"_type": "layout/grid"` is in hand.

---

### 3. Normalize

Run the normalization script to replace all hardcoded platform values with template variables:

```bash
node .claude/skills/migrate-dashboard/scripts/normalize.js <dashboard.json> [plugins/<Name>/<version>/defaultContent/scopes.json] [--scope-name "Scope Name"]
```

Pass `scopes.json` when the dashboard has tiles with `config.variables` or `config.scope` (perspective dashboard). The script reads `config.dataStream.name` from each tile to generate the correct `{{dataStreams.[name]}}` reference.

If `scopes.json` has multiple entries, read it first and determine which scope applies. It may be obvious from the dashboard content (e.g. tile titles referencing "Agent" or "Organization"), but if not, ask the user before running the script. Pass the chosen scope via `--scope-name "Scope Name"`. Run once per scope if the dashboard mixes tiles from multiple scopes.

Verify the output — check that:

- No `config-*` IDs remain (all → `{{configId}}`)
- No `space-*` IDs remain (all → `{{workspaceId}}`)
- All `config.dataStream.id` values are `{{dataStreams.[name]}}` form
- All `config.activePluginConfigIds` are `["{{configId}}"]`
- `config.scopes[]` entries with `ids_defaultScopeIds` bindings are removed
- Perspective tiles: `config.variables`, `config.scope.variable`, `config.scope.scope` are templatized

**Done when:** no hardcoded platform IDs remain in any tile.

---

### 4. Confirm title and timeframe

Ask the user to confirm the dashboard title (suggest one derived from the content).

Ask for timeframe. Default: `last24hours`. Options:

`last1hour` · `last12hours` · `last24hours` · `last30days` · `thisMonth` · `thisQuarter` · `thisYear` · `lastMonth` · `lastQuarter` · `lastYear` · `none`

**Done when:** title confirmed and timeframe chosen.

---

### 5. Save

Write to `plugins/<Name>/<version>/defaultContent/<camelCaseName>.dash.json`:

```json
{
"name": "<Confirmed Title>",
"schemaVersion": "1.5",
"timeframe": "<chosen>",
"variables": [],
"dashboard": <normalized contents>
}
```

For a perspective dashboard, set `"variables"` to the variable template from scopes.json, e.g.:

```json
"variables": ["{{variables.[Algolia Index]}}"]
```

For a non-perspective dashboard, leave `"variables": []`.

See [source-example.json](references/source-example.json) and [migrated-example.json](references/migrated-example.json) for a before/after reference.

**Done when:** file written.

---

### 6. Update manifest

Ask where in `manifest.json` this dashboard should appear. Update `manifest.json` with the new entry at the specified position.

**Done when:** `manifest.json` updated and order confirmed.
216 changes: 216 additions & 0 deletions .claude/skills/migrate-dashboard/references/migrated-example.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
{
"name": "Algolia Index",
"schemaVersion": "1.5",
"timeframe": "last24hours",
"variables": ["{{variables.[Algolia Index]}}"],
"dashboard": {
"_type": "layout/grid",
"version": 1,
"contents": [
{
"static": false,
"w": 4,
"moved": false,
"x": 0,
"h": 4,
"i": "9b9fc2b9-c56d-47de-a4c4-d151e467a210",
"y": 6,
"z": 0,
"config": {
"timeframe": "none",
"variables": ["{{variables.[Algolia Index]}}"],
"dataStream": {
"pluginConfigId": "{{configId}}",
"id": "{{dataStreams.[properties]}}"
},
"scope": {
"variable": "{{variables.[Algolia Index]}}",
"workspace": "{{workspaceId}}",
"scope": "{{scopes.[Algolia Indices]}}"
},
"_type": "tile/data-stream",
"description": "",
"activePluginConfigIds": ["{{configId}}"],
"title": "Index Details",
"visualisation": {
"type": "data-stream-table",
"config": {
"data-stream-table": {
"transpose": true
}
}
}
}
},
{
"static": false,
"w": 2,
"moved": false,
"x": 0,
"h": 4,
"i": "dfa41039-1657-4142-b6a5-16ac7abffb9d",
"y": 4,
"z": 0,
"config": {
"variables": ["{{variables.[Algolia Index]}}"],
"dataStream": {
"name": "searchCount",
"pluginConfigId": "{{configId}}",
"id": "{{dataStreams.[searchCount]}}",
"sort": { "by": [["date_byDay", "asc"]] },
"group": {
"by": [["date", "byDay"]],
"aggregate": [{ "type": "sum", "names": ["count"] }]
}
},
"scope": {
"variable": "{{variables.[Algolia Index]}}",
"workspace": "{{workspaceId}}",
"scope": "{{scopes.[Algolia Indices]}}"
},
"_type": "tile/data-stream",
"description": "",
"activePluginConfigIds": ["{{configId}}"],
"title": "Search Volume",
"visualisation": {
"type": "data-stream-line-graph",
"config": {
"data-stream-line-graph": {
"yAxisLabel": "Searches",
"xAxisColumn": "date_byDay",
"showLegend": false,
"showYAxisLabel": true,
"seriesColumn": "none",
"showTrendLine": false,
"legendPosition": "bottom",
"yAxisColumn": ["count_sum"]
}
}
}
}
},
{
"static": false,
"w": 2,
"moved": false,
"x": 2,
"h": 4,
"i": "09cc7897-4e96-44b3-9205-144fcf3ef5fb",
"y": 4,
"z": 0,
"config": {
"variables": ["{{variables.[Algolia Index]}}"],
"dataStream": {
"name": "noResultRate",
"pluginConfigId": "{{configId}}",
"id": "{{dataStreams.[noResultRate]}}",
"sort": { "by": [["date_byDay", "asc"]] },
"group": {
"by": [["date", "byDay"]],
"aggregate": [{ "type": "mean", "names": ["rate"] }]
}
},
"scope": {
"variable": "{{variables.[Algolia Index]}}",
"workspace": "{{workspaceId}}",
"scope": "{{scopes.[Algolia Indices]}}"
},
"_type": "tile/data-stream",
"description": "",
"activePluginConfigIds": ["{{configId}}"],
"title": "No-Result Rate",
"visualisation": {
"type": "data-stream-line-graph",
"config": {
"data-stream-line-graph": {
"yAxisLabel": "No-Result Rate",
"xAxisColumn": "date_byDay",
"showLegend": false,
"showYAxisLabel": true,
"seriesColumn": "none",
"showTrendLine": false,
"legendPosition": "bottom",
"yAxisColumn": ["rate_mean"]
}
}
}
}
},
{
"static": false,
"w": 2,
"moved": false,
"x": 0,
"h": 4,
"i": "1c816936-aafc-42dc-8096-10f0281a6e17",
"y": 0,
"z": 0,
"config": {
"variables": ["{{variables.[Algolia Index]}}"],
"dataStream": {
"name": "topSearches",
"pluginConfigId": "{{configId}}",
"id": "{{dataStreams.[topSearches]}}",
"sort": { "by": [["count", "desc"]] }
},
"scope": {
"variable": "{{variables.[Algolia Index]}}",
"workspace": "{{workspaceId}}",
"scope": "{{scopes.[Algolia Indices]}}"
},
"_type": "tile/data-stream",
"description": "",
"activePluginConfigIds": ["{{configId}}"],
"title": "Top Searches",
"visualisation": {
"type": "data-stream-table",
"config": {
"data-stream-table": {
"columnOrder": ["search", "count", "nbHits"],
"transpose": false
}
}
}
}
},
{
"static": false,
"w": 2,
"moved": false,
"x": 2,
"h": 4,
"i": "1727a100-9c88-41cb-b157-c0f197b1b13e",
"y": 0,
"z": 0,
"config": {
"variables": ["{{variables.[Algolia Index]}}"],
"dataStream": {
"name": "noResultSearches",
"pluginConfigId": "{{configId}}",
"id": "{{dataStreams.[noResultSearches]}}",
"sort": { "by": [["count", "desc"]] }
},
"scope": {
"variable": "{{variables.[Algolia Index]}}",
"workspace": "{{workspaceId}}",
"scope": "{{scopes.[Algolia Indices]}}"
},
"_type": "tile/data-stream",
"description": "",
"activePluginConfigIds": ["{{configId}}"],
"title": "Searches With No Results",
"visualisation": {
"type": "data-stream-table",
"config": {
"data-stream-table": {
"columnOrder": ["search", "count", "withFilterCount"],
"transpose": false
}
}
}
}
}
],
"columns": 4
}
}
Loading
Loading