Chat agent for any FastAPI app. Built on the Cursor SDK. The agent runs in your project directory, reads source code, queries data files, edits code.
uv add cursorfy
export CURSOR_API_KEY=crsr_...from fastapi import FastAPI
import cursorfy
app = FastAPI()
cursorfy.mount(app) # adds POST /api/chat + /cursorfy/static/*<script defer src="/cursorfy/static/chat.js"></script>[ ASK ] button appears. Press A to open, Esc to close, T for
light/dark toggle.
cursorfy init # scaffold a dashboard from data in cwd
cursorfy init --run # also start uvicorn after
cursorfy init --model claude-opus-4-7cursorfy.mount(app) # function call
@cursorfy.enable # decorator on factory
app = cursorfy.attach(FastAPI()) # chainedAll take the same kwargs:
| kwarg | default | purpose |
|---|---|---|
cwd |
auto | Folder the agent runs against |
model |
"composer-2.5" |
Default Cursor model |
valid_models |
6 builtin ids | Allowed in the dropdown |
preamble |
builtin | System prompt ({cwd} substituted) |
api_key |
$CURSOR_API_KEY / .env |
Override env var |
prefix |
"" |
Route prefix |
static_path |
"/cursorfy/static" |
JS/CSS mount path |
- Every file in
cwdvia Cursor tools (read_file, grep, terminal, edit_file). Any format: CSV, parquet, JSON, sqlite, Excel. Uses DuckDB for SQL across mixed formats. - A screenshot of the page the user is looking at.
- This file (AGENTS.md), appended to the system prompt.
Browser
├ chat.js [ ASK ] FAB + floating panel, SSE stream
└ POST /api/chat → ChatHandler
├ AsyncClient.launch_bridge(workspace=cwd)
├ AsyncAgent.create / resume (LRU by session_id)
└ thinking/tool/answer events
cursorfy/
├── __init__.py # mount() / enable / attach (widget API)
├── chat.py # ChatHandler, agent cache, SSE
├── init/ # cursorfy init scaffolder
│ ├── __init__.py # CLI entry, init_command, SCAFFOLD_PROMPT loader
│ └── scaffold_prompt.md
└── static/
├── chat.js # widget
└── chat.css # light/dark, --cursorfy-* vars
examples/ # all data is real and citable
├── taxi/ # NYC Yellow Taxi (parquet + zone CSV, DuckDB)
├── github/ # Top 30 OSS repos (CSV, polars, GitHub Search API)
├── sp500/ # S&P 500 5y close + SPY holdings (CSV, polars, yfinance)
├── co2/ # OWID global CO2 emissions (CSV, polars)
├── olympics/ # Paris 2024 + Tokyo + Rio medals (CSV, polars)
└── movies/ # MovieLens latest-small (4 CSVs, polars joins)
assets/
├── make_tiles.py # playwright tile capture (all examples)
└── tiles/ # one PNG per example, for the README gallery
docs/ is git-ignored; it's a local scratchpad for launch copy and
work-in-progress screenshots, not shipped with the package.
Each example matches the same shape so the agent only has to learn the template once. Tufte rules apply.
- Module docstring is a story-led headline + a
Run:block with port. - 4 KPI tiles + 2-3 Plotly charts + a sortable table.
- A second table is fine in place of a chart when the data is ranked.
- JS data injected via
json.dumps(). - CSS uses
var(--cursorfy-*)throughout; charts re-theme on toggle. AGENTS.mdper example documents schema, real findings, common questions, and the chart conventions. The agent reads it on every session.- File budgets:
app.pyunder 220 lines; data files under 1 MB.
The scaffolder enforces Tufte. The chat agent must too when adding charts.
- One accent color (
var(--cursorfy-cyan));var(--cursorfy-coral)for negatives only. - No rainbow palettes. No pie / donut / 3D / gauge / radar.
- No hex colors anywhere; read CSS vars at runtime via
getComputedStyle. - Tables for ranked lists up to 20 items; charts for trends and distributions.
- 2-5 series on a chart? Cyan-to-muted ramp via
color-mix(), never new families. - 6+ series? Small multiples.
- Scatter with >10 points? Hover-only labels (no text collisions).
- Faint gridlines only; no axis line; no tick marks; tight margins.
displayModeBar:falsealways. Bar chart y-axis starts at zero.- Tooltips full-precision via
hovertemplate.
- Body font Poppins; UI labels (KPI labels, chart titles, table
th) inherit it. Monospace only on numeric values (.stat-value,td.num). - Never set
font-familyat thetablelevel; only ontd.num. That bug drags column headers and text cells like "Manhattan" into monospace, which clashes with the Poppins KPI labels above. - Sharp corners (no
border-radius). - All colors via cursorfy CSS variables; never hex.
The full scaffold prompt lives in cursorfy/init/scaffold_prompt.md;
it's the source of truth and is what every cursorfy init user runs.
Override these CSS variables:
:root {
--cursorfy-bg: #001926;
--cursorfy-card: #0a2936;
--cursorfy-text: #ffffff;
--cursorfy-muted: #7ba3b8;
--cursorfy-subtle: #4a6d7c;
--cursorfy-cyan: #62e4fb;
--cursorfy-coral: #ff6b6b;
--cursorfy-shadow: 0 12px 32px rgba(0, 0, 0, 0.55);
}All tints derived via color-mix(). Change --cursorfy-cyan, everything follows.
- Agent has read/write access to the project directory. Don't put secrets next to it.
- Set
CURSOR_API_KEYvia your platform's secret manager. Never commit it. - First message takes 2-5s (cold start). Follow-ups resume the agent.