@graphit/cli 0.1.10 → 0.1.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/dashboard.js +12 -1
- package/dist/commands/dashboard.js.map +1 -1
- package/package.json +1 -1
- package/skills/graphit/SKILL.md +15 -7
- package/skills/graphit/cursor/graphit-chart-patterns.mdc +3 -0
- package/skills/graphit/graphit.mdc +11 -6
- package/skills/graphit/references/chart-patterns.md +3 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { readFile } from "node:fs/promises";
|
|
2
2
|
import { apiClient } from "../api/client.js";
|
|
3
3
|
import { getFrontendUrl } from "../config.js";
|
|
4
|
-
import { output, errorOutput } from "../output/format.js";
|
|
4
|
+
import { output, errorOutput, getOutputFormat } from "../output/format.js";
|
|
5
5
|
function dashboardUrl(id) {
|
|
6
6
|
return `${getFrontendUrl()}/custom-dashboards/${id}`;
|
|
7
7
|
}
|
|
@@ -76,6 +76,17 @@ export function registerDashboardCommands(program) {
|
|
|
76
76
|
return;
|
|
77
77
|
}
|
|
78
78
|
const resp = await apiClient.put(`/api/v1/cli/dashboards/${id}/html`, { html });
|
|
79
|
+
// Entity SQL warnings stay in the JSON body (agents parse stdout);
|
|
80
|
+
// in table mode print them line-by-line on stderr instead, since the
|
|
81
|
+
// key-value table renders arrays as [object Object].
|
|
82
|
+
const warnings = resp.entity_sql_warnings;
|
|
83
|
+
if (warnings?.length && getOutputFormat(this) === "table") {
|
|
84
|
+
delete resp.entity_sql_warnings;
|
|
85
|
+
for (const w of warnings) {
|
|
86
|
+
const where = w.entity_id ? `entity "${w.entity_id}"` : "dashboard";
|
|
87
|
+
console.error(`warning: ${where}: ${w.problem}`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
79
90
|
output(this, { ...resp, url: dashboardUrl(resp.id) }, { keyValue: true });
|
|
80
91
|
}
|
|
81
92
|
catch (err) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dashboard.js","sourceRoot":"","sources":["../../src/commands/dashboard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"dashboard.js","sourceRoot":"","sources":["../../src/commands/dashboard.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/skills/graphit/SKILL.md
CHANGED
|
@@ -57,13 +57,14 @@ NEVER query the warehouse directly when a cached data source covers the table. D
|
|
|
57
57
|
**Right:** `graphit ds list` first, find the DS ID, then `graphit query "SELECT SUM(cost) FROM marketing_ua" --ds ds_abc123`.
|
|
58
58
|
|
|
59
59
|
### 3. EVERY element must have entity wrapping
|
|
60
|
-
Without `data-graphit-*` attributes, elements are invisible to the platform - no click info, no mentions, no KB provenance. Every chart, KPI card, table, and text section needs ALL
|
|
60
|
+
Without `data-graphit-*` attributes, elements are invisible to the platform - no click info, no mentions, no KB provenance. Every chart, KPI card, table, and text section needs ALL FIVE attributes:
|
|
61
61
|
|
|
62
62
|
```html
|
|
63
63
|
<div data-graphit-id="revenue-trend"
|
|
64
64
|
data-graphit-label="Revenue Trend"
|
|
65
65
|
data-graphit-kb="metric:REVENUE,dimension:REGION,table:ORDERS"
|
|
66
|
-
data-graphit-sql="SELECT region, SUM(revenue) FROM
|
|
66
|
+
data-graphit-sql="SELECT region, SUM(revenue) FROM orders_ds GROUP BY region"
|
|
67
|
+
data-graphit-ds="ds_abc123">
|
|
67
68
|
<!-- chart/KPI/table content here -->
|
|
68
69
|
</div>
|
|
69
70
|
```
|
|
@@ -73,11 +74,17 @@ Without `data-graphit-*` attributes, elements are invisible to the platform - no
|
|
|
73
74
|
| `data-graphit-id` | Unique kebab-case | `"spend-by-source"` |
|
|
74
75
|
| `data-graphit-label` | Human-readable name | `"Ad Spend by Source"` |
|
|
75
76
|
| `data-graphit-kb` | `type:NAME` comma-separated | `"metric:CPI,dimension:MEDIA_SOURCE,table:MARKETING_UA"` |
|
|
76
|
-
| `data-graphit-sql` | SQL
|
|
77
|
+
| `data-graphit-sql` | Executable SQL (HTML-encode `<>&"`) | `"SELECT ..."` |
|
|
78
|
+
| `data-graphit-ds` | Data source ID from `graphit ds list` | `"ds_abc123"` |
|
|
77
79
|
|
|
78
|
-
KB types: `metric`, `dimension`, `table`, `rule`. Names are UPPER_SNAKE_CASE matching the KB exactly.
|
|
80
|
+
KB types: `metric`, `dimension`, `table`, `rule`. Names are UPPER_SNAKE_CASE matching the KB exactly. **Names are unique per type per org** - creating a duplicate name will fail. Check `graphit kb list <type>` first; update the existing asset instead of creating a new one.
|
|
79
81
|
Missing any attribute = broken entity. Missing wrapping entirely = invisible to the platform.
|
|
80
82
|
|
|
83
|
+
**SQL must be executable.** The platform runs `data-graphit-sql` against the data source when a user opens the entity's details panel. It MUST use the real DS table name and only columns that exist in the DS - never invented summary columns, CTE aliases, JS variable names, or prose. If the chart's resolve call uses a CTE, put the full WITH query. If JS builds SQL dynamically, store one representative executable variant (e.g. the default date range).
|
|
84
|
+
|
|
85
|
+
**Wrong:** `data-graphit-sql="SELECT INSTALL_TIME, ROIAP_D0, ROIAP_D3 FROM UA_DS"` when the DS has no ROIAP_D0 column (the chart computes it via CASE) - the details panel errors.
|
|
86
|
+
**Right:** `data-graphit-sql="SELECT INSTALL_TIME, SUM(CASE WHEN SENIORITY=0 THEN TOTAL_IAP END)/NULLIF(SUM(COST),0) AS ROIAP_D0 FROM UA_DS GROUP BY 1"` - same derivation the chart runs.
|
|
87
|
+
|
|
81
88
|
**Label = visible title.** The `data-graphit-label` MUST match the card's visible heading exactly. Users see the label in @ mention dropdowns and entity panels - if it doesn't match the title on screen, they can't find their chart.
|
|
82
89
|
|
|
83
90
|
### 4. ALWAYS use graphit.resolve() for live data
|
|
@@ -95,7 +102,7 @@ NEVER embed query results as static JS variables. The dashboard iframe provides
|
|
|
95
102
|
3. **Find a data source** (`graphit ds list`) - prefer cached data sources (~100ms) over live warehouse (~10s).
|
|
96
103
|
4. **Query data to validate** - run queries via the CLI to verify SQL and preview results. Show the user what you found.
|
|
97
104
|
5. **Build HTML** - write `graphit.resolve()` calls for live data + `graphit.chart/table/kpi` for rendering. All CSS in `<style>`, all JS in `<script>`. Write to a local `.html` file.
|
|
98
|
-
6. **Save** with `graphit dashboard update-html <id> --file <path>`.
|
|
105
|
+
6. **Save** with `graphit dashboard update-html <id> --file <path>`. If the response contains `entity_sql_warnings`, an entity's `data-graphit-sql` is missing, matches no data source, or fails against the DS schema - fix the flagged entities and save again before finishing.
|
|
99
106
|
7. Give the user the dashboard URL so they can open it.
|
|
100
107
|
|
|
101
108
|
## Workflow: KB Build / Onboarding
|
|
@@ -266,7 +273,7 @@ The iframe also provides optional convenience helpers if you want quick standard
|
|
|
266
273
|
|
|
267
274
|
These are shortcuts, not requirements. Use them when a standard chart is all you need. Hand-roll when you want full control over the visualization.
|
|
268
275
|
|
|
269
|
-
`graphit.chart` types: `"bar"`, `"horizontal-bar"` (alias `"hbar"` - use when category labels are long), `"line"`, `"area"`, `"donut"` (alias `"pie"`), `"scatter"` (alias `"bubble"`), `"stacked-bar"` (alias `"stacked"`), `"heatmap"`, `"funnel"`, `"gauge"`, `"sparkline"`. Config: `x` (category field), `y` (value field), `series` (group-by field), `title`, `height` (140-900px), `valueFormat` (`"currency"` | `"percent"` | `"number"`), `colors` (array). Scatter adds: `size` (bubble radius field), `label` (tooltip field). Gauge adds: `min`, `max`, `format`. Sparkline adds: `width`, `showValue`.
|
|
276
|
+
`graphit.chart` types: `"bar"`, `"horizontal-bar"` (alias `"hbar"` - use when category labels are long), `"line"`, `"area"`, `"donut"` (alias `"pie"`), `"scatter"` (alias `"bubble"`), `"stacked-bar"` (alias `"stacked"`), `"heatmap"`, `"funnel"`, `"gauge"`, `"sparkline"`. Config: `x` (category field), `y` (value field), `series` (group-by field), `title`, `height` (140-900px), `valueFormat` (`"currency"` | `"percent"` | `"number"`), `colors` (array). Dual axis (bar/line/area): `y2` (secondary value field, right Y-axis with independent scale), `y2Format`, `y2Label`; `y2` and `series` are mutually exclusive; bar+y2 renders as combo chart (bars + dashed line overlay). Scatter adds: `size` (bubble radius field), `label` (tooltip field). Gauge adds: `min`, `max`, `format`. Sparkline adds: `width`, `showValue`.
|
|
270
277
|
|
|
271
278
|
`graphit.kpi` config: `value`, `label`, `format` (`"currency"` | `"percent"` | `"number"`), `compareValue`, `compareLabel`.
|
|
272
279
|
|
|
@@ -276,7 +283,8 @@ These are shortcuts, not requirements. Use them when a standard chart is all you
|
|
|
276
283
|
<div data-graphit-id="spend-by-source"
|
|
277
284
|
data-graphit-label="Ad Spend by Source"
|
|
278
285
|
data-graphit-kb="metric:CPI,dimension:MEDIA_SOURCE,table:MARKETING_UA"
|
|
279
|
-
data-graphit-sql="SELECT MEDIA_SOURCE, SUM(APPSFLYER_COST) as spend FROM MARKETING_UA GROUP BY MEDIA_SOURCE ORDER BY spend DESC"
|
|
286
|
+
data-graphit-sql="SELECT MEDIA_SOURCE, SUM(APPSFLYER_COST) as spend FROM MARKETING_UA GROUP BY MEDIA_SOURCE ORDER BY spend DESC"
|
|
287
|
+
data-graphit-ds="ds_abc123">
|
|
280
288
|
<div id="spend-chart"></div>
|
|
281
289
|
</div>
|
|
282
290
|
<script>
|
|
@@ -14,9 +14,12 @@ All chart types are available via `graphit.chart()`. All data comes from `graphi
|
|
|
14
14
|
|
|
15
15
|
### bar
|
|
16
16
|
SVG vertical bars. Multi-series: grouped side-by-side. Y-axis: 4 grid lines with formatted labels. X-axis: category labels (sampled if >12). Rounded top corners (rx=3). `<title>` tooltips per bar.
|
|
17
|
+
**Dual axis (combo chart):** `y2` field adds a dashed line overlay on a secondary right-side Y-axis. Bars use the left scale, the line uses the right scale. Config: `y2`, `y2Format`, `y2Label`.
|
|
17
18
|
|
|
18
19
|
### line / area
|
|
19
20
|
SVG line with circle dots at each point. Area adds filled path below (opacity 0.18). Multi-series: separate colored paths. Y-axis scaled to data range (min to max). `<title>` tooltips per dot.
|
|
21
|
+
**Dual axis:** `y2` field adds a secondary right-side Y-axis with independent scaling. Secondary line is dashed (`stroke-dasharray="6 3"`). `y2` and `series` are mutually exclusive.
|
|
22
|
+
Config: `y2` (secondary value field), `y2Format` (right axis format), `y2Label` (legend name for secondary).
|
|
20
23
|
|
|
21
24
|
### donut / pie
|
|
22
25
|
SVG pie slices with center hole (58% of radius). Center shows formatted total. Legend below with color swatches.
|
|
@@ -26,13 +26,14 @@ NEVER query the warehouse directly when a cached data source covers the table. D
|
|
|
26
26
|
Run `graphit ds list` FIRST. If a DS covers your table, use `graphit query "SQL" --ds <id>`. Only use `--warehouse` if NO data source exists and the user approves.
|
|
27
27
|
|
|
28
28
|
### 3. EVERY element must have entity wrapping
|
|
29
|
-
Without `data-graphit-*` attributes, elements are invisible to the platform. Every chart, KPI card, table, and text section needs ALL
|
|
29
|
+
Without `data-graphit-*` attributes, elements are invisible to the platform. Every chart, KPI card, table, and text section needs ALL FIVE attributes:
|
|
30
30
|
|
|
31
31
|
```html
|
|
32
32
|
<div data-graphit-id="revenue-trend"
|
|
33
33
|
data-graphit-label="Revenue Trend"
|
|
34
34
|
data-graphit-kb="metric:REVENUE,dimension:REGION,table:ORDERS"
|
|
35
|
-
data-graphit-sql="SELECT region, SUM(revenue) FROM
|
|
35
|
+
data-graphit-sql="SELECT region, SUM(revenue) FROM orders_ds GROUP BY region"
|
|
36
|
+
data-graphit-ds="ds_abc123">
|
|
36
37
|
<!-- chart/KPI/table content here -->
|
|
37
38
|
</div>
|
|
38
39
|
```
|
|
@@ -42,9 +43,12 @@ Without `data-graphit-*` attributes, elements are invisible to the platform. Eve
|
|
|
42
43
|
| `data-graphit-id` | Unique kebab-case | `"spend-by-source"` |
|
|
43
44
|
| `data-graphit-label` | Human-readable name | `"Ad Spend by Source"` |
|
|
44
45
|
| `data-graphit-kb` | `type:NAME` comma-separated | `"metric:CPI,dimension:MEDIA_SOURCE,table:MARKETING_UA"` |
|
|
45
|
-
| `data-graphit-sql` | SQL
|
|
46
|
+
| `data-graphit-sql` | Executable SQL (HTML-encode `<>&"`) | `"SELECT ..."` |
|
|
47
|
+
| `data-graphit-ds` | Data source ID from `graphit ds list` | `"ds_abc123"` |
|
|
46
48
|
|
|
47
|
-
KB types: `metric`, `dimension`, `table`, `rule`. Names are UPPER_SNAKE_CASE matching the KB exactly.
|
|
49
|
+
KB types: `metric`, `dimension`, `table`, `rule`. Names are UPPER_SNAKE_CASE matching the KB exactly. **Names are unique per type per org** - creating a duplicate name will fail. Check `graphit kb list <type>` first; update the existing asset instead of creating a new one.
|
|
50
|
+
|
|
51
|
+
**SQL must be executable.** The platform runs `data-graphit-sql` against the data source when a user opens the entity's details panel. It MUST use the real DS table name and only columns that exist in the DS - never invented summary columns, CTE aliases, JS variable names, or prose. If the chart's resolve call uses a CTE, put the full WITH query. If JS builds SQL dynamically, store one representative executable variant (e.g. the default date range).
|
|
48
52
|
|
|
49
53
|
**Label = visible title.** The `data-graphit-label` MUST match the card's visible heading exactly.
|
|
50
54
|
|
|
@@ -63,7 +67,7 @@ NEVER embed query results as static JS variables. The dashboard iframe provides
|
|
|
63
67
|
3. **Find a data source** (`graphit ds list`) - prefer cached data sources over live warehouse.
|
|
64
68
|
4. **Query data to validate** - run queries via the CLI to verify SQL and preview results.
|
|
65
69
|
5. **Build HTML** - write `graphit.resolve()` calls for live data + `graphit.chart/table/kpi` for rendering. All CSS in `<style>`, all JS in `<script>`.
|
|
66
|
-
6. **Save** with `graphit dashboard update-html <id> --file <path>`.
|
|
70
|
+
6. **Save** with `graphit dashboard update-html <id> --file <path>`. If the response contains `entity_sql_warnings`, an entity's `data-graphit-sql` is missing, matches no data source, or fails against the DS schema - fix the flagged entities and save again before finishing.
|
|
67
71
|
7. Give the user the dashboard URL.
|
|
68
72
|
|
|
69
73
|
## Workflow: KB Build / Onboarding
|
|
@@ -107,7 +111,8 @@ const result = await graphit.resolve({
|
|
|
107
111
|
<div data-graphit-id="spend-by-source"
|
|
108
112
|
data-graphit-label="Ad Spend by Source"
|
|
109
113
|
data-graphit-kb="metric:CPI,dimension:MEDIA_SOURCE,table:MARKETING_UA"
|
|
110
|
-
data-graphit-sql="SELECT MEDIA_SOURCE, SUM(APPSFLYER_COST) as spend FROM MARKETING_UA GROUP BY MEDIA_SOURCE ORDER BY spend DESC"
|
|
114
|
+
data-graphit-sql="SELECT MEDIA_SOURCE, SUM(APPSFLYER_COST) as spend FROM MARKETING_UA GROUP BY MEDIA_SOURCE ORDER BY spend DESC"
|
|
115
|
+
data-graphit-ds="ds_abc123">
|
|
111
116
|
<div id="spend-chart"></div>
|
|
112
117
|
</div>
|
|
113
118
|
<script>
|
|
@@ -8,9 +8,12 @@ All chart types are available via `graphit.chart()`. All data comes from `graphi
|
|
|
8
8
|
|
|
9
9
|
### bar
|
|
10
10
|
SVG vertical bars. Multi-series: grouped side-by-side. Y-axis: 4 grid lines with formatted labels. X-axis: category labels (sampled if >12). Rounded top corners (rx=3). `<title>` tooltips per bar.
|
|
11
|
+
**Dual axis (combo chart):** `y2` field adds a dashed line overlay on a secondary right-side Y-axis. Bars use the left scale, the line uses the right scale. Config: `y2`, `y2Format`, `y2Label`.
|
|
11
12
|
|
|
12
13
|
### line / area
|
|
13
14
|
SVG line with circle dots at each point. Area adds filled path below (opacity 0.18). Multi-series: separate colored paths. Y-axis scaled to data range (min to max). `<title>` tooltips per dot.
|
|
15
|
+
**Dual axis:** `y2` field adds a secondary right-side Y-axis with independent scaling. Secondary line is dashed (`stroke-dasharray="6 3"`). `y2` and `series` are mutually exclusive.
|
|
16
|
+
Config: `y2` (secondary value field), `y2Format` (right axis format), `y2Label` (legend name for secondary).
|
|
14
17
|
|
|
15
18
|
### donut / pie
|
|
16
19
|
SVG pie slices with center hole (58% of radius). Center shows formatted total. Legend below with color swatches. Each slice is a wedge path from center.
|