aiagent/skill/embedded/builtin/modify-dashboard/SKILL.md
Help users modify an existing dashboard using natural language — not create a new one. Three typical kinds of requests:
| Request | What you change |
|---|---|
| Change variables | A template variable's value expression (definition), default value, whether it is multi-select (multi), display label (label) |
| Check and fix variables | Scan variable definitions and the references to variables inside charts, detect smells (a chart references an undefined variable, a variable returns no value, datasource references are inconsistent, etc.) and fix them |
| Change chart series | The series of a chart (panel): query expression (PromQL), legend, unit, adding/removing series, changing the title, changing the chart type (e.g., stat→timeseries) |
Editing series queries (
queries) is only supported for Prometheus/VictoriaMetrics panels; SQL/log panels (mysql, ck, es, etc.) can only have their unit, title, description changed or be deleted — passingquerieswill be rejected by the tool.
update_dashboard is a proposal-style write: after you call it (passing only the part you want to change), the tool computes the diff, directly shows the user the list of changes and pauses the conversation; once the user confirms, the system automatically persists it to the database — the confirmation step neither needs nor goes through you. Therefore:
get_dashboard_detail(id, include_config=true) to read the current variables, chart summaries, and variable health check.update_dashboard once, passing only the variables/panels/fix_datasource you want to change. This call is the last step of this turn; the system takes over the display and confirmation.proposal_id/confirmed (those are parameters for the system's confirmation channel).update_dashboard again (the old proposal is automatically voided).dashboard_id (injected by the frontend from /dashboards/<id>, or already determined in a previous turn) → use it directly, don't call list_dashboards again./dashboards/<id> link → take the id from it.list_dashboards(query="...") to match by name; if there are multiple candidates or no match, list them and ask the user — do not guess.Read the business group and the datasources from the dashboard itself; do not ask the user for them.
get_dashboard_detail(id=<id>, include_config=true)
In the response:
variables: for each variable, name / type / label / definition / multi / default_value / datasource_valuepanels: for each chart, id / name / type / unit / queries (each series contains ref / promql / legend / instant / step / hide; fields that are not set are omitted); charts inside collapsed rows (row) have been flattened invariable_lint: the list of smells caught by the variable health check (e.g., "the query expression of chart X references an undefined variable $foo")Prefer id when locating a chart (e.g., panel-3); names may be duplicated.
When changing PromQL or fixing a variable's definition, you can first use query_prometheus / list_metrics / get_metric_labels to verify that the new expression really has data before proposing, to avoid the chart still being empty after the change.
For variable-fix tasks: for each item caught in variable_lint, decide on a fix action (change the definition / rename the reference / fix the datasource reference) and put them all into the same proposal.
Call update_dashboard, passing only the part you want to change (everything else is preserved as-is; the tool won't touch it). The tool will show the user the list of changes and wait for confirmation; this turn ends here:
variables (a JSON array, matched by name):
[{"name":"ident","default_value":"web01","multi":false}]
name does not exist it is treated as adding a new query variable, and an addition must carry definition (omitting it raises an error — a misspelled name falls into the addition branch, and this guard catches it); delete:true deletes it.panels (a JSON array, located by id first, otherwise by name):
[{"id":"panel-2","unit":"percent","queries":[{"promql":"avg(cpu_usage_active{cpu=\"cpu-total\",ident=~\"$ident\"})","legend":"{{ident}}"}]}]
queries is passed in it is incrementally merged with the existing series: matched by ref (the original series' refId), only the fields you write are overwritten; the rest (step/hide/mode, the refId-associated overrides, etc.) are preserved as-is; anything without a ref is always treated as a new series (no position match). Existing series that do not appear in queries will not be deleted and are preserved as-is — so when changing only one series, pass just that one (with its ref); you don't need to list all the series of the whole chart.ref (without ref it adds a series instead of changing the original one); to delete a series, put its ref on that series item and write delete:true.new_name changes the title, unit changes the unit, description changes the description, type changes the chart type (only timeseries/stat/gauge/barGauge/pie/table; changing the type resets the chart's type-style options to the defaults of the new type, and changing to timeseries also clears the instant flag on series to restore range queries; a row layout row cannot be changed), delete:true (on the panel item) deletes the whole chart. Not passing queries leaves the series untouched.fix_datasource: true: re-points dangling or hard-coded datasource references in charts/variables uniformly to the dashboard's datasource variable. Suitable for fixing smells like "chart returns no data / datasource references are inconsistent."update_dashboard; just state the reason in a single sentence and give a suggestion.The changes list returned by the tool is the set of changes actually persisted to the database; restate to the user based on it.
=~ instead of = in PromQL, e.g., ident=~"$ident".{{label}} form for the legend template, e.g., {{ident}}.list_metrics / get_metric_labels first; don't make it up.update_dashboard call (pass variables and panels together); the change list and confirmation are gated by the system and won't be skipped.