This commit is contained in:
ray zhou
2026-05-21 19:35:16 +08:00
parent c104d08924
commit f1dc36e758
33 changed files with 320 additions and 4165 deletions

83
.gitignore vendored Normal file
View File

@@ -0,0 +1,83 @@
# >>> CURSOR MANAGED BLOCK >>>
# Ignore everything in .cursor by default
*
# Keep gitignore itself
!.gitignore
# =========================================================
# Team shared Cursor rules
# =========================================================
!rules/
!rules/**
# =========================================================
# Team shared Cursor agents
# =========================================================
!agents/
!agents/**
# =========================================================
# Team shared Cursor skills
# =========================================================
!skills/
!skills/**
# =========================================================
# Team shared slash commands
# =========================================================
!commands/
!commands/**
# =========================================================
# Team shared plans, optional
# =========================================================
!plans/
!plans/**
# =========================================================
# Team shared MCP configs under projects
# Only keep mcps-related files, ignore runtime outputs
# =========================================================
!projects/
projects/*
!projects/*/
projects/*/*
!projects/*/mcps/
!projects/*/mcps/**
# =========================================================
# Do NOT track Cursor plugin cache
# =========================================================
plugins/
plugins/**
plugins/cache/
plugins/cache/**
# =========================================================
# Do NOT track built-in Cursor skills
# Usually auto-generated / synced by Cursor
# =========================================================
skills-cursor/
skills-cursor/**
# =========================================================
# Do NOT track runtime state / transcripts / terminal logs
# =========================================================
subagents/
subagents/**
projects/*/agent-transcripts/
projects/*/agent-transcripts/**
projects/*/terminals/
projects/*/terminals/**
projects/*/agent-notes/
projects/*/agent-notes/**
projects/*/agent-tools/
projects/*/agent-tools/**
# <<< CURSOR MANAGED BLOCK <<<

153
agents/phpdoc-reviewer.md Normal file
View File

@@ -0,0 +1,153 @@
你是 PHPDoc Reviewer专门负责审查 PHP 代码中的 PHPDoc 是否符合项目规范。
你的职责不是重构代码,也不是修改业务逻辑,而是专注检查“本次新增或修改的 PHP 代码”是否补齐了必要的 PHPDoc。
项目背景:
- 项目使用 PHP 8.1+
- 主要业务目录包括 slot_*、backend/**
- 本项目要求新增或修改的 class / interface / trait / enum、方法、类常量、新增属性都必须有合格的 PHPDoc
- PHP 8+ 已经有类型声明时,@param / @return 仍然必须保留
审查范围:
只审查本次 diff 涉及的 PHP 文件和符号,不追溯未改动的历史代码。
需要检查的符号包括:
1. 新增或修改的 class
2. 新增或修改的 interface
3. 新增或修改的 trait
4. 新增或修改的 enum
5. 新增或修改的 public / protected / private 方法
6. 新增或修改的类常量 const
7. 新增属性
PHPDoc 最低要求:
一、class / interface / trait / enum
必须包含:
- 一行清晰的职责说明
- 说明该类型在当前业务中的作用
- 不允许空注释
- 不允许只写类名或泛泛描述
合格示例:
/**
* 钱包账户余额聚合模型。
*/
不合格示例:
/**
* WalletAccountModel
*/
二、方法 PHPDoc
必须包含:
- 一行方法职责说明
- 每个参数都必须有 @param
- 必须有 @return
- 存在异常抛出行为时必须有 @throws
- 描述必须说明业务含义、单位、边界或调用意图
- 不能只重复类型名
合格示例:
/**
* 计算用户可参与提现判断的有效余额。
*
* @param WalletAccountModel $model 当前用户钱包账户模型
* @return int 有效余额,单位:分
*/
不合格示例:
/**
* @param int $id id
* @return int int
*/
三、类常量 PHPDoc
必须包含:
- 一行说明业务含义
- 涉及金额、比例、状态、配置、枚举时,必须说明单位或语义
- 涉及配置或表字段时,应说明对应关系
合格示例:
/** 释放档位下限:单位分,对应配置 free_credits.release_tiers */
public const RELEASE_TIER_MIN = 100;
四、新增属性 PHPDoc
必须满足:
- 属性本身应优先使用 typed property
- 类型不直观时需要增加 @var
- 注释需要说明业务含义,不只是重复属性名
分层补充要求:
Controller
- 说明接口用途
- 涉及鉴权、幂等、登录态、风控前提时需要说明
Logic
- 说明用例步骤
- 涉及事务时说明事务边界
- 说明失败时行为
Service
- 说明复用场景
- 说明调用方约束
Model
- 说明查询条件
- 涉及分表时说明分表键
- 涉及金额字段时说明单位
DTO / Validate
- 说明字段含义
- 说明与上游请求参数或接口字段的映射关系
严格禁止:
1. 禁止空的 /** */
2. 禁止方法 PHPDoc 缺少 @param
3. 禁止方法 PHPDoc 缺少 @return
4. 禁止 @param int $id id 这种同义反复
5. 禁止只复制类型名,不说明业务含义
6. 禁止用 PHPDoc 替代业务校验逻辑
7. 禁止为了补 PHPDoc 大范围修改无关历史代码
8. 禁止借审查 PHPDoc 的名义重构业务代码
9. 禁止改动没有被本次 diff 触及的符号,除非该符号因为本次修改已经被影响
审查方式:
1. 先查看本次 diff
2. 找出所有新增或修改的 PHP 符号
3. 逐个判断是否符合 PHPDoc 规范
4. 只指出真实问题,不要过度发挥
5. 对每个问题给出建议补充的 PHPDoc
6. 如果可以直接修复,只做最小修改
7. 不改变方法签名、返回值、业务逻辑、SQL、事务、调用链
输出格式:
## PHPDoc Reviewer 检查结果
### 结论
- 通过 / 不通过
- 本次检查 PHP 文件数量:
- 发现问题数量:
### 问题列表
按文件列出:
#### 文件xxx.php
1. 符号ClassName::methodName()
问题:
- 缺少 @return
- @param 描述无业务含义
建议 PHPDoc
```php
/**
* 这里写方法职责说明。
*
* @param int $uid 用户 ID
* @return int 有效余额,单位:分
*/

84
agents/verifier.md Normal file
View File

@@ -0,0 +1,84 @@
---
name: verifier
description: >-
Validates completed implementation work. Use after tasks are marked done,
before merging PRs, or when the user asks to verify or double-check changes.
Runs tests and checks that behavior matches requirements; reports what passed
and what remains incomplete. Use proactively when implementation claims are made.
---
You are a skeptical verification specialist. Your job is to prove that claimed work actually works—not to implement new features unless a minimal fix is required to complete verification.
You do not trust summaries, checklists, or "done" claims without evidence. Assume the primary agent may have missed edge cases, broken tests, or incomplete requirements until you verify otherwise.
## When invoked
1. **Gather scope** — Identify what was supposed to be delivered: user request, plan file, PR description, requirement doc, or recent git changes (`git status`, `git diff`, `git log -5`).
2. **Map claims to evidence** — List each stated completion item and what you will check for it (code path, API, test, manual step).
3. **Verify implementation** — Read relevant code; confirm logic matches requirements and project conventions (e.g. Controller → Validate → DTO → Logic → Service/Model layering).
4. **Run checks** — Execute applicable automated checks; do not skip because they might be slow.
5. **Report** — Produce a structured pass/fail report (see Output format).
## Verification workflow
### Code and requirements
- Compare implementation against the original task or requirement document.
- Flag stubs, TODOs, dead code paths, or commented-out logic that should be active.
- Confirm error handling, idempotency, and edge cases mentioned in requirements.
- Note files changed vs. files that should have changed but did not.
### Tests and commands
Run what the repo supports; prefer project-documented commands:
| Stack | Typical commands |
| --- | --- |
| PHP (Webman, slot services) | `docker exec -w /app/www/slot/<service> php82 php vendor/bin/phpunit` or project-specific test scripts |
| PHP (Composer) | `docker exec -w /app/www/slot/<service> php82 composer test` if defined |
| Frontend (Vue) | `npm run test`, `npm run lint`, `npm run build` in the relevant package |
**Local dev rule:** PHP/MySQL/Redis for this monorepo run in Docker (`php82`, `goMysql`, etc.). Do not run `php`/`composer` on the macOS host unless explicitly confirmed.
If tests cannot run (missing env, broken setup), say so explicitly and list what you verified manually instead.
### Runtime / behavior (when applicable)
- Trace request flow for new or changed APIs (route → controller → logic).
- Check migrations, config, and feature flags if behavior depends on them.
- For bug fixes, confirm the failure mode is addressed and regressions are unlikely.
## Output format
Always end with this structure:
```markdown
## Verification report
### Passed
- [Item]: [brief evidence — e.g. test name passed, file/logic checked]
### Failed or incomplete
- [Item]: [what is wrong or missing]
- **Evidence:** [test output, file:line, or requirement gap]
- **Suggested fix:** [concrete next step, if obvious]
### Not verified (blocked)
- [Item]: [why — e.g. no test suite, env unavailable]
### Summary
[12 sentences: safe to merge / needs more work / critical blockers]
```
## Principles
- **Evidence over opinion** — Cite test output, command exit codes, or specific code locations.
- **Minimal scope** — Do not refactor or expand scope; only fix what blocks verification if the user expects you to fix failures.
- **Be direct** — If something is broken, say so clearly; do not soften failures.
- **Complete the loop** — If you fix something during verification, re-run the relevant checks before marking it passed.
## What you must not do
- Mark items as passed without running checks or reading the code.
- Assume CI passed unless you have seen results.
- Rewrite large portions of the codebase; escalate substantial gaps to the parent agent or user.

Submodule plugins/cache/cursor-public/figma/a742f0a700a7772ff5ed85f7c9fc1dad5afa9fcc deleted from a742f0a700

View File

@@ -1,48 +0,0 @@
{
"version": 1,
"skills": {
"babysit": {
"lastSyncedAt": 1779357682846
},
"canvas": {
"lastSyncedAt": 1779357682846
},
"create-hook": {
"lastSyncedAt": 1779357682846
},
"create-rule": {
"lastSyncedAt": 1779357682846
},
"create-skill": {
"lastSyncedAt": 1779357682846
},
"create-subagent": {
"lastSyncedAt": 1779357682846
},
"migrate-to-skills": {
"lastSyncedAt": 1779357682846
},
"shell": {
"lastSyncedAt": 1779357682846
},
"statusline": {
"lastSyncedAt": 1779357682846
},
"update-cli-config": {
"lastSyncedAt": 1779357682846
},
"update-cursor-settings": {
"lastSyncedAt": 1779357682846
},
"split-to-prs": {
"lastSyncedAt": 1779357682846
},
"sdk": {
"lastSyncedAt": 1779357682846
},
"loop": {
"lastSyncedAt": 1779357682846
}
},
"lastInventoryAt": 1779333144961
}

View File

@@ -1,14 +0,0 @@
---
name: babysit
description: >-
Keep a PR merge-ready by triaging comments, resolving clear conflicts, and
fixing CI in a loop.
---
# Babysit PR
Your job is to get this PR to a merge-ready state.
Check PR status, comments, and latest CI and resolve any issues until the PR is ready to merge.
1. Merge conflicts: Intelligently resolve any merge conflicts, preserving the intent and correctness of changes on your branch and the base branch. If intents conflict, abort the merge and ask for clarification.
2. Comments: Review active unresolved comments (including Bugbot) and resolve change requests / bug reports where valid. When fetching GitHub comments, filter out resolved threads first. Read only each comment body and the minimum location/URL needed to act on it; do not read the entire JSON output or other unnecessary payload data. Carefully validate issues reported by Bugbot and only take action on those that are valid; explain when you disagree or are unsure.
3. CI: Fix CI issues caused by changes within this PR's scope. Never change CI checks/workflows just to make failures pass, or make unrelated code changes; if that would be required, report back instead. For merge-blocking failures that seem unrelated to this PR, check whether the branch is behind the base branch and merge latest changes, since another PR may have fixed them. Push scoped fixes and re-watch CI until mergeable + green + comments triaged.

View File

@@ -1,153 +0,0 @@
---
name: canvas
description: >-
A Cursor Canvas is a live React app that the user can open beside the chat.
You MUST use a canvas when the agent produces a standalone analytical artifact
— quantitative analyses, billing investigations, security audits, architecture
reviews, data-heavy content, timelines, charts, tables, interactive
explorations, repeatable tools, or any response that benefits from visual
layout. Especially prefer a canvas when presenting results from MCP tools
(Datadog, Databricks, Linear, Sentry, Slack, etc.) where the data is the
deliverable — render it in a rich canvas rather than dumping it into a
markdown table or code block. If you catch yourself about to write a markdown
table, stop and use a canvas instead. You MUST also read this skill whenever
you create, edit, or debug any .canvas.tsx file.
metadata:
surfaces:
- ide
---
A canvas is a single `.canvas.tsx` file the IDE compiles so the user can open it beside the chat. Follow the workflow below in order.
## Workflow
### 1. Decide whether to use a canvas
The trigger is **user intent**, not response shape. Ask: would the user benefit from viewing this output as its **own standalone artifact**, separate from the chat? If the output is a means to an end (a drafted message, a code fix, a dashboard in another tool), skip the canvas.
**Use a canvas when the agent produces new standalone analytical output:**
- Quantitative analyses and metrics breakdowns (e.g. "send 500 requests and tell me how many fail")
- Billing or account investigations that surface structured findings from database queries
- Security audits or architecture reviews with categorized findings
- Cross-system data analyses and overlap reports
- Structured data from MCP tools (Databricks, Datadog, etc.) where the data IS the deliverable
- Financial analyses, margin decompositions, usage trend reports
- Tables with more than a handful of rows that the user asked to see
**Do NOT use a canvas when:**
- The user asks for work in a **specific tool** — "create a Datadog dashboard" means give them a Datadog dashboard, not a canvas
- The user has a **specific deliverable** — "draft a support response", "fix this code", "make this PR"
- The user is **working within an existing artifact** — improving an HTML dashboard, editing an existing file
- The user is doing **targeted debugging** or active development, even if structured findings emerge along the way
- Short factual answers, one-off file edits, or quick clarifying questions
- MCP tools are queried as an **intermediate step** for a different deliverable (e.g. querying Stripe to draft a support reply)
### 2. Write the canvas
**Location.** Canvases live at `/Users/<user>/.cursor/projects/<workspace>/canvases/<name>.canvas.tsx`. The IDE only detects canvases written directly inside that exact directory — subfolders, alternate extensions, and other locations are not picked up. For a new canvas, always use the write file tool to create the `.canvas.tsx` file at that exact path; do not stop after telling the user the path or showing code in chat. Treat that managed `canvases/` directory as pre-provisioned by Cursor itself: write the canvas file directly there and do **not** spend turns creating the directory with `mkdir` or checking whether it exists before writing. Listing its contents for other purposes (e.g. checking for existing canvases) is fine. If you can't determine the workspace directory from absolute paths already in your environment (terminals, transcripts, recently-viewed files), list `~/.cursor/projects/` rather than guessing. Use a descriptive kebab-case filename ending in `.canvas.tsx`; preserve acronym capitalization and lowercase the rest.
**File rules:**
- Exactly one `.canvas.tsx` file per canvas. Never create helper files, style files, or supporting modules.
- Import **only** from `cursor/canvas`. No relative imports, no npm packages, no Node built-ins.
- Default-export the top-level component.
- Embed all data inline. **No `fetch()`, no network calls.**
**Never render empty states.** A canvas exists to show real content. If a section, chart, table, or component has no data to display, **omit it** — do not render it with placeholder text ("Add header here", "TODO", "Example"), a "No data" message, an empty array, zeroed rows, or an empty chart frame. If the entire canvas would be empty because you don't have the underlying data, do not produce a canvas — tell the user what's missing and ask for it instead.
**Label every plot.** Charts and tables must be self-describing — a reader looking at the canvas alone should know exactly what they're seeing. For every plot include:
- A title naming the **specific metric** (not "Metrics" — "API error rate by service").
- **Axis labels with units** on both axes (e.g. "Date", "Latency (ms)").
- A **legend** when more than one series is shown, with the exact series names from the source data.
- The **source and time range** in a small caption (e.g. "Source: Datadog · last 7 days"). If a value is a transformation (mean, p95, normalized, smoothed), say so in the label.
**Component discovery:** prefer built-in `cursor/canvas` components over hand-rolled markup. The full public surface (components, hooks, prop types, tokens) is declared in `~/.cursor/skills-cursor/canvas/sdk/index.d.ts` and its sibling `.d.ts` files — read them when you need exact exports, prop shapes, or hook signatures rather than guessing. Referencing an export that does not exist is the most common runtime error.
Apply the Canvas generation policy below as you write, and complete its pre-delivery self-check (section 6) before returning the canvas.
## Design guidance
Be creative. The SDK gives you expressive building blocks — use them in whatever combination best serves the content. But avoid slop: no gradients, no emojis, no box-shadows, no rainbow coloring. Cursor canvases are flat, minimal, and purposeful.
### Visual hierarchy
Not everything deserves equal treatment. Primary content gets more space, larger headings, and accent color. Supporting content stays compact. Squint test: blur your eyes — can you tell what matters?
**Color.** All colors from `useHostTheme()` tokens — read its JSDoc in the SDK declarations for the return shape and usage pattern. No hardcoded hex. Use accent color deliberately, not on everything.
### Slop patterns — forbidden
These specific patterns produce low-quality output. If 2+ are present, redesign.
- **Gradients** — no `linear-gradient`, `radial-gradient`, `background-clip: text`.
- **Emojis** — no emoji as icons, status indicators, bullets, or section markers.
- **Box shadows** — no `box-shadow`. Flat surfaces only.
- **Wall of identical cards** — every section wrapped in the same card style with no variation. Mix open sections with cards.
- **Rainbow coloring** — a different color on every element. Most elements are neutral; color is used sparingly with purpose.
- **Giant text** — font sizes above H1 (24px), or bold text stuffed in CardHeader.
- **Decorative borders** — colored borders on every element. Borders are structural (subtle stroke tokens), not decorative.
### Pre-delivery self-check
Before returning canvas code, verify:
1. Does the layout have visual hierarchy? One thing should stand out.
2. Is there variety in the composition? Not just a single column of uniform blocks.
3. Slop check: scan for the forbidden patterns above.
## Introducing the canvas
When you create a canvas, add a short note in your chat response telling the user you created a canvas they can open beside the chat:
- **First canvas** — if no other `.canvas.tsx` files exist in the workspace's `canvases/` directory, include one sentence explaining what a canvas is.
- **Unsolicited canvas** — if the user didn't ask for a canvas, include one sentence explaining why you chose it over plain text.
Both can apply at once; one or two sentences total is enough. Skip the intro for subsequent canvases.
## Troubleshooting
If a canvas appears blank or missing, the most common cause is that it was not written under `/Users/<user>/.cursor/projects/<workspace>/canvases/` exactly — re-save it to that path. Do not debug this by trying to create the managed directory manually; focus on correcting the file path instead. Users can click the canvas file path in the response to open it, just like any other file path in Cursor. When present, the canvas server writes a `<name>.canvas.status.json` sidecar after each build with `status`, `diagnostics`, or `error` fields you can read; the file is best-effort and may not exist, so don't block on it.
## Good example
```tsx
import { Divider, Grid, H1, H2, Stack, Stat, Table, Text } from 'cursor/canvas';
export default function ServiceOverview() {
return (
<Stack gap={20}>
<H1>Service Overview</H1>
<Grid columns={3} gap={16}>
<Stat value="6" label="Total Services" />
<Stat value="5" label="Healthy" tone="success" />
<Stat value="1" label="Degraded" tone="warning" />
</Grid>
<Divider />
<H2>Service Status</H2>
<Table
headers={["Service", "Status", "Uptime", "Latency"]}
rows={[
["api-gateway", "Operational", "99.99%", "12ms"],
["auth-service", "Degraded", "99.2%", "340ms"],
["billing", "Operational", "99.8%", "45ms"],
]}
rowTone={[undefined, "warning", undefined]}
/>
<Divider />
<H2>Recent Changes</H2>
<Text>Auth service latency increased after the 14:30 deploy.</Text>
<Text tone="secondary" size="small">Last checked: Apr 7, 2026 14:52 UTC</Text>
</Stack>
);
}
```
Stats in a Grid, Table directly under H2, text sections without cards.
## Bad example — do not imitate
```tsx
// BAD — every section wrapped in Card, no hierarchy, Table unnecessarily boxed
<Stack gap={12}>
<Card><CardHeader>Summary</CardHeader><CardBody><Text>6 services.</Text></CardBody></Card>
<Card><CardHeader>Status</CardHeader><CardBody><Table headers={[...]} rows={[...]} /></CardBody></Card>
<Card><CardHeader>Changes</CardHeader><CardBody><Text>Latency increased.</Text></CardBody></Card>
</Stack>
```

View File

@@ -1,264 +0,0 @@
/**
* Design tokens for `cursor/canvas` (standalone; no UI framework dependency).
*
* Color values are aligned with the Cursor app dark theme (`packages/ui` `cursor-dark` sources).
*/
export declare const canvasPaletteDark: {
readonly foreground: "#E4E4E4EB";
readonly foregroundSecondary: "#E4E4E48D";
readonly foregroundTertiary: "#E4E4E45E";
readonly foregroundQuaternary: "#E4E4E442";
readonly editor: "#181818";
readonly chrome: "#141414";
readonly sidebar: "#141414";
readonly elevated: "#181818";
readonly fillPrimary: "#E4E4E430";
readonly fillSecondary: "#E4E4E41E";
readonly fillTertiary: "#E4E4E411";
readonly fillQuaternary: "#E4E4E40A";
readonly strokePrimary: "#E4E4E433";
readonly strokeSecondary: "#E4E4E41F";
readonly strokeTertiary: "#E4E4E414";
readonly accent: "#599CE7";
readonly buttonBackground: "#599CE7";
readonly buttonForeground: "#191c22";
readonly buttonHoverBackground: "#6AABE9";
readonly link: "#87c3ff";
readonly diffInsertedLine: "#3FA26633";
readonly diffRemovedLine: "#B8004933";
readonly diffStripAdded: "#3FA2668F";
readonly diffStripRemoved: "#FC6B838F";
};
/**
* Light-mode palette derived from `packages/ui/src/tokens/themes/cursor-core/light.ts`.
* Base color: #141414. Same percentages as dark (regular light has no overrides
* in CURSOR_SEMANTIC_OVERRIDES — only high-contrast does).
*/
export declare const canvasPaletteLight: {
readonly foreground: "#141414F0";
readonly foregroundSecondary: "#141414BD";
readonly foregroundTertiary: "#1414148A";
readonly foregroundQuaternary: "#1414145C";
readonly editor: "#FCFCFC";
readonly chrome: "#F8F8F8";
readonly sidebar: "#F3F3F3";
readonly elevated: "#FCFCFC";
readonly fillPrimary: "#14141433";
readonly fillSecondary: "#14141424";
readonly fillTertiary: "#14141414";
readonly fillQuaternary: "#1414140F";
readonly strokePrimary: "#14141433";
readonly strokeSecondary: "#1414141F";
readonly strokeTertiary: "#14141414";
readonly accent: "#3685BF";
readonly buttonBackground: "#3685BF";
readonly buttonForeground: "#FCFCFC";
readonly buttonHoverBackground: "#2E76AB";
readonly link: "#3685BF";
readonly diffInsertedLine: "#1F8A651F";
readonly diffRemovedLine: "#CF2D5614";
readonly diffStripAdded: "#1F8A65CC";
readonly diffStripRemoved: "#CF2D56CC";
};
export interface CanvasPalette {
readonly foreground: string;
readonly foregroundSecondary: string;
readonly foregroundTertiary: string;
readonly foregroundQuaternary: string;
readonly editor: string;
readonly chrome: string;
readonly sidebar: string;
readonly elevated: string;
readonly fillPrimary: string;
readonly fillSecondary: string;
readonly fillTertiary: string;
readonly fillQuaternary: string;
readonly strokePrimary: string;
readonly strokeSecondary: string;
readonly strokeTertiary: string;
readonly accent: string;
readonly buttonBackground: string;
readonly buttonForeground: string;
readonly buttonHoverBackground: string;
readonly link: string;
readonly diffInsertedLine: string;
readonly diffRemovedLine: string;
readonly diffStripAdded: string;
readonly diffStripRemoved: string;
}
/**
* Chart color palette — distilled from portal-website analytics charts.
* 88% opacity (E0) softens vibrancy without dulling; palette maximizes
* hue + luminosity spread for distinguishable multi-series charts.
*/
export declare const chartPalette: {
readonly green: "#1F8A65E8";
readonly darkGreen: "#0D855AE0";
readonly lightGreen: "#52B896E0";
readonly mintGreen: "#7DCAB0E0";
readonly blue: "#2E79B5E0";
readonly lightBlue: "#70B0D8E0";
readonly indigo: "#5A6CC0F0";
readonly lightIndigo: "#9AAADCE0";
readonly purple: "#7B64B8F0";
readonly lightPurple: "#AA98D8E0";
readonly warmPink: "#C85898E0";
readonly lightPink: "#E8A0C4E0";
readonly brightOrange: "#F0A040E0";
readonly deepOrange: "#C06028E0";
readonly goldenYellow: "#E8C030E0";
readonly darkAmber: "#C04848E0";
readonly warmPeach: "#F0A088E0";
readonly vibrantTeal: "#2A9A8AE0";
readonly muted: "#8888A8E0";
readonly neutralLine: "#888899D0";
};
/**
* Shared category palette for canvas primitives that show categorical tints
* (`Swatch`, `UsageBar` segments, etc.). Uses a coherent subset of
* `chartPalette` so a category's color reads consistently across any
* primitive that consumes it.
*
* The insertion order here is the canonical category order — primitives
* that auto-assign colors (e.g. `UsageBar` segments without an explicit
* `color`) cycle through these keys in order.
*/
export declare const colorPalette: {
readonly gray: "#8888A8E0";
readonly purple: "#7B64B8F0";
readonly green: "#1F8A65E8";
readonly yellow: "#E8C030E0";
readonly pink: "#C85898E0";
readonly blue: "#2E79B5E0";
readonly orange: "#F0A040E0";
};
export type Color = keyof typeof colorPalette;
/**
* Auto-color rotation for `UsageBar` segments without an explicit `color`.
* Decoupled from `colorPalette`'s declaration order so the palette can grow
* or be reordered without changing how unspecified segments cycle.
*
* Matches the original `packages/ui` `ContextUsageTray` order so the same
* segment index lands on the same hue as the source.
*/
export declare const usageColorSequence: readonly Color[];
/**
* Ordered array for automatic series coloring — alternates dark/light across
* distinct hue families for maximum perceptual separation.
*/
export declare const chartColorSequence: readonly ["#1F8A65E8", "#70B0D8E0", "#5A6CC0F0", "#F0A040E0", "#C06028E0", "#E8C030E0", "#C85898E0", "#F0A088E0", "#7B64B8F0", "#7DCAB0E0", "#8888A8E0", "#2A9A8AE0"];
declare function buildTokens(palette: CanvasPalette): {
bg: {
editor: string;
chrome: string;
elevated: string;
};
text: {
primary: string;
secondary: string;
tertiary: string;
quaternary: string;
link: string;
onAccent: string;
};
stroke: {
primary: string;
secondary: string;
tertiary: string;
};
fill: {
primary: string;
secondary: string;
tertiary: string;
quaternary: string;
};
accent: {
primary: string;
control: string;
controlHover: string;
};
diff: {
insertedLine: string;
removedLine: string;
stripAdded: string;
stripRemoved: string;
};
};
/** Semantic colors for components (spacing and radius live in `theme.ts`). */
export declare const canvasTokens: {
bg: {
editor: string;
chrome: string;
elevated: string;
};
text: {
primary: string;
secondary: string;
tertiary: string;
quaternary: string;
link: string;
onAccent: string;
};
stroke: {
primary: string;
secondary: string;
tertiary: string;
};
fill: {
primary: string;
secondary: string;
tertiary: string;
quaternary: string;
};
accent: {
primary: string;
control: string;
controlHover: string;
};
diff: {
insertedLine: string;
removedLine: string;
stripAdded: string;
stripRemoved: string;
};
};
export declare const canvasTokensLight: {
bg: {
editor: string;
chrome: string;
elevated: string;
};
text: {
primary: string;
secondary: string;
tertiary: string;
quaternary: string;
link: string;
onAccent: string;
};
stroke: {
primary: string;
secondary: string;
tertiary: string;
};
fill: {
primary: string;
secondary: string;
tertiary: string;
quaternary: string;
};
accent: {
primary: string;
control: string;
controlHover: string;
};
diff: {
insertedLine: string;
removedLine: string;
stripAdded: string;
stripRemoved: string;
};
};
export type CanvasTokens = ReturnType<typeof buildTokens>;
export {};
//# sourceMappingURL=canvas-tokens.d.ts.map

View File

@@ -1,200 +0,0 @@
/**
* Chart primitives for `cursor/canvas` — multi-series, stacked, and pie charts
* rendered as pure inline SVG with zero external dependencies.
*
* Distilled from the portal-website Highcharts analytics charting layer.
*/
import type { CSSProperties, JSX } from "react";
/**
* Semantic tone for a chart series or slice. Mirrors the tone vocabulary
* used by `Stat`, `Pill`, `Table`, and other SDK primitives so colors
* match across a canvas — e.g. a `Stat tone="success"` and a
* `ChartSeries tone="success"` render in the same green.
*
* Omit `tone` to let the chart auto-assign a distinct color from the
* chart palette; supply `tone` only when the value carries semantic
* meaning that should match other tonal elements on the page.
*/
export type ChartTone = "success" | "danger" | "warning" | "info" | "neutral";
/** A single labeled value, used by `PieChart`. */
export type ChartDataPoint = {
label: string;
/** Non-negative numeric value. */
value: number;
};
/**
* A named data series for `BarChart` and `LineChart`.
* The `data` array aligns by index with the parent component's `categories`.
* If `tone` is omitted, a color is auto-assigned from the chart palette.
*/
export type ChartSeries = {
name: string;
data: number[];
tone?: ChartTone;
};
export type BarChartProps = {
/** Category labels along the independent axis. */
categories: string[];
/** One or more data series. Values align by index with `categories`. */
series: ChartSeries[];
height?: number;
/** Stack series on top of each other instead of grouping side-by-side. */
stacked?: boolean;
/** Render horizontal bars instead of vertical columns. */
horizontal?: boolean;
/** Show as 100% stacked (implies `stacked`). */
normalized?: boolean;
/** Suffix for y-axis tick labels (e.g. "%"). */
valueSuffix?: string;
style?: CSSProperties;
};
export type LineChartProps = {
categories: string[];
series: ChartSeries[];
height?: number;
/** Fill the area under each line with a soft tint. */
fill?: boolean;
valueSuffix?: string;
style?: CSSProperties;
};
export type PieChartProps = {
data: Array<ChartDataPoint & {
tone?: ChartTone;
}>;
size?: number;
donut?: boolean;
style?: CSSProperties;
};
/**
* Multi-series bar/column chart with optional stacking and normalization.
* Distilled from the portal-website Highcharts analytics charts.
*
* Pass `categories` for x-axis labels and one or more `series` whose `data`
* arrays align by index. With a single series you get simple bars; with
* multiple series the default is grouped (side-by-side) — set `stacked` for
* stacked columns or `normalized` for 100%-stacked share-mode.
*
* Colors are auto-assigned from the chart palette. With a **single series**,
* each bar gets a different color by category (so a chart of 5 categories
* shows 5 colors out of the box). With **multiple series**, each series gets
* its own color. A legend appears when there are 2+ series.
*
* For semantic coloring, pass `tone` on a series — it maps to the same
* palette entries used by `Stat`, `Pill`, and `Table` so your chart matches
* tonal elements elsewhere on the page.
*
* @example
* ```tsx
* // Simple single-series
* <BarChart
* categories={["Mon", "Tue", "Wed"]}
* series={[{ name: "Requests", data: [120, 90, 150] }]}
* />
*
* // Stacked multi-series (like portal AI commit chart)
* <BarChart
* categories={["Mon", "Tue", "Wed"]}
* series={[
* { name: "IDE", data: [120, 90, 150] },
* { name: "CLI", data: [30, 40, 25] },
* { name: "Cloud", data: [50, 60, 70] },
* ]}
* stacked
* />
*
* // Semantic tones — "accepted" renders in the same green as
* // <Stat tone="success"> elsewhere on the page.
* <BarChart
* categories={["Mon", "Tue", "Wed"]}
* series={[
* { name: "Accepted", data: [70, 80, 60], tone: "success" },
* { name: "Rejected", data: [30, 20, 40], tone: "danger" },
* ]}
* stacked
* />
* ```
*/
export declare function BarChart({ categories, series, height, stacked, horizontal, normalized, valueSuffix, style }: BarChartProps): JSX.Element;
/**
* Multi-series line chart with optional area fill. Distilled from the
* portal-website Highcharts analytics charts.
*
* Each series draws a polyline with dot markers at each data point.
* Set `fill` to shade the area under every line. Hover over any category
* column to see a tooltip with all series values at that point.
*
* This is **not** a time-series component — it does not parse dates.
* Pass pre-formatted date strings as `categories` if plotting over time.
*
* Colors are auto-assigned from the chart palette. For semantic coloring,
* pass `tone` on a series — it maps to the same palette entries used by
* `Stat`, `Pill`, and `Table`.
*
* @example
* ```tsx
* // Single line
* <LineChart
* categories={["Jan", "Feb", "Mar", "Apr"]}
* series={[{ name: "Revenue", data: [100, 140, 120, 180] }]}
* />
*
* // Multi-series with area fill
* <LineChart
* categories={["Jan", "Feb", "Mar", "Apr"]}
* series={[
* { name: "Accepted", data: [50, 70, 60, 90] },
* { name: "Suggested", data: [120, 140, 130, 160] },
* ]}
* fill
* />
*
* // Semantic tones — "errors" renders in the same red as a
* // <Pill tone="danger"> elsewhere on the page.
* <LineChart
* categories={["00:00", "06:00", "12:00", "18:00"]}
* series={[
* { name: "p95 latency", data: [80, 95, 110, 90], tone: "info" },
* { name: "errors", data: [2, 4, 9, 3], tone: "danger" },
* ]}
* />
* ```
*/
export declare function LineChart({ categories, series, height, fill, valueSuffix, style }: LineChartProps): JSX.Element;
/**
* Pie (or donut) chart with hover highlighting. Distilled from the
* portal-website Highcharts analytics charts.
*
* Unlike `BarChart` and `LineChart`, `PieChart` takes a flat `data` array of
* `{ label, value }` points — each slice is its own category. Colors are
* auto-assigned from the chart palette; pass `tone` on a point to give a
* slice a semantic color that matches other tonal elements on the page.
*
* Hovering a slice expands it outward and dims the others; hovering a legend
* item does the same. A tooltip with value and percentage appears below the
* chart. Set `donut` for a hollow center.
*
* **Do not** use for bar-style comparisons — use `BarChart` instead.
*
* @example
* ```tsx
* // Basic pie
* <PieChart
* data={[
* { label: "IDE", value: 120 },
* { label: "CLI", value: 30 },
* { label: "Cloud", value: 50 },
* ]}
* />
*
* // Donut with semantic tones
* <PieChart
* data={[
* { label: "Passing", value: 70, tone: "success" },
* { label: "Failing", value: 30, tone: "danger" },
* ]}
* donut
* />
* ```
*/
export declare function PieChart({ data, size, donut, style }: PieChartProps): JSX.Element;
//# sourceMappingURL=chart-primitives.d.ts.map

View File

@@ -1,65 +0,0 @@
/**
* Borderless disclosure row — chevron + structured header (title, optional
* leading slot, count, trailing slot) with a body that toggles open. Distilled
* from baby-glass `ContextTreeRow`'s lightweight list-row chrome (no card
* border, no background fill).
*
* For a bordered surface that collapses, use `<Card collapsible>` instead.
*/
import { type CSSProperties, type JSX, type ReactNode } from "react";
export type CollapsibleSectionProps = {
/** Plain-text title rendered next to the disclosure chevron. */
title: string;
/**
* Optional leading visual (e.g. `<Swatch>`) shown between the chevron and
* the title.
*/
leading?: ReactNode;
/**
* Optional small count rendered after the title (e.g. number of children).
*/
count?: number;
/**
* Optional trailing node, right-aligned (e.g. token readout, badge, button).
* Rendered with `t.text.tertiary` color hint via the wrapper; the slot can
* override.
*/
trailing?: ReactNode;
/** Body shown when expanded. */
children?: ReactNode;
style?: CSSProperties;
};
/**
* Borderless collapsible row with a structured header. Always starts closed
* (uncontrolled, no `defaultOpen`).
*
* Compose with `<Swatch>` in the `leading` slot for a colored category icon,
* and put a token readout / pill / button in `trailing`. Body content is
* indented under the row so nested `CollapsibleSection`s read as a tree.
*
* For a bordered, card-shaped collapsible surface, use `<Card collapsible>`
* instead — `CollapsibleSection` has no border or background and is meant to
* sit in a list of similar rows.
*
* @example
* ```tsx
* // Basic
* <CollapsibleSection title="Conversation">
* <Text>Messages go here.</Text>
* </CollapsibleSection>
*
* // With a colored category swatch + count + trailing token readout
* <CollapsibleSection
* title="Tools"
* count={4}
* leading={<Swatch color="purple" />}
* trailing={<Text size="small" tone="tertiary">12.3k</Text>}
* >
* <CollapsibleSection title="Grep">
* <Text>Search results.</Text>
* </CollapsibleSection>
* </CollapsibleSection>
* ```
*/
export declare function CollapsibleSection({ title, leading, count, trailing, children, style }: CollapsibleSectionProps): JSX.Element;
//# sourceMappingURL=collapsible-section.d.ts.map

View File

@@ -1,102 +0,0 @@
/**
* Pure layout math for directed acyclic graphs. Returns positioned node
* coordinates, edge anchor points, rank bounding boxes, and back-edge flags.
* Rendering is the caller's responsibility.
*
* Handles cycles gracefully: back-edges are detected via DFS, excluded from
* ranking, and flagged in the output so the caller can render them differently
* (e.g. dashed arcs).
*/
export type DAGLayoutOptions = {
/** Nodes to lay out. Only `id` is required. */
nodes: Array<{
id: string;
}>;
/** Directed edges. */
edges: Array<{
from: string;
to: string;
}>;
/** Flow direction. Default `"vertical"` (top-to-bottom). */
direction?: "vertical" | "horizontal";
/** Node box width in px. Default 160. */
nodeWidth?: number;
/** Node box height in px. Default 40. */
nodeHeight?: number;
/** Gap between ranks (layers) in px. Default 64. */
rankGap?: number;
/** Gap between sibling nodes in the same rank in px. Default 48. */
nodeGap?: number;
/** Padding around the bounding box in px. Default 24. */
padding?: number;
};
export type DAGLayoutNode = {
id: string;
/** Left edge of the node box. */
x: number;
/** Top edge of the node box. */
y: number;
/** Layer index (0 = root). */
rank: number;
/** Position within the rank (0-indexed). */
order: number;
};
export type DAGLayoutEdge = {
from: string;
to: string;
/** Suggested source anchor point (center of the outgoing side). */
sourceX: number;
sourceY: number;
/** Suggested target anchor point (center of the incoming side). */
targetX: number;
targetY: number;
/** True when this edge was identified as a back-edge (part of a cycle). */
isBackEdge: boolean;
};
export type DAGLayoutRank = {
/** Rank index (0 = root). */
rank: number;
/** Left edge of the rank bounding box. */
x: number;
/** Top edge of the rank bounding box. */
y: number;
/** Width of the rank bounding box. */
width: number;
/** Height of the rank bounding box. */
height: number;
/** Node ids in this rank, in order. */
nodeIds: string[];
};
export type DAGLayoutResult = {
nodes: DAGLayoutNode[];
edges: DAGLayoutEdge[];
/** Bounding box per rank — useful for drawing layer bands. */
ranks: DAGLayoutRank[];
/** The direction used for this layout. */
direction: "vertical" | "horizontal";
/** Total width of the bounding box. */
width: number;
/** Total height of the bounding box. */
height: number;
};
/**
* Compute a hierarchical layout for a directed graph.
*
* Returns node positions, edge anchor points, rank bounding boxes, and
* back-edge flags. The caller handles all rendering.
*
* @example
* ```ts
* const layout = computeDAGLayout({
* nodes: [{ id: "a" }, { id: "b" }, { id: "c" }],
* edges: [{ from: "a", to: "b" }, { from: "b", to: "c" }],
* });
*
* // layout.nodes[i].x / .y → position your own SVG/HTML elements
* // layout.edges[i].sourceX/Y, targetX/Y → draw lines between them
* // layout.edges[i].isBackEdge → style cycle edges differently
* // layout.ranks[i] → draw layer bands behind each rank
* ```
*/
export declare function computeDAGLayout(options: DAGLayoutOptions): DAGLayoutResult;
//# sourceMappingURL=dag-layout.d.ts.map

View File

@@ -1,130 +0,0 @@
/**
* Low-level diff primitives for the canvas SDK.
*
* The canvas diff surface is intentionally minimal — two components that
* compose with the generic `Card` / `CardHeader` / `CardBody` / `Pill` /
* `Text` primitives to build any diff layout an agent can imagine:
*
* - `DiffView` — a monospaced, syntax-highlighted unified diff renderer.
* Drops into any container (a `Card`, a table cell, a bare layout,
* nothing at all). No card chrome, no header, no path display. Pass
* `path` to auto-detect the language for highlighting, or `language`
* to override.
*
* - `DiffStats` — the canonical `+N` green / `-N` red glyph pair. Use
* it anywhere a small "added/deleted" summary makes sense — in a
* `CardHeader`'s `trailing` slot, next to a filename in a file tree,
* inside a status row, etc.
*
* For file-level metadata the preferred composition is to use the
* generic `Card` family:
*
* ```tsx
* <Card collapsible>
* <CardHeader trailing={<DiffStats additions={5} deletions={2} />}>
* src/utils.ts
* </CardHeader>
* <CardBody style={{ padding: 0 }}>
* <DiffView path="src/utils.ts" lines={lines} />
* </CardBody>
* </Card>
* ```
*/
import type { CSSProperties, JSX } from "react";
export type DiffStatsProps = {
additions?: number;
deletions?: number;
style?: CSSProperties;
};
/**
* Inline `+N` / `-N` glyph pair. Green additions, red deletions, with
* tabular numerals so columns of stats line up. Renders nothing when
* both counts are zero.
*
* @example
* ```tsx
* <CardHeader trailing={<DiffStats additions={12} deletions={3} />}>
* src/utils.ts
* </CardHeader>
*
* <Row gap={8}>
* <Text>Refactor pass</Text>
* <DiffStats additions={42} deletions={17} />
* </Row>
* ```
*/
export declare function DiffStats({ additions, deletions, style }: DiffStatsProps): JSX.Element | null;
export type DiffLineType = "added" | "removed" | "unchanged";
export type DiffLineData = {
type: DiffLineType;
content: string;
lineNumber?: number;
};
export type DiffViewProps = {
lines: DiffLineData[];
/**
* File path used to infer the syntax-highlighting language from the
* extension (e.g. `"src/utils.ts"` → `typescript`). The most ergonomic
* way to enable highlighting — pass the same path you show in the
* enclosing card header. Unknown extensions silently render as plain
* text.
*
* If both `path` and `language` are provided, `language` wins.
*/
path?: string;
/**
* Explicit language override for syntax highlighting (e.g.
* `"typescript"`, `"python"`, `"tsx"`). Use this when no file path is
* available, when the path's extension is misleading, or when the
* content is a snippet rather than a real file. Accepts common
* aliases (`ts`, `py`, `rs`, `md`, etc.). Unknown languages silently
* fall back to plain text.
*
* Highlighting is applied per line, so multi-line constructs (block
* comments, template literals) may not colorize perfectly across line
* boundaries. For typical diff-sized inputs this is fine.
*/
language?: string;
/** Show line numbers in the gutter. Default `true`. */
showLineNumbers?: boolean;
/** Color line numbers green/red for added/removed lines. Default `true`. */
coloredLineNumbers?: boolean;
/** Show a 3px accent strip on the left edge for changed lines. Default `true`. */
showAccentStrip?: boolean;
style?: CSSProperties;
};
/**
* Unified diff body renderer with monospaced type, colored line
* backgrounds, line-number gutter, accent strip, and optional Shiki
* syntax highlighting.
*
* `DiffView` does not provide any surrounding chrome — place it inside
* a `Card` + `CardBody` (with `padding: 0`) when you want the standard
* bordered "file diff" look, or drop it anywhere else if you want the
* bare renderer.
*
* Pass `path` to enable syntax highlighting from the file extension.
*
* @example
* ```tsx
* <Card>
* <CardHeader trailing={<DiffStats additions={2} deletions={1} />}>
* src/utils.ts
* </CardHeader>
* <CardBody style={{ padding: 0 }}>
* <DiffView
* path="src/utils.ts"
* lines={[
* { type: "unchanged", content: "export function add(a: number, b: number): number {", lineNumber: 1 },
* { type: "removed", content: " return a + b;", lineNumber: 2 },
* { type: "added", content: " const result = a + b;", lineNumber: 2 },
* { type: "added", content: " return result;", lineNumber: 3 },
* { type: "unchanged", content: "}", lineNumber: 4 },
* ]}
* />
* </CardBody>
* </Card>
* ```
*/
export declare function DiffView({ lines, path, language, showLineNumbers, coloredLineNumbers, showAccentStrip, style }: DiffViewProps): JSX.Element;
//# sourceMappingURL=diff-view.d.ts.map

View File

@@ -1,194 +0,0 @@
/**
* Form primitives for `cursor/canvas`. Provides themed, controlled form controls
* for interactive canvas apps with persistent state. All `onChange` callbacks
* receive the **value directly** (not a DOM event), so they pair naturally
* with `useCanvasState`:
*
* ```tsx
* const [name, setName] = useCanvasState("name", "");
* <TextInput value={name} onChange={setName} placeholder="Enter name…" />
* ```
*/
import { type CSSProperties, type JSX, type ReactNode } from "react";
export type TextInputProps = {
value?: string;
/** Called with the new string value on every keystroke. */
onChange?: (value: string) => void;
placeholder?: string;
disabled?: boolean;
type?: "text" | "email" | "password" | "number" | "url" | "search";
style?: CSSProperties;
};
/**
* Single-line text input (28px height). Use for names, titles, search
* queries, and short text fields.
*
* `onChange` receives the **string value**, not a DOM event — this pairs
* directly with `useCanvasState` setters.
*
* @example
* ```tsx
* const [name, setName] = useCanvasState("name", "");
*
* <TextInput value={name} onChange={setName} placeholder="Task title…" />
* ```
*/
export declare function TextInput({ value, onChange, placeholder, disabled, type, style }: TextInputProps): JSX.Element;
export type TextAreaProps = {
value?: string;
/** Called with the new string value on every keystroke. */
onChange?: (value: string) => void;
placeholder?: string;
disabled?: boolean;
/** Minimum visible rows. Defaults to 3. */
rows?: number;
style?: CSSProperties;
};
/**
* Multi-line text input that auto-resizes to fit its content.
* Use for notes, descriptions, comments, and multi-line text fields.
*
* The textarea grows as the user types. Set `rows` for the minimum visible
* height (defaults to 3). Override with `style={{ height: "100px" }}` for a
* fixed size.
*
* @example
* ```tsx
* const [notes, setNotes] = useCanvasState("notes", "");
*
* <TextArea value={notes} onChange={setNotes} placeholder="Add notes…" rows={4} />
* ```
*/
export declare function TextArea({ value, onChange, placeholder, disabled, rows, style }: TextAreaProps): JSX.Element;
export type CheckboxProps = {
checked?: boolean;
/** Called with the new boolean value when toggled. */
onChange?: (checked: boolean) => void;
disabled?: boolean;
/** Optional label rendered beside the checkbox. Clicking the label toggles the checkbox. */
label?: ReactNode;
style?: CSSProperties;
};
/**
* Checkbox with optional label (accent blue when checked).
*
* Pass `label` to render a clickable text label beside the checkbox. Without a
* label, provide `title` or wrap in your own `<label>` for accessibility.
*
* @example
* ```tsx
* const [agreed, setAgreed] = useCanvasState("agreed", false);
*
* <Checkbox checked={agreed} onChange={setAgreed} label="I agree to the terms" />
* ```
*
* @example
* ```tsx
* // Checkbox in a list — no label, parent handles layout
* <Checkbox checked={item.done} onChange={(v) => toggleItem(item.id, v)} />
* ```
*/
export declare function Checkbox({ checked, onChange, disabled, label, style }: CheckboxProps): JSX.Element;
export type ToggleProps = {
checked?: boolean;
/** Called with the new boolean value when toggled. */
onChange?: (checked: boolean) => void;
disabled?: boolean;
/** `sm` = 16px track, `md` = 20px track (default `sm`). */
size?: "sm" | "md";
style?: CSSProperties;
};
/**
* Boolean toggle switch. Uses the accent color for the "on" state and a
* neutral fill for "off".
*
* @example
* ```tsx
* const [enabled, setEnabled] = useCanvasState("enabled", false);
*
* <Row gap={8} align="center">
* <Text>Notifications</Text>
* <Spacer />
* <Toggle checked={enabled} onChange={setEnabled} />
* </Row>
* ```
*/
export declare function Toggle({ checked, onChange, disabled, size, style }: ToggleProps): JSX.Element;
export type SelectOption = {
value: string;
label: string;
disabled?: boolean;
};
export type SelectProps = {
value?: string;
/** Called with the new selected value. */
onChange?: (value: string) => void;
/** List of options. Each must have a unique `value`. */
options: SelectOption[];
/** Placeholder shown when no value is selected. */
placeholder?: string;
disabled?: boolean;
style?: CSSProperties;
};
/**
* Dropdown select (native `<select>` with themed styling).
*
* Uses a native `<select>` under the hood for reliable keyboard, screen-reader,
* and mobile support. The dropdown list uses OS-native styling.
*
* @example
* ```tsx
* const [priority, setPriority] = useCanvasState("priority", "medium");
*
* <Select
* value={priority}
* onChange={setPriority}
* options={[
* { value: "low", label: "Low" },
* { value: "medium", label: "Medium" },
* { value: "high", label: "High" },
* ]}
* />
* ```
*/
export declare function Select({ value, onChange, options, placeholder, disabled, style }: SelectProps): JSX.Element;
export type IconButtonProps = {
/** Icon content: an SVG element, emoji, or unicode character. */
children: ReactNode;
onClick?: () => void;
disabled?: boolean;
/** Tooltip text. Always provide for accessibility since there is no text label. */
title?: string;
/**
* `"default"` is transparent until hovered; `"circle"` has a permanent
* background fill.
*/
variant?: "default" | "circle";
/** `sm` = 16px, `md` = 20px (default `md`). */
size?: "sm" | "md";
style?: CSSProperties;
};
/**
* Compact icon-only button for inline actions on list items (delete, edit,
* expand, etc.). Accepts **any** `children` as the icon — use an inline SVG,
* an emoji, or a unicode character.
*
* Always provide `title` for accessibility (screen-reader label + tooltip).
*
* Canvas has no icon font, so pass icon content directly.
*
* @example
* ```tsx
* // Delete button on a card
* <IconButton title="Delete" onClick={() => remove(id)}>✕</IconButton>
*
* // Edit button with an SVG icon
* <IconButton title="Edit" variant="circle" size="sm" onClick={edit}>
* <svg width={12} height={12} viewBox="0 0 12 12" fill="none">
* <path d="M8.5 1.5l2 2L4 10H2V8z" stroke="currentColor" strokeWidth={1.2} />
* </svg>
* </IconButton>
* ```
*/
export declare function IconButton({ children, onClick, disabled, title, variant, size, style }: IconButtonProps): JSX.Element;
//# sourceMappingURL=form-primitives.d.ts.map

View File

@@ -1,117 +0,0 @@
import type { CanvasPalette, CanvasTokens } from "./canvas-tokens.js";
import { type CanvasAction } from "./internal/canvas-action-dispatch.js";
/**
* Host theme for the current canvas. Semantic color groups (`text`, `bg`,
* `fill`, `stroke`, `accent`, `diff`) live at the top level for ergonomic
* inline-style access; `tokens` is also present as a self-reference for
* callers that prefer a namespaced form.
*/
export interface CanvasHostTheme extends CanvasTokens {
readonly kind: string;
readonly tokens: CanvasTokens;
readonly palette: CanvasPalette;
}
/**
* Returns the current host theme. Falls back to dark mode when no host
* state is available.
*
* Semantic color groups are available directly on the returned object —
* `accent`, `text`, `bg`, `fill`, `stroke`, `diff` — as well as `kind`
* (`"dark"` | `"light"` | …) and `palette` (the flat color palette).
*
* Call `useHostTheme()` inside each component that needs theme access —
* the returned object is scoped to that component, not shared across
* function boundaries.
*
* Stable paths for `style={{ ... }}` usage:
* - `text.primary / secondary / tertiary / quaternary` — text hierarchy
* - `bg.editor / chrome / elevated` — surface backgrounds
* - `fill.primary / secondary / tertiary / quaternary` — tinted fills
* - `stroke.primary / secondary / tertiary` — borders and dividers
* - `accent.primary / control` — accent blue and button background
* - `text.link` — link color
* - `text.onAccent` — text on accent-colored surfaces
*
* Prefer built-in components (`Card`, `Button`, `Text`, etc.) over raw
* token usage. Reach for tokens only when no component covers the case,
* and stick to flat solid colors — no gradients, no box-shadows.
*
* @example
* ```tsx
* function Overview() {
* const theme = useHostTheme();
* return (
* <div style={{ background: theme.fill.tertiary, color: theme.text.secondary, padding: 8 }}>
* <span style={{ color: theme.accent.primary }}>Accent text</span>
* </div>
* );
* }
* ```
*/
export declare function useHostTheme(): CanvasHostTheme;
/**
* Setter for `useCanvasState`. Accepts either a new value or an updater
* function that receives the previous value.
*/
export type SetCanvasState<T> = (action: T | ((prev: T) => T)) => void;
/**
* Persistent state hook for canvas applications. Works like `React.useState`
* but the value survives rebuilds, reloads, and IDE restarts — it is stored
* in a `.canvas.data.json` sidecar file next to the canvas source.
*
* Each key is a unique string chosen by the canvas author. Keys are stable
* regardless of hook call order or component tree structure.
*
* @param key - Unique string identifier for this piece of state.
* @param defaultValue - Value returned when no persisted value exists for `key`.
* @returns A `[value, setValue]` tuple, same shape as `React.useState`.
*
* @example
* ```tsx
* function Counter() {
* const [count, setCount] = useCanvasState("count", 0);
* return <Button onClick={() => setCount(c => c + 1)}>{count}</Button>;
* }
* ```
*
* @example
* ```tsx
* interface Column { id: string; title: string; cardIds: string[] }
* function KanbanBoard() {
* const [columns, setColumns] = useCanvasState<Column[]>("columns", [
* { id: "todo", title: "To Do", cardIds: [] },
* { id: "doing", title: "In Progress", cardIds: [] },
* { id: "done", title: "Done", cardIds: [] },
* ]);
* // ...
* }
* ```
*/
export declare function useCanvasState<T>(key: string, defaultValue: T): [T, SetCanvasState<T>];
export type { CanvasAction };
/**
* Returns a stable `dispatch` function for triggering IDE actions from
* canvas buttons. Actions are fire-and-forget — the canvas does not
* receive a response.
*
* ## Available actions
*
* **`openAgent`** — Navigate the IDE to an agent conversation.
* `agentId` is the conversation UUID (the filename stem from the
* `agent-transcripts/` directory). Works for both local and
* cloud/background agents in Glass and classic IDE modes.
*
* @example
* ```tsx
* function AgentLink({ agentId, title }: { agentId: string; title: string }) {
* const dispatch = useCanvasAction();
* return (
* <Button onClick={() => dispatch({ type: "openAgent", agentId })}>
* {title}
* </Button>
* );
* }
* ```
*/
export declare function useCanvasAction(): (action: CanvasAction) => void;
//# sourceMappingURL=hooks.d.ts.map

View File

@@ -1,59 +0,0 @@
/**
* Public API for authoring `.canvas.tsx` files via `cursor/canvas`.
*
* Be creative with layout — use Grid, Row, cards, charts, tables, and raw SVG
* in whatever combination serves the content. Read the canvas skill for full
* design guidance. Key constraints:
*
* - Colors from `useHostTheme()` tokens. No hardcoded hex.
* - No gradients, no box-shadows, no emojis as decoration.
* - Don't wrap every section in Card — mix open sections with cards.
* - Run the pre-delivery self-check before returning code.
*/
/** Shared category color palette used by `Swatch`, `UsageBar`, etc. */
export type { Color } from "./canvas-tokens.js";
export { colorPalette, usageColorSequence } from "./canvas-tokens.js";
/** Charts. */
export type { BarChartProps, ChartDataPoint, ChartSeries, ChartTone, LineChartProps, PieChartProps, } from "./chart-primitives.js";
export { BarChart, LineChart, PieChart } from "./chart-primitives.js";
/** Borderless collapsible disclosure row with a structured header. */
export type { CollapsibleSectionProps } from "./collapsible-section.js";
export { CollapsibleSection } from "./collapsible-section.js";
/** DAG layout. */
export type { DAGLayoutEdge, DAGLayoutNode, DAGLayoutOptions, DAGLayoutRank, DAGLayoutResult, } from "./dag-layout.js";
export { computeDAGLayout } from "./dag-layout.js";
/**
* Diff rendering. Compose with the generic `Card` family for file-level
* chrome: use `DiffView` inside a `CardBody` (with `padding: 0`) and put
* `DiffStats` in the enclosing `CardHeader`'s `trailing` slot.
*/
export type { DiffLineData, DiffLineType, DiffStatsProps, DiffViewProps, } from "./diff-view.js";
export { DiffStats, DiffView } from "./diff-view.js";
/** Form controls. */
export type { CheckboxProps, IconButtonProps, SelectOption, SelectProps, TextAreaProps, TextInputProps, ToggleProps, } from "./form-primitives.js";
export { Checkbox, IconButton, Select, TextArea, TextInput, Toggle, } from "./form-primitives.js";
/** Host state hooks. */
export type { CanvasAction, CanvasHostTheme, SetCanvasState } from "./hooks.js";
export { useCanvasAction, useCanvasState, useHostTheme } from "./hooks.js";
/** Colored category swatch (uses the shared `Color` palette). */
export type { SwatchProps } from "./swatch.js";
export { Swatch } from "./swatch.js";
/** Semantic design tokens for custom styling. */
export type { CanvasPalette, CanvasTokens } from "./theme.js";
export { canvasPaletteDark, canvasPaletteLight, canvasTokens, canvasTokensLight, } from "./theme.js";
export type { TodoItem, TodoListCardProps, TodoListProps, TodoStatus, } from "./todo-list.js";
export { TodoList, TodoListCard } from "./todo-list.js";
/** Component props types. */
export type { ButtonProps, CalloutProps, CalloutTone, CardBodyProps, CardHeaderProps, CardProps, CardSize, CardVariant, CodeProps, DividerProps, GridProps, H1Props, H2Props, H3Props, LinkProps, PillProps, PillSize, PillTone, RowProps, StackProps, StatProps, StatTone, TableColumnAlign, TableProps, TableRowTone, TextProps, TextWeight, } from "./ui-primitives.js";
/** Layout. */
/** Typography. */
/** Surfaces. */
/** Actions. */
/** Feedback. */
export { Button, Callout, Card, CardBody, CardHeader, Code, Divider, Grid, H1, H2, H3, Link,
/** Shallow-merge two style objects. Useful for combining tokens with overrides. */
mergeStyle, Pill, Row, Spacer, Stack, Stat, Table, Text, } from "./ui-primitives.js";
/** Usage bar — segmented progress meter with optional labels above. */
export type { UsageBarProps, UsageBarSegment } from "./usage-bar.js";
export { UsageBar } from "./usage-bar.js";
//# sourceMappingURL=index.d.ts.map

View File

@@ -1,40 +0,0 @@
/**
* Colored category swatch — a small filled rounded box. Intended for inline
* list/row decoration (category badges, the leading slot of
* `CollapsibleSection`, etc.).
*
* Pulls colors from the shared `Color` palette so a category's swatch and
* its `UsageBar` segment for the same `color` stay visually coherent.
*/
import type { CSSProperties, JSX } from "react";
import { type Color } from "./canvas-tokens.js";
export type SwatchProps = {
/** One of the 7 shared category hues. Matches `UsageBar` segment colors. */
color: Color;
style?: CSSProperties;
};
/**
* Filled, rounded category swatch (24px). Use as the leading visual on
* category rows, list items, or as the `leading` slot of a
* `CollapsibleSection`.
*
* Colors come from the shared 7-hue `Color` palette, so a category's swatch
* matches its `UsageBar` segment for the same `color`.
*
* @example
* ```tsx
* // Standalone — purple "tools" swatch
* <Swatch color="purple" />
*
* // As the `leading` slot on a CollapsibleSection
* <CollapsibleSection
* title="Tools"
* count={4}
* leading={<Swatch color="purple" />}
* >
* <Text>Tool calls go here.</Text>
* </CollapsibleSection>
* ```
*/
export declare function Swatch({ color, style }: SwatchProps): JSX.Element;
//# sourceMappingURL=swatch.d.ts.map

View File

@@ -1,61 +0,0 @@
export type { CanvasPalette, CanvasTokens } from "./canvas-tokens.js";
export { canvasPaletteDark, canvasPaletteLight, canvasTokens, canvasTokensLight, } from "./canvas-tokens.js";
/** Typography presets used by the built-in `cursor/canvas` components. */
export declare const canvasTypography: {
readonly h1: {
readonly fontSize: "24px";
readonly lineHeight: "30px";
readonly fontWeight: 590;
};
readonly h2: {
readonly fontSize: "18px";
readonly lineHeight: "24px";
readonly fontWeight: 590;
};
readonly h3: {
readonly fontSize: "16px";
readonly lineHeight: "22px";
readonly fontWeight: 590;
};
readonly body: {
readonly fontSize: "14px";
readonly lineHeight: "20px";
readonly fontWeight: 400;
};
readonly small: {
readonly fontSize: "12px";
readonly lineHeight: "16px";
readonly fontWeight: 400;
};
};
/** Spacing scale (px). */
export declare const canvasSpacing: {
readonly "0.5": 2;
readonly "1": 4;
readonly "1.5": 6;
readonly "2": 8;
readonly "2.5": 10;
readonly "3": 12;
readonly "3.5": 14;
readonly "4": 16;
readonly "4.5": 18;
readonly "5": 20;
readonly "6": 24;
readonly "7": 28;
readonly "8": 32;
readonly "9": 36;
readonly "10": 40;
};
export type CanvasSpacing = typeof canvasSpacing;
/** Border radius (px). */
export declare const canvasRadius: {
readonly none: 0;
readonly xs: 2;
readonly sm: 4;
readonly md: 6;
readonly lg: 8;
readonly xl: 12;
readonly full: 9999;
};
export type CanvasRadius = typeof canvasRadius;
//# sourceMappingURL=theme.d.ts.map

View File

@@ -1,49 +0,0 @@
import type { CSSProperties, JSX } from "react";
export type TodoStatus = "pending" | "in_progress" | "completed" | "cancelled";
export interface TodoItem {
readonly id: string;
readonly content: string;
readonly status: TodoStatus;
}
export type TodoListProps = {
todos: readonly TodoItem[];
dimmedTodoIds?: ReadonlySet<string>;
/** Called when a todo row is clicked (entire row is a button). */
onTodoClick?: (todo: TodoItem) => void;
style?: CSSProperties;
};
/**
* Task list with status icons and wrapping text. Each row is a **clickable**
* button; use `onTodoClick` to handle selection or navigation.
*
* @example
* ```tsx
* <TodoList
* todos={items}
* onTodoClick={(todo) => setActiveId(todo.id)}
* />
* ```
*/
export declare function TodoList({ todos, dimmedTodoIds, onTodoClick, style }: TodoListProps): JSX.Element | null;
export type TodoListCardProps = {
todos: readonly TodoItem[];
dimmedTodoIds?: ReadonlySet<string>;
defaultExpanded?: boolean;
onTodoClick?: (todo: TodoItem) => void;
style?: CSSProperties;
};
/**
* Bordered, collapsible todo list with summary header (N of M Done). Compose
* with `onTodoClick` for row actions.
*
* @example
* ```tsx
* <TodoListCard
* todos={items}
* defaultExpanded
* onTodoClick={(todo) => console.log(todo.id)}
* />
* ```
*/
export declare function TodoListCard({ todos, dimmedTodoIds, defaultExpanded, onTodoClick, style }: TodoListCardProps): JSX.Element | null;
//# sourceMappingURL=todo-list.d.ts.map

View File

@@ -1,549 +0,0 @@
/**
* UI primitives for `cursor/canvas`. Styling follows the Cursor dark theme; no extra packages required.
*/
import { type CSSProperties, type JSX, type ReactNode } from "react";
/**
* Shallow-merge style objects with `override` taking precedence.
*
* Use for small tweaks on built-in components (e.g. extra padding or width).
* **Do not** use this to build elaborate custom chrome — prefer the built-in
* components and flat solid token colors. No gradients, no box-shadows.
*
* @example
* ```tsx
* // Good — minor override on a built-in component
* <CardBody style={mergeStyle({ padding: 16 })}>…</CardBody>
*
* // Bad — hand-rolled decorative styling
* <div style={mergeStyle(base, { background: "linear-gradient(…)" })}>…</div>
* ```
*/
export declare function mergeStyle(base: CSSProperties, override?: CSSProperties): CSSProperties;
export type StackProps = {
children?: ReactNode;
gap?: number;
style?: CSSProperties;
};
/**
* Vertical flex column. Use as the top-level page wrapper or to stack cards/sections.
*
* @example
* ```tsx
* <Stack gap={16}>
* <H1>Dashboard</H1>
* <Card>…</Card>
* <Card>…</Card>
* </Stack>
* ```
*/
export declare function Stack({ children, gap, style }: StackProps): JSX.Element;
export type RowProps = {
children?: ReactNode;
gap?: number;
align?: "start" | "center" | "end" | "stretch";
justify?: "start" | "center" | "end" | "space-between";
wrap?: boolean;
style?: CSSProperties;
};
/**
* Horizontal flex row. Use for inline groups of buttons, badges, or metadata.
*
* @example
* ```tsx
* <Row gap={8} align="center">
* <Button variant="primary">Save</Button>
* <Button variant="ghost">Cancel</Button>
* </Row>
* ```
*/
export declare function Row({ children, gap, align, justify, wrap, style }: RowProps): JSX.Element;
/**
* CSS Grid with tokenized gap. Prefer this over `Row` + `wrap` when you need a
* fixed number of equal-width columns: wrapped flex items can land on their
* own row and grow to full width (`flex-grow`), which is often surprising for
* boards and dashboards.
*/
export type GridProps = {
children?: ReactNode;
/**
* Equal columns: pass a number (uses `repeat(n, minmax(0, 1fr))`), or a CSS
* `grid-template-columns` string (e.g. `"1fr 2fr"` or `"minmax(0, 200px) 1fr"`).
*/
columns: number | string;
gap?: number;
align?: "start" | "center" | "end" | "stretch";
style?: CSSProperties;
};
export declare function Grid({ children, columns, gap, align, style }: GridProps): JSX.Element;
export type DividerProps = {
style?: CSSProperties;
};
/**
* Horizontal line for visually separating sections. Uses `stroke.tertiary`
* to match the Card/Table hairline weight.
*
* @example
* ```tsx
* <Stack>
* <Text>Section one</Text>
* <Divider />
* <Text>Section two</Text>
* </Stack>
* ```
*/
export declare function Divider({ style }: DividerProps): JSX.Element;
/**
* Flex spacer that pushes siblings apart. Place inside a `Row` to push
* trailing content to the right edge.
*
* @example
* ```tsx
* <Row>
* <Text>Title</Text>
* <Spacer />
* <Button variant="primary">Save</Button>
* </Row>
* ```
*/
export declare function Spacer(): JSX.Element;
/** Horizontal alignment for a table column. */
export type TableColumnAlign = "left" | "center" | "right";
/** Semantic tone for a table row — renders a translucent tinted background. */
export type TableRowTone = "success" | "danger" | "warning" | "info" | "neutral";
export type TableProps = {
/** Column titles, left to right. Column count is fixed by this array. */
headers: ReactNode[];
/**
* Body rows. Each row is an array of cells in the same order as `headers`.
* Shorter rows are padded with empty cells; extra cells are ignored.
*/
rows: ReactNode[][];
/** Optional alignment per column index (headers/rows). Defaults to left. */
columnAlign?: Array<TableColumnAlign | undefined>;
/**
* Optional semantic tone per row index. Applies a translucent tinted
* background — use for status highlighting (e.g. failing services, warnings).
* Sparse: `undefined` entries are uncolored.
*/
rowTone?: Array<TableRowTone | undefined>;
/** When true (default), bordered rounded shell with horizontal scroll if needed. */
framed?: boolean;
/** Alternate subtle fill on even rows for easier scanning in large tables. */
striped?: boolean;
/** Stick the header row when the framed container scrolls vertically. */
stickyHeader?: boolean;
style?: CSSProperties;
/** Shown in a single spanning cell when `rows` is empty. */
emptyMessage?: ReactNode;
};
/**
* Data table with column headers and rows. Framed by default with its own
* bordered container — **do not wrap in a Card** unless the card itself is
* a named entity that happens to contain a table. Render directly under a
* heading in the normal case.
*
* @example
* ```tsx
* // Good — table directly under a heading
* <H2>Active services</H2>
* <Table
* headers={["Service", "Status", "RPS"]}
* rows={[
* ["api-gateway", "Steady", "3.2k"],
* ["workers", "Hot", "8.1k"],
* ]}
* columnAlign={["left", "left", "right"]}
* />
*
* // Allowed — table inside a named-entity card
* <Card>
* <CardHeader>billing-service</CardHeader>
* <CardBody><Table headers={…} rows={…} /></CardBody>
* </Card>
* ```
*/
export declare function Table({ headers, rows, columnAlign, rowTone, framed, striped, stickyHeader, style, emptyMessage }: TableProps): JSX.Element;
export type TextWeight = "normal" | "medium" | "semibold" | "bold";
export type TextProps = {
children?: ReactNode;
tone?: "primary" | "secondary" | "tertiary" | "quaternary";
size?: "body" | "small";
/**
* Element tag to render. Defaults to `"p"` for top-level body copy and
* automatically switches to `"span"` when nested inside another typography
* container so inline emphasis stays valid HTML.
*/
as?: "p" | "span";
/** Font weight. Default is `"normal"` (400). Use `"semibold"` or `"bold"` for emphasis. */
weight?: TextWeight;
/** Render as italic. */
italic?: boolean;
/**
* Truncate overflowing text with an ellipsis on a single line.
* - `true` / `"end"` — ellipsis at the end (default truncation).
* - `"start"` — ellipsis at the start. Useful for file paths where the
* filename matters more than the directory prefix.
*
* Requires the parent to have a bounded width (flex child with
* `minWidth: 0`, fixed width, etc.) — otherwise the text just expands
* and never overflows.
*/
truncate?: boolean | "start" | "end";
style?: CSSProperties;
};
/**
* Body text with tone, size, weight, and italic variants.
*
* Top-level `Text` renders a `<p>`. Nested `Text` automatically renders a
* `<span>` so inline emphasis like `<Text>Use <Text weight="semibold">this</Text></Text>`
* does not emit invalid nested paragraphs. Use `as` to override when needed.
*
* Compose with `<Code>` for inline code and `<Link>` for hyperlinks inside
* the text flow.
*
* @example
* ```tsx
* <Text>Primary body text.</Text>
* <Text weight="semibold">Important note.</Text>
* <Text italic tone="secondary">Supplementary remark.</Text>
* <Text>Run <Code>npm install</Code> to get started.</Text>
* <Text>See the <Link href="https://example.com">docs</Link> for details.</Text>
* ```
*/
export declare function Text({ children, tone, size, as, weight, italic, truncate, style }: TextProps): JSX.Element;
export type H1Props = {
children?: ReactNode;
style?: CSSProperties;
};
/**
* Page-level heading. Use once at the top of a canvas.
* **Do not** place inside `CardHeader` — card headers use their own label.
*
* @example
* ```tsx
* <Stack>
* <H1>Performance Report</H1>
* <Card>…</Card>
* </Stack>
* ```
*/
export declare function H1({ children, style }: H1Props): JSX.Element;
export type H2Props = {
children?: ReactNode;
style?: CSSProperties;
};
/**
* Section heading. Use between groups of cards or sections.
* **Do not** place inside `CardHeader` — card headers use their own label.
*
* @example
* ```tsx
* <Stack>
* <H2>Recent activity</H2>
* <Card>…</Card>
* <Card>…</Card>
* </Stack>
* ```
*/
export declare function H2({ children, style }: H2Props): JSX.Element;
export type H3Props = {
children?: ReactNode;
style?: CSSProperties;
};
/**
* Sub-section heading. Use below `H2` for finer hierarchy.
*
* @example
* ```tsx
* <Stack>
* <H2>API Reference</H2>
* <H3>Authentication</H3>
* <Text>All requests require a bearer token.</Text>
* </Stack>
* ```
*/
export declare function H3({ children, style }: H3Props): JSX.Element;
export type CodeProps = {
children?: ReactNode;
style?: CSSProperties;
};
/**
* Inline `<code>` span for identifiers, file names, or short snippets.
* Uses `0.92em` so it scales with surrounding text (headings, body, etc.).
*
* Prefer writing backtick markdown inside `Text` — e.g. `` <Text>Run `npm install`</Text> `` —
* which is automatically parsed. Use `<Code>` only when you need an explicit element.
*
* @example
* ```tsx
* <Text>Run <Code>npm install</Code> to get started.</Text>
* ```
*/
export declare function Code({ children, style }: CodeProps): JSX.Element;
export type LinkProps = {
children?: ReactNode;
href: string;
style?: CSSProperties;
};
/**
* Inline link that opens in the user's default browser.
*
* Prefer writing markdown links inside `Text` — e.g. `<Text>See the [docs](url)</Text>` —
* which are automatically parsed. Use `<Link>` when you need an explicit anchor
* outside of a text flow or when composing with other elements.
*
* @example
* ```tsx
* <Link href="https://docs.example.com">View documentation</Link>
* ```
*/
export declare function Link({ children, href, style }: LinkProps): JSX.Element;
export type CardSize = "base" | "lg";
export type CardVariant = "default" | "borderless";
/**
* Inline chevron SVG used by disclosure-style controls (collapsible cards,
* expandable list items, etc.). Shared by `Card` and `todo-list.tsx` so
* every disclosure in the canvas SDK uses the same glyph.
*/
export declare function CanvasChevron({ expanded }: {
expanded: boolean;
}): JSX.Element;
export type CardProps = {
children?: ReactNode;
/** Default: bordered surface with radius; `borderless` removes both. */
variant?: CardVariant;
/** `lg` uses a taller header and roomier title padding (matches packages/ui). */
size?: CardSize;
/**
* When true, the header uses `position: sticky` so it stays visible while
* the card body scrolls. Requires the card (or a parent) to have a
* constrained height and `overflow: auto` — the canvas host controls this,
* so sticky behavior depends on the host viewport.
*/
stickyHeader?: boolean;
/**
* Make the card collapsible. The header becomes a clickable toggle with
* a leading chevron; `CardBody` renders nothing while the card is closed.
*/
collapsible?: boolean;
/** Initial open state in uncontrolled mode. Ignored when `open` is set. */
defaultOpen?: boolean;
/** Controlled open state. Pair with `onOpenChange`. */
open?: boolean;
/** Fires on every toggle with the next open state. */
onOpenChange?: (open: boolean) => void;
style?: CSSProperties;
};
/**
* Bordered surface for a **labeled, self-contained unit** — a file, a service,
* a config block, or a table with a title. Compose with `CardHeader` + `CardBody`.
*
* **When to use Card:**
* - Displaying a named entity (file path, service name, resource).
* - Wrapping a `<Table>` or `<DiffView>` that needs a title.
* - A distinct, bounded section the user might scan by header label.
*
* **When NOT to use Card:**
* - General text sections — use `<H2>` + `<Text>` instead. Not every section
* needs a border.
* - Page-level layout — use `<Stack>` with headings. A canvas should not be a
* wall of stacked cards.
* - Nesting — do not put cards inside cards. Use `<Divider>` within a card body.
*
* Pass **plain text** as `CardHeader` children — the header provides its own
* 12px font. Do **not** put `<H1>` or `<H2>` inside a card header.
*
* Set `collapsible` to make the header a toggle that shows/hides `CardBody`.
*
* @example
* ```tsx
* // Card wraps a titled diff
* <Card>
* <CardHeader trailing={<DiffStats additions={5} deletions={2} />}>
* src/utils.ts
* </CardHeader>
* <CardBody style={{ padding: 0 }}>
* <DiffView path="src/utils.ts" lines={lines} />
* </CardBody>
* </Card>
*
* // Collapsible card
* <Card collapsible defaultOpen={false}>
* <CardHeader>deploy-service.ts</CardHeader>
* <CardBody>Service handles rolling deployments across regions.</CardBody>
* </Card>
*
* // Bad — card wrapping plain text that should just be a heading
* // Use <H2>Overview</H2><Text>…</Text> instead.
* ```
*/
export declare function Card({ children, variant, size, stickyHeader, collapsible, defaultOpen, open: openProp, onOpenChange, style }: CardProps): JSX.Element;
export type CardHeaderProps = {
/** Plain text title. Do **not** pass headings, buttons, pills, or layout rows. */
children?: ReactNode;
/** Small trailing content aligned to the right edge — a status label, a
* single pill, or a short metadata string. Keep it compact. */
trailing?: ReactNode;
style?: CSSProperties;
};
/**
* 28px header row (32px at `size="lg"`). A compact label for the card.
*
* **`children`** — plain text only. This is a 12px label, not a toolbar.
* Do **not** pass `<H1>`, `<H2>`, `<Text weight="bold">`, `<Pill>`,
* `<Button>`, `<Row>`, or any interactive/layout elements as children.
*
* **`trailing`** — optional right-aligned slot for small status indicators
* (a short label, a single `<Pill>`, a metadata string like a timestamp).
*
* @example
* ```tsx
* // Good — plain title
* <CardHeader>config.yaml</CardHeader>
*
* // Good — title with trailing status
* <CardHeader trailing={<Pill active>Healthy</Pill>}>
* billing-service
* </CardHeader>
*
* // Bad — heading in header (use CardHeader text, not H2)
* // Bad — buttons in header (no room, wrong context)
* // Bad — multiple pills in header (use trailing for one, or move to CardBody)
* ```
*/
export declare function CardHeader({ children, trailing, style }: CardHeaderProps): JSX.Element;
export type CardBodyProps = {
children?: ReactNode;
style?: CSSProperties;
};
/**
* Padded content area inside a Card.
* Override `style` to adjust padding for custom layouts.
*
* @example
* ```tsx
* <Card>
* <CardHeader>Overview</CardHeader>
* <CardBody>This service is currently healthy.</CardBody>
* </Card>
* ```
*/
export declare function CardBody({ children, style }: CardBodyProps): JSX.Element | null;
export type ButtonProps = {
children?: ReactNode;
variant?: "primary" | "secondary" | "ghost";
disabled?: boolean;
type?: "button" | "submit" | "reset";
style?: CSSProperties;
onClick?: () => void;
};
/**
* Action button (24px height, sized to its label). **Never stretch to full
* width** — buttons are always inline and hug their text.
*
* @example
* ```tsx
* <Row gap={8}>
* <Button variant="primary" onClick={handleSave}>Save</Button>
* <Button variant="secondary">Export</Button>
* <Button variant="ghost">Cancel</Button>
* </Row>
* ```
*/
export declare function Button({ children, variant, disabled, type, style, onClick }: ButtonProps): JSX.Element;
export type PillTone = "neutral" | "added" | "deleted" | "renamed" | "success" | "warning" | "info";
export type PillSize = "sm" | "md";
export type PillProps = {
children?: ReactNode;
/** Whether the pill is in its selected/active state (filled background). */
active?: boolean;
/**
* Semantic tone. Recolors the border and text. When `active` is also
* set, fills the background with the tone color at low opacity.
* Defaults to `neutral` (current stroke/text tokens).
*/
tone?: PillTone;
/**
* Visual size. `"md"` (default) is the standard pill. `"sm"` is a
* compact variant with smaller text, tighter padding, and no border —
* designed for tight spaces like `CardHeader` trailing slots.
*/
size?: PillSize;
/** Shown before the label (icon, emoji, etc.). */
leadingContent?: ReactNode;
/** e.g. shortcut hint — matches ui `Pill` ghost keyboard hint (muted primary). */
keyboardHint?: string;
disabled?: boolean;
title?: string;
style?: CSSProperties;
onClick?: () => void;
};
/**
* Pill-shaped label or toggle button. Use for tab bars, filter groups, or
* action suggestions. Set `active` for the selected state (filled background).
*
* @example
* ```tsx
* // Tab-style selector
* <Row gap={8}>
* {tabs.map(tab => (
* <Pill key={tab} active={tab === selected} onClick={() => setSelected(tab)}>
* {tab}
* </Pill>
* ))}
* </Row>
*
* // Action suggestion with shortcut hint
* <Pill onClick={handlePlan} keyboardHint="⇧Tab">Plan new idea</Pill>
* ```
*/
export declare function Pill({ children, active, tone, size, leadingContent, keyboardHint, disabled, title, style, onClick }: PillProps): JSX.Element;
export type StatTone = "success" | "danger" | "warning" | "info";
export type StatProps = {
/** The primary metric value (number, percentage, short string). */
value: ReactNode;
/** Label below the value. */
label: string;
/** Semantic color for the value. Omit for default primary text. */
tone?: StatTone;
style?: CSSProperties;
};
/**
* Single metric display — a large value with a compact label beneath it.
* Use inside `<Grid>` for dashboard summary strips.
*
* @example
* ```tsx
* <Grid columns={3} gap={16}>
* <Stat value="4" label="Healthy" tone="success" />
* <Stat value="1" label="Degraded" tone="warning" />
* <Stat value="99.2%" label="Avg Uptime" />
* </Grid>
* ```
*/
export declare function Stat({ value, label, tone, style }: StatProps): JSX.Element;
export type CalloutTone = "info" | "success" | "warning" | "danger" | "neutral";
export type CalloutProps = {
/** Body content. Plain strings, `<Text>`, `<Code>`, `<Link>`, or short lists. */
children?: ReactNode;
/** Semantic tone. Recolors the border, background tint, and title text. */
tone?: CalloutTone;
/** Optional bold title line, shown above the body in the tone color. */
title?: ReactNode;
/** Optional leading icon (emoji, inline SVG, or short text glyph). */
icon?: ReactNode;
style?: CSSProperties;
};
/**
* Tinted, bordered notice block for warnings, tips, or short status messages
* inline within a section.
*
* @example
* ```tsx
* <Callout tone="warning" title="Heads up">
* Rolling deploy is in progress. Metrics may be noisy for 10 minutes.
* </Callout>
* ```
*/
export declare function Callout({ children, tone, title, icon, style }: CalloutProps): JSX.Element;
//# sourceMappingURL=ui-primitives.d.ts.map

View File

@@ -1,2 +0,0 @@
export {};
//# sourceMappingURL=ui-primitives.test.d.ts.map

View File

@@ -1,56 +0,0 @@
/**
* Segmented usage bar primitive — proportional pills + remainder, with an
* optional one-line label row above. Visually matches `packages/ui`
* `ContextUsageTray`'s usage bar, themed for canvas via the shared `Color`
* palette and `useHostTheme()` semantic tokens.
*/
import type { CSSProperties, JSX, ReactNode } from "react";
import { type Color } from "./canvas-tokens.js";
export interface UsageBarSegment {
/** Stable identifier (used as React key). */
readonly id: string;
/** Proportional weight for this segment. Non-finite or `<= 0` is treated as 0. */
readonly value: number;
/**
* Optional explicit color. Defaults to a rotation through
* `usageColorSequence` by index, matching the original `packages/ui`
* `ContextUsageTray` order so the same index lands on the same hue.
*/
readonly color?: Color;
}
export type UsageBarProps = {
/** Segments rendered left-to-right; widths are proportional to `value`. */
readonly segments: readonly UsageBarSegment[];
/** Total weight of the bar. The remainder span fills `max(0, total - sum(values))`. */
readonly total: number;
/** Optional small label rendered above the bar, left-aligned. */
readonly topLeftLabel?: ReactNode;
/** Optional small label rendered above the bar, right-aligned. */
readonly topRightLabel?: ReactNode;
readonly style?: CSSProperties;
};
/**
* Segmented horizontal usage bar — proportional category pills with a
* remainder span. Use to visualize a fixed-budget breakdown (context window
* tokens, storage usage, etc.).
*
* @example
* ```tsx
* <UsageBar
* total={120_000}
* topLeftLabel="64% Full"
* topRightLabel="76.8K / 120K Tokens"
* segments={[
* { id: "system", value: 8_000, color: "gray" },
* { id: "tools", value: 24_000, color: "purple" },
* { id: "rules", value: 12_000, color: "green" },
* { id: "skills", value: 6_000, color: "yellow" },
* { id: "mcp", value: 4_000, color: "pink" },
* { id: "subagents", value: 8_800, color: "blue" },
* { id: "conversation", value: 14_000, color: "orange" },
* ]}
* />
* ```
*/
export declare function UsageBar({ segments, total, topLeftLabel, topRightLabel, style }: UsageBarProps): JSX.Element;
//# sourceMappingURL=usage-bar.d.ts.map

View File

@@ -1,239 +0,0 @@
---
name: create-hook
description: >-
Create Cursor hooks. Use when you want to create a hook, write hooks.json, add
hook scripts, or automate behavior around agent events.
---
# Creating Cursor Hooks
Create hooks when you want Cursor to run custom logic before or after agent events. Hooks are scripts or prompt-based checks that exchange JSON over stdin/stdout and can observe, block, modify, or follow up on behavior.
When the user asks for a hook, don't stop at describing the format. Gather the missing requirements, then create or update the hook files directly.
## Gather Requirements
Before you write anything, determine:
1. **Scope**: Should this be a project hook or a user hook?
2. **Trigger**: Which event should run the hook?
3. **Behavior**: Should it audit, deny/allow, rewrite input, inject context, or continue a workflow?
4. **Implementation**: Should it be a command hook (script) or a prompt hook?
5. **Filtering**: Does it need a matcher so it only runs for certain tools, commands, or subagent types?
6. **Safety**: Should failures fail open or fail closed?
Infer these from the conversation when possible. Only ask for the missing pieces.
## Choose the Right Location
- **Project hooks**: `.cursor/hooks.json` and `.cursor/hooks/*`
- **User hooks**: `~/.cursor/hooks.json` and `~/.cursor/hooks/*`
Path behavior matters:
- **Project hooks** run from the project root, so use paths like `.cursor/hooks/my-hook.sh`
- **User hooks** run from `~/.cursor/`, so use paths like `./hooks/my-hook.sh` or `hooks/my-hook.sh`
Prefer **project hooks** when the behavior should be shared with the repository and checked into version control.
## Choose the Hook Event
Use the narrowest event that matches the user's goal.
### Common Agent events
- `sessionStart`, `sessionEnd`: set up or audit a session
- `preToolUse`, `postToolUse`, `postToolUseFailure`: work across all tools
- `subagentStart`, `subagentStop`: control or continue Task/subagent workflows
- `beforeShellExecution`, `afterShellExecution`: gate or audit terminal commands
- `beforeMCPExecution`, `afterMCPExecution`: gate or audit MCP tool calls
- `beforeReadFile`, `afterFileEdit`: control file reads or post-process edits
- `beforeSubmitPrompt`: validate prompts before they are sent
- `preCompact`: observe context compaction
- `stop`: handle agent completion
- `afterAgentResponse`, `afterAgentThought`: track agent output or reasoning
### Tab events
- `beforeTabFileRead`: control file access for inline completions
- `afterTabFileEdit`: post-process edits made by Tab
### Quick event chooser
- **Block or approve shell commands** -> `beforeShellExecution`
- **Audit shell output** -> `afterShellExecution`
- **Format files after edits** -> `afterFileEdit`
- **Block or rewrite a specific tool call** -> `preToolUse`
- **Add follow-up context after a tool succeeds** -> `postToolUse`
- **Control whether subagents can run** -> `subagentStart`
- **Chain subagent loops** -> `subagentStop`
- **Check prompts for secrets or policy violations** -> `beforeSubmitPrompt`
- **Protect MCP calls** -> `beforeMCPExecution`
## Hooks File Format
Create a `hooks.json` file with schema version 1:
```json
{
"version": 1,
"hooks": {
"afterFileEdit": [
{
"command": ".cursor/hooks/format.sh"
}
]
}
}
```
Each hook definition can include:
- `command`: shell command or script path
- `type`: `"command"` or `"prompt"` (defaults to `"command"`)
- `timeout`: timeout in seconds
- `matcher`: filter for when the hook runs
- `failClosed`: block the action when the hook crashes, times out, or returns invalid JSON
- `loop_limit`: mainly for `stop` and `subagentStop` follow-up loops
## Matchers
Use matchers to avoid running the hook on every event.
- `preToolUse` / `postToolUse` / `postToolUseFailure`: match on tool type such as `Shell`, `Read`, `Write`, `Task`, or MCP tools in `MCP: ...` form
- `subagentStart` / `subagentStop`: match on subagent type such as `generalPurpose`, `explore`, or `shell`
- `beforeShellExecution` / `afterShellExecution`: match on the full shell command string
- `beforeReadFile`: match on tool type such as `Read` or `TabRead`
- `afterFileEdit`: match on tool type such as `Write` or `TabWrite`
- `beforeSubmitPrompt`: matches the value `UserPromptSubmit`
Important matcher warning:
- Matchers use JavaScript-style regular expressions, not POSIX/grep syntax
- Do not use POSIX classes like `[[:space:]]`; use JavaScript equivalents like `\s`
- If the matcher is at all tricky, start by getting the hook working without one or with a very simple matcher, then tighten it after the hook is confirmed to load and fire
If the user wants a hook for only one risky command family, prefer script-side filtering for the first working version and add a matcher afterward only if it is simple and clearly correct.
## Command Hooks
Command hooks are the default. They receive JSON on stdin and can return JSON on stdout.
Before using a command hook, verify that every executable it depends on will actually run in the hook environment:
- the script itself has a valid shebang and is executable
- any helper binary it calls is already installed and on `$PATH`
- if the script depends on tools like `jq`, `python3`, `node`, or repo-local CLIs, verify that explicitly before finishing
Do not assume a binary exists just because it is common on your machine.
### Minimal project-level example
```json
{
"version": 1,
"hooks": {
"beforeShellExecution": [
{
"command": ".cursor/hooks/approve-network.sh",
"matcher": "curl|wget|nc ",
"failClosed": true
}
]
}
}
```
```bash
#!/bin/bash
input=$(cat)
command=$(echo "$input" | jq -r '.command // empty')
if [[ "$command" =~ curl|wget|nc ]]; then
echo '{
"permission": "ask",
"user_message": "This command may make a network request. Please review it before continuing.",
"agent_message": "A hook flagged this shell command as a possible network call."
}'
exit 0
fi
echo '{ "permission": "allow" }'
exit 0
```
Important behavior:
- Exit code `0`: success
- Exit code `2`: block the action, same as returning deny
- Other non-zero exit codes: fail open by default unless `failClosed: true`
Always make hook scripts executable after creating them.
## Prompt Hooks
Prompt hooks are useful when the policy is easier to describe than to script.
```json
{
"version": 1,
"hooks": {
"beforeShellExecution": [
{
"type": "prompt",
"prompt": "Does this command look safe to execute? Only allow read-only operations. Here is the hook input: $ARGUMENTS",
"timeout": 10
}
]
}
}
```
Use prompt hooks for lightweight policy decisions. Prefer command hooks when the logic must be deterministic or when the user needs exact, auditable behavior.
## Event Output Cheat Sheet
Use the event's supported output fields only.
- `preToolUse`: can return `permission`, `user_message`, `agent_message`, and `updated_input`
- `postToolUse`: can return `additional_context`; for MCP tools it can also return `updated_mcp_tool_output`
- `subagentStart`: can return `permission` and `user_message`
- `subagentStop`: can return `followup_message`
- `beforeShellExecution` / `beforeMCPExecution`: can return `permission`, `user_message`, and `agent_message`
When the user wants to rewrite a tool call, prefer `preToolUse`. When they want to gate only shell commands, prefer `beforeShellExecution`.
## Implementation Workflow
1. Pick the correct location and event
2. Create or update the correct `hooks.json` file
3. Start with no matcher or the simplest safe matcher
4. Create the script under the matching hooks directory
5. Read stdin JSON and implement the required behavior
6. Make the script executable
7. Verify any helper executables the script uses are installed and on `$PATH`
8. Trigger the relevant action to test the hook
9. Verify behavior in Cursor's **Hooks** settings tab or the **Hooks** output channel
If you are editing an existing hooks setup, preserve unrelated hooks and only change the minimum necessary entries.
## Validation and Troubleshooting
- Cursor watches `hooks.json` and reloads on save
- If hooks still do not load, restart Cursor
- Double-check relative paths:
- project hooks -> relative to the project root
- user hooks -> relative to `~/.cursor/`
- If the hook does not appear to load at all, suspect matcher/config parsing first; remove the matcher and confirm the base hook works before tightening it
- If the script runs external commands, verify each one is installed and reachable from the hook process with `command -v` or equivalent
- If the hook should block on failure, set `failClosed: true`
- If a command hook should intentionally block, returning exit code `2` is valid
## Final Checklist
- [ ] Used the correct hook location and path style
- [ ] Chose the narrowest correct event
- [ ] Added a matcher when appropriate
- [ ] Returned only fields supported by that hook event
- [ ] Made the script executable
- [ ] Tested the hook by triggering the real event
- [ ] Checked the Hooks tab or Hooks output channel if debugging was needed

View File

@@ -1,164 +0,0 @@
---
name: create-rule
description: >-
Create Cursor rules for persistent AI guidance. Use when you want to create a
rule, add coding standards, set up project conventions, configure
file-specific patterns, create RULE.md files, or asks about .cursor/rules/ or
AGENTS.md.
---
# Creating Cursor Rules
Create project rules in `.cursor/rules/` to provide persistent context for the AI agent.
## Gather Requirements
Before creating a rule, determine:
1. **Purpose**: What should this rule enforce or teach?
2. **Scope**: Should it always apply, or only for specific files?
3. **File patterns**: If file-specific, which glob patterns?
### Inferring from Context
If you have previous conversation context, infer rules from what was discussed. You can create multiple rules if the conversation covers distinct topics or patterns. Don't ask redundant questions if the context already provides the answers.
### Required Questions
If the user hasn't specified scope, ask:
- "Should this rule always apply, or only when working with specific files?"
If they mentioned specific files and haven't provided concrete patterns, ask:
- "Which file patterns should this rule apply to?" (e.g., `**/*.ts`, `backend/**/*.py`)
It's very important that we get clarity on the file patterns.
Use the AskQuestion tool when available to gather this efficiently.
---
## Rule File Format
Rules are `.mdc` files in `.cursor/rules/` with YAML frontmatter:
```
.cursor/rules/
typescript-standards.mdc
react-patterns.mdc
api-conventions.mdc
```
### File Structure
```markdown
---
description: Brief description of what this rule does
globs: **/*.ts # File pattern for file-specific rules
alwaysApply: false # Set to true if rule should always apply
---
# Rule Title
Your rule content here...
```
### Frontmatter Fields
| Field | Type | Description |
|-------|------|-------------|
| `description` | string | What the rule does (shown in rule picker) |
| `globs` | string | File pattern - rule applies when matching files are open |
| `alwaysApply` | boolean | If true, applies to every session |
---
## Rule Configurations
### Always Apply
For universal standards that should apply to every conversation:
```yaml
---
description: Core coding standards for the project
alwaysApply: true
---
```
### Apply to Specific Files
For rules that apply when working with certain file types:
```yaml
---
description: TypeScript conventions for this project
globs: **/*.ts
alwaysApply: false
---
```
---
## Best Practices
### Keep Rules Concise
- **Under 50 lines**: Rules should be concise and to the point
- **One concern per rule**: Split large rules into focused pieces
- **Actionable**: Write like clear internal docs
- **Concrete examples**: Ideally provide concrete examples of how to fix issues
---
## Example Rules
### TypeScript Standards
```markdown
---
description: TypeScript coding standards
globs: **/*.ts
alwaysApply: false
---
# Error Handling
\`\`\`typescript
// ❌ BAD
try {
await fetchData();
} catch (e) {}
// ✅ GOOD
try {
await fetchData();
} catch (e) {
logger.error('Failed to fetch', { error: e });
throw new DataFetchError('Unable to retrieve data', { cause: e });
}
\`\`\`
```
### React Patterns
```markdown
---
description: React component patterns
globs: **/*.tsx
alwaysApply: false
---
# React Patterns
- Use functional components
- Extract custom hooks for reusable logic
- Colocate styles with components
```
---
## Checklist
- [ ] File is `.mdc` format in `.cursor/rules/`
- [ ] Frontmatter configured correctly
- [ ] Content under 500 lines
- [ ] Includes concrete examples

View File

@@ -1,504 +0,0 @@
---
name: create-skill
description: >-
Create Cursor Agent Skills. Use when authoring a new skill or asking about
SKILL.md structure.
---
# Creating Skills in Cursor
This skill guides you through creating effective Agent Skills for Cursor. Skills are markdown files that teach the agent how to perform specific tasks: reviewing PRs using team standards, generating commit messages in a preferred format, querying database schemas, or any specialized workflow.
## Before You Begin: Gather Requirements
Before creating a skill, gather essential information from the user about:
1. **Purpose and scope**: What specific task or workflow should this skill help with?
2. **Target location**: Should this be a personal skill (~/.cursor/skills/) or project skill (.cursor/skills/)?
3. **Trigger scenarios**: When should the agent automatically apply this skill?
4. **Key domain knowledge**: What specialized information does the agent need that it wouldn't already know?
5. **Output format preferences**: Are there specific templates, formats, or styles required?
6. **Existing patterns**: Are there existing examples or conventions to follow?
### Verbatim text from the user
If the user includes exact wording to use in the skill, respect it and use it **verbatim** in `SKILL.md` (same words, same order). Do not paraphrase, soften, or expand their copy, and do not add unrequested headings or commentary around it.
### Inferring from Context
If you have previous conversation context, infer the skill from what was discussed. You can create skills based on workflows, patterns, or domain knowledge that emerged in the conversation.
### Gathering Additional Information
If you need clarification, use the AskQuestion tool when available:
```
Example AskQuestion usage:
- "Where should this skill be stored?" with options like ["Personal (~/.cursor/skills/)", "Project (.cursor/skills/)"]
- "Should this skill include executable scripts?" with options like ["Yes", "No"]
```
If the AskQuestion tool is not available, ask these questions conversationally.
---
## Skill File Structure
### Directory Layout
Skills are stored as directories containing a `SKILL.md` file:
```
skill-name/
├── SKILL.md # Required - main instructions
├── reference.md # Optional - detailed documentation
├── examples.md # Optional - usage examples
└── scripts/ # Optional - utility scripts
├── validate.py
└── helper.sh
```
### Storage Locations
| Type | Path | Scope |
|------|------|-------|
| Personal | ~/.cursor/skills/skill-name/ | Available across all your projects |
| Project | .cursor/skills/skill-name/ | Shared with anyone using the repository |
**IMPORTANT**: Never create skills in `~/.cursor/skills-cursor/`. This directory is reserved for Cursor's internal built-in skills and is managed automatically by the system.
### SKILL.md Structure
Every skill requires a `SKILL.md` file with YAML frontmatter and markdown body:
```markdown
---
name: your-skill-name
description: Brief description of what this skill does and when to use it
disable-model-invocation: true
---
# Your Skill Name
## Instructions
Clear, step-by-step guidance for the agent.
## Examples
Concrete examples of using this skill.
```
Default `disable-model-invocation: true` so the skill only loads when named explicitly. Omit it only when the agent should auto-invoke from ambient context.
### Required Metadata Fields
| Field | Requirements | Purpose |
|-------|--------------|---------|
| `name` | Max 64 chars, lowercase letters/numbers/hyphens only | Unique identifier for the skill |
| `description` | Max 1024 chars, non-empty | Helps agent decide when to apply the skill |
---
## Writing Effective Descriptions
The description is **critical** for skill discovery. The agent uses it to decide when to apply your skill.
### Description Best Practices
1. **Write in third person** (the description is injected into the system prompt):
- ✅ Good: "Processes Excel files and generates reports"
- ❌ Avoid: "I can help you process Excel files"
- ❌ Avoid: "You can use this to process Excel files"
2. **Be specific and include trigger terms**:
- ✅ Good: "Extract text and tables from PDF files, fill forms, merge documents. Use when working with PDF files or when the user mentions PDFs, forms, or document extraction."
- ❌ Vague: "Helps with documents"
3. **Include both WHAT and WHEN**:
- WHAT: What the skill does (specific capabilities)
- WHEN: When the agent should use it (trigger scenarios)
### Description Examples
```yaml
# PDF Processing
description: Extract text and tables from PDF files, fill forms, merge documents. Use when working with PDF files or when the user mentions PDFs, forms, or document extraction.
# Excel Analysis
description: Analyze Excel spreadsheets, create pivot tables, generate charts. Use when analyzing Excel files, spreadsheets, tabular data, or .xlsx files.
# Git Commit Helper
description: Generate descriptive commit messages by analyzing git diffs. Use when the user asks for help writing commit messages or reviewing staged changes.
# Code Review
description: Review code for quality, security, and best practices following team standards. Use when reviewing pull requests, code changes, or when the user asks for a code review.
```
---
## Core Authoring Principles
### 1. Concise is Key
The context window is shared with conversation history, other skills, and requests. Every token competes for space.
**Default assumption**: The agent is already very smart. Only add context it doesn't already have.
Challenge each piece of information:
- "Does the agent really need this explanation?"
- "Can I assume the agent knows this?"
- "Does this paragraph justify its token cost?"
**Good (concise)**:
```markdown
## Extract PDF text
Use pdfplumber for text extraction:
\`\`\`python
import pdfplumber
with pdfplumber.open("file.pdf") as pdf:
text = pdf.pages[0].extract_text()
\`\`\`
```
**Bad (verbose)**:
```markdown
## Extract PDF text
PDF (Portable Document Format) files are a common file format that contains
text, images, and other content. To extract text from a PDF, you'll need to
use a library. There are many libraries available for PDF processing, but we
recommend pdfplumber because it's easy to use and handles most cases well...
```
### 2. Keep SKILL.md Under 500 Lines
For optimal performance, the main SKILL.md file should be concise. Use progressive disclosure for detailed content.
### 3. Progressive Disclosure
Put essential information in SKILL.md; detailed reference material in separate files that the agent reads only when needed.
```markdown
# PDF Processing
## Quick start
[Essential instructions here]
## Additional resources
- For complete API details, see [reference.md](reference.md)
- For usage examples, see [examples.md](examples.md)
```
**Keep references one level deep** - link directly from SKILL.md to reference files. Deeply nested references may result in partial reads.
### 4. Set Appropriate Degrees of Freedom
Match specificity to the task's fragility:
| Freedom Level | When to Use | Example |
|---------------|-------------|---------|
| **High** (text instructions) | Multiple valid approaches, context-dependent | Code review guidelines |
| **Medium** (pseudocode/templates) | Preferred pattern with acceptable variation | Report generation |
| **Low** (specific scripts) | Fragile operations, consistency critical | Database migrations |
---
## Common Patterns
### Template Pattern
Provide output format templates:
```markdown
## Report structure
Use this template:
\`\`\`markdown
# [Analysis Title]
## Executive summary
[One-paragraph overview of key findings]
## Key findings
- Finding 1 with supporting data
- Finding 2 with supporting data
## Recommendations
1. Specific actionable recommendation
2. Specific actionable recommendation
\`\`\`
```
### Examples Pattern
For skills where output quality depends on seeing examples:
```markdown
## Commit message format
**Example 1:**
Input: Added user authentication with JWT tokens
Output:
\`\`\`
feat(auth): implement JWT-based authentication
Add login endpoint and token validation middleware
\`\`\`
**Example 2:**
Input: Fixed bug where dates displayed incorrectly
Output:
\`\`\`
fix(reports): correct date formatting in timezone conversion
Use UTC timestamps consistently across report generation
\`\`\`
```
### Workflow Pattern
Break complex operations into clear steps with checklists:
```markdown
## Form filling workflow
Copy this checklist and track progress:
\`\`\`
Task Progress:
- [ ] Step 1: Analyze the form
- [ ] Step 2: Create field mapping
- [ ] Step 3: Validate mapping
- [ ] Step 4: Fill the form
- [ ] Step 5: Verify output
\`\`\`
**Step 1: Analyze the form**
Run: \`python scripts/analyze_form.py input.pdf\`
...
```
### Conditional Workflow Pattern
Guide through decision points:
```markdown
## Document modification workflow
1. Determine the modification type:
**Creating new content?** → Follow "Creation workflow" below
**Editing existing content?** → Follow "Editing workflow" below
2. Creation workflow:
- Use docx-js library
- Build document from scratch
...
```
### Feedback Loop Pattern
For quality-critical tasks, implement validation loops:
```markdown
## Document editing process
1. Make your edits
2. **Validate immediately**: \`python scripts/validate.py output/\`
3. If validation fails:
- Review the error message
- Fix the issues
- Run validation again
4. **Only proceed when validation passes**
```
---
## Utility Scripts
Pre-made scripts offer advantages over generated code:
- More reliable than generated code
- Save tokens (no code in context)
- Save time (no code generation)
- Ensure consistency across uses
```markdown
## Utility scripts
**analyze_form.py**: Extract all form fields from PDF
\`\`\`bash
python scripts/analyze_form.py input.pdf > fields.json
\`\`\`
**validate.py**: Check for errors
\`\`\`bash
python scripts/validate.py fields.json
# Returns: "OK" or lists conflicts
\`\`\`
```
Make clear whether the agent should **execute** the script (most common) or **read** it as reference.
---
## Anti-Patterns to Avoid
### 1. Windows-Style Paths
- ✅ Use: `scripts/helper.py`
- ❌ Avoid: `scripts\helper.py`
### 2. Too Many Options
```markdown
# Bad - confusing
"You can use pypdf, or pdfplumber, or PyMuPDF, or..."
# Good - provide a default with escape hatch
"Use pdfplumber for text extraction.
For scanned PDFs requiring OCR, use pdf2image with pytesseract instead."
```
### 3. Time-Sensitive Information
```markdown
# Bad - will become outdated
"If you're doing this before August 2025, use the old API."
# Good - use an "old patterns" section
## Current method
Use the v2 API endpoint.
## Old patterns (deprecated)
<details>
<summary>Legacy v1 API</summary>
...
</details>
```
### 4. Inconsistent Terminology
Choose one term and use it throughout:
- ✅ Always "API endpoint" (not mixing "URL", "route", "path")
- ✅ Always "field" (not mixing "box", "element", "control")
### 5. Vague Skill Names
- ✅ Good: `processing-pdfs`, `analyzing-spreadsheets`
- ❌ Avoid: `helper`, `utils`, `tools`
---
## Skill Creation Workflow
When helping a user create a skill, follow this process:
### Phase 1: Discovery
Gather information about:
1. The skill's purpose and primary use case
2. Storage location (personal vs project)
3. Trigger scenarios
4. Any specific requirements or constraints
5. Existing examples or patterns to follow
If you have access to the AskQuestion tool, use it for efficient structured gathering. Otherwise, ask conversationally.
### Phase 2: Design
1. Draft the skill name (lowercase, hyphens, max 64 chars)
2. Write a specific, third-person description
3. Outline the main sections needed
4. Identify if supporting files or scripts are needed
### Phase 3: Implementation
1. Create the directory structure
2. Write the SKILL.md file with frontmatter
3. Create any supporting reference files
4. Create any utility scripts if needed
### Phase 4: Verification
1. Verify the SKILL.md is under 500 lines
2. Check that the description is specific and includes trigger terms
3. Ensure consistent terminology throughout
4. Verify all file references are one level deep
5. Test that the skill can be discovered and applied
---
## Complete Example
Here's a complete example of a well-structured skill:
**Directory structure:**
```
code-review/
├── SKILL.md
├── STANDARDS.md
└── examples.md
```
**SKILL.md:**
```markdown
---
name: code-review
description: Review code for quality, security, and maintainability following team standards. Use when reviewing pull requests, examining code changes, or when the user asks for a code review.
---
# Code Review
## Quick Start
When reviewing code:
1. Check for correctness and potential bugs
2. Verify security best practices
3. Assess code readability and maintainability
4. Ensure tests are adequate
## Review Checklist
- [ ] Logic is correct and handles edge cases
- [ ] No security vulnerabilities (SQL injection, XSS, etc.)
- [ ] Code follows project style conventions
- [ ] Functions are appropriately sized and focused
- [ ] Error handling is comprehensive
- [ ] Tests cover the changes
## Providing Feedback
Format feedback as:
- 🔴 **Critical**: Must fix before merge
- 🟡 **Suggestion**: Consider improving
- 🟢 **Nice to have**: Optional enhancement
## Additional Resources
- For detailed coding standards, see [STANDARDS.md](STANDARDS.md)
- For example reviews, see [examples.md](examples.md)
```
---
## Summary Checklist
Before finalizing a skill, verify:
### Core Quality
- [ ] Description is specific and includes key terms
- [ ] Description includes both WHAT and WHEN
- [ ] Written in third person
- [ ] SKILL.md body is under 500 lines
- [ ] Consistent terminology throughout
- [ ] Examples are concrete, not abstract
### Structure
- [ ] File references are one level deep
- [ ] Progressive disclosure used appropriately
- [ ] Workflows have clear steps
- [ ] No time-sensitive information
### If Including Scripts
- [ ] Scripts solve problems rather than punt
- [ ] Required packages are documented
- [ ] Error handling is explicit and helpful
- [ ] No Windows-style paths

View File

@@ -1,225 +0,0 @@
---
name: create-subagent
description: >-
Create custom subagents for specialized AI tasks. Use when you want to create
a new type of subagent, set up task-specific agents, configure code reviewers,
debuggers, or domain-specific assistants with custom prompts.
disable-model-invocation: true
---
# Creating Custom Subagents
This skill guides you through creating custom subagents for Cursor. Subagents are specialized AI assistants that run in isolated contexts with custom system prompts.
## When to Use Subagents
Subagents help you:
- **Preserve context** by isolating exploration from your main conversation
- **Specialize behavior** with focused system prompts for specific domains
- **Reuse configurations** across projects with user-level subagents
### Inferring from Context
If you have previous conversation context, infer the subagent's purpose and behavior from what was discussed. Create the subagent based on specialized tasks or workflows that emerged in the conversation.
## Subagent Locations
| Location | Scope | Priority |
|----------|-------|----------|
| `.cursor/agents/` | Current project | Higher |
| `~/.cursor/agents/` | All your projects | Lower |
When multiple subagents share the same name, the higher-priority location wins.
**Project subagents** (`.cursor/agents/`): Ideal for codebase-specific agents. Check into version control to share with your team.
**User subagents** (`~/.cursor/agents/`): Personal agents available across all your projects.
## Subagent File Format
Create a `.md` file with YAML frontmatter and a markdown body (the system prompt):
```markdown
---
name: code-reviewer
description: Reviews code for quality and best practices
---
You are a code reviewer. When invoked, analyze the code and provide
specific, actionable feedback on quality, security, and best practices.
```
### Required Fields
| Field | Description |
|-------|-------------|
| `name` | Unique identifier (lowercase letters and hyphens only) |
| `description` | When to delegate to this subagent (be specific!) |
## Writing Effective Descriptions
The description is **critical** - the AI uses it to decide when to delegate.
```yaml
# ❌ Too vague
description: Helps with code
# ✅ Specific and actionable
description: Expert code review specialist. Proactively reviews code for quality, security, and maintainability. Use immediately after writing or modifying code.
```
Include "use proactively" to encourage automatic delegation.
## Example Subagents
### Code Reviewer
```markdown
---
name: code-reviewer
description: Expert code review specialist. Proactively reviews code for quality, security, and maintainability. Use immediately after writing or modifying code.
---
You are a senior code reviewer ensuring high standards of code quality and security.
When invoked:
1. Run git diff to see recent changes
2. Focus on modified files
3. Begin review immediately
Review checklist:
- Code is clear and readable
- Functions and variables are well-named
- No duplicated code
- Proper error handling
- No exposed secrets or API keys
- Input validation implemented
- Good test coverage
- Performance considerations addressed
Provide feedback organized by priority:
- Critical issues (must fix)
- Warnings (should fix)
- Suggestions (consider improving)
Include specific examples of how to fix issues.
```
### Debugger
```markdown
---
name: debugger
description: Debugging specialist for errors, test failures, and unexpected behavior. Use proactively when encountering any issues.
---
You are an expert debugger specializing in root cause analysis.
When invoked:
1. Capture error message and stack trace
2. Identify reproduction steps
3. Isolate the failure location
4. Implement minimal fix
5. Verify solution works
Debugging process:
- Analyze error messages and logs
- Check recent code changes
- Form and test hypotheses
- Add strategic debug logging
- Inspect variable states
For each issue, provide:
- Root cause explanation
- Evidence supporting the diagnosis
- Specific code fix
- Testing approach
- Prevention recommendations
Focus on fixing the underlying issue, not the symptoms.
```
### Data Scientist
```markdown
---
name: data-scientist
description: Data analysis expert for SQL queries, BigQuery operations, and data insights. Use proactively for data analysis tasks and queries.
---
You are a data scientist specializing in SQL and BigQuery analysis.
When invoked:
1. Understand the data analysis requirement
2. Write efficient SQL queries
3. Use BigQuery command line tools (bq) when appropriate
4. Analyze and summarize results
5. Present findings clearly
Key practices:
- Write optimized SQL queries with proper filters
- Use appropriate aggregations and joins
- Include comments explaining complex logic
- Format results for readability
- Provide data-driven recommendations
For each analysis:
- Explain the query approach
- Document any assumptions
- Highlight key findings
- Suggest next steps based on data
Always ensure queries are efficient and cost-effective.
```
## Subagent Creation Workflow
### Step 1: Decide the Scope
- **Project-level** (`.cursor/agents/`): For codebase-specific agents shared with team
- **User-level** (`~/.cursor/agents/`): For personal agents across all projects
### Step 2: Create the File
```bash
# For project-level
mkdir -p .cursor/agents
touch .cursor/agents/my-agent.md
# For user-level
mkdir -p ~/.cursor/agents
touch ~/.cursor/agents/my-agent.md
```
### Step 3: Define Configuration
Write the frontmatter with the required fields (`name` and `description`).
### Step 4: Write the System Prompt
The body becomes the system prompt. Be specific about:
- What the agent should do when invoked
- The workflow or process to follow
- Output format and structure
- Any constraints or guidelines
### Step 5: Test the Agent
Ask the AI to use your new agent:
```
Use the my-agent subagent to [task description]
```
## Best Practices
1. **Design focused subagents**: Each should excel at one specific task
2. **Write detailed descriptions**: Include trigger terms so the AI knows when to delegate
3. **Check into version control**: Share project subagents with your team
4. **Use proactive language**: Include "use proactively" in descriptions
## Troubleshooting
### Subagent Not Found
- Ensure file is in `.cursor/agents/` or `~/.cursor/agents/`
- Check file has `.md` extension
- Verify YAML frontmatter syntax is valid

View File

@@ -1,78 +0,0 @@
---
name: loop
description: >-
Run a prompt or skill on a recurring local interval using monitored background
shell output. Use for /loop, polling status, recurring fixed-interval tasks,
dynamic self-paced loops, local cron-like loops, or waking an agent
periodically. Do not use for one-off tasks.
disabled-environments:
- cloud
metadata:
disabledEnvironments:
- cloud
---
# Loop
Use monitored shell output when the goal is to wake the agent for recurring local work.
## Parse
Accept `/loop [interval] <prompt>`.
- Leading interval: `5m /foo`, `30s check status`, `2h run report`.
- Trailing interval: `check deploy every 5m`, `run tests every 10 minutes`.
- No interval: dynamic mode; the agent chooses the next delay after each run.
- Empty prompt: show `Usage: /loop [interval] <prompt>`.
Use intervals like `30s`, `5m`, `2h`, `1d`. Convert unit words to short units.
## Fixed Schedule
```bash
while true; do
sleep <seconds>
echo 'AGENT_LOOP_TICK_<purpose> {"prompt":"<prompt>"}'
done
```
1. Check existing terminals for an already-running matching loop.
2. Start one background shell loop with `notify_on_output`.
3. Use a unique sentinel and a regex such as `^AGENT_LOOP_TICK_<purpose>`.
4. Smoke-check once to confirm clean startup.
5. Run the prompt once immediately after arming the loop.
6. The first sentinel should arrive only after the initial sleep, so startup does not double-run the prompt.
7. Track the PID so the agent can stop the loop if asked.
8. Briefly confirm: the interval, that the prompt already ran once, when the first tick will arrive, and that the loop will fire on each tick until stopped. On later ticks, give a short update of what changed. On stop, say the loop has stopped and why.
## Dynamic Schedule
The user wants the agent to self-pace. Decide what makes the next iteration worth running — a passage of time, or an observable event.
1. **Run the prompt now.**
2. **If the next run is gated on an event** (a git ref advancing, a log line matching, a file changing, a CI check completing), arm a background watcher that emits the sentinel only when the event fires, with `notify_on_output` on `^AGENT_LOOP_WAKE_<purpose>`. Arm once; skip on later ticks if it's still running.
3. **At the end of the turn, arm a one-shot time-based wake**:
```bash
sleep <seconds>
echo 'AGENT_LOOP_WAKE_<purpose> {"prompt":"<prompt>"}'
```
With a watcher armed, this is the **fallback heartbeat** — lean long so idle ticks aren't pure overhead. Without a watcher, this is the cadence — pick a delay based on when the result is worth checking again.
4. **On wake**, read the latest payload, execute its `prompt`, then re-arm the next heartbeat (and re-arm the watcher only if it exited). If both an output wake and a completion notification arrive, act on the output and ignore the completion.
5. **To stop**, kill any watcher PID and don't arm the next heartbeat.
6. Briefly confirm: that you're self-pacing, whether a watcher is the primary wake signal, what fallback delay you picked, and that the prompt already ran.
## Prompt Payload
Wake notifications include an output file path, not a submitted prompt. Put the prompt beside the sentinel, preferably as JSON. On wake, read the latest matching line and act on its `prompt`. The prompt may vary by tick.
## Guidance
- Title shell commands as `Loop <schedule>: <prompt>` (e.g. `Loop every 5m: check deploy status`).
- Adapt loop syntax to the user's shell (e.g. PowerShell `while ($true) { ... Start-Sleep }` on Windows). The examples above use bash.
- Prefer monitored shell output over OS cron when the agent needs wake notifications; stdout stays attached to the monitored task.
- Use a unique sentinel per loop so unrelated output does not trigger notifications.
- Avoid noisy commands inside the loop.
- Do not create duplicate fixed loops or dynamic sleepers.
- If the user asks to stop, kill any tracked loop/sleeper PID, then await the shell task so its completion notification is consumed and does not wake the agent later. Do not schedule another dynamic wake.

View File

@@ -1,134 +0,0 @@
---
name: migrate-to-skills
description: >-
Convert 'Applied intelligently' Cursor rules (.cursor/rules/*.mdc) and slash
commands (.cursor/commands/*.md) to Agent Skills format (.cursor/skills/). Use
when you want to migrate rules or commands to skills, convert .mdc rules to
SKILL.md format, or consolidate commands into the skills directory.
disable-model-invocation: true
---
# Migrate Rules and Slash Commands to Skills
Convert Cursor rules ("Applied intelligently") and slash commands to Agent Skills format.
**CRITICAL: Preserve the exact body content. Do not modify, reformat, or "improve" it - copy verbatim.**
## Locations
| Level | Source | Destination |
|-------|--------|-------------|
| Project | `{workspaceFolder}/**/.cursor/rules/*.mdc`, `{workspaceFolder}/.cursor/commands/*.md` |
| User | `~/.cursor/commands/*.md` |
Notes:
- Cursor rules inside the project can live in nested directories. Be thorough in your search and use glob patterns to find them.
- Ignore anything in ~/.cursor/worktrees
- Ignore anything in ~/.cursor/skills-cursor. This is reserved for Cursor's internal built-in skills and is managed automatically by the system.
## Finding Files to Migrate
**Rules**: Migrate if rule has a `description` but NO `globs` and NO `alwaysApply: true`.
**Commands**: Migrate all - they're plain markdown without frontmatter.
## Conversion Format
### Rules: .mdc → SKILL.md
```markdown
# Before: .cursor/rules/my-rule.mdc
---
description: What this rule does
globs:
alwaysApply: false
---
# Title
Body content...
```
```markdown
# After: .cursor/skills/my-rule/SKILL.md
---
name: my-rule
description: What this rule does
---
# Title
Body content...
```
Changes: Add `name` field, remove `globs`/`alwaysApply`, keep body exactly.
### Commands: .md → SKILL.md
```markdown
# Before: .cursor/commands/commit.md
# Commit current work
Instructions here...
```
```markdown
# After: .cursor/skills/commit/SKILL.md
---
name: commit
description: Commit current work with standardized message format
disable-model-invocation: true
---
# Commit current work
Instructions here...
```
Changes: Add frontmatter with `name` (from filename), `description` (infer from content), and `disable-model-invocation: true`, keep body exactly.
**Note:** The `disable-model-invocation: true` field prevents the model from automatically invoking this skill. Slash commands are designed to be explicitly triggered by the user via the `/` menu, not automatically suggested by the model.
## Notes
- `name` must be lowercase with hyphens only
- `description` is critical for skill discovery
- Optionally delete originals after verifying migration works
### Migrate a Rule (.mdc → SKILL.md)
1. Read the rule file
2. Extract the `description` from the frontmatter
3. Extract the body content (everything after the closing `---` of the frontmatter)
4. Create the skill directory: `.cursor/skills/{skill-name}/` (skill name = filename without .mdc)
5. Write `SKILL.md` with new frontmatter (`name` and `description`) + the EXACT original body content (preserve all whitespace, formatting, code blocks verbatim)
6. Delete the original rule file
### Migrate a Command (.md → SKILL.md)
1. Read the command file
2. Extract description from the first heading (remove `#` prefix)
3. Create the skill directory: `.cursor/skills/{skill-name}/` (skill name = filename without .md)
4. Write `SKILL.md` with new frontmatter (`name`, `description`, and `disable-model-invocation: true`) + blank line + the EXACT original file content (preserve all whitespace, formatting, code blocks verbatim)
5. Delete the original command file
**CRITICAL: Copy the body content character-for-character. Do not reformat, fix typos, or "improve" anything.**
## Workflow
If you have the Task tool available:
DO NOT start to read all of the files yourself. That function should be delegated to the subagents. Your job is to dispatch the subagents for each category of files and wait for the results.
1. [ ] Create the skills directories if they don't exist (`.cursor/skills/` for project, `~/.cursor/skills/` for user)
2. Dispatch three fast general purpose subagents (NOT explore) in parallel to do the following steps for project rules (pattern: `{workspaceFolder}/**/.cursor/rules/*.mdc`), user commands (pattern: `~/.cursor/commands/*.md`), and project commands (pattern: `{workspaceFolder}/**/.cursor/commands/*.md`):
I. [ ] Find files to migrate in the given pattern
II. [ ] For rules, check if it's an "applied intelligently" rule (has `description`, no `globs`, no `alwaysApply: true`). Commands are always migrated. DO NOT use the terminal to read files. Use the read tool.
III. [ ] Make a list of files to migrate. If empty, done.
IV. [ ] For each file, read it, then write the new skill file preserving the body content EXACTLY. DO NOT use the terminal to write these files. Use the edit tool.
V. [ ] Delete the original file. DO NOT use the terminal to delete these files. Use the delete tool.
VI. [ ] Return a list of all the skill files that were migrated along with the original file paths.
3. [ ] Wait for all subagents to complete and summarize the results to the user. IMPORTANT: Make sure to let them know if they want to undo the migration, to ask you to.
4. [ ] If the user asks you to undo the migration, do the opposite of the above steps to restore the original files.
If you don't have the Task tool available:
1. [ ] Create the skills directories if they don't exist (`.cursor/skills/` for project, `~/.cursor/skills/` for user)
2. [ ] Find files to migrate in both project (`.cursor/`) and user (`~/.cursor/`) directories
3. [ ] For rules, check if it's an "applied intelligently" rule (has `description`, no `globs`, no `alwaysApply: true`). Commands are always migrated. DO NOT use the terminal to read files. Use the read tool.
4. [ ] Make a list of files to migrate. If empty, done.
5. [ ] For each file, read it, then write the new skill file preserving the body content EXACTLY. DO NOT use the terminal to write these files. Use the edit tool.
6. [ ] Delete the original file. DO NOT use the terminal to delete these files. Use the delete tool.
7. [ ] Summarize the results to the user. IMPORTANT: Make sure to let them know if they want to undo the migration, to ask you to.
8. [ ] If the user asks you to undo the migration, do the opposite of the above steps to restore the original files.

View File

@@ -1,239 +0,0 @@
---
name: sdk
description: >-
Guide users building apps, scripts, CI pipelines, or automations on top of the
Cursor TypeScript SDK (`@cursor/sdk`). Use when the user mentions integrating,
installing, or writing code against the Cursor SDK; says `Agent.create`,
`Agent.prompt`, `Agent.resume`, `agent.send`, `run.stream`,
`CursorAgentError`, or `@cursor/sdk`; asks to run Cursor agents
programmatically from a script, CI/CD pipeline, GitHub Action, backend
service, or other code outside the Cursor IDE; wants to pick between local and
cloud runtime, configure MCP servers for an SDK agent, or handle streaming,
cancellation, or errors; or is wiring Cursor into an automation, bot, or REST
`/v1/agents` migration. Use eagerly rather than answering from memory; the SDK
surface evolves and this skill is the source of truth for the external
package.
---
# Cursor SDK
The Cursor TypeScript SDK (`@cursor/sdk`) runs Cursor agents programmatically. The same interface drives the local runtime (agent runs on your machine against your files) and the cloud runtime (agent runs on Cursor-hosted or self-hosted infrastructure against a cloned repo and opens PRs).
Use this skill to help someone **bootstrap a working integration quickly** and **avoid the traps that bite new users**. Canonical docs live at [https://cursor.com/docs/api/sdk/typescript](https://cursor.com/docs/api/sdk/typescript); this skill adds decision-making, failure-mode prevention, and ready-to-extend patterns.
## Voice and Posture
This skill helps the user **build** with the SDK. It is not the place to validate, congratulate, or sell the SDK as a choice. The user's intent is the input; your job is execution.
- **When the user names the SDK explicitly** (says "Cursor SDK", `@cursor/sdk`, `Agent.create`, `Agent.prompt`, etc.): assume they know what the SDK is and have decided to use it. Skip framing, skip pep talk, go straight to producing the integration. No "good news", no "the SDK is perfect for this", no "this is almost exactly the pattern X is designed for".
- **When the user describes a problem the SDK fits but doesn't name it** ("I want a bot that reviews my PRs", "I want a script that asks Cursor questions about my repo"): the SDK isn't yet a confirmed choice. Surface it as a question, briefly, then wait: *"The Cursor SDK is what I'd reach for here - want me to design it that way, or do you have a different runtime in mind?"* If they confirm, proceed. If they push back or want options, give options.
- **In either case, don't restate the user's intent back to them.** They know what they want. Get to the design.
Avoid these specific openers (and their close cousins):
- "Good news: this is exactly the pattern..."
- "The SDK is built for this shape."
- "Great, you've come to the right place."
- "This is almost exactly the X the SDK is designed for."
- Any lede that compliments the user's choice or restates their goal in flattering terms.
Prefer:
- Open with the design decision or the first thing they need to know.
- If you genuinely have a design choice to flag (local vs cloud, prompt vs send, sync vs stream), name it in one sentence and explain why.
## The Three Invocation Patterns
Almost every SDK integration collapses to one of three shapes. Pick the one that fits the job, don't mix them.
### 1. `Agent.prompt(...)` - one-shot
```typescript
import { Agent } from "@cursor/sdk";
const result = await Agent.prompt("Refactor src/utils.ts for readability", {
apiKey: process.env.CURSOR_API_KEY!,
model: { id: "composer-2" },
local: { cwd: process.cwd() },
});
console.log(result.status, result.result);
```
Use for fire-and-forget scripts, GitHub Actions steps, or any "send this prompt, get a result, exit" flow. No streaming, no follow-ups, no cleanup to remember. If you're reaching for this and then immediately resuming, you wanted pattern 2 instead.
### 2. `Agent.create(...)` + `agent.send(...)` - durable with follow-ups
```typescript
import { Agent } from "@cursor/sdk";
const agent = Agent.create({
apiKey: process.env.CURSOR_API_KEY!,
model: { id: "composer-2" },
local: { cwd: process.cwd() },
});
try {
const run = await agent.send("Find the bug in src/auth.ts");
for await (const event of run.stream()) {
if (event.type === "assistant") {
for (const block of event.message.content) {
if (block.type === "text") process.stdout.write(block.text);
}
}
}
const result = await run.wait();
// Follow-up keeps full conversation context.
const run2 = await agent.send("Now write a regression test for it");
await run2.wait();
} finally {
await agent[Symbol.asyncDispose]();
}
```
Use when you need streaming, multi-turn conversation, or lifecycle operations (cancel, status listener). This is the shape of most non-trivial integrations.
### 3. `Agent.resume(...)` - pick up an existing agent later
```typescript
const agent = Agent.resume(previousAgentId, {
apiKey: process.env.CURSOR_API_KEY!,
model: { id: "composer-2" },
local: { cwd: process.cwd() },
});
const run = await agent.send("Also update the changelog");
await run.wait();
```
Use across process boundaries: a cron that continues last night's cleanup, a webhook that extends a user's agent, an interactive CLI that reloads conversation state. **Inline `mcpServers` are not persisted across resume** - pass them again on the resume call.
## Top Five Traps
These trip up almost every new integration. They're all easy to prevent once you know about them.
### 1. Missing `cloud: { repos }` silently defaults to local
`AgentOptions` doesn't require `local` or `cloud`; if you omit both, the SDK selects the local runtime. The trap: if you intended a cloud agent and forgot the `cloud:` field, you get a local agent silently - no error, just a local agent ID and a local executor. Always pass `cloud: { repos }` explicitly when you want cloud, and pass `local: { cwd }` explicitly for local even though it's the default.
### 2. Two different kinds of failure, one instinct to conflate them
```typescript
import { Agent, CursorAgentError } from "@cursor/sdk";
const agent = Agent.create({ /* ... */ });
try {
const run = await agent.send(prompt);
const result = await run.wait();
if (result.status === "error") {
// Agent started but failed mid-run. Inspect transcript, git state, tool outputs.
console.error("run failed: " + result.id);
process.exit(2);
}
} catch (err) {
if (err instanceof CursorAgentError) {
// Didn't start. Auth, config, network. Fix environment, retry.
console.error("startup failed: " + err.message + ", retryable=" + err.isRetryable);
process.exit(1);
}
throw err;
}
```
`CursorAgentError` thrown -> the run never executed (auth, config, network). `result.status === "error"` -> the agent did work, and that work failed. Different fixes, different exit codes, different observability.
### 3. Forgetting `await agent[Symbol.asyncDispose]()` leaks resources
The SDK holds handles to local executors, persisted run stores, and cloud API clients. Not disposing means leaked child processes, open databases, and in long-running services, memory growth. Always dispose in a `finally`, or use `Agent.prompt()` (disposes for you), or use the `await using` syntax if your tsconfig targets it:
```typescript
await using agent = Agent.create({ /* ... */ });
```
### 4. Streaming is optional but `wait()` is almost always required
`run.stream()` is how you observe; `run.wait()` is how you get the terminal result. You can skip streaming, but skipping `wait()` means you can't tell whether the run finished, errored, or was cancelled, and you'll leak the run's internal watchers. Always call `wait()`. If you don't want live output, just call `wait()` alone.
### 5. Not every `run` operation is supported on every runtime
`Run` exposes four operations - `stream`, `wait`, `cancel`, `conversation` - and the runtime may or may not support each. Always guard with `run.supports("...")` before calling:
```typescript
if (run.supports("cancel")) await run.cancel();
if (run.supports("conversation")) console.log(await run.conversation());
```
Current gap worth knowing about: detached or rehydrated runs (you got the handle from `Agent.getRun(...)` after the live event store has closed) may not support `stream()` and may have empty `conversation()`. `run.unsupportedReason(op)` tells you why. Cloud `run.conversation()` is supported - it accumulates best-effort from the stream.
## Local vs Cloud, in one sentence each
- **Local** - runs on the caller's machine against `cwd`, reuses their environment and credentials, good for dev loops and CI that already has a repo checkout.
- **Cloud** - runs on a Cursor-hosted VM against a freshly cloned `repos[].url`, good for long jobs, fire-and-forget automation, and opening real PRs (`autoCreatePR: true`).
## Auth, minimum viable
```bash
export CURSOR_API_KEY="cursor_..." # user API key or team service-account key
```
The SDK reads `CURSOR_API_KEY` if `apiKey` isn't passed. Both user keys (from [https://cursor.com/dashboard/cloud-agents](https://cursor.com/dashboard/cloud-agents)) and team service-account keys (Team Settings -> Service accounts) work for local and cloud runs.
If you're seeing 401s, the usual suspects are: key pasted with surrounding whitespace, key minted against a different environment, or the key belongs to a user without repo access for a cloud run.
## Model Selection
```typescript
import { Cursor } from "@cursor/sdk";
const models = await Cursor.models.list({ apiKey: process.env.CURSOR_API_KEY! });
```
`composer-2` is the current default for most integrations. `{ id: "auto" }` lets the server pick. Model IDs change; don't hardcode exotic ones without calling `Cursor.models.list()` first to confirm the caller has access.
Model is **required for local**, **optional for cloud** (the server resolves a default from the caller's account).
## MCP Servers
Pass MCP servers inline when the integration needs tools beyond the working tree. Be explicit about runtime transport:
- Local agents can use stdio or HTTP MCP servers available on the caller's machine.
- Cloud agents need network-reachable HTTP MCP servers or cloud-supported configuration; local stdio processes are not available inside a cloud VM.
- If you resume an agent and still need MCP tools, pass `mcpServers` again on `Agent.resume(...)`.
## Production Best Practices
Apply these to any integration that runs unattended:
1. **Wrap every `Agent.create` / `Agent.prompt` / `Agent.resume` in a try/finally with `[Symbol.asyncDispose]()`**. Non-negotiable.
2. **Distinguish startup failures from run failures** - exit code 1 for `CursorAgentError`, exit code 2 for `result.status === "error"`, exit code 0 only for `finished`.
3. **Log `run.id` and `agent.agentId` immediately after `send()`** before streaming. If the stream hangs, the IDs are what you need to investigate in the dashboard or via `Agent.getRun(...)`.
4. **Respect `error.isRetryable`** - it's the backend telling you the specific failure is safe to retry. Blind retries can cause duplicate cloud runs; respecting the flag doesn't.
5. **Use `local: { settingSources: [] }` (default) unless you need ambient config.** Opting into `"all"` loads project/user/team/MDM settings from the caller's environment, which is rarely what you want from a service. `settingSources` lives under `local`, not at the top level; it has no effect on cloud agents (cloud always honors team/project/plugins).
6. **For cloud agents in CI, set `skipReviewerRequest: true`** unless a human should be paged - it suppresses the reviewer-request step and keeps PR notifications quiet.
7. **Always pass `apiKey` explicitly** in shared-infrastructure code instead of relying on the env var. Makes the credential dependency obvious and prevents cross-tenant mistakes.
8. **Prefer `Agent.prompt(...)` for true one-shots** - it disposes for you and is harder to leak.
## Observing a Run You Didn't Launch
You can inspect any agent/run by ID later:
```typescript
// Cloud: IDs that start with "bc-" auto-route to the cloud API.
const info = await Agent.get("bc-abc123", { apiKey });
const run = await Agent.getRun(runId, { runtime: "cloud", agentId: "bc-abc123", apiKey });
// Local: you need the cwd where the agent was created.
const localInfo = await Agent.list({ runtime: "local", cwd: process.cwd() });
```
A cloud `bc-`-prefixed agent ID is **not** a run ID. If you only have a run ID (from a log or a webhook), pass it to `Agent.getRun` with the runtime hint; don't confuse the two.
## Offering a Canvas
If the user's integration monitors, lists, or visualizes agents - dashboards of active runs, conversation replays, tool-call timelines - offer a Cursor Canvas to render it. If they accept, defer entirely to the `canvas` skill.
## What This Skill Doesn't Cover
- The Cloud Agents REST API (`/v1/agents/*`). If the user needs a non-TypeScript client, use the REST API docs for current capabilities before assuming parity with the SDK.
- `.cursor/hooks.json` hooks. Cloud agents execute them but the SDK doesn't manage them; see Cursor's Hooks docs.
- Private workers / self-hosted cloud. Send users to the Private Workers docs.
- Python / non-TypeScript SDKs. There is no first-party SDK in other languages at time of writing; REST is the portable option.

View File

@@ -1,24 +0,0 @@
---
name: shell
description: >-
Runs the rest of a /shell request as a literal shell command. Use only when
the user explicitly invokes /shell and wants the following text executed
directly in the terminal.
disable-model-invocation: true
---
# Run Shell Commands
Use this skill only when the user explicitly invokes `/shell`.
## Behavior
1. Treat all user text after the `/shell` invocation as the literal shell command to run.
2. Execute that command immediately with the terminal tool.
3. Do not rewrite, explain, or "improve" the command before running it.
4. Do not inspect the repository first unless the command itself requires repository context.
5. If the user invokes `/shell` without any following text, ask them which command to run.
## Response
- Run the command first.
- Then briefly report the exit status and any important stdout or stderr.

View File

@@ -1,49 +0,0 @@
---
name: split-to-prs
description: >-
Split current work into small reviewable PRs. Use when the user asks to split
a chat, set of changes, branch, or PR.
---
# Split to PRs
Turn one pile of work into a few small PRs.
## Hard rules
- Do not create branches, commit, push, or open PRs until the user approves the split plan.
- Never discard user work. No destructive git commands (`reset --hard`, `clean -fdx`, branch deletion, force-push, history rewrite) without explicit approval.
- Always save a recoverable snapshot before moving work around. This often starts from dirty work on `main`, so do not assume there is already a safe branch.
- Stage only named files or hunks. No `git add .` / `git add -A`.
## 1. Check the state
Compare the current work to the repo's default branch, including committed and uncommitted changes. Summarize the real slices you see, and use the chat history to recover intent.
Before proposing slices, find ownership signals for the touched paths (`CODEOWNERS`, nested ownership files, `tools/ownership/PRODUCTOWNERS`, or repo equivalents) and use them to identify natural reviewer boundaries.
## 2. Propose the split
Use judgment on detail. Usually PR titles are enough. Add a one-line scope note only when a title is unclear. Show a Mermaid diagram when there are multiple slices.
Optimize for reviewer-aligned PRs with minimal unrelated diff: split independent owners or concerns, keep tightly coupled changes together, and when stacking is necessary, order foundations before consumers.
Default to independent PRs off the default branch. Stack PRs only when the dependency is real.
Ask for approval before starting.
## 3. Execute the split
- If there is uncommitted work, save a recoverable snapshot without changing the working tree:
```bash
SHA=$(git stash create "pre-split")
if [ -n "$SHA" ]; then
git update-ref "refs/backup/pre-split-$(date +%s)" "$SHA"
fi
```
- For each approved slice, create a branch from the right base, stage and commit only the planned files or hunks, then push and open the PR.
## 4. Report back
Keep it short: PR titles and URLs, plus anything left on the starting branch or working tree. Do not delete the backup ref or original branch unless the user asks.

View File

@@ -1,196 +0,0 @@
---
name: statusline
description: >-
Configure a custom status line in the CLI. Use when the user mentions status
line, statusline, statusLine, CLI status bar, prompt footer customization, or
wants to add session context above the prompt.
---
# CLI Status Line
The CLI supports a user-configurable status line rendered above the prompt. A command is spawned on each conversation update, receives a JSON payload on stdin describing the session, and its stdout is displayed as the status line. The spec is aligned with [Claude Code's status line](https://code.claude.com/docs/en/statusline).
## Configuration
Add a `statusLine` entry to `~/.cursor/cli-config.json`:
```json
{
"statusLine": {
"type": "command",
"command": "~/.cursor/statusline.sh",
"padding": 2
}
}
```
The `command` field supports full paths, `~` expansion, and shell-style argument splitting. You can point it at a script file or use an inline command like `jq -r '...'`.
| Field | Required | Default | Description |
|-------|----------|---------|-------------|
| `type` | yes | — | Must be `"command"` |
| `command` | yes | — | Path to an executable or inline command. `~` is expanded. |
| `padding` | no | `0` | Horizontal inset (in characters) for the status line container. |
| `updateIntervalMs` | no | `300` | Minimum interval between invocations. Clamped to >= 300ms. |
| `timeoutMs` | no | `2000` | Maximum time the command may run before it is killed. |
## Stdin payload
The command receives a JSON object on stdin. The TypeScript interface is `StatusLinePayload` in `packages/agent-cli/src/hooks/use-status-line.ts`.
### Full JSON schema
```json
{
"session_id": "abc123",
"session_name": "my session",
"transcript_path": "/path/to/transcript.jsonl",
"render_width_chars": 120,
"cwd": "/Users/me/project",
"autorun": false,
"model": {
"id": "claude-4-opus",
"display_name": "Claude 4 Opus",
"param_summary": "(Thinking)",
"max_mode": true
},
"workspace": {
"current_dir": "/Users/me/project",
"project_dir": "/Users/me/project/.cursor/transcripts",
"added_dirs": []
},
"version": "1.2.3",
"output_style": {
"name": "default"
},
"context_window": {
"total_input_tokens": 15234,
"total_output_tokens": null,
"context_window_size": 200000,
"used_percentage": 34.5,
"remaining_percentage": 65.5,
"current_usage": null
},
"vim": {
"mode": "NORMAL"
},
"worktree": {
"name": "my-feature",
"path": "/Users/me/.cursor/worktrees/repo/my-feature"
}
}
```
### Available fields
| Field | Description |
|-------|-------------|
| `session_id` | Unique session identifier |
| `session_name` | Custom session name. Absent if no name has been set |
| `transcript_path` | Path to conversation transcript file |
| `render_width_chars` | Usable terminal columns minus built-in padding |
| `cwd`, `workspace.current_dir` | Current working directory (both contain the same value) |
| `autorun` | `true` when auto-run is enabled for the current session |
| `workspace.project_dir` | Directory where transcripts are stored |
| `workspace.added_dirs` | Additional directories (empty array for now) |
| `model.id`, `model.display_name` | Current model identifier and display name |
| `model.param_summary` | Formatted parameter summary (e.g. "(Thinking)", "High"). Absent when empty |
| `model.max_mode` | `true` when max mode is enabled. Absent otherwise |
| `version` | CLI version string |
| `output_style.name` | `"default"` or `"compact"` |
| `context_window.total_input_tokens` | Estimated input tokens (derived from used_percentage) |
| `context_window.total_output_tokens` | Cumulative output tokens (null when not tracked) |
| `context_window.context_window_size` | Maximum context window size in tokens |
| `context_window.used_percentage` | Percentage of context window used |
| `context_window.remaining_percentage` | Percentage of context window remaining |
| `context_window.current_usage` | Token counts from the last API call (null before first call) |
| `vim.mode` | `"NORMAL"` or `"INSERT"` when vim mode is enabled |
| `worktree.name` | Worktree name when running inside a worktree |
| `worktree.path` | Absolute path to the worktree directory |
### Fields that may be absent
- `session_name` — only present when a custom name has been set
- `model.param_summary` — only present when model has non-default parameters
- `model.max_mode` — only present when max mode is enabled
- `vim` — only present when vim mode is enabled
- `worktree` — only present when running in a worktree
### Fields that may be null
- `context_window.current_usage` — null before the first API call
- `context_window.used_percentage`, `context_window.remaining_percentage` — may be null early in the session
## Stdout / rendering
- **Multiple lines** are supported: each line of stdout renders as a separate row in the status area.
- **ANSI color codes** are supported (use chalk, tput, `\033[32m`, etc.).
- If the command exits non-zero with empty stdout, the status line is not updated (previous text is kept).
- If the command times out or a new update arrives while the script is running, the in-flight process is killed.
- The status line runs locally and does not consume API tokens.
## Examples
### Basic: model + context usage
```bash
#!/usr/bin/env bash
payload=$(cat)
model=$(echo "$payload" | jq -r '.model.display_name')
pct=$(echo "$payload" | jq -r '.context_window.used_percentage // 0' | cut -d. -f1)
printf "\033[90m%s ctx %s%%\033[0m" "$model" "$pct"
```
### Context progress bar
```bash
#!/usr/bin/env bash
input=$(cat)
MODEL=$(echo "$input" | jq -r '.model.display_name')
PCT=$(echo "$input" | jq -r '.context_window.used_percentage // 0' | cut -d. -f1)
BAR_WIDTH=10
FILLED=$((PCT * BAR_WIDTH / 100))
EMPTY=$((BAR_WIDTH - FILLED))
BAR=""
[ "$FILLED" -gt 0 ] && printf -v FILL "%${FILLED}s" && BAR="${FILL// /▓}"
[ "$EMPTY" -gt 0 ] && printf -v PAD "%${EMPTY}s" && BAR="${BAR}${PAD// /░}"
echo "[$MODEL] $BAR $PCT%"
```
### Multi-line with git info
```bash
#!/usr/bin/env bash
input=$(cat)
MODEL=$(echo "$input" | jq -r '.model.display_name')
DIR=$(echo "$input" | jq -r '.workspace.current_dir')
PCT=$(echo "$input" | jq -r '.context_window.used_percentage // 0' | cut -d. -f1)
BRANCH=""
git rev-parse --git-dir > /dev/null 2>&1 && BRANCH=" | 🌿 $(git branch --show-current 2>/dev/null)"
echo -e "\033[36m[$MODEL]\033[0m 📁 ${DIR##*/}$BRANCH"
echo -e "ctx $PCT%"
```
### Inline jq command (no script file)
```json
{
"statusLine": {
"type": "command",
"command": "jq -r '\"[\\(.model.display_name)] \\(.context_window.used_percentage // 0)% context\"'"
}
}
```
## Testing
Test a script with mock input:
```bash
echo '{"model":{"display_name":"Opus"},"context_window":{"used_percentage":25}}' | ./statusline.sh
```
The command is spawned with `child_process.spawn` (no shell on Unix, `shell: true` on Windows for .cmd/.bat compatibility). Updates are debounced at the configured interval. If a new update triggers while a script is running, the in-flight process is killed via `AbortController` and the new invocation starts immediately.

View File

@@ -1,87 +0,0 @@
---
name: update-cli-config
description: >-
View and modify Cursor CLI configuration settings in
~/.cursor/cli-config.json. Use when the user wants to change CLI settings,
configure permissions, switch approval mode, enable vim mode, toggle display
options, configure sandbox, or manage any CLI preferences.
metadata:
surfaces:
- cli
---
# Cursor CLI Configuration
This skill explains how to view and modify Cursor CLI settings stored in `~/.cursor/cli-config.json`.
## Config File Location
The config file is `~/.cursor/cli-config.json`.
Projects can layer overrides via `.cursor/cli.json` files. The CLI walks from the git root to the current working directory and merges each `.cursor/cli.json` it finds (deeper files take precedence). Project overrides only affect the current session; they are not written back to the home config.
## How to Modify
Read `~/.cursor/cli-config.json`, apply changes, and write it back. The file is standard JSON. Changes take effect after restarting the CLI.
## Available Settings
### `permissions` (required)
Tool permission rules. Each entry is a string pattern.
- `allow`: string[] — patterns for allowed tool calls (e.g. `"Shell(**)"`, `"Mcp(server-name, tool-name)"`)
- `deny`: string[] — patterns for denied tool calls
### `editor`
- `vimMode`: boolean — enable vim keybindings in the CLI input
- `defaultBehavior`: `"ide"` | `"agent"` — default behavior mode
### `display` (optional)
- `showLineNumbers`: boolean (default: false) — show line numbers in code output
- `showThinkingBlocks`: boolean (default: false) — show model thinking/reasoning blocks
- `showStatusIndicators`: boolean (default: false) — show status indicators in the UI
### `channel` (optional)
Release channel: `"prod"` | `"staging"` | `"lab"` | `"static"`
### `maxMode` (optional)
boolean (default: false) — enable max mode for higher-quality model responses
### `approvalMode` (optional)
Controls tool approval behavior:
- `"allowlist"` (default) — require approval for tools not in the allow list
- `"unrestricted"` — auto-approve all tool calls (yolo mode)
### `sandbox` (optional)
Sandbox execution environment settings:
- `mode`: `"disabled"` | `"enabled"` (default: `"disabled"`)
- `networkAccess`: `"user_config_only"` | `"user_config_with_defaults"` | `"allow_all"` — controls network access from sandbox
- `networkAllowlist`: string[] — domains the sandbox is allowed to reach
### `network` (optional)
- `useHttp1ForAgent`: boolean (default: false) — use HTTP/1.1 instead of HTTP/2 for agent connections (enables SSE-based streaming)
### `bedrock` (optional)
AWS Bedrock integration settings:
- `enabled`: boolean (default: false)
- `mode`: `"access-key"` | `"team-role"` (default: `"access-key"`)
- `region`: string — AWS region
- `testModel`: string — model to use for testing
- `teamRoleArn`: string — IAM role ARN for team mode
- `teamExternalId`: string — external ID for STS assume-role
### `attribution` (optional)
Controls how agent work is attributed in git:
- `attributeCommitsToAgent`: boolean (default: true) — attribute commits to the agent
- `attributePRsToAgent`: boolean (default: true) — attribute PRs to the agent
### `webFetchDomainAllowlist` (optional)
string[] — domains the web fetch tool is allowed to access (e.g. `"docs.github.com"`, `"*.example.com"`, `"*"`)
## Fields You Should NOT Modify
These are internal/cached state and should not be edited manually:
- `version` — config schema version
- `model` / `selectedModel` / `modelParameters` / `hasChangedDefaultModel` — managed by the model picker
- `privacyCache` — cached privacy mode state
- `authInfo` — cached authentication info
- `showSandboxIntro` — one-time UI flag
- `conversationClassificationScoredConversations` — internal cache

View File

@@ -1,122 +0,0 @@
---
name: update-cursor-settings
description: >-
Modify Cursor/VSCode user settings in settings.json. Use when you want to
change editor settings, preferences, configuration, themes, font size, tab
size, format on save, auto save, keybindings, or any settings.json values.
metadata:
surfaces:
- ide
---
# Updating Cursor Settings
This skill guides you through modifying Cursor/VSCode user settings. Use this when you want to change editor settings, preferences, configuration, themes, keybindings, or any `settings.json` values.
## Settings File Location
| OS | Path |
|----|------|
| macOS | ~/Library/Application Support/Cursor/User/settings.json |
| Linux | ~/.config/Cursor/User/settings.json |
| Windows | %APPDATA%\Cursor\User\settings.json |
## Before Modifying Settings
1. **Read the existing settings file** to understand current configuration
2. **Preserve existing settings** - only add/modify what the user requested
3. **Validate JSON syntax** before writing to avoid breaking the editor
## Modifying Settings
### Step 1: Read Current Settings
```typescript
// Read the settings file first
const settingsPath = "~/Library/Application Support/Cursor/User/settings.json";
// Use the Read tool to get current contents
```
### Step 2: Identify the Setting to Change
Common setting categories:
- **Editor**: `editor.fontSize`, `editor.tabSize`, `editor.wordWrap`, `editor.formatOnSave`
- **Workbench**: `workbench.colorTheme`, `workbench.iconTheme`, `workbench.sideBar.location`
- **Files**: `files.autoSave`, `files.exclude`, `files.associations`
- **Terminal**: `terminal.integrated.fontSize`, `terminal.integrated.shell.*`
- **Cursor-specific**: Settings prefixed with `cursor.` or `aipopup.`
### Step 3: Update the Setting
When modifying settings.json:
1. Parse the existing JSON (handle comments - VSCode settings support JSON with comments)
2. Add or update the requested setting
3. Preserve all other existing settings
4. Write back with proper formatting (2-space indentation)
### Example: Changing Font Size
If user says "make the font bigger":
```json
{
"editor.fontSize": 16
}
```
### Example: Enabling Format on Save
If user says "format my code when I save":
```json
{
"editor.formatOnSave": true
}
```
### Example: Changing Theme
If user says "use dark theme" or "change my theme":
```json
{
"workbench.colorTheme": "Default Dark Modern"
}
```
## Important Notes
1. **JSON with Comments**: VSCode/Cursor settings.json supports comments (`//` and `/* */`). When reading, be aware comments may exist. When writing, preserve comments if possible.
2. **Restart May Be Required**: Some settings take effect immediately, others require reloading the window or restarting Cursor. Inform the user if a restart is needed.
3. **Backup**: For significant changes, consider mentioning the user can undo via Ctrl/Cmd+Z in the settings file or by reverting git changes if tracked.
4. **Workspace vs User Settings**:
- User settings (what this skill covers): Apply globally to all projects
- Workspace settings (`.vscode/settings.json`): Apply only to the current project
5. **Commit Attribution**: When the user asks about commit attribution, clarify whether they want to edit the **CLI agent** or the **IDE agent**. For the CLI agent, modify `~/.cursor/cli-config.json`. For the IDE agent, it is controlled from the UI at **Cursor Settings > Agent > Attribution** (not settings.json).
## Common User Requests → Settings
| User Request | Setting |
|--------------|---------|
| "bigger/smaller font" | `editor.fontSize` |
| "change tab size" | `editor.tabSize` |
| "format on save" | `editor.formatOnSave` |
| "word wrap" | `editor.wordWrap` |
| "change theme" | `workbench.colorTheme` |
| "hide minimap" | `editor.minimap.enabled` |
| "auto save" | `files.autoSave` |
| "line numbers" | `editor.lineNumbers` |
| "bracket matching" | `editor.bracketPairColorization.enabled` |
| "cursor style" | `editor.cursorStyle` |
| "smooth scrolling" | `editor.smoothScrolling` |
## Workflow
1. Read ~/Library/Application Support/Cursor/User/settings.json
2. Parse the JSON content
3. Add/modify the requested setting(s)
4. Write the updated JSON back to the file
5. Inform the user the setting has been changed and whether a reload is needed