@graphit/cli 0.2.113 → 0.2.136

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.
Files changed (41) hide show
  1. package/.claude-plugin/marketplace.json +3 -3
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.codex-plugin/plugin.json +1 -1
  4. package/README.md +2 -0
  5. package/bin/graphit +1 -1
  6. package/bin/graphit.ps1 +1 -1
  7. package/dist/commands/connector.js +66 -2
  8. package/dist/commands/connector.js.map +1 -1
  9. package/dist/commands/ds-config.d.ts +45 -0
  10. package/dist/commands/ds-config.js +122 -0
  11. package/dist/commands/ds-config.js.map +1 -0
  12. package/dist/commands/ds.d.ts +0 -45
  13. package/dist/commands/ds.js +4 -128
  14. package/dist/commands/ds.js.map +1 -1
  15. package/dist/commands/governance.js +1 -17
  16. package/dist/commands/governance.js.map +1 -1
  17. package/dist/commands/kb-create.js +2 -2
  18. package/dist/commands/kb-create.js.map +1 -1
  19. package/dist/commands/kb-update.js +2 -2
  20. package/dist/commands/kb-update.js.map +1 -1
  21. package/dist/commands/query.d.ts +38 -0
  22. package/dist/commands/query.js +145 -18
  23. package/dist/commands/query.js.map +1 -1
  24. package/dist/index.js +2 -0
  25. package/dist/index.js.map +1 -1
  26. package/dist/skill-guard.d.ts +4 -0
  27. package/dist/skill-guard.js +75 -0
  28. package/dist/skill-guard.js.map +1 -0
  29. package/package.json +1 -1
  30. package/scripts/plugin-status.mjs +48 -0
  31. package/skills/graphit/SKILL.md +15 -14
  32. package/skills/graphit/VERSION.json +1 -1
  33. package/skills/graphit/cursor/graphit-sql-reference.mdc +2 -2
  34. package/skills/graphit/graphit.mdc +6 -9
  35. package/skills/graphit/references/data-sources.md +7 -3
  36. package/skills/graphit/references/governance-explained.md +2 -2
  37. package/skills/graphit/references/governance.md +22 -17
  38. package/skills/graphit/references/kb-actions.md +2 -3
  39. package/skills/graphit/references/kb-traversal.md +1 -1
  40. package/skills/graphit/references/runtime.md +5 -1
  41. package/skills/graphit/references/sql-reference.md +15 -2
@@ -31,6 +31,8 @@ const result = await graphit.resolve({
31
31
  - `maxRows` (optional) defaults to **10,000**, capped at **50,000**. Aggregate to a chartable grain well under the default; raise it only for a genuine row-level export, never above the cap.
32
32
  - `result.data` is an array of row objects you render however you want.
33
33
 
34
+ MUST: every resolve that feeds a rendered graph, KPI, or table carries attribution - `target` (an element inside the entity wrapper), or `sourceEntityId` plus `targetEntityIds` when one result feeds several graphs. Attribution is what records the live filtered query behind each entity's details panel; an unattributed resolve leaves that panel showing the static `data-graphit-sql` example with its baked default filters, so a user who changes a filter sees the SQL never move. Saving a page that has filters or params and zero attributed resolves returns an `unattributed_resolves` warning. Queries that feed no visual (filter option lists, freshness probes) stay unattributed.
35
+
34
36
  CRITICAL: use KB reference syntax (`{{metric:NAME}}`, `{{dim:NAME}}`) inside the resolve `sql` whenever a KB asset exists - the server expands it at query time, which produces the governed trust tier. See `governance.md` for the syntax and trust tiers.
35
37
 
36
38
  Error handling: `graphit.resolve()` rejects on timeout (120s), bad SQL, or an invalid data source ID. Wrap calls in try/catch and show a user-visible error in the target element on failure. Verify the SQL returns data via the CLI before embedding it.
@@ -113,13 +115,15 @@ A resolve query that follows these shapes serves from a semantic cache in roughl
113
115
  - `CURRENT_DATE`-relative predicates.
114
116
  - Top-N with the aggregate only in ORDER BY (`... GROUP BY dim ORDER BY SUM(metric) DESC LIMIT N` with no decomposable aggregate in SELECT).
115
117
 
118
+ **Fresh anchors.** When a query needs a data-driven anchor - the latest install date, a "days since" reference, a MAX(date) - never fetch it once at page load into a JS variable and reuse it across refreshes. A long-open tab silently goes stale and every maturity gate or rolling window computed against it drifts. Re-resolve the anchor inside each refresh cycle, or derive it in a subquery so the query always anchors to the current data.
119
+
116
120
  ## Rate-limit budget
117
121
 
118
122
  `graphit.resolve()` is rate-limited to 120 requests per minute per user per dashboard. Each call counts as one request. Design for that budget:
119
123
 
120
124
  - **Single refresh function.** Put all queries in ONE `Promise.all` inside one `refresh()` function so they share the same time window. NEVER scatter `graphit.resolve()` across independent event handlers or timeouts - that turns one user action into several bursts.
121
125
  - **Count queries per interaction.** 6 charts is 6 requests per filter change, about 20 changes per minute of budget; 12 charts is about 10 changes per minute. With 10 or more charts and 3 or more filters, debounce filter changes (300ms) so rapid clicks do not each trigger a full refresh.
122
- - **Reuse trend data for KPIs.** If you already fetch a weekly time series, derive the KPI total and its sparkline from that result in JS instead of running a separate aggregate query - one query serves both. When one result serves several graphs like this, anchor every graph it feeds with `targetEntityIds` (keep `target` on the primary) so each graph's details panel shows the live filtered query, not stale base SQL. Canonical KPI-row example: `kpi.md`.
126
+ - **Reuse trend data for KPIs.** If you already fetch a weekly time series, derive the KPI total and its sparkline from that result in JS instead of running a separate aggregate query - one query serves both. Anchor the extra graphs it feeds with `targetEntityIds` per the resolve attribution rule above. Canonical KPI-row example: `kpi.md`.
123
127
  - **Avoid redundant refreshes.** If a filter affects only some charts, split into targeted refresh functions (`refreshKPIs()`, `refreshCharts()`) so unchanged sections do not re-query.
124
128
  - **No polling.** NEVER use `setInterval(refresh, ...)`. Data sources update on their own schedule; a polling dashboard burns the entire budget.
125
129
 
@@ -1,6 +1,6 @@
1
1
  # SQL Reference
2
2
 
3
- Consult when writing queries. Data source queries (`graphit query --ds`) run DuckDB. Warehouse queries (`graphit query --warehouse`) run Snowflake. You MUST use the correct dialect. To read a table's columns, use `graphit kb explore table <NAME>` - never `DESCRIBE`/DDL (`graphit query` runs SELECT only).
3
+ Consult when writing queries. Data source queries (`graphit query --ds`) always run DuckDB. Warehouse queries (`graphit query --warehouse`) run the connected warehouse - Snowflake or BigQuery - and the dialect follows the connection type; governance parses your SQL in that dialect. You MUST use the correct dialect. To read a table's columns, use `graphit kb explore table <NAME>` - never `DESCRIBE`/DDL (`graphit query` runs SELECT only).
4
4
 
5
5
  ## DuckDB vs Snowflake Translation
6
6
 
@@ -50,6 +50,19 @@ NEVER use `->>` in Snowflake or `:field::STRING` in DuckDB.
50
50
  - `COUNT_IF` MUST receive a boolean expression, not a raw INT column. Use `COUNT_IF(is_active = 1)`, not `COUNT_IF(is_active)`
51
51
  - String matching: prefer `ILIKE` (case-insensitive) over `LIKE`
52
52
 
53
+ ## BigQuery Standard SQL Notes
54
+
55
+ Applies only to `--warehouse` queries on a BigQuery connection. Key differences from Snowflake:
56
+
57
+ - Date math is `DATE_ADD(date, INTERVAL N DAY)` and `DATE_DIFF(a, b, DAY)` - the unit is a bare keyword and `DATE_DIFF` arg order is `(later, earlier, unit)`
58
+ - Null/branch: `IFNULL(a, b)` or `COALESCE(a, b)`; `IF(cond, a, b)` (not `IFF`); `NULLIF` as usual
59
+ - Error-tolerant: `SAFE_CAST(x AS INT64)`, `SAFE_DIVIDE(a, b)`, and the `SAFE.` prefix on most functions (`SAFE.PARSE_DATE(...)`) return NULL instead of erroring
60
+ - Dates/strings: `FORMAT_DATE('%Y-%m', d)`, `PARSE_DATE('%Y-%m-%d', s)`, `EXTRACT(MONTH FROM d)`, `DATE_TRUNC(d, MONTH)` (unit is a keyword, date-arg first)
61
+ - Approx: `APPROX_COUNT_DISTINCT(x)` (not `APPROX_COUNT_DISTINCT` colon syntax); `COUNTIF(cond)` is one word
62
+ - Like DuckDB and unlike Snowflake, BigQuery has `GROUP BY ALL` and `SELECT * EXCEPT(col)`
63
+ - Identifiers: back-tick fully-qualified names `` `project.dataset.table` ``; strings use single quotes
64
+ - JSON: `JSON_VALUE(col, '$.field')` (scalar) / `JSON_QUERY(col, '$.field')` (subtree) - not Snowflake colon syntax or DuckDB arrows
65
+
53
66
  ## SQL Formatting Standards (Both Engines)
54
67
 
55
68
  - Keywords UPPERCASE: `SELECT`, `FROM`, `WHERE`, `JOIN`, `GROUP BY`, `ORDER BY`
@@ -117,7 +130,7 @@ When the query feeds a canvas `graphit.resolve()` call, write it in the cache-fr
117
130
 
118
131
  ## Data source routing
119
132
 
120
- Always prefer a cached data source (`graphit query "SQL" --ds <NAME>`, roughly 100ms via DuckDB) over a live warehouse query (`graphit query "SQL" --warehouse --connection <id>`, roughly 10s via Snowflake). Pass the data source name to `--ds` - the same name you SELECT FROM (a full id or unique id-prefix also works). The full routing table, the `ds list` output template, and the source-shape guidance live in `data-sources.md`.
133
+ Always prefer a cached data source (`graphit query "SQL" --ds <NAME>`, roughly 100ms via DuckDB) over a live warehouse query (`graphit query "SQL" --warehouse --connection <id>`, roughly 10s via the connected warehouse). Pass the data source name to `--ds` - the same name you SELECT FROM (a full id or unique id-prefix also works). The full routing table, the `ds list` output template, and the source-shape guidance live in `data-sources.md`.
121
134
 
122
135
  ## Percent scaling
123
136