@graphit/cli 0.1.4 → 0.1.6

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 (68) hide show
  1. package/dist/api/client.d.ts +16 -0
  2. package/dist/api/client.js +67 -0
  3. package/dist/api/client.js.map +1 -0
  4. package/dist/auth/credentials.d.ts +15 -0
  5. package/dist/auth/credentials.js +34 -0
  6. package/dist/auth/credentials.js.map +1 -0
  7. package/dist/auth/login.d.ts +2 -0
  8. package/dist/auth/login.js +230 -0
  9. package/dist/auth/login.js.map +1 -0
  10. package/dist/auth/token.d.ts +5 -0
  11. package/dist/auth/token.js +42 -0
  12. package/dist/auth/token.js.map +1 -0
  13. package/dist/commands/auth.d.ts +2 -0
  14. package/dist/commands/auth.js +68 -0
  15. package/dist/commands/auth.js.map +1 -0
  16. package/dist/commands/connector.d.ts +2 -0
  17. package/dist/commands/connector.js +97 -0
  18. package/dist/commands/connector.js.map +1 -0
  19. package/dist/commands/dashboard.d.ts +2 -0
  20. package/dist/commands/dashboard.js +124 -0
  21. package/dist/commands/dashboard.js.map +1 -0
  22. package/dist/commands/ds.d.ts +2 -0
  23. package/dist/commands/ds.js +53 -0
  24. package/dist/commands/ds.js.map +1 -0
  25. package/dist/commands/kb.d.ts +2 -0
  26. package/dist/commands/kb.js +505 -0
  27. package/dist/commands/kb.js.map +1 -0
  28. package/dist/commands/query.d.ts +2 -0
  29. package/dist/commands/query.js +61 -0
  30. package/dist/commands/query.js.map +1 -0
  31. package/dist/commands/setup.d.ts +2 -0
  32. package/dist/commands/setup.js +173 -0
  33. package/dist/commands/setup.js.map +1 -0
  34. package/dist/config.d.ts +4 -0
  35. package/dist/config.js +9 -0
  36. package/dist/config.js.map +1 -0
  37. package/dist/index.d.ts +2 -0
  38. package/dist/index.js +27 -0
  39. package/dist/index.js.map +1 -0
  40. package/dist/output/format.d.ts +8 -0
  41. package/dist/output/format.js +30 -0
  42. package/dist/output/format.js.map +1 -0
  43. package/dist/output/json.d.ts +1 -0
  44. package/dist/output/json.js +4 -0
  45. package/dist/output/json.js.map +1 -0
  46. package/dist/output/table.d.ts +2 -0
  47. package/dist/output/table.js +17 -0
  48. package/dist/output/table.js.map +1 -0
  49. package/dist/update-check.d.ts +3 -0
  50. package/dist/update-check.js +78 -0
  51. package/dist/update-check.js.map +1 -0
  52. package/package.json +1 -1
  53. package/skills/graphit/SKILL.md +84 -8
  54. package/skills/graphit/cursor/graphit-kb-actions.mdc +101 -0
  55. package/skills/graphit/cursor/graphit-kb-awareness.mdc +88 -0
  56. package/skills/graphit/cursor/graphit-kb-explanation.mdc +56 -0
  57. package/skills/graphit/cursor/graphit-kb-exploration.mdc +7 -13
  58. package/skills/graphit/cursor/graphit-kb-graph-structure.mdc +61 -0
  59. package/skills/graphit/cursor/graphit-kb-traversal.mdc +64 -0
  60. package/skills/graphit/cursor/graphit-parameterized-metrics.mdc +95 -0
  61. package/skills/graphit/graphit.mdc +12 -1
  62. package/skills/graphit/references/kb-actions.md +95 -0
  63. package/skills/graphit/references/kb-awareness.md +82 -0
  64. package/skills/graphit/references/kb-explanation.md +50 -0
  65. package/skills/graphit/references/kb-exploration.md +7 -13
  66. package/skills/graphit/references/kb-graph-structure.md +55 -0
  67. package/skills/graphit/references/kb-traversal.md +58 -0
  68. package/skills/graphit/references/parameterized-metrics.md +89 -0
@@ -0,0 +1,101 @@
1
+ ---
2
+ alwaysApply: false
3
+ description: "Graphit: Every KB action has a CLI equivalent. The tool syntax below maps to CLI commands:"
4
+ globs: []
5
+ ---
6
+
7
+ # KB Actions - CLI Parity Matrix
8
+
9
+ Every KB action has a CLI equivalent. The tool syntax below maps to CLI commands:
10
+
11
+ | Agent Tool Syntax | CLI Command |
12
+ |---|---|
13
+ | `create(entity_type="metric", name=..., calculation=...)` | `graphit kb create metric --name X --sql "..." --table T` |
14
+ | `create(entity_type="dimension", name=..., expression=...)` | `graphit kb create dimension --name X --expr "..." --table T` |
15
+ | `create(entity_type="rule", name=..., rule_text=...)` | `graphit kb create rule --name X --sql "..." --table T` |
16
+ | `create(entity_type="synonym", term=..., canonical=...)` | `graphit kb create synonym --term X --canonical Y --type metric` |
17
+ | `create(entity_type="domain", name=...)` | `graphit kb create domain --name X --description "..."` |
18
+ | `create(entity_type="relationship", ...)` | `graphit kb create relationship --name X --primary-table T --primary-column C --related-table T2 --related-column C2` |
19
+ | `create(entity_type="topic_metadata", name=...)` | `graphit kb create topic --name X --description "..."` |
20
+ | `edit(entity_type=..., id=..., field=value)` | `graphit kb update <type> NAME --field value` |
21
+ | `edit(entity_type="table", id=..., domain_id=...)` | `graphit kb update table NAME --domain DOMAIN` |
22
+ | `delete(entity_type=..., id=...)` | `graphit kb delete <type> NAME --yes` |
23
+ | `search(query="...")` | `graphit kb search "query"` |
24
+ | `list(entity_type="domain")` | `graphit kb list domains` |
25
+ | `read(entity_type="metric", name=...)` | `graphit kb get metric NAME` |
26
+ | `kb_explore(entity_type="table", entity_name="X")` | `graphit kb explore table X` |
27
+
28
+ Full CRUD parity: all agent actions have CLI equivalents.
29
+
30
+ ## Asset CRUD
31
+
32
+ | UI Action | Agent Tool Path |
33
+ |-----------|----------------|
34
+ | Create metric | `create(entity_type="metric", name=..., calculation=..., dependencies=...)` |
35
+ | Create dimension | `create(entity_type="dimension", name=..., table_name=..., expression=...)` |
36
+ | Create rule | `create(entity_type="rule", name=..., rule_text=...)` |
37
+ | Create synonym | `create(entity_type="synonym", term=..., canonical=..., canonical_type=...)` |
38
+ | Create relationship | `create(entity_type="relationship", primary_table=..., primary_column=..., related_table=..., related_column=...)` |
39
+ | Edit any asset | `edit(entity_type=..., id=..., field=new_value)` |
40
+ | Delete any asset | `delete(entity_type=..., id=...)` |
41
+ | Toggle verified | `edit(entity_type=..., id=..., verified=true/false)` |
42
+
43
+ ## Topic Management
44
+
45
+ | UI Action | Agent Tool Path |
46
+ |-----------|----------------|
47
+ | Create topic | `create(entity_type="topic_metadata", name=..., description=...)` |
48
+ | Edit topic description | `edit(entity_type="topic_metadata", id=..., description=...)` |
49
+ | Delete topic metadata | `delete(entity_type="topic_metadata", id=...)` |
50
+ | Toggle topic verified | `edit(entity_type="topic_metadata", id=..., verified=true/false)` |
51
+ | Add topic to asset | `edit(entity_type="metric", id=..., topics=[...existing, "NEW_TOPIC"])` - append to existing list |
52
+ | Remove topic from asset | `edit(entity_type="metric", id=..., topics=[...without removed])` - filter out the topic |
53
+ | Move asset between topics | `edit(entity_type="metric", id=..., topics=["NEW_TOPIC"])` - replace the list |
54
+ | Rename topic (cascade) | `edit(entity_type="topic_metadata", id=..., name=...)` then batch `edit` on each asset that references the old name |
55
+ | Bulk reassign topics | Present plan via `ask_user`, then execute N `edit` calls on approval |
56
+
57
+ ## Domain Management
58
+
59
+ | UI Action | Agent Tool Path |
60
+ |-----------|----------------|
61
+ | Create domain | `create(entity_type="domain", name=..., description=..., color=...)` |
62
+ | Edit domain | `edit(entity_type="domain", id=..., description=..., color=...)` |
63
+ | Delete domain | `delete(entity_type="domain", id=...)` |
64
+
65
+ ## Domain Assignment (Cascade Model)
66
+
67
+ Domain is a single **home** set on the table; every asset on that table inherits it. Assets never store their own home domain - set it on the table, not the asset.
68
+
69
+ | UI Action | Agent Tool Path |
70
+ |-----------|----------------|
71
+ | Set a table's home domain | `edit(entity_type="table", id=..., domain_id="MARKETING")` - cascades to every asset on that table |
72
+ | Clear a table's home domain | `edit(entity_type="table", id=..., domain_id=None)` - its assets fall back to Uncategorized |
73
+ | Reference asset onto another table | `edit(entity_type="metric", id=..., secondary_tables=["OTHER_TABLE"])` - appears under the target table as a read-only pointer (marked with `*`). Metrics/dimensions require identical columns; rules require only target existence. |
74
+ | Remove a table reference | `edit(entity_type="metric", id=..., secondary_tables=[...without removed])` |
75
+ | Mark a synonym cross-cutting to extra domains | `edit(entity_type="synonym", id=..., extra_domain_ids=["FINANCE"])` - adds badges + filter match, does NOT move it in the tree |
76
+ | Remove cross-cutting domains from synonym | `edit(entity_type="synonym", id=..., extra_domain_ids=[])` |
77
+
78
+ A table-backed asset's (metric, dimension, rule) domain is derived from its dependency tables plus any `secondary_tables`. A synonym can also carry `extra_domain_ids` for cross-cutting relevance. To re-home a whole table of assets, change `domain_id` on the table once - do not edit assets individually.
79
+
80
+ ## Navigation and Discovery
81
+
82
+ | UI Action | Agent Tool Path |
83
+ |-----------|----------------|
84
+ | Search KB | `search(query="...", types=[...])` |
85
+ | Filter by topic | `search(query="...", topics=["REVENUE"])` |
86
+ | Filter by domain | `search(query="...", domain="MARKETING")` - matches assets whose dependency or secondary tables are in the MARKETING domain. Synonyms also match via `extra_domain_ids`. |
87
+ | List all domains | `list(entity_type="domain")` - returns every domain with name, description, color, and asset_count; includes an "Uncategorized" entry when tables with no domain exist. Report every domain it returns; do not drop ones with asset_count=0, and do not use `search` or `kb_explore` |
88
+ | List all tables | `list(entity_type="table")` - returns every table with name, domain_id (null if uncategorized), and verified status. Use to see which tables are assigned to which domain, or to find uncategorized tables |
89
+ | Browse by table | `search(query="...", types=["metric", "dimension"])` then filter results by table |
90
+ | What depends on X? | `kb_explore(entity_type="table", entity_name="X", edge_types=["depends_on"])` |
91
+ | What joins with table? | `kb_explore(entity_type="table", entity_name="X", edge_types=["joins"])` |
92
+ | What's in topic X? | `kb_explore(entity_type="topic", entity_name="X", edge_types=["tagged_with"])` |
93
+ | What's in domain X? | `kb_explore(entity_type="domain", entity_name="X")` |
94
+
95
+ ## View Controls (UI-Only, No Agent Parity Needed)
96
+
97
+ These actions are purely UI navigation state and have no agent equivalent:
98
+ - Switch view mode (Tree / By Topic / By Table / Flat)
99
+ - Filter dropdowns (domain, table, topic, type, verified-only)
100
+ - Expand/collapse tree nodes
101
+ - Drag-drop asset onto topic (maps to "Add topic to asset" above)
@@ -0,0 +1,88 @@
1
+ ---
2
+ alwaysApply: false
3
+ description: "Graphit: > Load when: planning graphs that involve metrics (aggregations) or dimensions (grouping/filtering columns), or when the user references a business co"
4
+ globs: []
5
+ ---
6
+
7
+ # KB-Aware Planning
8
+
9
+ > Load when: planning graphs that involve metrics (aggregations) or dimensions (grouping/filtering columns), or when the user references a business concept that might already exist as a KB asset.
10
+
11
+ Before writing inline SQL with raw aggregations or column references, check whether a KB asset already defines the concept. Using KB assets ensures consistent formulas across dashboards and makes future graphs faster to create.
12
+
13
+ ## KB-First Discovery
14
+
15
+ Before proposing graphs with inline aggregations or custom column expressions:
16
+
17
+ 1. Check the KB Overview in the system prompt for existing metrics and dimensions
18
+ 2. If the KB Overview shows assets that match the user's request, reference them by name
19
+ 3. If no matching asset exists, recommend creating it first, then build the graph referencing the new asset
20
+
21
+ If the KB Overview shows tables but no metrics or dimensions, this is the strongest signal to suggest KB asset creation. Propose foundational metrics and dimensions based on the table schema before planning any graphs. If the user explicitly declines ("just build it", "skip KB"), respect that and work from table schema.
22
+
23
+ ## When to Propose KB Asset Creation
24
+
25
+ | Signal | Proposed asset | Why |
26
+ |--------|---------------|-----|
27
+ | User requests a business metric (revenue, DAU, conversion rate) with no KB match | Metric | Reusable formula across dashboards |
28
+ | User groups by a derived expression (date bucket, category mapping, JSON extraction) | Dimension | Consistent grouping logic |
29
+ | User describes a business rule ("active means logged in within 30 days") | Rule | Applied automatically to future queries |
30
+ | User uses a business term not in KB ("GMV", "churn", "ARPU") | Synonym | Maps colloquial terms to defined metrics |
31
+
32
+ Propose creation as the default path: "Your KB doesn't have a revenue metric yet. I'd recommend creating one first - that way it's reusable across all your dashboards with a consistent formula. Then I'll build the graph using it. Sound good?"
33
+
34
+ ## Metric vs Dimension Distinction
35
+
36
+ | Property | Metric | Dimension |
37
+ |----------|--------|-----------|
38
+ | Formula type | Aggregation required (SUM, COUNT, AVG, MIN, MAX) | Row-level only (no aggregates) |
39
+ | Table scope | Can reference multiple tables | Exactly one table |
40
+ | Purpose | Measures (what you count/sum) | Grouping axes (how you slice) |
41
+ | Example | `SUM(ORDERS.AMOUNT)` | `DATE_TRUNC('month', EVENTS.EVENT_TS)` |
42
+ | Invalid example | `ORDERS.AMOUNT` (no aggregate) | `SUM(EVENTS.DURATION)` (has aggregate) |
43
+
44
+ ## Topics
45
+
46
+ When creating any KB asset, consider which business topic it belongs to and pass `topics` (e.g., `["ACQUISITION"]`, `["MONETIZATION"]`). Check the KB Overview for existing topic names first to avoid duplicates. UPPER_SNAKE_CASE.
47
+
48
+ ## Reuse Over Reinvention
49
+
50
+ When planning multi-graph dashboards, identify shared concepts across graphs and propose KB assets for them:
51
+
52
+ - If 3 graphs all use `SUM(ORDERS.AMOUNT)`, propose a TOTAL_REVENUE metric once
53
+ - If 2 graphs group by `DATE_TRUNC('month', TS)`, propose a MONTHLY dimension once
54
+ - Reference the KB asset by name in subsequent graphs instead of repeating the formula
55
+
56
+ ## Cross-Table Referencing
57
+
58
+ When a metric or dimension already exists on one table and the user wants the same concept on a sibling table with identical columns, **reference it** instead of creating a duplicate:
59
+
60
+ `edit(entity_type="metric", id="TOTAL_REVENUE", secondary_tables=["ORDERS_12M"])`
61
+
62
+ - Metrics/dimensions: the target table must contain every column the formula references (validated on save).
63
+ - Rules: the target table just needs to exist (no column check).
64
+ - This is a pointer, not a copy. Edits propagate to all placements.
65
+ - In the KB tree, referenced placements appear with a `*` suffix and link back to the original.
66
+
67
+ Suggest referencing when: the user has sibling data sources with the same schema (e.g., 6M and 12M windows), or asks to "make X available on Y table".
68
+
69
+ > For metric formula syntax, aggregation patterns, and WHERE clause rules, see `kb_authoring/references/formula-syntax.md`. For creating KB assets, hand off to `kb_authoring`.
70
+
71
+ ## KB Graph Navigation
72
+
73
+ For structural questions about the KB (what is a topic, what depends on X, how do domains work), graph traversal, or KB organization requests, activate the `kb_navigation` shared skill. It provides:
74
+
75
+ - `kb_explore` tool for graph traversal (what depends on X, what joins with Y)
76
+ - Structural explanations grounded in actual KB data
77
+ - Full action parity with the manual KB page UI
78
+ - Topic and domain filtering via `search` tool
79
+
80
+ **When to activate kb_navigation vs stay in current skill:**
81
+
82
+ | User Intent | Activate kb_navigation? |
83
+ |-------------|------------------------|
84
+ | "What is a topic?" or "How do domains work?" | Yes - structural question |
85
+ | "What depends on this table?" | Yes - graph traversal |
86
+ | "Organize my KB" or "Clean up topics" | Yes - KB management |
87
+ | "Show me revenue metrics for my graph" | No - use search + read, stay in current skill |
88
+ | "Create a metric called X" | No - use kb_expert or kb_authoring |
@@ -0,0 +1,56 @@
1
+ ---
2
+ alwaysApply: false
3
+ description: "Graphit: Tier 1 structural questions about KB concepts. Answer these using the patterns below, grounded in actual KB data when possible. Use business-friendly "
4
+ globs: []
5
+ ---
6
+
7
+ # KB Explanation - Structural Q&A Patterns
8
+
9
+ Tier 1 structural questions about KB concepts. Answer these using the patterns below, grounded in actual KB data when possible. Use business-friendly language - no implementation details.
10
+
11
+ ## Tier 1: Structural (In Scope)
12
+
13
+ Answer directly using these patterns. Always verify against actual KB state when the question involves specific entities.
14
+
15
+ ### What is a topic?
16
+ A topic is a business concept tag you apply to metrics, dimensions, rules, and synonyms. Examples: REVENUE, ACQUISITION, RETENTION. Any asset can have multiple topics. Topics help organize the KB by business meaning rather than physical table location.
17
+
18
+ ### What is a domain?
19
+ A domain is a high-level business area like MARKETING, SALES, or PRODUCT. Domains are broader than topics - think of them as departments or business units. A domain is set on a table, and every metric, dimension, and rule built on that table belongs to that domain automatically. An org typically has 3-10 domains and 10-30+ topics.
20
+
21
+ ### How do domains and topics differ?
22
+ The difference is granularity and how they attach:
23
+ - A **domain** is coarse (5-10 per org: MARKETING, FINANCE, PRODUCT) and is set on the table. Each asset has one home domain, inherited from the table its data comes from. An asset can also be marked relevant to a few extra domains when it genuinely spans areas.
24
+ - A **topic** is fine (10-30+ per org: REVENUE, ACQUISITION, RETENTION) and is tagged directly on assets. An asset can carry several topics at once.
25
+
26
+ A single domain like MARKETING might contain topics like ACQUISITION, ATTRIBUTION, and CAMPAIGN_PERFORMANCE.
27
+
28
+ ### What is a table in the KB?
29
+ A table represents a physical data location in the warehouse. Metrics and dimensions depend on tables because their SQL formulas reference table columns. Tables are structural, not tags - the dependency is derived from the SQL, not manually assigned.
30
+
31
+ ### What is a relationship?
32
+ A relationship documents a JOIN between two tables. It specifies which columns connect them and what kind of join to use. When the agent writes SQL across multiple tables, it uses relationships to build correct JOINs.
33
+
34
+ ### Why is [metric] under [table]?
35
+ A metric appears under a table because the metric's SQL formula depends on columns in that table. Check the metric's dependencies to see exactly which columns it uses. A metric can depend on multiple tables - it appears under each one, with the first listed as primary. An asset can also appear under a table via **referencing** (`secondary_tables`) - this shows as a `*` marker in the tree and means the asset was made available on that table as a read-only pointer. The original lives on its home table.
36
+
37
+ ### Why is [asset] in [topic]?
38
+ An asset appears under a topic because someone (user, agent, or scanner) tagged it with that topic. Topics are manually assigned tags, not automatically derived. Check the asset's topics list to see all its topic assignments.
39
+
40
+ ### Why is [asset] in [domain]?
41
+ An asset's domain comes from the table its data lives in: whoever set the table's domain, every asset on that table inherits it. So a metric is in MARKETING because its primary table is a MARKETING table, not because the metric itself was tagged. If an asset genuinely spans areas, it can also be marked relevant to extra domains, which show as badges and match domain filters without moving it in the tree. To change a metric's home domain, change the domain on its table.
42
+
43
+ ### How do tables and topics differ?
44
+ Tables are physical (where the data lives in the warehouse). Topics are conceptual (what the data is about). One table can have assets tagged with many different topics, and one topic can span assets across multiple tables.
45
+
46
+ ### What is memory?
47
+ Memory stores org-level context notes - background information about the organization, its goals, terminology, or conventions. Memory is global (not scoped to any table, domain, or topic) and appears at the root of the KB tree.
48
+
49
+ ## Tier 2: Semantic (Out of Scope for This Skill)
50
+
51
+ Questions about what a specific business concept means at this organization (e.g., "what does revenue mean for us?", "how do we define an active user?") are semantic questions, not structural ones. For these:
52
+ 1. Search the KB for the relevant metric or rule
53
+ 2. Read its description and calculation
54
+ 3. Present what the KB says - do not interpret or extend it
55
+
56
+ Do not attempt to answer semantic questions from general knowledge. The KB is the source of truth for this org's definitions.
@@ -12,12 +12,11 @@ Consult when starting a dashboard build. The Knowledge Base contains reusable me
12
12
 
13
13
  Before writing raw SQL with inline aggregations or column references:
14
14
 
15
- 1. List what exists: `graphit kb list metrics`, `graphit kb list dimensions`, `graphit kb list rules`
16
- 2. If a KB metric matches the user's request, use its formula: `graphit kb get metric REVENUE`
17
- 3. If no match exists, consider whether to build the dashboard from raw columns or suggest KB asset creation first
18
- 4. Explore relationships: `graphit kb explore metric REVENUE` shows which tables and dimensions connect to a metric
19
-
20
- KB assets ensure consistent formulas across dashboards. If 3 charts all need `SUM(ORDERS.AMOUNT)`, one TOTAL_REVENUE metric is better than 3 inline aggregations.
15
+ 1. Understand organization: `graphit kb list domains` shows business-area groupings (MARKETING, PRODUCT, FINANCE, etc.)
16
+ 2. List what exists: `graphit kb list metrics`, `graphit kb list dimensions`, `graphit kb list rules`
17
+ 3. If a KB metric matches the user's request, use its formula: `graphit kb get metric REVENUE`
18
+ 4. If no match exists, consider whether to build the dashboard from raw columns or suggest KB asset creation first
19
+ 5. Explore relationships: `graphit kb explore metric REVENUE` shows which tables and dimensions connect to a metric
21
20
 
22
21
  ## Metric vs Dimension
23
22
 
@@ -40,13 +39,6 @@ All KB assets use UPPER_SNAKE_CASE. The names are auto-sanitized:
40
39
  | `COUNT_*` | `COUNT_ACTIVE_USERS` | Count metrics |
41
40
  | `*_RATE` | `CONVERSION_RATE`, `CHURN_RATE` | Ratios/percentages |
42
41
 
43
- ## Reuse Over Reinvention
44
-
45
- When building multi-chart dashboards, identify shared concepts:
46
- - If 3 charts use `SUM(ORDERS.AMOUNT)`, reference one TOTAL_REVENUE metric
47
- - If 2 charts group by `DATE_TRUNC('month', TS)`, reference one MONTHLY dimension
48
- - Discover existing assets: `graphit kb search "revenue"`
49
-
50
42
  ## When to Suggest KB Asset Creation
51
43
 
52
44
  | Signal | Propose |
@@ -73,3 +65,5 @@ CASE WHEN USERS.AGE >= 18 THEN 'adult' ELSE 'minor' END
73
65
  ```
74
66
 
75
67
  Conditionals use CASE WHEN (not FILTER WHERE - Snowflake doesn't support it). Always guard division with NULLIF.
68
+
69
+ For deeper coverage, consult: `kb-graph-structure.md` (graph model), `kb-awareness.md` (KB-first planning), `kb-explanation.md` (structural Q&A), `parameterized-metrics.md` (metric templates), `kb-traversal.md` (graph queries), `kb-actions.md` (full CRUD).
@@ -0,0 +1,61 @@
1
+ ---
2
+ alwaysApply: false
3
+ description: "Graphit: The Knowledge Base is a labeled property graph. Assets carry tags that place them in a tree, but the underlying structure is a graph with typed edges."
4
+ globs: []
5
+ ---
6
+
7
+ # KB Graph Structure
8
+
9
+ The Knowledge Base is a labeled property graph. Assets carry tags that place them in a tree, but the underlying structure is a graph with typed edges.
10
+
11
+ ## Node Types
12
+
13
+ | Type | Count Range | Description |
14
+ |------|-------------|-------------|
15
+ | metric | 10-200+ | Aggregation formula (SUM, COUNT, AVG) that computes a business KPI |
16
+ | dimension | 20-500+ | Row-level SQL expression on one table, used for grouping or filtering |
17
+ | rule | 5-100+ | Free-text business constraint, optionally scoped to one or more tables |
18
+ | synonym | 10-100+ | Maps a business term to a canonical metric, dimension, or column |
19
+ | table | 2-50+ | Physical data location in the warehouse, with typed columns |
20
+ | topic | 5-30+ | Business-concept tag applied to assets (e.g., REVENUE, ACQUISITION) |
21
+ | domain | 3-10 | High-level business area (e.g., MARKETING, SALES, PRODUCT) |
22
+ | relationship | 2-30+ | Documented JOIN pattern between two tables |
23
+ | memory | 1-5 | Org-level context notes, always global scope |
24
+
25
+ ## Edge Types
26
+
27
+ | Edge | From | To | Meaning |
28
+ |------|------|----|---------|
29
+ | depends_on | metric, dimension | table | Asset's SQL references columns in this table |
30
+ | tagged_with | metric, dimension, rule, synonym | topic | Asset carries this business-concept tag |
31
+ | in_domain | table, metric, dimension, rule, synonym | domain | A table has one home domain; assets inherit that home from their primary table, plus any cross-cutting extras |
32
+ | joins | relationship | table, table | Two tables have a documented JOIN on specific columns |
33
+ | references | rule, synonym | table, column | Rule or synonym references a specific table or column name |
34
+
35
+ ## Multi-Membership Semantics
36
+
37
+ Tables and topics are multi-valued; domain works differently - a single **home** that cascades:
38
+
39
+ - A metric can depend on **multiple tables** (e.g., JOIN across ORDERS and CUSTOMERS), and appears under each.
40
+ - An asset has **one home domain**, inherited from its primary table (the first table its SQL depends on). The asset does not carry its own domain tag - set the domain on the table and every asset on that table inherits it.
41
+ - An asset can carry **multiple topics** (e.g., ARPU tagged with both REVENUE and MONETIZATION).
42
+ - An asset can be **referenced** onto additional tables via `secondary_tables`. Referenced placements appear in the tree under the target table with a `*` suffix and link back to the original. The asset is editable only from its home table.
43
+ - Synonyms can carry **cross-cutting domains** in `extra_domain_ids` for relevance beyond the home domain. Table-backed assets (metrics, dimensions, rules) derive domain membership entirely from their tables.
44
+
45
+ For multiple tables, the tree shows the asset under each table with a link icon. For domain, the tree places each table (and its assets) under its one home domain only - cross-cutting extras are badges, not duplicate placements.
46
+
47
+ ## Tags vs Structure
48
+
49
+ | Concept | Type | Cardinality | Notes |
50
+ |---------|------|-------------|-------|
51
+ | Domain | Home (on table) + secondary_tables | One home per asset (from its table); domain derived from all tables | Coarser grouping (~5-10 per org). Home cascades from the primary table; `secondary_tables` extends reach; synonyms use `extra_domain_ids` |
52
+ | Topic | Pure tag | Any (typically 1-3) | Finer grouping (~10-30+ per org) |
53
+ | Table | Structural dependency | Any (typically 1-2) | Based on SQL dependencies, not manual tagging |
54
+ | Relationship | First-class edge | Exactly 2 tables | Explicit JOIN documentation |
55
+ | Memory | Global | N/A | Not scoped to any table, domain, or topic |
56
+
57
+ **Key insight for traversal:** Topics are multi-valued tags on assets (agent adds/removes via `edit`). Domain is a single home set on the **table** (`edit(entity_type="table", domain_id=...)`), which cascades to every asset on that table. Table-backed assets derive domain membership from all their tables (primary + `secondary_tables`). Synonyms use `extra_domain_ids` for cross-cutting domain relevance. Table dependencies are structural (derived from SQL). Relationships are explicit edges between tables.
58
+
59
+ ## Tree Rendering Order
60
+
61
+ The default tree renders as: Domain > Table > Topic > Asset, where each table sits under its one home domain. This is a visualization choice; the data model also supports other groupings (Topic > Table, flat list). When the user asks "where is X?", an asset lives under its primary table's home domain - report that, plus any domains from its `secondary_tables` placements and its topics.
@@ -0,0 +1,64 @@
1
+ ---
2
+ alwaysApply: false
3
+ description: "Graphit: Common graph queries and which tool to use for each. Agent tool names map to CLI commands: `search` = `graphit kb search`, `read` = `graphit kb get`, "
4
+ globs: []
5
+ ---
6
+
7
+ # KB Traversal - Worked Examples
8
+
9
+ Common graph queries and which tool to use for each. Agent tool names map to CLI commands: `search` = `graphit kb search`, `read` = `graphit kb get`, `list` = `graphit kb list`, `kb_explore` = `graphit kb explore`.
10
+
11
+ ## Tool Selection Guide
12
+
13
+ | Question Shape | Best Tool | Why |
14
+ |----------------|-----------|-----|
15
+ | Semantic ("find things like X") | `search` | Embedding-based discovery across all asset types |
16
+ | Pointed retrieval ("get metric Y") | `read` | Direct fetch by exact name, returns full details |
17
+ | Topic/domain filter ("metrics in REVENUE") | `search` with topics/domain params | Filtered discovery, intersects with query |
18
+ | Connectivity ("what depends on X") | `kb_explore` | Graph walk from a starting entity, returns typed edges |
19
+ | Joins specifically | `kb_explore` with edge_types=["joins"] | Targeted relationship traversal |
20
+ | Multi-hop ("what's 2 hops from X") | `kb_explore` with max_depth=2 | Recursive walk, capped at 3 hops |
21
+
22
+ ## Common Queries
23
+
24
+ ### "Find all revenue metrics"
25
+ Use `search` with a topic filter to get metrics tagged with the REVENUE topic. If the user means revenue conceptually (not a specific topic), use a semantic search query instead.
26
+
27
+ ### "What depends on ORDERS table?"
28
+ Use `kb_explore` starting from the table, filtering to depends_on edges. Returns all metrics and dimensions whose SQL references ORDERS columns.
29
+
30
+ ### "What joins with MARKETING_UA_DS?"
31
+ Use `kb_explore` starting from the table, filtering to joins edges. Returns relationship entities that document JOINs involving that table.
32
+
33
+ ### "What domains do we have?"
34
+ Use `list` with the domain entity type (`list(entity_type="domain")`). It returns every domain with its description and asset_count, plus an "Uncategorized" entry when tables with no domain exist. Report all of them, including ones with asset_count=0. Do NOT use `search` (domain is not a searchable type) or `kb_explore` (it traverses from one named domain - a wildcard like "*" returns nothing).
35
+
36
+ ### "What's left in uncategorized?"
37
+ Two paths: (1) `list(entity_type="table")` and filter for `domain_id=null` to find unassigned tables, then `kb_explore(entity_type="domain", entity_name="Uncategorized")` to get all tables and assets in the pseudo-domain; or (2) just use `kb_explore(entity_type="domain", entity_name="Uncategorized")` directly to get everything.
38
+
39
+ ### "Show me everything in the MARKETING domain"
40
+ Use `kb_explore` starting from the domain. Returns tables (via in_domain edge) and all assets within those tables (via depends_on edges at depth 2).
41
+
42
+ ### "What topics does ARPU_D1 belong to?"
43
+ Use `kb_explore` starting from the metric, filtering to tagged_with edges. Returns all topic nodes the metric is tagged with.
44
+
45
+ ### "Find metrics in same table as ARPU_D1 but different topic"
46
+ Two-step query:
47
+ 1. Use `kb_explore` on ARPU_D1 to find its table(s) and topic(s)
48
+ 2. Use `search` filtered to that table's metrics, then compare topics in the results
49
+
50
+ ### "What domains have retention metrics?"
51
+ Use `search` with topics=["RETENTION"] and types=["metric"]. A metric's domain is derived from its dependency tables and secondary_tables - there is no domain field directly on the metric. Group the results by that effective domain. To get the home domain for one metric, `kb_explore` from the metric with edge_types=["in_domain"] returns it.
52
+
53
+ ### "Show me the full graph around table X"
54
+ Use `kb_explore` with max_depth=2 and no edge_type filter. Returns the table's domain, all dependent assets, all topics those assets carry, and all relationships the table participates in.
55
+
56
+ ## Depth Guidelines
57
+
58
+ | Depth | Use When |
59
+ |-------|----------|
60
+ | 1 (default) | Direct neighbors only. Best for most questions. |
61
+ | 2 | When you need one extra hop (e.g., table's assets AND their topics) |
62
+ | 3 (max) | Full neighborhood. Use sparingly - can return many results for connected tables. |
63
+
64
+ Start at depth 1. Only increase if the user's question requires it or the initial results are insufficient.
@@ -0,0 +1,95 @@
1
+ ---
2
+ alwaysApply: false
3
+ description: "Graphit: A parameterized metric is a template with `${PARAM:NAME}` tokens in its calculation. Each token maps to a named parameter with a list of values (value"
4
+ globs: []
5
+ ---
6
+
7
+ # Parameterized Metrics
8
+
9
+ A parameterized metric is a template with `${PARAM:NAME}` tokens in its calculation. Each token maps to a named parameter with a list of values (value_name to SQL fragment). The system auto-generates child variants for every combination of parameter values.
10
+
11
+ Example: ROAS with parameters REVENUE (9 values) and DN (4 values) produces 36 validated child metrics from 1 template.
12
+
13
+ ## When to Parameterize
14
+
15
+ **Create parameterized** when columns follow a variant pattern:
16
+ - `total_iap`, `total_iap_new_users`, `estimated_gross_iap` -> one REVENUE parameter
17
+ - Metrics that differ only by a WHERE clause (D7, D30, D90) -> one DN parameter
18
+
19
+ **Stay standalone** when:
20
+ - Metric has no natural variants
21
+ - Only 1-2 variants (template overhead not worth it)
22
+ - Variants use fundamentally different formulas
23
+
24
+ ## Creating a Parameterized Metric
25
+
26
+ Use `${PARAM:NAME}` tokens in the calculation, then pass `parameters` with the value map. The `placeholder` field is optional (omit it for the new token format):
27
+
28
+ ```json
29
+ "calculation": "SUM(${PARAM:REVENUE}) / SUM(cost) * 100 ${PARAM:DN}",
30
+ "parameters": [
31
+ {
32
+ "name": "REVENUE",
33
+ "values": {"ALL_BOOKINGS": "total_iap", "NEW_BOOKINGS": "total_iap_new_users"},
34
+ "default_value": "ALL_BOOKINGS"
35
+ },
36
+ {
37
+ "name": "DN",
38
+ "values": {"D0": "", "D7": "WHERE day <= 7"},
39
+ "default_value": "D0"
40
+ }
41
+ ]
42
+ ```
43
+
44
+ | Rule | Detail |
45
+ |------|--------|
46
+ | Token matching | Every `${PARAM:NAME}` in the calculation must have a matching parameter by name |
47
+ | Empty values | Valid as no-ops (e.g. D0 adds no filter) |
48
+ | Variant cap | Max 100 (Cartesian product of all parameter values) |
49
+ | Child naming | Auto-generated: `{PARENT}_{VALUE1}_{VALUE2}` (e.g. ROAS_ALL_BOOKINGS_D7) |
50
+
51
+ ## Editing a Template
52
+
53
+ All edits go through the parent template. Children are read-only.
54
+
55
+ | User intent | Edit action |
56
+ |-------------|-------------|
57
+ | Change formula | Edit `calculation` on the template - children re-generate |
58
+ | Add a variant (e.g. D120) | Edit `parameters`, add the new value to the relevant parameter |
59
+ | Remove variants | Edit `parameters`, remove values from the relevant parameter |
60
+ | Change description/topics | Edit those fields on the template - no child re-generation |
61
+
62
+ If a child metric is targeted for edit or delete, redirect to the parent:
63
+ "ROAS_ALL_BOOKINGS_D7 is a variant of ROAS. Edit the parent template ROAS instead."
64
+
65
+ ## Using Parameterized Metrics in Queries
66
+
67
+ When a user asks for a specific variant (e.g. "show me D7 ROAS for new users"):
68
+
69
+ 1. Find the template in KB context (marked with `[P:N]` in the tree)
70
+ 2. Infer axis values from the user's message: REVENUE=NEW_BOOKINGS, DN=D7
71
+ 3. Use `read` with `entity_type="metric"` and pass `axis_values` to get the resolved child
72
+ 4. Use the returned validated child calculation in your graph SQL
73
+
74
+ ### NEVER
75
+ - Substitute tokens manually in the template formula. Always use the backend-returned child SQL, which has been pre-validated.
76
+
77
+ ### Defaults and Errors
78
+ - **Default resolution:** When the user doesn't specify parameters, pass empty `axis_values` - the backend fills defaults.
79
+ - **Invalid values:** If you pass an invalid axis value, the backend returns valid options. Retry with a valid value or ask the user.
80
+
81
+ ## Contrast: Wrong vs Right
82
+
83
+ | Wrong | Right |
84
+ |-------|-------|
85
+ | Manually replace `${PARAM:REVENUE}` with `total_iap` in the formula and pass to graph SQL | Use `read` with `axis_values` to get pre-validated child calculation |
86
+ | Create separate ROAS_ALL_BOOKINGS, ROAS_NEW_BOOKINGS as standalone metrics | Create one ROAS template with a REVENUE parameter |
87
+ | Edit ROAS_ALL_BOOKINGS_D7 directly | Edit the parent ROAS template instead |
88
+
89
+ ## Deleting a Template
90
+
91
+ Delete on the parent cascade-deletes all children. Blast radius includes all children plus their downstream refs (graphs, rules, synonyms).
92
+
93
+ ## Verification
94
+
95
+ Verify on a template sets `verified=True` on the parent and all children.
@@ -56,7 +56,7 @@ NEVER embed query results as static JS variables. The dashboard iframe provides
56
56
 
57
57
  ---
58
58
 
59
- ## Workflow
59
+ ## Workflow: Dashboard Build
60
60
 
61
61
  1. **Ask the user** what dashboard they want.
62
62
  2. **Explore KB** to understand available metrics, dimensions, tables, and rules.
@@ -66,6 +66,17 @@ NEVER embed query results as static JS variables. The dashboard iframe provides
66
66
  6. **Save** with `graphit dashboard update-html <id> --file <path>`.
67
67
  7. Give the user the dashboard URL.
68
68
 
69
+ ## Workflow: KB Build / Onboarding
70
+
71
+ When the user wants to build or populate their KB from files, schemas, or scratch. Consult companion rules: graphit-kb-graph-structure (graph model), graphit-kb-traversal (tool selection), graphit-kb-actions (CLI commands), graphit-kb-awareness (when to propose assets), graphit-parameterized-metrics (template metrics).
72
+
73
+ 1. **Audit current KB state FIRST.** Traverse, don't just list: `graphit kb list domains`, then `graphit kb explore domain <NAME>` for each (returns full tree in one call). Add `graphit kb list synonyms` and `graphit kb list relationships` for globals. Present tree summary.
74
+ 2. **Analyze input.** Read the user's files/schema. Classify every concept by asset type (see graphit-kb-explanation for definitions, graphit-kb-awareness for signals).
75
+ 3. **Present a tree plan** showing Domain > Table > Topic > Asset hierarchy. Mark existing assets with (exists), new ones with **NEW**. Include global items (synonyms, relationships) at the bottom. Propose parameterized templates for variant metrics (D7/D30/D90).
76
+ 4. **Wait for approval.** Do not create anything until confirmed.
77
+ 5. **Execute top-down.** Create in order: domains, topics, metrics (with --topics), dimensions, rules, synonyms, relationships, then attachments (domain->table, secondary_tables).
78
+ 6. **Verify.** Re-traverse with `graphit kb explore domain <NAME>`, confirm counts match plan.
79
+
69
80
  ## graphit.resolve() - Live Data API
70
81
 
71
82
  ```js
@@ -0,0 +1,95 @@
1
+ # KB Actions - CLI Parity Matrix
2
+
3
+ Every KB action has a CLI equivalent. The tool syntax below maps to CLI commands:
4
+
5
+ | Agent Tool Syntax | CLI Command |
6
+ |---|---|
7
+ | `create(entity_type="metric", name=..., calculation=...)` | `graphit kb create metric --name X --sql "..." --table T` |
8
+ | `create(entity_type="dimension", name=..., expression=...)` | `graphit kb create dimension --name X --expr "..." --table T` |
9
+ | `create(entity_type="rule", name=..., rule_text=...)` | `graphit kb create rule --name X --sql "..." --table T` |
10
+ | `create(entity_type="synonym", term=..., canonical=...)` | `graphit kb create synonym --term X --canonical Y --type metric` |
11
+ | `create(entity_type="domain", name=...)` | `graphit kb create domain --name X --description "..."` |
12
+ | `create(entity_type="relationship", ...)` | `graphit kb create relationship --name X --primary-table T --primary-column C --related-table T2 --related-column C2` |
13
+ | `create(entity_type="topic_metadata", name=...)` | `graphit kb create topic --name X --description "..."` |
14
+ | `edit(entity_type=..., id=..., field=value)` | `graphit kb update <type> NAME --field value` |
15
+ | `edit(entity_type="table", id=..., domain_id=...)` | `graphit kb update table NAME --domain DOMAIN` |
16
+ | `delete(entity_type=..., id=...)` | `graphit kb delete <type> NAME --yes` |
17
+ | `search(query="...")` | `graphit kb search "query"` |
18
+ | `list(entity_type="domain")` | `graphit kb list domains` |
19
+ | `read(entity_type="metric", name=...)` | `graphit kb get metric NAME` |
20
+ | `kb_explore(entity_type="table", entity_name="X")` | `graphit kb explore table X` |
21
+
22
+ Full CRUD parity: all agent actions have CLI equivalents.
23
+
24
+ ## Asset CRUD
25
+
26
+ | UI Action | Agent Tool Path |
27
+ |-----------|----------------|
28
+ | Create metric | `create(entity_type="metric", name=..., calculation=..., dependencies=...)` |
29
+ | Create dimension | `create(entity_type="dimension", name=..., table_name=..., expression=...)` |
30
+ | Create rule | `create(entity_type="rule", name=..., rule_text=...)` |
31
+ | Create synonym | `create(entity_type="synonym", term=..., canonical=..., canonical_type=...)` |
32
+ | Create relationship | `create(entity_type="relationship", primary_table=..., primary_column=..., related_table=..., related_column=...)` |
33
+ | Edit any asset | `edit(entity_type=..., id=..., field=new_value)` |
34
+ | Delete any asset | `delete(entity_type=..., id=...)` |
35
+ | Toggle verified | `edit(entity_type=..., id=..., verified=true/false)` |
36
+
37
+ ## Topic Management
38
+
39
+ | UI Action | Agent Tool Path |
40
+ |-----------|----------------|
41
+ | Create topic | `create(entity_type="topic_metadata", name=..., description=...)` |
42
+ | Edit topic description | `edit(entity_type="topic_metadata", id=..., description=...)` |
43
+ | Delete topic metadata | `delete(entity_type="topic_metadata", id=...)` |
44
+ | Toggle topic verified | `edit(entity_type="topic_metadata", id=..., verified=true/false)` |
45
+ | Add topic to asset | `edit(entity_type="metric", id=..., topics=[...existing, "NEW_TOPIC"])` - append to existing list |
46
+ | Remove topic from asset | `edit(entity_type="metric", id=..., topics=[...without removed])` - filter out the topic |
47
+ | Move asset between topics | `edit(entity_type="metric", id=..., topics=["NEW_TOPIC"])` - replace the list |
48
+ | Rename topic (cascade) | `edit(entity_type="topic_metadata", id=..., name=...)` then batch `edit` on each asset that references the old name |
49
+ | Bulk reassign topics | Present plan via `ask_user`, then execute N `edit` calls on approval |
50
+
51
+ ## Domain Management
52
+
53
+ | UI Action | Agent Tool Path |
54
+ |-----------|----------------|
55
+ | Create domain | `create(entity_type="domain", name=..., description=..., color=...)` |
56
+ | Edit domain | `edit(entity_type="domain", id=..., description=..., color=...)` |
57
+ | Delete domain | `delete(entity_type="domain", id=...)` |
58
+
59
+ ## Domain Assignment (Cascade Model)
60
+
61
+ Domain is a single **home** set on the table; every asset on that table inherits it. Assets never store their own home domain - set it on the table, not the asset.
62
+
63
+ | UI Action | Agent Tool Path |
64
+ |-----------|----------------|
65
+ | Set a table's home domain | `edit(entity_type="table", id=..., domain_id="MARKETING")` - cascades to every asset on that table |
66
+ | Clear a table's home domain | `edit(entity_type="table", id=..., domain_id=None)` - its assets fall back to Uncategorized |
67
+ | Reference asset onto another table | `edit(entity_type="metric", id=..., secondary_tables=["OTHER_TABLE"])` - appears under the target table as a read-only pointer (marked with `*`). Metrics/dimensions require identical columns; rules require only target existence. |
68
+ | Remove a table reference | `edit(entity_type="metric", id=..., secondary_tables=[...without removed])` |
69
+ | Mark a synonym cross-cutting to extra domains | `edit(entity_type="synonym", id=..., extra_domain_ids=["FINANCE"])` - adds badges + filter match, does NOT move it in the tree |
70
+ | Remove cross-cutting domains from synonym | `edit(entity_type="synonym", id=..., extra_domain_ids=[])` |
71
+
72
+ A table-backed asset's (metric, dimension, rule) domain is derived from its dependency tables plus any `secondary_tables`. A synonym can also carry `extra_domain_ids` for cross-cutting relevance. To re-home a whole table of assets, change `domain_id` on the table once - do not edit assets individually.
73
+
74
+ ## Navigation and Discovery
75
+
76
+ | UI Action | Agent Tool Path |
77
+ |-----------|----------------|
78
+ | Search KB | `search(query="...", types=[...])` |
79
+ | Filter by topic | `search(query="...", topics=["REVENUE"])` |
80
+ | Filter by domain | `search(query="...", domain="MARKETING")` - matches assets whose dependency or secondary tables are in the MARKETING domain. Synonyms also match via `extra_domain_ids`. |
81
+ | List all domains | `list(entity_type="domain")` - returns every domain with name, description, color, and asset_count; includes an "Uncategorized" entry when tables with no domain exist. Report every domain it returns; do not drop ones with asset_count=0, and do not use `search` or `kb_explore` |
82
+ | List all tables | `list(entity_type="table")` - returns every table with name, domain_id (null if uncategorized), and verified status. Use to see which tables are assigned to which domain, or to find uncategorized tables |
83
+ | Browse by table | `search(query="...", types=["metric", "dimension"])` then filter results by table |
84
+ | What depends on X? | `kb_explore(entity_type="table", entity_name="X", edge_types=["depends_on"])` |
85
+ | What joins with table? | `kb_explore(entity_type="table", entity_name="X", edge_types=["joins"])` |
86
+ | What's in topic X? | `kb_explore(entity_type="topic", entity_name="X", edge_types=["tagged_with"])` |
87
+ | What's in domain X? | `kb_explore(entity_type="domain", entity_name="X")` |
88
+
89
+ ## View Controls (UI-Only, No Agent Parity Needed)
90
+
91
+ These actions are purely UI navigation state and have no agent equivalent:
92
+ - Switch view mode (Tree / By Topic / By Table / Flat)
93
+ - Filter dropdowns (domain, table, topic, type, verified-only)
94
+ - Expand/collapse tree nodes
95
+ - Drag-drop asset onto topic (maps to "Add topic to asset" above)