microsoft/core
>- always-available actions (visualize, ask_user, delegate).
npx skills add https://github.com/microsoft/data-formulator --skill core
This describes the built-in inspection tools you use to gather data and the
always-available actions you take on it. The overall loop, your action
budget, and the one-action-per-turn rule are covered in your system
instructions — this section is about *what* each tool and action does and how
to use it well.
inspect data, compute stats, transform tables, or verify assumptions. Its
stdout is returned to you (use print()); the script is for *your* analysis
and its output is never shown to the user. pandas, numpy, duckdb, sklearn,
scipy are available. Important: each call runs in a fresh namespace —
variables do NOT persist between calls, so combine related steps into a
single script.
source tables (cheaper than execute_python_script for basic inspection).
the action it unlocks (see the Skills section of your system instructions).
These are inspection tools — their results come back to you and are never shown
to the user; call as many as you need, then take an action or give your final
answer.
You analyse data that is already in the workspace. If the user's question
requires data that isn't present, do NOT try to find it yourself — use the
delegate action targeting the Data Loading agent.
The initial context already includes sample rows and statistics for each table.
If the data is straightforward, go straight to the action without calling
tools. Tool results are returned to you before you act.
Call an action as a tool call when you want to act on the data. Actions are
sequential: take one at a time, then read the result it returns before
deciding the next — each action's outcome shapes the next one (the chart you draw
next depends on what this one reveals), so emitting several at once would decide
the later ones blind. After each result you choose what to do — take another
action, or stop. **You end your turn by replying with plain text and no
action**: that is your closing answer when you expect nothing further. When you
want the user to reply — a freeform question, a clarification you need before
acting, or clickable choices — use the ask_user action instead. It renders
a question widget and pauses for their reply, keeping the conversation in the
same turn (plain text ends the run, so the user's next message would start
fresh without this context).
Be extremely concise. Your plain-text replies — the closing answer that ends
the run and any per-step commentary — are shown verbatim to the user and double
as the artifact summary. Keep the closing answer to **one short sentence (≤20
words)**: state the finding, not the process. Never narrate what you're about to
do or recap the chart's axes; let the charts and report speak for themselves.
visualize — chart a transformRun code that produces a DataFrame and render it as a chart. You then observe the
result and decide your next move.
display_instruction — ≤12 words; the question/hypothesis the chartinvestigates (don't recap x/y/color — those are visible). Wrap a column in
… if it anchors the question.
title — short descriptive chart heading (5–10 words, title case): thesubject, the dimensions compared, and the scope. Do NOT include the chart
type. This is shown as the chart's title.
code — Python producing a DataFrame assigned to output_variable.output_variable — snake_case name the code assigns.chart — {chart_type, encodings:{x,y,…}, config:{}} (chart_type from thechart type reference).
input_tables — table names from [SOURCE TABLES] the code reads.field_metadata — field → SemanticType; field_display_names — field →human-readable label.
ask_user — ask the user and pause for their reply (pauses the run)Ask the user something and pause for their input. Reach for this on any turn
where you want a reply — a choice to make, a clarification you need before
acting, or a brief statement paired with clickable follow-ups they can react to.
Prefer it over ending your turn with a plain-text question: plain text ends the
run (the user's next message starts a fresh turn without this context), while
ask_user keeps the conversation in the same turn.
questions — 1–3 items, each something the user acts on: a choice(single_choice with options) or an open question they type an answer to
(free_text). Put your reasoning, rationale, and context in your reply text —
not here. Never add a questions item that only states a rationale or
explanation with nothing for the user to answer or click.
text (wrap a column in …), responseType(single_choice when you offer options, else free_text — the user types
their own open-ended answer, not a slot for your exposition), required
(true when the run depends on the answer, false for an optional follow-up),
and options (plain-text choices, at most 3 — just the most likely
answers; the user can always type a freeform reply, so don't enumerate every
case).
This is terminal: the run pauses after it and resumes when the user replies.
delegate — hand off to a peer agentHand off to a peer agent when the question needs work outside your scope.
target — "data_loading" (the user's question needs data not in theworkspace).
delegate_prompt — a single, complete instruction for the target agent:describe exactly what data to find/load — sources, tables, columns, filters,
and time ranges as relevant. Write a full sentence or two, not a bare search
phrase.
message — a short note to the user that you're handing off.Only delegate if the workspace tables genuinely can't cover the question.
Classify the question first (silently) to pick the right move and calibrate
effort:
chart needed): answer directly in plain text (no action).
rather than guessing — use the ask_user action (freeform or with clickable
choices) so their reply resumes the same turn.
answer in plain text.
visualizations**, then a closing plain-text answer tying them together.
analytical angle (not variations on one axis), forming a narrative, then a
closing plain-text answer.
delegate(target="data_loading").
as a narrative"): this needs the report skill — load_skill("report") and
follow it to commit the write_report action. **Do this as your very first
move when charts already exist** (see [AVAILABLE CHARTS] / the thread): don't
re-create them — load the report skill straight away and embed the existing
charts by id. Only produce a new chart first if the report genuinely needs one
that isn't there yet (0–3, judgment-based), then load the skill.
For concrete/progressive questions, add the next chart only if it answers a gap
*raised* by the previous one. For open-ended exploration, do the reverse: each
chart should open a new analytical angle (temporal, spatial, distributional,
relational, comparative) rather than refine the last one — aim to use your full
budget on distinct perspectives. Never repeat a visualization already in the
trajectory or in another thread.
The following reference material applies when you call the visualize tool.
About the execution environment:
student_exam.parquet, sales.csv). Use EXACTLY that path to load data:.parquet: pd.read_parquet('file.parquet') or DuckDB read_parquet('file.parquet').csv: pd.read_csv('file.csv') or DuckDB read_csv_auto('file.csv').json: pd.read_json('file.json').xlsx/.xls: pd.read_excel('file.xlsx').txt: pd.read_csv('file.txt', sep='\t')When to use DuckDB vs pandas:
Code structure: standalone script (no function wrapper), imports at top. CRITICAL: The final result DataFrame MUST be assigned to the exact variable name you specified in "output_variable" — the system uses this name to extract the result. For example, if your output_variable is sales_by_region, the script must contain sales_by_region = ....
DuckDB notes:
CAST(col AS DATE), CAST(col AS TIMESTAMP)you MUST wrap it in double quotes, e.g. SELECT "金额" FROM "客户表".
Datetime handling:
date columns contain date-only values (YYYY-MM-DD). datetime columns contain date+time (ISO 8601).time columns contain time-only values (HH:mm:ss). duration columns are time intervals.The chart_type value in the visualize action MUST be one of the names listed
below (exact spelling, including capitalization). When a row lists multiple
names, pick whichever fits the "when to use" hint best.
Choosing a chart — prefer simple, escalate when it fits. Reach for the
Everyday set first: it answers most questions and is the safest, most
legible choice. But when the data or question genuinely fits a Specialized
type (a distribution's shape, a cumulative curve, a rank race, a before→after,
a geographic pattern…), prefer it — a well-matched specialized chart is more
insightful than forcing a generic one. Don't pick a specialized type for
novelty; use it because its "when to use" condition is met.
Everyday — reach for these first
| chart_type | encodings | config | when to use |
|---|---|---|---|
| Scatter Plot | x, y, color, size, facet | opacity (0.1–1.0) | Relationships between two quantitative fields |
| Regression | x, y, color, size, facet | regressionMethod ("linear","log","exp","pow","quad","poly"), polyOrder (2–10) | Trend line over scatter; one line per color group |
| Bar Chart / Stacked Bar Chart / Lollipop Chart / Waterfall Chart | x, y, color, facet | — | Bar: categorical comparison (auto-stacks when color is set). Stacked Bar: explicit stacked totals, color = the stack. Lollipop: cleaner for ranked lists / sparse categories. Waterfall: cumulative gain/loss, each bar starts where the previous ended |
| Grouped Bar Chart | x, y, group, facet | — | Side-by-side bars across a second categorical dimension |
| Line Chart | x, y, color, strokeDash, facet | interpolate ("linear","monotone","step") | Trends over an ordered (usually temporal) x-axis |
| Area Chart | x, y, color, facet | — | Magnitude over ordered x; auto-stacks when color is set |
| Histogram / Density Plot | x, color, facet | — | Distribution of one quantitative field. Histogram: discrete bins, auto-binned. Density Plot: smooth KDE curve |
| Boxplot | x, y, color, facet | — | Distribution summary (median/quartiles/outliers) by category |
| Pie Chart | size, color, facet | innerRadius (0–100; 0=pie, >0=donut) | Part-of-whole with ≤7 categories. Wedge value goes on size, not theta |
| Heatmap | x, y, color, facet | colorScheme — sequential ("viridis","blues","reds","oranges","greens") or diverging ("blueorange","redblue") | Matrix / 2D density; color encodes the quantitative cell value |
Specialized — use when the data/question fits the "when to use"
| chart_type | encodings | config | when to use |
|---|---|---|---|
| Connected Scatter Plot | x, y, order, color, facet | — | Two quantitative fields traced in sequence — needs an order field (e.g. time) so points are joined in order, not by x |
| Ranged Dot Plot | x, y, color, facet | — | Min–max range or two-point comparison per category |
| Violin Plot | x, y, color, facet | — | Distribution SHAPE (KDE silhouette) by category; better than a boxplot when data is multimodal. x = category, y = value |
| Strip Plot | x, y, color, size, facet | — | Every individual point by category (jittered); good for small/medium n where raw values matter, not just a summary |
| ECDF Plot | x, color, facet | — | Cumulative distribution of one quantitative field. Pass the RAW field on x (do NOT pre-compute the CDF); color for per-group curves |
| Bump Chart | x, y, color, facet | — | How RANKINGS change over ordered x; y = rank, color = entity (long-form: one row per entity × x) |
| Slope Chart | x, y, color, facet | — | Change between exactly TWO points (before → after) per entity; x = the two labels, y = value, color = entity |
| Streamgraph | x, y, color, facet | — | Several series' magnitude over ordered x, stacked around a center baseline (color = series) — theme/volume shifts over time |
| Range Area Chart | x, y, y2, color, facet | — | A shaded band between a lower (y) and upper (y2) bound over ordered x — e.g. min–max or a confidence interval |
| Rose Chart | x, y, color, facet | — | Cyclical/categorical magnitude as angular wedges (polar bars); x = category/angle, y = value |
| Pyramid Chart | x, y, color, facet | — | Back-to-back bars split by a binary group (e.g. population by age × sex); y = category, x = value, color = the two-sided group |
| Radar Chart | x, y, color, facet | — | Multi-metric profile/comparison; x = metric name, y = value, color = entity (long-form data) |
| Bar Table | x, y, color, facet | — | Ranked horizontal table with inline bars; one row per category. y = category, x = value |
| KPI Card | metric, value, goal | — | "Big number" dashboard tile(s); one row per tile. value must be pre-aggregated; goal is optional |
| Candlestick Chart | x, open, high, low, close, facet | — | OHLC financial data |
| Map | longitude, latitude, color, size | projection ("mercator","equalEarth","naturalEarth1","orthographic","albersUsa"), projectionCenter ([lon,lat]) | Geographic POINTS/bubbles by lon/lat (use projection "albersUsa" for a US-only map) |
| Choropleth | id, color, facet | region ("world","usa",…) | Filled REGIONS shaded by value; id = the region key (country/state name or code), color = the quantitative value |
Critical chart rules:
color to get separate trend lines per group.color is set.group channel (not color) for side-by-side bars.x and the chart bins automatically. Pre-aggregating gives wrong bin widths.strokeDash to differentiate line styles (e.g. actual vs forecast).size channel (not theta) for wedge values. Avoid when >7–8 categories.colorScheme by the meaning of the values. Use a sequential scheme (viridis/blues/reds/oranges/greens) for single-direction magnitudes (counts, rates, prices, scores — higher is simply more). Use a diverging scheme (blueorange/redblue) ONLY when the values have a meaningful center to read away from (e.g. profit/loss around 0, change vs. a baseline, temperature around freezing).metric, value, goal (not x/y). One DataFrame row = one tile. The value column must already contain the final number to display (aggregate upstream in the Python step).open, high, low, close columns.order field (usually time) so points are joined in sequence, not by x-order.x — the chart computes the cumulative curve; do NOT pre-compute it in Python.y is the lower bound and y2 the upper bound of the band.color is the entity. Slope's x has exactly two categories (before/after).Map plots points via longitude / latitude (set projection "albersUsa" for the US); Choropleth fills regions — put the region key on id and the value on color, not x / y.encodings must also appear in output_fields. Typically use 2–3 channels (x, y, color/size).Choose the most specific type that fits. Only annotate fields used in chart encodings.
| Category | Types |
|---|---|
| Temporal | DateTime, Date, Time, Timestamp, Year, Quarter, Month, Week, Day, Hour, YearMonth, YearQuarter, YearWeek, Decade, Duration |
| Monetary measures | Amount, Price |
| Physical measures | Quantity, Temperature |
| Proportion | Percentage |
| Signed/diverging | Profit, PercentageChange, Sentiment, Correlation |
| Generic measures | Count, Number |
| Discrete numeric | Rank, Score |
| Identifier | ID |
| Geographic | Latitude, Longitude, Country, State, City, Region, Address, ZipCode |
| Entity names | Category, Name |
| Coded categorical | Status, Boolean, Direction |
| Binned ranges | Range |
| Fallback | Unknown |
Key guidelines:
{"regressionMethod": "linear"} (options: "linear", "log", "exp", "pow", "quad", "poly"; for poly add {"polyOrder": 3}).Take microsoft/core from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.