dataface 0.1.2__py3-none-any.whl

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 (455) hide show
  1. d3_format/__init__.py +14 -0
  2. d3_format/errors.py +19 -0
  3. d3_format/format.py +551 -0
  4. d3_format/spec.py +159 -0
  5. dataface/DATAFACE_SYNTAX.md +1135 -0
  6. dataface/__init__.py +93 -0
  7. dataface/_docs_site.py +20 -0
  8. dataface/_install_hint.py +26 -0
  9. dataface/agent_api/__init__.py +79 -0
  10. dataface/agent_api/_init_templates/__init__.py +0 -0
  11. dataface/agent_api/_init_templates/agents_dft_snippet.md +26 -0
  12. dataface/agent_api/_init_templates/dataface.yml +15 -0
  13. dataface/agent_api/_init_templates/faces-dataface.yml +144 -0
  14. dataface/agent_api/_init_templates/index.md +24 -0
  15. dataface/agent_api/_paths.py +118 -0
  16. dataface/agent_api/_project_agents_md.py +43 -0
  17. dataface/agent_api/_session_store.py +486 -0
  18. dataface/agent_api/_state.py +28 -0
  19. dataface/agent_api/chat.py +221 -0
  20. dataface/agent_api/dashboards.py +257 -0
  21. dataface/agent_api/describe.py +366 -0
  22. dataface/agent_api/describe_query.py +120 -0
  23. dataface/agent_api/docs/__init__.py +25 -0
  24. dataface/agent_api/docs/_loader.py +292 -0
  25. dataface/agent_api/docs/yaml-reference.md +2757 -0
  26. dataface/agent_api/file_refs.py +118 -0
  27. dataface/agent_api/init.py +126 -0
  28. dataface/agent_api/inspect.py +128 -0
  29. dataface/agent_api/mcp_install.py +170 -0
  30. dataface/agent_api/query.py +274 -0
  31. dataface/agent_api/schema.py +658 -0
  32. dataface/agent_api/schema_search.py +284 -0
  33. dataface/agent_api/search.py +270 -0
  34. dataface/agent_api/skill_install.py +141 -0
  35. dataface/agent_api/skill_render.py +90 -0
  36. dataface/agent_api/skills.py +293 -0
  37. dataface/agent_api/surface_aliases.yaml +128 -0
  38. dataface/agent_api/validate.py +175 -0
  39. dataface/agent_api/validate_query.py +84 -0
  40. dataface/ai/__init__.py +39 -0
  41. dataface/ai/agent.py +139 -0
  42. dataface/ai/context.py +45 -0
  43. dataface/ai/events.py +62 -0
  44. dataface/ai/external_mcp.py +610 -0
  45. dataface/ai/generate_sql.py +96 -0
  46. dataface/ai/llm.py +403 -0
  47. dataface/ai/mcp/__init__.py +51 -0
  48. dataface/ai/mcp/server.py +289 -0
  49. dataface/ai/memories.py +85 -0
  50. dataface/ai/prompts.py +177 -0
  51. dataface/ai/schema_context.py +138 -0
  52. dataface/ai/skills/before-after-comparison/SKILL.md +102 -0
  53. dataface/ai/skills/before-after-comparison/examples/before-after-comparison.yml +24 -0
  54. dataface/ai/skills/dashboard-build/SKILL.md +212 -0
  55. dataface/ai/skills/dashboard-build/examples/_smoke.yml +15 -0
  56. dataface/ai/skills/dashboard-design/SKILL.md +182 -0
  57. dataface/ai/skills/dashboard-review/SKILL.md +113 -0
  58. dataface/ai/skills/dashboard-structural-review/SKILL.md +173 -0
  59. dataface/ai/skills/dashboard-visual-review/SKILL.md +139 -0
  60. dataface/ai/skills/dataface-mcp-setup/SKILL.md +177 -0
  61. dataface/ai/skills/dataface-troubleshooting/SKILL.md +225 -0
  62. dataface/ai/skills/drill-down-link/SKILL.md +112 -0
  63. dataface/ai/skills/drill-down-link/examples/drill-down-link.yml +27 -0
  64. dataface/ai/skills/faceted-small-multiples/SKILL.md +116 -0
  65. dataface/ai/skills/faceted-small-multiples/examples/faceted-small-multiples.yml +33 -0
  66. dataface/ai/skills/filter-bar-with-variables/SKILL.md +105 -0
  67. dataface/ai/skills/filter-bar-with-variables/examples/filter-bar-with-variables.yml +49 -0
  68. dataface/ai/skills/kpi-row/SKILL.md +101 -0
  69. dataface/ai/skills/kpi-row/examples/kpi-row.yml +55 -0
  70. dataface/ai/skills/report-design/SKILL.md +184 -0
  71. dataface/ai/skills/single-metric-bignum/SKILL.md +90 -0
  72. dataface/ai/skills/single-metric-bignum/examples/single-metric-bignum.yml +27 -0
  73. dataface/ai/skills/table-heavy-ops-dashboard/SKILL.md +114 -0
  74. dataface/ai/skills/table-heavy-ops-dashboard/examples/table-heavy-ops-dashboard.yml +48 -0
  75. dataface/ai/skills/time-series-trend/SKILL.md +93 -0
  76. dataface/ai/skills/time-series-trend/examples/time-series-trend.yml +26 -0
  77. dataface/ai/skills/top-n-with-detail/SKILL.md +98 -0
  78. dataface/ai/skills/top-n-with-detail/examples/top-n-with-detail.yml +45 -0
  79. dataface/ai/skills/two-by-two-grid-overview/SKILL.md +78 -0
  80. dataface/ai/skills/two-by-two-grid-overview/examples/two-by-two-grid-overview.yml +64 -0
  81. dataface/ai/tool_schemas.py +132 -0
  82. dataface/ai/tools/__init__.py +312 -0
  83. dataface/ai/yaml_utils.py +57 -0
  84. dataface/cli/__init__.py +3 -0
  85. dataface/cli/_console.py +48 -0
  86. dataface/cli/_error_format.py +83 -0
  87. dataface/cli/_extras.py +190 -0
  88. dataface/cli/_json_output.py +8 -0
  89. dataface/cli/_parsing.py +17 -0
  90. dataface/cli/_version_info.py +56 -0
  91. dataface/cli/commands/__init__.py +3 -0
  92. dataface/cli/commands/_agent_input.py +205 -0
  93. dataface/cli/commands/_agent_server.py +115 -0
  94. dataface/cli/commands/chat.py +645 -0
  95. dataface/cli/commands/describe.py +107 -0
  96. dataface/cli/commands/docs.py +131 -0
  97. dataface/cli/commands/extension.py +179 -0
  98. dataface/cli/commands/init.py +240 -0
  99. dataface/cli/commands/inspect.py +94 -0
  100. dataface/cli/commands/mcp_init.py +167 -0
  101. dataface/cli/commands/query.py +386 -0
  102. dataface/cli/commands/render.py +291 -0
  103. dataface/cli/commands/schema.py +411 -0
  104. dataface/cli/commands/search.py +49 -0
  105. dataface/cli/commands/serve.py +114 -0
  106. dataface/cli/commands/skills.py +133 -0
  107. dataface/cli/commands/skills_init.py +161 -0
  108. dataface/cli/commands/validate.py +63 -0
  109. dataface/cli/main.py +1501 -0
  110. dataface/core/__init__.py +75 -0
  111. dataface/core/compile/__init__.py +244 -0
  112. dataface/core/compile/_jinja_helpers.py +78 -0
  113. dataface/core/compile/channel.py +222 -0
  114. dataface/core/compile/chart_focus.py +101 -0
  115. dataface/core/compile/chart_resolved.py +169 -0
  116. dataface/core/compile/chart_type_detection.py +489 -0
  117. dataface/core/compile/chart_update.py +261 -0
  118. dataface/core/compile/colors.py +64 -0
  119. dataface/core/compile/compiler.py +904 -0
  120. dataface/core/compile/config.py +823 -0
  121. dataface/core/compile/custom_chart_types.py +208 -0
  122. dataface/core/compile/data_table_attachment.py +1287 -0
  123. dataface/core/compile/detect.py +110 -0
  124. dataface/core/compile/errors.py +302 -0
  125. dataface/core/compile/filter_injection.py +319 -0
  126. dataface/core/compile/introspection.py +527 -0
  127. dataface/core/compile/jinja.py +511 -0
  128. dataface/core/compile/labels_env.py +52 -0
  129. dataface/core/compile/markdown.py +154 -0
  130. dataface/core/compile/meta.py +388 -0
  131. dataface/core/compile/models/__init__.py +0 -0
  132. dataface/core/compile/models/chart/__init__.py +0 -0
  133. dataface/core/compile/models/chart/authored.py +2137 -0
  134. dataface/core/compile/models/chart/compiled.py +398 -0
  135. dataface/core/compile/models/config.py +347 -0
  136. dataface/core/compile/models/face/__init__.py +0 -0
  137. dataface/core/compile/models/face/authored.py +659 -0
  138. dataface/core/compile/models/face/compiled.py +522 -0
  139. dataface/core/compile/models/factories.py +201 -0
  140. dataface/core/compile/models/markers.py +40 -0
  141. dataface/core/compile/models/palette.py +36 -0
  142. dataface/core/compile/models/primitives.py +415 -0
  143. dataface/core/compile/models/query/__init__.py +0 -0
  144. dataface/core/compile/models/query/authored.py +246 -0
  145. dataface/core/compile/models/query/compiled.py +710 -0
  146. dataface/core/compile/models/refs.py +137 -0
  147. dataface/core/compile/models/source.py +611 -0
  148. dataface/core/compile/models/style/__init__.py +0 -0
  149. dataface/core/compile/models/style/authored.py +481 -0
  150. dataface/core/compile/models/style/compiled.py +3399 -0
  151. dataface/core/compile/models/style/merged.py +1682 -0
  152. dataface/core/compile/models/theme.py +362 -0
  153. dataface/core/compile/models/variable/__init__.py +0 -0
  154. dataface/core/compile/models/variable/authored.py +254 -0
  155. dataface/core/compile/models/vega_lite/__init__.py +0 -0
  156. dataface/core/compile/models/vega_lite/config.py +510 -0
  157. dataface/core/compile/models/vega_lite/contracts.py +171 -0
  158. dataface/core/compile/normalize_charts.py +494 -0
  159. dataface/core/compile/normalize_layout.py +1000 -0
  160. dataface/core/compile/normalize_queries.py +297 -0
  161. dataface/core/compile/normalize_variables.py +489 -0
  162. dataface/core/compile/normalizer.py +543 -0
  163. dataface/core/compile/palette.py +1100 -0
  164. dataface/core/compile/parameterized.py +658 -0
  165. dataface/core/compile/parser.py +228 -0
  166. dataface/core/compile/schema.py +20 -0
  167. dataface/core/compile/schema_renderers/__init__.py +0 -0
  168. dataface/core/compile/schema_renderers/json_schema.py +163 -0
  169. dataface/core/compile/schema_renderers/prompt.py +152 -0
  170. dataface/core/compile/schema_renderers/vscode_schema.py +301 -0
  171. dataface/core/compile/sizing.py +2126 -0
  172. dataface/core/compile/sources.py +518 -0
  173. dataface/core/compile/sql_authoring_lint.py +56 -0
  174. dataface/core/compile/style_cascade.py +471 -0
  175. dataface/core/compile/typography.py +299 -0
  176. dataface/core/compile/validator.py +301 -0
  177. dataface/core/compile/variables.py +53 -0
  178. dataface/core/compile/vega_config.py +98 -0
  179. dataface/core/compile/vega_lite/__init__.py +6 -0
  180. dataface/core/compile/vega_lite/validation.py +95 -0
  181. dataface/core/compile/yaml_error_formatter.py +838 -0
  182. dataface/core/connections.py +38 -0
  183. dataface/core/dashboard.py +358 -0
  184. dataface/core/defaults/default_config.yml +101 -0
  185. dataface/core/defaults/palettes/categorical/category-10-dark.yml +32 -0
  186. dataface/core/defaults/palettes/categorical/category-10-light.yml +43 -0
  187. dataface/core/defaults/palettes/categorical/category-10.yml +31 -0
  188. dataface/core/defaults/palettes/categorical/category-6-tonal-blue.yml +22 -0
  189. dataface/core/defaults/palettes/categorical/category-6-tonal-brown.yml +29 -0
  190. dataface/core/defaults/palettes/categorical/category-6-tonal-green.yml +20 -0
  191. dataface/core/defaults/palettes/categorical/category-6-tonal-orange.yml +21 -0
  192. dataface/core/defaults/palettes/categorical/category-6-tonal-purple.yml +20 -0
  193. dataface/core/defaults/palettes/categorical/editorial-10-dark.yml +32 -0
  194. dataface/core/defaults/palettes/categorical/editorial-10.yml +40 -0
  195. dataface/core/defaults/palettes/categorical/hero-6.yml +17 -0
  196. dataface/core/defaults/palettes/categorical/single-blue.yml +11 -0
  197. dataface/core/defaults/palettes/categorical/tableau.yml +20 -0
  198. dataface/core/defaults/palettes/data/xkcd_colors.json +3803 -0
  199. dataface/core/defaults/palettes/diverging/blue-red.yml +25 -0
  200. dataface/core/defaults/palettes/diverging/coolwarm.yml +24 -0
  201. dataface/core/defaults/palettes/diverging/crimson-green.yml +23 -0
  202. dataface/core/defaults/palettes/diverging/orange-teal.yml +23 -0
  203. dataface/core/defaults/palettes/diverging/sunset.yml +24 -0
  204. dataface/core/defaults/palettes/scaffold/dft-creams.yml +38 -0
  205. dataface/core/defaults/palettes/scaffold/dft-grays.yml +53 -0
  206. dataface/core/defaults/palettes/sequential/amber.yml +22 -0
  207. dataface/core/defaults/palettes/sequential/blue.yml +22 -0
  208. dataface/core/defaults/palettes/sequential/brown.yml +22 -0
  209. dataface/core/defaults/palettes/sequential/gray.yml +22 -0
  210. dataface/core/defaults/palettes/sequential/green.yml +22 -0
  211. dataface/core/defaults/palettes/sequential/purple.yml +22 -0
  212. dataface/core/defaults/palettes/sequential/rust.yml +22 -0
  213. dataface/core/defaults/palettes/sequential/teal.yml +22 -0
  214. dataface/core/defaults/palettes/tone/negative.yml +32 -0
  215. dataface/core/defaults/palettes/tone/positive.yml +22 -0
  216. dataface/core/defaults/palettes/tone/warning.yml +22 -0
  217. dataface/core/defaults/themes/_base.yaml +786 -0
  218. dataface/core/defaults/themes/bi.yaml +16 -0
  219. dataface/core/defaults/themes/carbong100.yaml +41 -0
  220. dataface/core/defaults/themes/cream.yaml +122 -0
  221. dataface/core/defaults/themes/dark.yaml +40 -0
  222. dataface/core/defaults/themes/diagnostics-title-angle-extreme.yaml +9 -0
  223. dataface/core/defaults/themes/diagnostics-title-baseline-extreme.yaml +9 -0
  224. dataface/core/defaults/themes/diagnostics-title-baseline.yaml +24 -0
  225. dataface/core/defaults/themes/diagnostics-title-center.yaml +8 -0
  226. dataface/core/defaults/themes/diagnostics-title-color-extreme.yaml +24 -0
  227. dataface/core/defaults/themes/diagnostics-title-font-extreme.yaml +25 -0
  228. dataface/core/defaults/themes/diagnostics-title-left.yaml +8 -0
  229. dataface/core/defaults/themes/diagnostics-title-offset-extreme.yaml +9 -0
  230. dataface/core/defaults/themes/diagnostics-title-size-extreme.yaml +24 -0
  231. dataface/core/defaults/themes/diagnostics-title-weight-extreme.yaml +24 -0
  232. dataface/core/defaults/themes/editorial.yaml +147 -0
  233. dataface/core/defaults/themes/light.yaml +30 -0
  234. dataface/core/defaults/themes/looker.yaml +17 -0
  235. dataface/core/defaults/themes/stark.yaml +134 -0
  236. dataface/core/errors/__init__.py +67 -0
  237. dataface/core/errors/codes_compile.py +56 -0
  238. dataface/core/errors/codes_execute.py +177 -0
  239. dataface/core/errors/codes_render.py +106 -0
  240. dataface/core/errors/codes_unknown.py +15 -0
  241. dataface/core/errors/hints.py +74 -0
  242. dataface/core/errors/registry.py +42 -0
  243. dataface/core/errors/structured.py +92 -0
  244. dataface/core/execute/__init__.py +91 -0
  245. dataface/core/execute/adapters/__init__.py +49 -0
  246. dataface/core/execute/adapters/adapter_registry.py +400 -0
  247. dataface/core/execute/adapters/base.py +245 -0
  248. dataface/core/execute/adapters/csv_adapter.py +239 -0
  249. dataface/core/execute/adapters/dbt_adapter.py +283 -0
  250. dataface/core/execute/adapters/dbt_adapter_factory.py +212 -0
  251. dataface/core/execute/adapters/dbt_macro_loader.py +95 -0
  252. dataface/core/execute/adapters/dbt_utils.py +150 -0
  253. dataface/core/execute/adapters/http_adapter.py +224 -0
  254. dataface/core/execute/adapters/metricflow_adapter.py +94 -0
  255. dataface/core/execute/adapters/schema_resolver_adapter.py +144 -0
  256. dataface/core/execute/adapters/sql_adapter.py +710 -0
  257. dataface/core/execute/adapters/values_adapter.py +58 -0
  258. dataface/core/execute/batch.py +744 -0
  259. dataface/core/execute/cache_backend.py +135 -0
  260. dataface/core/execute/cache_keys.py +66 -0
  261. dataface/core/execute/dbt_jinja.py +21 -0
  262. dataface/core/execute/dialects/__init__.py +121 -0
  263. dataface/core/execute/dialects/athena.py +75 -0
  264. dataface/core/execute/dialects/base.py +302 -0
  265. dataface/core/execute/dialects/bigquery.py +38 -0
  266. dataface/core/execute/dialects/databricks.py +68 -0
  267. dataface/core/execute/dialects/duckdb.py +35 -0
  268. dataface/core/execute/dialects/mysql.py +68 -0
  269. dataface/core/execute/dialects/postgres.py +39 -0
  270. dataface/core/execute/dialects/redshift.py +12 -0
  271. dataface/core/execute/dialects/snowflake.py +51 -0
  272. dataface/core/execute/dialects/sqlserver.py +92 -0
  273. dataface/core/execute/duckdb_cache.py +712 -0
  274. dataface/core/execute/duckdb_config.py +26 -0
  275. dataface/core/execute/errors.py +213 -0
  276. dataface/core/execute/executor.py +1249 -0
  277. dataface/core/execute/parallel.py +162 -0
  278. dataface/core/execute/setup_sql.py +58 -0
  279. dataface/core/execute/source_registry.py +72 -0
  280. dataface/core/execute/source_resolver.py +255 -0
  281. dataface/core/execute/sql_guard.py +387 -0
  282. dataface/core/execute/sql_literals.py +199 -0
  283. dataface/core/fonts.py +52 -0
  284. dataface/core/inspect/__init__.py +32 -0
  285. dataface/core/inspect/cache_factory.py +98 -0
  286. dataface/core/inspect/db_types.py +162 -0
  287. dataface/core/inspect/dbt_schema.py +96 -0
  288. dataface/core/inspect/defaults.yml +37 -0
  289. dataface/core/inspect/fanout_risk.py +109 -0
  290. dataface/core/inspect/manifest_utils.py +77 -0
  291. dataface/core/inspect/partials/categorical.yml +40 -0
  292. dataface/core/inspect/partials/date.yml +40 -0
  293. dataface/core/inspect/partials/numeric.yml +55 -0
  294. dataface/core/inspect/partition_types.py +38 -0
  295. dataface/core/inspect/query_validator.py +975 -0
  296. dataface/core/inspect/renderer.py +354 -0
  297. dataface/core/inspect/resolver.py +808 -0
  298. dataface/core/inspect/search.py +461 -0
  299. dataface/core/inspect/sources/__init__.py +32 -0
  300. dataface/core/inspect/sources/dbt.py +738 -0
  301. dataface/core/inspect/sources/duckdb_utils.py +66 -0
  302. dataface/core/inspect/templates/__init__.py +1 -0
  303. dataface/core/inspect/templates/categorical_column.yml +196 -0
  304. dataface/core/inspect/templates/charts.yml +109 -0
  305. dataface/core/inspect/templates/date_column.yml +248 -0
  306. dataface/core/inspect/templates/model.yml +138 -0
  307. dataface/core/inspect/templates/numeric_column.yml +261 -0
  308. dataface/core/inspect/templates/quality.yml +80 -0
  309. dataface/core/inspect/templates/string_column.yml +263 -0
  310. dataface/core/project_roots.py +165 -0
  311. dataface/core/render/__init__.py +87 -0
  312. dataface/core/render/board_links.py +176 -0
  313. dataface/core/render/chart/__init__.py +27 -0
  314. dataface/core/render/chart/arc_attached_table.py +251 -0
  315. dataface/core/render/chart/artifacts.py +16 -0
  316. dataface/core/render/chart/callout.py +225 -0
  317. dataface/core/render/chart/decisions.py +358 -0
  318. dataface/core/render/chart/geo.py +700 -0
  319. dataface/core/render/chart/kpi.py +916 -0
  320. dataface/core/render/chart/labels.py +76 -0
  321. dataface/core/render/chart/pipeline.py +818 -0
  322. dataface/core/render/chart/presentation.py +36 -0
  323. dataface/core/render/chart/profile.py +3438 -0
  324. dataface/core/render/chart/render_single.py +347 -0
  325. dataface/core/render/chart/renderers.py +193 -0
  326. dataface/core/render/chart/rendering.py +565 -0
  327. dataface/core/render/chart/serialization.py +90 -0
  328. dataface/core/render/chart/spark.py +496 -0
  329. dataface/core/render/chart/spark_bar.py +370 -0
  330. dataface/core/render/chart/spec_builders.py +154 -0
  331. dataface/core/render/chart/standard_renderer.py +2645 -0
  332. dataface/core/render/chart/table.py +2957 -0
  333. dataface/core/render/chart/table_support.py +1452 -0
  334. dataface/core/render/chart/tick_values.py +66 -0
  335. dataface/core/render/chart/time_unit_detect.py +809 -0
  336. dataface/core/render/chart/title_overflow.py +157 -0
  337. dataface/core/render/chart/type_inference.py +122 -0
  338. dataface/core/render/chart/validation.py +99 -0
  339. dataface/core/render/chart/vega_lite.py +125 -0
  340. dataface/core/render/chart/vega_lite_types.py +268 -0
  341. dataface/core/render/chart/vl_field_maps.py +346 -0
  342. dataface/core/render/chart_interactivity.py +24 -0
  343. dataface/core/render/control_registry.py +287 -0
  344. dataface/core/render/converters/__init__.py +24 -0
  345. dataface/core/render/converters/chart.py +276 -0
  346. dataface/core/render/converters/html.py +98 -0
  347. dataface/core/render/converters/pdf.py +40 -0
  348. dataface/core/render/converters/png.py +41 -0
  349. dataface/core/render/errors.py +144 -0
  350. dataface/core/render/face_api.py +160 -0
  351. dataface/core/render/faces.py +1194 -0
  352. dataface/core/render/font_measurement.py +48 -0
  353. dataface/core/render/font_support.py +197 -0
  354. dataface/core/render/fonts/DFTSansTabular-Regular.ttf +0 -0
  355. dataface/core/render/fonts/DFTSansTabular-Regular.woff2 +0 -0
  356. dataface/core/render/fonts/DFTSerifOldstyleProportional-Regular.ttf +0 -0
  357. dataface/core/render/fonts/DFTSerifOldstyleTabular-Regular.ttf +0 -0
  358. dataface/core/render/fonts/InterVariable.ttf +0 -0
  359. dataface/core/render/fonts/InterVariable.woff2 +0 -0
  360. dataface/core/render/fonts/NOTO_COLOR_EMOJI_LICENSE.txt +93 -0
  361. dataface/core/render/fonts/NOTO_EMOJI_LICENSE.txt +93 -0
  362. dataface/core/render/fonts/NotoColorEmoji-Regular.ttf +0 -0
  363. dataface/core/render/fonts/NotoColorEmoji-Regular.woff2 +0 -0
  364. dataface/core/render/fonts/NotoEmoji-Regular.ttf +0 -0
  365. dataface/core/render/fonts/NotoEmoji-Regular.woff2 +0 -0
  366. dataface/core/render/fonts/SOURCE_CODE_PRO_LICENSE.txt +93 -0
  367. dataface/core/render/fonts/SOURCE_SERIF_4_LICENSE.txt +98 -0
  368. dataface/core/render/fonts/SourceCodePro-Regular.ttf +0 -0
  369. dataface/core/render/fonts/SourceSerif4-Regular.ttf +0 -0
  370. dataface/core/render/fonts/_emoji_font_face.css +43 -0
  371. dataface/core/render/fonts/source-serif-4-variable-latin.woff2 +0 -0
  372. dataface/core/render/format_utils.py +329 -0
  373. dataface/core/render/geo_defaults.yml +28 -0
  374. dataface/core/render/json_format.py +146 -0
  375. dataface/core/render/layout_sizing.py +865 -0
  376. dataface/core/render/layouts.py +541 -0
  377. dataface/core/render/markdown_defaults.yml +16 -0
  378. dataface/core/render/missing_vars_prompt.py +79 -0
  379. dataface/core/render/placeholder.py +389 -0
  380. dataface/core/render/render_result.py +14 -0
  381. dataface/core/render/renderer.py +467 -0
  382. dataface/core/render/script_embedding.py +16 -0
  383. dataface/core/render/svg_utils.py +212 -0
  384. dataface/core/render/template_loader.py +69 -0
  385. dataface/core/render/templates/controls/_styles.css +606 -0
  386. dataface/core/render/templates/controls/checkbox.html +16 -0
  387. dataface/core/render/templates/controls/date.html +16 -0
  388. dataface/core/render/templates/controls/number.html +19 -0
  389. dataface/core/render/templates/controls/readonly.html +9 -0
  390. dataface/core/render/templates/controls/select.html +21 -0
  391. dataface/core/render/templates/controls/slider.html +22 -0
  392. dataface/core/render/templates/controls/text.html +16 -0
  393. dataface/core/render/templates/scripts/chart_interactivity.js +191 -0
  394. dataface/core/render/templates/scripts/variables.js +976 -0
  395. dataface/core/render/templates/svg/grid_pattern.svg +3 -0
  396. dataface/core/render/templates/svg/styles.css +51 -0
  397. dataface/core/render/terminal.py +311 -0
  398. dataface/core/render/terminal_charts.py +563 -0
  399. dataface/core/render/terminal_defaults.yml +2 -0
  400. dataface/core/render/terminal_layouts.py +299 -0
  401. dataface/core/render/terminal_text.py +31 -0
  402. dataface/core/render/text/__init__.py +1 -0
  403. dataface/core/render/text/case.py +113 -0
  404. dataface/core/render/text_format.py +129 -0
  405. dataface/core/render/utils.py +106 -0
  406. dataface/core/render/variable_controls.py +946 -0
  407. dataface/core/render/variable_input_refinement.py +140 -0
  408. dataface/core/render/warnings/__init__.py +15 -0
  409. dataface/core/render/warnings/bar_color_1_to_1_with_x.py +80 -0
  410. dataface/core/render/warnings/base.py +44 -0
  411. dataface/core/render/warnings/fanout_risk.py +15 -0
  412. dataface/core/render/warnings/from_query_diagnostic.py +56 -0
  413. dataface/core/render/warnings/missing_join_predicate.py +13 -0
  414. dataface/core/render/warnings/query_parse_error.py +14 -0
  415. dataface/core/render/warnings/query_returned_zero_rows.py +42 -0
  416. dataface/core/render/warnings/reaggregation.py +14 -0
  417. dataface/core/render/warnings/registry.py +45 -0
  418. dataface/core/render/warnings/suppression.py +46 -0
  419. dataface/core/render/warnings/temporal_single_point.py +63 -0
  420. dataface/core/render/warnings/unreferenced_chart.py +15 -0
  421. dataface/core/render/warnings/y_encoding_mostly_null.py +76 -0
  422. dataface/core/render/yaml_format.py +167 -0
  423. dataface/core/resolve_face.py +195 -0
  424. dataface/core/schema/__init__.py +0 -0
  425. dataface/core/schema/guidance.py +151 -0
  426. dataface/core/scoped_paths.py +59 -0
  427. dataface/core/serve/__init__.py +14 -0
  428. dataface/core/serve/bootstrap.py +39 -0
  429. dataface/core/serve/embedded.py +57 -0
  430. dataface/core/serve/port.py +129 -0
  431. dataface/core/serve/server.py +938 -0
  432. dataface/core/serve/templates/__init__.py +0 -0
  433. dataface/core/serve/templates/directory.yml +6 -0
  434. dataface/core/serve/templates/error.html.j2 +217 -0
  435. dataface/core/utils.py +121 -0
  436. dataface/core/validate.py +64 -0
  437. dataface/integrations/__init__.py +0 -0
  438. dataface/integrations/highlighting.py +351 -0
  439. dataface/integrations/markdown.py +537 -0
  440. dataface/py.typed +0 -0
  441. dataface-0.1.2.dist-info/METADATA +375 -0
  442. dataface-0.1.2.dist-info/RECORD +455 -0
  443. dataface-0.1.2.dist-info/WHEEL +4 -0
  444. dataface-0.1.2.dist-info/entry_points.txt +2 -0
  445. dataface-0.1.2.dist-info/licenses/LICENSE +202 -0
  446. mdsvg/__init__.py +168 -0
  447. mdsvg/fonts.py +656 -0
  448. mdsvg/images.py +299 -0
  449. mdsvg/parser.py +629 -0
  450. mdsvg/playground.py +284 -0
  451. mdsvg/py.typed +2 -0
  452. mdsvg/renderer.py +1623 -0
  453. mdsvg/style.py +355 -0
  454. mdsvg/types.py +200 -0
  455. mdsvg/utils.py +86 -0
dataface/__init__.py ADDED
@@ -0,0 +1,93 @@
1
+ """Dataface - dbt-native dataface and visualization layer.
2
+
3
+ This package provides a declarative YAML-based approach to building
4
+ datafaces (analytics dashboards) that integrate with dbt projects.
5
+
6
+ Architecture:
7
+ The codebase is organized into:
8
+
9
+ - core/: The engine (compile, execute, render, serve, inspect)
10
+ - cli/: Command-line interface
11
+ - ai/: AI interfaces (MCP server, tool schemas, prompts)
12
+ - apps/: Web applications (playground, cloud)
13
+
14
+ Quick Start:
15
+ >>> from pathlib import Path
16
+ >>> from dataface import compile, Executor, render
17
+ >>> from dataface.core.execute.adapters import build_adapter_registry
18
+ >>>
19
+ >>> # Compile YAML to Face
20
+ >>> result = compile(yaml_content)
21
+ >>> if result.success:
22
+ ... face = result.face
23
+ ...
24
+ ... # Create executor and render
25
+ ... registry = build_adapter_registry(Path.cwd())
26
+ ... executor = Executor(face, registry, query_registry=result.query_registry)
27
+ ... svg = render(face, executor, format="svg")
28
+ """
29
+
30
+ import logging
31
+ from importlib.metadata import version as _v
32
+
33
+ __version__ = _v("dataface")
34
+
35
+ # Set up package-level logger
36
+ # This logger is the parent for all dataface submodule loggers
37
+ logger = logging.getLogger("dataface")
38
+
39
+ # Configure a NullHandler to avoid "No handler found" warnings
40
+ # Actual handlers should be configured by the application entry points (CLI, serve)
41
+ logger.addHandler(logging.NullHandler())
42
+
43
+ # Re-export from core for convenience
44
+ from dataface.core import validate
45
+ from dataface.core.compile import (
46
+ AnyQuery,
47
+ AuthoredFace,
48
+ Chart,
49
+ CompilationError,
50
+ CompileResult,
51
+ Face,
52
+ Layout,
53
+ LayoutItem,
54
+ Variable,
55
+ compile,
56
+ compile_file,
57
+ )
58
+ from dataface.core.execute import (
59
+ ExecutionError,
60
+ Executor,
61
+ )
62
+
63
+ # Import from render submodule
64
+ # Note: We import `render` function but this shadows `dataface.render` module access
65
+ # Tests that need to patch `dataface.core.render.renderer` should import the module directly
66
+ from dataface.core.render import RenderError, render
67
+
68
+ __all__ = [
69
+ # Version
70
+ "__version__",
71
+ # Logging
72
+ "logger",
73
+ # Compile
74
+ "compile",
75
+ "compile_file",
76
+ "CompileResult",
77
+ "Face",
78
+ "Chart",
79
+ "AnyQuery",
80
+ "Layout",
81
+ "LayoutItem",
82
+ "AuthoredFace",
83
+ "Variable",
84
+ "CompilationError",
85
+ # Execute
86
+ "Executor",
87
+ "ExecutionError",
88
+ # Render
89
+ "render",
90
+ "RenderError",
91
+ # Validate
92
+ "validate",
93
+ ]
dataface/_docs_site.py ADDED
@@ -0,0 +1,20 @@
1
+ """Public docs site base URL for CLI hints and cross-links."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+
7
+ DEFAULT_DOCS_SITE_URL = "https://docs.dataface.com"
8
+
9
+
10
+ def docs_site_url() -> str:
11
+ """Return the docs site base URL without a trailing slash.
12
+
13
+ ``DFT_DOCS_URL`` overrides the shipped default when set to a non-empty
14
+ value. Used by ``dft docs`` (web link on the topic index), the playground
15
+ gallery, and other surfaces that link out to the MkDocs site.
16
+ """
17
+ raw = os.environ.get("DFT_DOCS_URL", DEFAULT_DOCS_SITE_URL).strip()
18
+ if not raw:
19
+ return DEFAULT_DOCS_SITE_URL
20
+ return raw.rstrip("/")
@@ -0,0 +1,26 @@
1
+ """Canonical install-command hints surfaced from runtime error messages.
2
+
3
+ Centralized here so every CLI/runtime hint stays in lock-step with
4
+ ``dataface/README.md`` and the wheel-shipped MCP-setup skill. The bare
5
+ ``pip install`` form is used (not ``uv pip install``) — customers may
6
+ not have ``uv`` on PATH, and bare ``pip`` is universally available with
7
+ any Python install. ``cli/_extras.py`` separately emits ``uv pip install
8
+ --python <path>`` when uv is the active installer (the smoke-install CI
9
+ test pins both legs); this helper is only the canonical fallback string.
10
+
11
+ If you change the canonical form, also update the TypeScript mirror at
12
+ ``apps/ide/vscode-extension/src/utils/install-hint.ts``.
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+
18
+ def install_hint(extra: str | None = None) -> str:
19
+ """Return the canonical ``pip install`` one-liner for Dataface.
20
+
21
+ ``extra`` is an optional optional-dependency group (``"mcp"``,
22
+ ``"chat"``, ``"playground"``, ``"bigquery"``); when set, the
23
+ returned command installs Dataface with that extras bracket.
24
+ """
25
+ spec = f"dataface[{extra}]" if extra else "dataface"
26
+ return f'pip install "{spec}"'
@@ -0,0 +1,79 @@
1
+ """dataface.agent_api — typed Python API for Dataface agent surfaces.
2
+
3
+ This package is the canonical home for every function exposed to AI agents
4
+ via CLI commands (``dataface/cli/commands/``) or MCP server modules
5
+ (``dataface/ai/mcp/``).
6
+
7
+ Contract
8
+ --------
9
+ Every function in this package must satisfy all three rules:
10
+
11
+ 1. **Typed args and typed returns.** Function signatures use concrete Python
12
+ types — ``Path``, ``str``, ``int``, Pydantic models, or ``TypedDict``
13
+ subclasses. Return types are never ``dict[str, Any]`` or JSON strings.
14
+ Callers should not need to parse, coerce, or guess the shape of results.
15
+
16
+ 2. **No Cloud/Django assumptions.** Do not import ``django``, Django ORM
17
+ models, ``HttpRequest``, ``settings``, or any ``apps.cloud`` module.
18
+ Functions here must work in a standalone Python environment — a CLI
19
+ session, a test, or an MCP server — without a running Django process.
20
+
21
+ 3. **I/O scoped to the verb.** A ``render`` function does not write to disk.
22
+ A ``save`` function writes exactly one file. An ``execute`` function runs
23
+ SQL and nothing else. Side effects must match what the verb name implies
24
+ and nothing more.
25
+
26
+ Thin-wrapper rule
27
+ -----------------
28
+ ``dataface/cli/commands/`` and ``dataface/ai/mcp/`` are *thin wrappers* over
29
+ this package. They are permitted to contain only:
30
+
31
+ - Argument parsing and output formatting
32
+ - A single call into ``dataface.agent_api``
33
+
34
+ Any business logic (validation, path resolution, compilation, execution,
35
+ rendering) belongs here, not in the CLI or MCP layer. Code review blocks
36
+ CLI/MCP files that contain logic beyond parse-and-dispatch.
37
+ """
38
+
39
+ from dataface.agent_api import skills as skills
40
+ from dataface.agent_api._paths import (
41
+ RenderSetup as RenderSetup,
42
+ setup_render_for_face as setup_render_for_face,
43
+ )
44
+ from dataface.agent_api.dashboards import (
45
+ RenderedDashboard as RenderedDashboard,
46
+ render_dashboard as render_dashboard,
47
+ )
48
+ from dataface.agent_api.describe import (
49
+ DescribeFaceArgs,
50
+ DescribeFaceResult,
51
+ describe_face,
52
+ )
53
+ from dataface.agent_api.docs import DocsArgs, DocsResult, DocsSearchHit, Topic
54
+ from dataface.agent_api.init import (
55
+ InitResult as InitResult,
56
+ init_project as init_project,
57
+ )
58
+ from dataface.agent_api.query import QueryFaceResult, query_face
59
+ from dataface.agent_api.validate import ValidateDashboardArgs, ValidateResult
60
+
61
+ __all__ = [
62
+ "DescribeFaceArgs",
63
+ "DescribeFaceResult",
64
+ "DocsArgs",
65
+ "DocsResult",
66
+ "DocsSearchHit",
67
+ "InitResult",
68
+ "QueryFaceResult",
69
+ "RenderSetup",
70
+ "RenderedDashboard",
71
+ "Topic",
72
+ "ValidateDashboardArgs",
73
+ "ValidateResult",
74
+ "describe_face",
75
+ "init_project",
76
+ "query_face",
77
+ "render_dashboard",
78
+ "setup_render_for_face",
79
+ ]
File without changes
@@ -0,0 +1,26 @@
1
+
2
+ <!-- dft-dataface:start -->
3
+ ## Dataface
4
+
5
+ This project uses [Dataface](https://github.com/fivetran/dataface) (`dft`) for visualizations, dashboards, and reports (YAML in `faces/`).
6
+
7
+ Learn how to work here — run:
8
+
9
+ - `dft skills dashboard-build`
10
+ - `dft skills dashboard-design`
11
+ - `dft skills report-design`
12
+ - `dft skills dashboard-review`
13
+ - `dft skills dataface-troubleshooting`
14
+
15
+ | Command | Use for |
16
+ |---------|---------|
17
+ | `dft schema` → `dft schema SRC` → `dft schema SRC SCH` → `dft schema SRC SCH TBL` → `dft schema SRC SCH TBL COL` | Drill data hierarchy: sources → schemas → tables → columns (each positional arg narrows one level) |
18
+ | `dft query` | Run queries |
19
+ | `dft serve` | Preview dashboards |
20
+ | `dft validate` | Check face YAML |
21
+ | `dft render` | Export |
22
+
23
+ Use `dft schema` for real names — do not invent columns.
24
+
25
+ More: `dft -h`, `dft docs`, `dft skills`. `dft init mcp` installs skills + wires MCP.
26
+ <!-- dft-dataface:end -->
@@ -0,0 +1,15 @@
1
+ # Dataface project configuration
2
+ #
3
+ # This file anchors your project root. Running `dft serve` from any
4
+ # subdirectory will walk up to find this file and use its directory
5
+ # as the serve root.
6
+ #
7
+ # Credentials and database identity stay in dbt profiles.yml / env vars.
8
+ # Only Dataface runtime and serving preferences belong here.
9
+ #
10
+ # serve:
11
+ # # Override the SQL dialect (auto-detected from dbt profile when possible)
12
+ # # dialect: bigquery
13
+ #
14
+ # # dbt target override (defaults to DBT_TARGET env var, then profile default)
15
+ # # target: dev
@@ -0,0 +1,144 @@
1
+ # Dataface Guide — your first dashboard
2
+ #
3
+ # This file is a self-contained tour of Dataface features.
4
+ # Every section works without a database: data lives inline.
5
+ # When you're ready, swap inline data for real SQL queries.
6
+ #
7
+ # Key concepts:
8
+ # title: dashboard title shown at the top
9
+ # text: markdown prose block — supports **bold**, *italic*, lists, code
10
+ # queries: data sources — inline values, CSV files, or SQL against a warehouse
11
+ # charts: visualizations wired to a query
12
+ # rows/cols: layout — rows stack vertically, cols split horizontally
13
+ # variables: interactive dropdowns and filters
14
+
15
+ title: "Dataface Guide"
16
+ description: "A tour of Dataface: queries, charts, layout, KPIs, and variables."
17
+
18
+ # ── Text blocks ───────────────────────────────────────────────────────────────
19
+ # Any `text:` field accepts markdown.
20
+ text: |
21
+ Welcome to **Dataface** — dashboards as YAML files.
22
+
23
+ This file is your starter guide. Edit it, add new `.yml` files under
24
+ `faces/`, and run `dft serve` to preview everything in your browser.
25
+
26
+ # ── Queries ───────────────────────────────────────────────────────────────────
27
+ # `type: values` embeds data directly — no database needed.
28
+ # Swap for `type: csv` (pointing at a file) or a SQL query once you're ready.
29
+ queries:
30
+ monthly_revenue:
31
+ type: values
32
+ columns: [month, revenue, target]
33
+ values:
34
+ - [Jan, 82000, 80000]
35
+ - [Feb, 91000, 85000]
36
+ - [Mar, 78000, 90000]
37
+ - [Apr, 105000, 95000]
38
+ - [May, 112000, 100000]
39
+ - [Jun, 99000, 105000]
40
+
41
+ category_mix:
42
+ type: values
43
+ columns: [category, amount]
44
+ values:
45
+ - [Software, 48000]
46
+ - [Services, 31000]
47
+ - [Hardware, 20000]
48
+
49
+ kpi_summary:
50
+ type: values
51
+ columns: [total_revenue, revenue_delta, avg_deal]
52
+ values:
53
+ - [567000, 0.14, 18900]
54
+
55
+ # ── Charts ────────────────────────────────────────────────────────────────────
56
+ # `type:` can be bar, line, area, pie, kpi, table, or scatter.
57
+ # `x:` and `y:` name columns from the query.
58
+ charts:
59
+ revenue_trend:
60
+ type: line
61
+ query: monthly_revenue
62
+ title: Monthly Revenue vs Target
63
+ x: month
64
+ y: revenue
65
+
66
+ revenue_bars:
67
+ type: bar
68
+ query: monthly_revenue
69
+ title: Revenue by Month
70
+ x: month
71
+ y: revenue
72
+
73
+ category_pie:
74
+ type: pie
75
+ query: category_mix
76
+ title: Revenue Mix
77
+ theta: amount
78
+ color: category
79
+
80
+ kpi_total:
81
+ type: kpi
82
+ query: kpi_summary
83
+ label: Total Revenue (H1)
84
+ value: total_revenue
85
+ format: currency_compact
86
+ support:
87
+ value: revenue_delta
88
+ label: vs H1 last year
89
+ format: percent_delta
90
+ glyph: "▲"
91
+ tone: positive
92
+
93
+ kpi_avg_deal:
94
+ type: kpi
95
+ query: kpi_summary
96
+ label: Avg Deal Size
97
+ value: avg_deal
98
+ format: currency_compact
99
+
100
+ data_table:
101
+ type: table
102
+ query: monthly_revenue
103
+ title: Monthly Detail
104
+
105
+ # ── Variables ─────────────────────────────────────────────────────────────────
106
+ # Variables add interactive dropdowns. Wire them to SQL queries with {{ varname }}.
107
+ # With `type: values` data above they show the UI but don't filter — swap to SQL
108
+ # queries and use `WHERE category = '{{ segment }}'` to make them live.
109
+ variables:
110
+ segment:
111
+ label: Segment
112
+ input: select
113
+ default: All
114
+ options:
115
+ static: [All, Software, Services, Hardware]
116
+
117
+ # ── Layout ────────────────────────────────────────────────────────────────────
118
+ # `rows:` stacks content vertically.
119
+ # `cols:` inside a row splits horizontally.
120
+ # Reference a chart by its key, or use a dict for text/title blocks.
121
+ rows:
122
+ # KPI row — four equal-width columns
123
+ - cols:
124
+ - kpi_total
125
+ - kpi_avg_deal
126
+
127
+ # Charts side by side
128
+ - cols:
129
+ - revenue_trend
130
+ - category_pie
131
+
132
+ # Full-width bar chart
133
+ - revenue_bars
134
+
135
+ # Annotated table with intro text
136
+ - cols:
137
+ - title: "Monthly Detail"
138
+ text: |
139
+ Scroll the table to see all rows.
140
+ Add `style.pagination.enabled: true` to paginate large datasets.
141
+ style:
142
+ padding: "16px"
143
+
144
+ - data_table
@@ -0,0 +1,24 @@
1
+ ---
2
+ title: Dataface
3
+ ---
4
+
5
+ # Welcome to Dataface
6
+
7
+ This is your project's landing page. Everything under `faces/` becomes a
8
+ served page — edit the files here to build dashboards, reports, and docs
9
+ for your data project.
10
+
11
+ ## Authoring modes
12
+
13
+ **YAML (`.yml`)** — structured dashboards with queries, charts, and layout.
14
+ See the [dataface guide](dataface) for a working example covering queries, charts, and variables.
15
+
16
+ **Markdown (`.md`)** — prose pages like this one. Add YAML frontmatter for
17
+ queries and charts, then embed them inline with `{% raw %}{{ chart my_chart }}{% endraw %}`.
18
+
19
+ ## Next steps
20
+
21
+ 1. Open `faces/dataface.yml` and tweak the content.
22
+ 2. Run `dft serve` and open the URL it prints.
23
+ 3. Add new `.yml` or `.md` files under `faces/` — they appear automatically.
24
+ 4. Run `dft validate` to validate your face YAML for errors.
@@ -0,0 +1,118 @@
1
+ """Shared path helpers for MCP tools and CLI render setup."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+ from pathlib import Path
7
+
8
+ from dataface.core.execute.adapters import AdapterRegistry, build_adapter_registry
9
+ from dataface.core.project_roots import (
10
+ discover_render_context,
11
+ discovery_boundary_for_face,
12
+ find_dataface_project,
13
+ )
14
+ from dataface.core.scoped_paths import (
15
+ _resolve_against_project,
16
+ project_root_for as project_root_for,
17
+ resolve_scoped_path as resolve_scoped_path,
18
+ )
19
+
20
+
21
+ def no_project_hint(project_dir: Path | None) -> str:
22
+ """Return a hint string when no project marker is found, else empty string."""
23
+ check = project_dir if project_dir is not None else Path.cwd()
24
+ if find_dataface_project(check) is not None:
25
+ return ""
26
+ return (
27
+ f" No Dataface project marker found at or above {check}."
28
+ f" Run from inside a Dataface project, or set project_dir."
29
+ )
30
+
31
+
32
+ @dataclass(frozen=True)
33
+ class RenderSetup:
34
+ """Resolved arguments for an `agent_api.render_dashboard` call.
35
+
36
+ Bundles the scoped path, scoped base, and adapter registry produced by
37
+ discovery. CLI/serve callers building this from a face on disk avoid
38
+ duplicating discovery boilerplate; tests can construct one directly.
39
+ """
40
+
41
+ adapter_registry: AdapterRegistry
42
+ scoped_path: Path | None
43
+ scoped_base: Path | None
44
+
45
+
46
+ def setup_render_for_face(
47
+ face_path: Path,
48
+ project_dir: Path | None = None,
49
+ *,
50
+ read_only: bool = True,
51
+ ) -> tuple[RenderSetup, Path]:
52
+ """Build a RenderSetup + resolved face_file for a face on disk.
53
+
54
+ Mirrors `core.render.face_api.render_face`'s discovery: walk upward from
55
+ the face's parent to find dbt_project.yml and any sibling _sources.yaml.
56
+ `project_dir` overrides project_root for project-level data paths but
57
+ never the discovered dbt path.
58
+ """
59
+ face_file = face_path
60
+ if not face_file.is_absolute():
61
+ face_file = _resolve_against_project(face_file, project_dir)
62
+ else:
63
+ face_file = face_file.resolve()
64
+
65
+ project_root, dbt_project_path = discover_render_context(
66
+ face_file.parent,
67
+ discovery_boundary_for_face(face_file.parent, project_dir),
68
+ )
69
+ if project_dir:
70
+ project_root = project_dir.resolve()
71
+
72
+ try:
73
+ scoped_path: Path | None = face_file.relative_to(project_root)
74
+ scoped_base: Path | None = project_root
75
+ except ValueError:
76
+ scoped_path = face_file
77
+ scoped_base = None
78
+
79
+ adapter_registry = build_adapter_registry(
80
+ project_root,
81
+ read_only=read_only,
82
+ dbt_project_path=dbt_project_path,
83
+ )
84
+ return (
85
+ RenderSetup(
86
+ adapter_registry=adapter_registry,
87
+ scoped_path=scoped_path,
88
+ scoped_base=scoped_base,
89
+ ),
90
+ face_file,
91
+ )
92
+
93
+
94
+ def setup_render_for_yaml(
95
+ project_dir: Path | None = None,
96
+ *,
97
+ read_only: bool = True,
98
+ ) -> RenderSetup:
99
+ """Build a RenderSetup for a stdin/yaml_content render (no face path).
100
+
101
+ Walks upward from `project_dir` (or cwd) to discover the dbt project so
102
+ yaml fed via stdin still resolves dbt-managed sources correctly.
103
+ """
104
+ output_dir = project_root_for(project_dir)
105
+ project_root, dbt_project_path = discover_render_context(output_dir, None)
106
+ if project_dir:
107
+ project_root = output_dir
108
+
109
+ adapter_registry = build_adapter_registry(
110
+ project_root,
111
+ read_only=read_only,
112
+ dbt_project_path=dbt_project_path,
113
+ )
114
+ return RenderSetup(
115
+ adapter_registry=adapter_registry,
116
+ scoped_path=None,
117
+ scoped_base=project_root,
118
+ )
@@ -0,0 +1,43 @@
1
+ """Append or refresh the Dataface blurb in a project's AGENTS.md."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import importlib.resources
6
+ from typing import Literal
7
+
8
+ FileAction = Literal["created", "refreshed", "appended"]
9
+
10
+ _START = "<!-- dft-dataface:start -->"
11
+ _END = "<!-- dft-dataface:end -->"
12
+
13
+ _TEMPLATES = importlib.resources.files("dataface.agent_api._init_templates")
14
+
15
+
16
+ def load_agents_snippet() -> str:
17
+ """Return the packaged markdown blurb (includes marker comments)."""
18
+ return _TEMPLATES.joinpath("agents_dft_snippet.md").read_text()
19
+
20
+
21
+ def has_dataface_markers(text: str) -> bool:
22
+ if _START not in text or _END not in text:
23
+ return False
24
+ return text.index(_START) < text.index(_END)
25
+
26
+
27
+ def merge_agents_snippet(
28
+ existing_text: str | None,
29
+ snippet: str,
30
+ ) -> tuple[str, FileAction]:
31
+ """Create, append, or refresh only the marked Dataface section."""
32
+ body = snippet if snippet.endswith("\n") else f"{snippet}\n"
33
+
34
+ if existing_text is None:
35
+ return body.lstrip("\n"), "created"
36
+
37
+ if has_dataface_markers(existing_text):
38
+ before = existing_text[: existing_text.index(_START)].rstrip()
39
+ after = existing_text[existing_text.index(_END) + len(_END) :].lstrip()
40
+ parts = [p for p in (before, body.strip(), after) if p]
41
+ return "\n\n".join(parts) + "\n", "refreshed"
42
+
43
+ return f"{existing_text.rstrip()}\n\n{body.lstrip()}", "appended"