Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .oxfmtrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"$schema": "./node_modules/oxfmt/configuration_schema.json",
"printWidth": 80,
"tabWidth": 2,
"sortPackageJson": false,
"ignorePatterns": [
"apps/cli-docs/**",
"packages/cli/**",
"packages/mcp-server-mocks/src/fixtures/**",
"**/worker-configuration.d.ts",
"**/tsconfig*.json"
]
}
26 changes: 26 additions & 0 deletions .oxlintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"$schema": "./node_modules/oxlint/configuration_schema.json",
"plugins": ["eslint", "typescript", "unicorn", "oxc", "jsx-a11y"],
"categories": {
"correctness": "error"
},
"ignorePatterns": [
"apps/cli-docs/**",
"packages/cli/**",
"**/worker-configuration.d.ts",
"**/tsconfig*.json",
"packages/mcp-server-mocks/src/fixtures/**"
],
"rules": {
"no-unused-vars": "off",
"jsx-a11y/interactive-supports-focus": "off"
},
"overrides": [
{
"files": ["packages/mcp-cloudflare/src/client/**/*.tsx"],
"rules": {
"jsx-a11y/no-static-element-interactions": "off"
}
}
]
}
2 changes: 1 addition & 1 deletion .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"recommendations": ["biomejs.biome"],
"recommendations": ["oxc.oxc-vscode", "biomejs.biome"],
Comment thread
cursor[bot] marked this conversation as resolved.
"unwantedRecommendations": []
}
13 changes: 5 additions & 8 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,21 +1,18 @@
{
"editor.defaultFormatter": "oxc.oxc-vscode",
"editor.formatOnSave": true,
"editor.formatOnSaveMode": "file",
"editor.codeActionsOnSave": {
"source.fixAll.biome": "explicit"
"source.fixAll.oxc": "explicit"
},
"files.trimTrailingWhitespace": false,
"files.trimFinalNewlines": false,
"files.insertFinalNewline": true,
"cursor.general.enableShadowWorkspace": true,
"[json]": {
"editor.tabSize": 2,
"editor.defaultFormatter": "biomejs.biome"
"editor.tabSize": 2
},
"[typescript]": {
"editor.tabSize": 2,
"editor.defaultFormatter": "biomejs.biome"
},
"[typescriptreact]": {
"editor.defaultFormatter": "biomejs.biome"
"editor.tabSize": 2
}
}
74 changes: 0 additions & 74 deletions biome.json

This file was deleted.

36 changes: 25 additions & 11 deletions docs/contributing/coding-guidelines.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,27 @@ Essential patterns and standards for Sentry MCP development.

## Code Style

### Biome Configuration
### Formatting and linting

- 2 spaces, double quotes, semicolons
- Max line: 100 chars
- Formatter width: 80 characters
- Trailing commas in multiline
- MCP and root files use Oxfmt and Oxlint; CLI files still use their own Biome rules.
- CLI's custom Grit rules and inline suppressions remain active until their
equivalents are available in the new linter.
- VS Code uses Oxc for root and MCP files. Oxfmt ignores `packages/cli/**`;
use `pnpm --filter sentry exec biome format --write <file>` to format a CLI
file and keep CLI's Biome checks in the pre-commit hook.

### Naming Conventions

- Files: `kebab-case.ts`
- Functions: `camelCase`
- Types/Classes: `PascalCase`
- Constants: `UPPER_SNAKE_CASE`

### Import Order

```typescript
// 1. Node built-ins
import { readFile } from "node:fs/promises";
Expand All @@ -50,14 +59,14 @@ export const toolName = {
description: "Clear, concise description",
parameters: z.object({
required: z.string().describe("Description"),
optional: z.string().optional()
optional: z.string().optional(),
}),
execute: async (params, context) => {
// 1. Validate inputs
// 2. Call API
// 3. Format output
return formatResponse(data);
}
},
};
```

Expand All @@ -68,17 +77,18 @@ describe("Component", () => {
it("handles normal case", async () => {
// Arrange
const input = createTestInput();

// Act
const result = await method(input);

// Assert
expect(result).toMatchInlineSnapshot();
});
});
```

Key practices:

- Use inline snapshots for formatting
- Mock with MSW
- Test success and error paths
Expand All @@ -87,29 +97,32 @@ Key practices:
## Quality Checklist

Before committing:

```bash
pnpm -w run lint # Biome check
pnpm -w run lint # Oxlint and ast-grep checks for MCP and root files
pnpm -w run lint:fix # Fix issues
pnpm -w run format # Format MCP and root files with Oxfmt
pnpm --filter sentry run lint # CLI Biome rules
pnpm tsc --noEmit # Type check
pnpm test # Run tests
pnpm -w run build # Build all
```

## JSDoc Pattern

```typescript
````typescript
/**
* Brief description.
*
*
* @param param - Description
* @returns What it returns
*
*
* @example
* ```typescript
* const result = func(param);
* ```
*/
```
````

## Security Essentials

Expand All @@ -121,6 +134,7 @@ pnpm -w run build # Build all
## Common Patterns

For shared patterns see:

- Error handling: [common-patterns.md](common-patterns.md#error-handling)
- Zod schemas: [common-patterns.md](common-patterns.md#zod-schema-patterns)
- API usage: [api-patterns.md](api-patterns.md)
Expand Down
19 changes: 12 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
},
"scripts": {
"docs:check": "node scripts/check-doc-links.mjs",
"test:ci-projects": "node --test scripts/ci-projects.test.mjs scripts/cli-nightly-version.test.mjs scripts/cloudflare-deployment.test.mjs scripts/deploy-workflow.test.mjs scripts/migrate-cloudflare-token.test.mjs",
"test:ci-projects": "node --test scripts/ci-projects.test.mjs scripts/cli-nightly-version.test.mjs scripts/cloudflare-deployment.test.mjs scripts/deploy-workflow.test.mjs scripts/migrate-cloudflare-token.test.mjs scripts/oxc-config.test.mjs",
"dev": "pnpm --filter '@sentry/mcp-cloudflare...' --if-present run build && dotenv -e .env -e .env.local -- pnpm --parallel --filter @sentry/mcp-cloudflare --filter @sentry/mcp-core --filter @sentry/mcp-server-mocks --if-present run dev",
"dev:stdio": "pnpm --filter '@sentry/mcp-server...' --if-present run build && dotenv -e .env -e .env.local -- pnpm --parallel --filter @sentry/mcp-server --filter @sentry/mcp-core --filter @sentry/mcp-server-mocks --if-present run dev",
"build": "dotenv -e .env -e .env.local -- pnpm -r --filter '!sentry' --filter '!sentry-cli-docs' --if-present run build",
Expand All @@ -32,10 +32,10 @@
"deploy:docs": "pnpm --filter sentry run generate:schema && pnpm --filter sentry run generate:docs && pnpm --filter sentry-cli-docs run build && pnpm --filter sentry-cli-docs run deploy",
"eval": "pnpm --filter '@sentry/mcp-server-evals...' --if-present run build && dotenv -e .env -e .env.local -- pnpm --filter @sentry/mcp-server-evals run eval",
"eval:ci": "pnpm --filter '@sentry/mcp-server-evals...' --if-present run build && CI=true dotenv -e .env -e .env.local -- pnpm --stream -r run eval:ci",
"format": "biome format --write",
"lint": "biome lint && pnpm run lint:ast-grep",
"format": "oxfmt --write",
"lint": "oxlint . && pnpm run lint:ast-grep",
"lint:ast-grep": "ast-grep scan && ast-grep test --skip-snapshot-tests",
"lint:fix": "biome lint --fix",
"lint:fix": "oxlint --fix .",
"pre-commit:generated": "node scripts/pre-commit-generated.mjs",
"inspector": "pnpx @modelcontextprotocol/inspector@latest",
"inspector:stdio": "pnpx @modelcontextprotocol/inspector@latest -- tsx packages/mcp-server/src/index.ts",
Expand All @@ -50,11 +50,12 @@
},
"dependencies": {
"@ast-grep/cli": "catalog:",
"@biomejs/biome": "catalog:",
"@types/node": "catalog:",
"dotenv": "catalog:",
"dotenv-cli": "catalog:",
"lint-staged": "catalog:",
"oxfmt": "catalog:",
"oxlint": "catalog:",
"simple-git-hooks": "catalog:",
"tsdown": "catalog:",
"tsx": "catalog:",
Expand All @@ -68,8 +69,12 @@
},
"lint-staged": {
"*": [
"biome format --write --no-errors-on-unmatched --files-ignore-unknown=true",
"biome lint --fix --no-errors-on-unmatched --files-ignore-unknown=true"
"oxfmt --write --no-error-on-unmatched-pattern",
"oxlint --fix --no-error-on-unmatched-pattern"
],
"packages/cli/**/*": [
"pnpm --filter sentry exec biome format --write --no-errors-on-unmatched --files-ignore-unknown=true",
"pnpm --filter sentry exec biome lint --fix --no-errors-on-unmatched --files-ignore-unknown=true"
]
},
"devDependencies": {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import {
Children,
isValidElement,
type KeyboardEvent,
type KeyboardEventHandler,
} from "react";
import { describe, expect, it, vi } from "vitest";
import StepsList from "./StepsList";

const steps = Array.from({ length: 6 }, (_, index) => ({
label: `Step ${index}`,
description: "",
startTime: 0,
pauseMs: null,
}));

describe("StepsList keyboard navigation", () => {
it.each(["Enter", " "])("restarts from the final step with %j", (key) => {
const restart = vi.fn();
const onSelectAction = vi.fn();
const element = StepsList({
steps,
globalIndex: steps.length - 1,
onSelectAction,
restart,
});
const button = Children.toArray(element.props.children).find(
(child) => isValidElement(child) && child.type === "button",
);
if (!isValidElement<{ onKeyDown: KeyboardEventHandler }>(button)) {
throw new Error("Step navigation button is missing");
}

const preventDefault = vi.fn();
button.props.onKeyDown({
key,
preventDefault,
} as unknown as KeyboardEvent<HTMLButtonElement>);

expect(preventDefault).toHaveBeenCalledOnce();
expect(restart).toHaveBeenCalledOnce();
expect(onSelectAction).not.toHaveBeenCalled();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,8 @@ export default function StepsList({
onKeyDown={(e) => {
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
if (globalIndex === 6) {
typeof restart === "function" && restart();
if (globalIndex === 5) {
if (typeof restart === "function") restart();
} else {
onSelectAction(Math.min(globalIndex + 1, steps.length - 1));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ export default function InstallTabs({
const from = c.offsetHeight;
const to = next.offsetHeight;
c.style.height = `${from}px`;
c.offsetHeight;
void c.offsetHeight;
c.style.transition = "height 300ms cubic-bezier(0.2, 0.8, 0.2, 1)";
c.style.height = `${to}px`;
const done = () => {
Expand Down
2 changes: 1 addition & 1 deletion packages/mcp-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@
"test:ci": "pnpm run generate-definitions && pnpm run validate-skills && vitest run --reporter=default --reporter=junit --outputFile=tests.junit.xml",
"test:watch": "pnpm run generate-definitions && vitest",
"tsc": "tsc --noEmit",
"generate-definitions": "tsx scripts/generate-definitions.ts && biome format --write src/toolDefinitions.json src/skillDefinitions.json",
"generate-definitions": "tsx scripts/generate-definitions.ts && oxfmt --write src/toolDefinitions.json src/skillDefinitions.json",
"generate-otel-namespaces": "tsx scripts/generate-otel-namespaces.ts",
"measure-tokens": "tsx scripts/measure-token-cost.ts",
"validate-skills": "tsx scripts/validate-skills-mapping.ts"
Expand Down
2 changes: 1 addition & 1 deletion packages/mcp-core/src/telem/logging.ts
Original file line number Diff line number Diff line change
Expand Up @@ -408,7 +408,7 @@ export function logIssue(

const { attachments, ...baseOptions } = options;
const extra: LogContext = {
...(baseOptions.extra ?? {}),
...baseOptions.extra,
...(attachments && Object.keys(attachments).length > 0
? { attachments: Object.keys(attachments) }
: {}),
Expand Down
Loading
Loading