Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Create a new ESLint rule with tests for eslintPluginScraps. Use when asked to "create a lint rule", "add an eslint rule", "scaffold a rule", "write a new scraps rule", or "new design system lint rule". Covers rule creation, test authoring, registration, and autofix implementation.
| Model | Eval pass | Runs |
|---|---|---|
| gemini-3.6-flash | 84% | 20 |
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-07 | ✗→✓ | ▲ Improved | 25% | 0% |
| case-08 | ✗→✓ | ▲ Improved | -16% | 0% |
| case-09 | ✗→✓ | ▲ Improved | 51% | 0% |
| case-10 | ✗→✓ | ▲ Improved | 11% | 0% |
| case-11 | ✗→✓ | ▲ Improved | -50% | 0% |
Create a new ESLint rule named $ARGUMENTS in the eslintPluginScraps plugin.
Read references/rule-archetypes.md and pick the archetype that matches your rule's intent:
| You want to... | Archetype | Reference to load | | ------------------------------------------- | ---------------------- | ---------------------------------------------------------------- | | Rewrite import paths | Import rewrite | Inline — simple pattern | | Validate token/value usage per CSS property | Property validation | style-collector-guide.md | | Restrict JSX elements in specific props | JSX structural | rule-archetypes.md §Archetype 3 | | Detect patterns in static CSS text | Template text analysis | rule-archetypes.md §Archetype 4 |
Read the relevant reference before writing code. The archetypes document which AST visitors to use, which shared utilities apply, and which patterns are NOT appropriate for each approach.
Before writing AST traversal logic, check static/oxlint/eslintPluginScraps/src/ast/ for reusable code:
| Utility | Location | Use for | | ----------------------- | ---------------------------------------- | ------------------------------------------------------------------- | | getStyledCallInfo | src/ast/utils/styled.ts | Classifying styled/css calls as element, component, or css | | createQuasiScanner | src/ast/scanner/index.ts | Scanning static CSS text in template literals (Archetype 4) | | createImportTracker | src/ast/tracker/imports.ts | Resolving where a local name was imported from | | createStyleCollector | src/ast/extractor/index.ts | Collecting CSS-in-JS _dynamic value_ declarations (NOT static text) | | shouldAnalyze | src/ast/extractor/index.ts | Fast pre-scan to skip files without Emotion usage | | normalizePropertyName | src/ast/utils/normalizePropertyName.ts | Normalizing CSS property names | | decomposeValue | src/ast/extractor/value-decomposer.ts | Breaking complex expressions into all possible values | | Theme tracker | src/ast/tracker/theme.ts | Tracking useTheme() and callback theme bindings |
If another rule already solves a similar problem, extract shared logic into src/ast/utils/ and reuse it.
static/oxlint/eslintPluginScraps/src/rules/$ARGUMENTS.tsstatic/oxlint/eslintPluginScraps/src/rules/$ARGUMENTS.spec.tstypescriptimport {ESLintUtils} from '@typescript-eslint/utils'; export const $RULE_NAME = ESLintUtils.RuleCreator.withoutDocs({ meta: { type: 'problem', docs: { description: '[Rule description]', }, fixable: 'code', // include if rule has autofix — see Autofix Guidance schema: [], messages: { forbidden: 'Error message shown to user', }, }, create(context) { return { // AST visitor methods — see your chosen archetype }; }, });
If your rule needs configurable options, load references/schema-patterns.md.
typescriptimport {RuleTester} from '@typescript-eslint/rule-tester'; import {$RULE_NAME} from './$ARGUMENTS'; const ruleTester = new RuleTester(); ruleTester.run('$ARGUMENTS', $RULE_NAME, { valid: [ { code: '// valid code', filename: '/project/src/file.tsx', }, ], invalid: [ { code: '// invalid code', filename: '/project/src/file.tsx', errors: [{messageId: 'forbidden'}], output: '// expected output after autofix', // REQUIRED for fixable rules }, ], });
Run tests:
bashpnpm test-ci "static/oxlint/eslintPluginScraps/src/rules/$ARGUMENTS.spec.ts"
Default stance: implement autofix unless the transformation is ambiguous or could change runtime behavior.
no-core-import.ts as canonical example)typescriptcontext.report({ node, messageId: 'forbidden', fix(fixer) { return fixer.replaceText(node, newText); // Also: fixer.replaceTextRange([start, end], text) // fixer.insertTextBefore(node, text) // fixer.insertTextAfter(node, text) // fixer.remove(node) // Return single fix or array of fixes }, });
When a rule is fixable, every invalid test case MUST include output showing the expected code after the fix.
Add to static/oxlint/eslintPluginScraps/src/rules/index.ts:
typescriptimport {$RULE_NAME} from './$ARGUMENTS'; export const rules = { // existing rules... $ARGUMENTS: $RULE_NAME, };
Add to eslint.config.ts inside the name: 'plugin/@sentry/scraps' block:
typescript'@sentry/scraps/$ARGUMENTS': 'error', // or with options: '@sentry/scraps/$ARGUMENTS': ['error', { /* options */ }],
bashpnpm test-ci "static/oxlint/eslintPluginScraps/src/rules/$ARGUMENTS.spec.ts"
If modifying an existing rule rather than creating a new one:
use-semantic-token): changes often only require editing the config file (e.g., src/config/tokenRules.ts), not the rule logicbuildPropertyToRule)my-rule-name — verb-noun pattern (e.g., no-token-import, use-semantic-token)myRuleNamemy-rule-name.ts, my-rule-name.spec.ts)| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→fail | 18,507 | 15,761 | -15% | 1 | 1 | 0% | 2,803 | 2,222 | -21% | 0 | 0 | — |
case-02 | fail→fail | 45,918 | 14,973 | -67% | 1 | 1 | 0% | 8,242 | 2,025 | -75% | 0 | 0 | — |
case-03 | fail→fail | 30,096 | 9,908 | -67% | 1 | 1 | 0% | 5,771 | 2,054 | -64% | 0 | 0 | — |
case-04 | fail→fail | 29,026 | 16,656 | -43% | 1 | 1 | 0% | 4,421 | 2,146 | -51% | 0 | 0 | — |
case-05 | fail→fail | 20,063 | 39,909 | +99% | 1 | 1 | 0% | 3,186 | 8,910 | +180% | 0 | 0 | — |
case-06 | fail→fail | 13,843 | 14,325 | +3% | 1 | 1 | 0% | 1,689 | 2,050 | +21% | 0 | 0 | — |
case-07 | fail→pass | 15,940 | 3,350 | -79% | 1 | 1 | 0% | 1,822 | 2,270 | +25% | 0 | 0 | — |
case-08 | fail→pass | 23,345 | 9,724 | -58% | 1 | 1 | 0% | 3,228 | 2,711 | -16% | 0 | 0 | — |
case-09 | fail→pass | 13,107 | 7,761 | -41% | 1 | 1 | 0% | 1,457 | 2,205 | +51% | 0 | 0 | — |
case-10 | fail→pass | 16,008 | 8,266 | -48% | 1 | 1 | 0% | 2,054 | 2,284 | +11% | 0 | 0 | — |
case-11 | fail→pass | 25,898 | 6,880 | -73% | 1 | 1 | 0% | 4,202 | 2,120 | -50% | 0 | 0 | — |
case-12 | fail→fail | 17,423 | 7,269 | -58% | 1 | 1 | 0% | 2,061 | 2,131 | +3% | 0 | 0 | — |
case-13 | fail→fail | 25,654 | 6,752 | -74% | 1 | 1 | 0% | 3,115 | 2,049 | -34% | 0 | 0 | — |
case-14 | fail→pass | 12,170 | 8,210 | -33% | 1 | 1 | 0% | 1,203 | 2,312 | +92% | 0 | 0 | — |
case-15 | fail→fail | 11,955 | 8,146 | -32% | 1 | 1 | 0% | 1,275 | 2,299 | +80% | 0 | 0 | — |
case-16 | pass→fail | 19,492 | 15,256 | -22% | 1 | 1 | 0% | 2,303 | 2,129 | -8% | 0 | 0 | — |
case-17 | pass→pass | 18,665 | 7,823 | -58% | 1 | 1 | 0% | 2,160 | 2,137 | -1% | 0 | 0 | — |
case-18 | fail→pass | 14,153 | 7,529 | -47% | 1 | 1 | 0% | 1,594 | 2,196 | +38% | 0 | 0 | — |
case-19 | fail→pass | 26,606 | 7,272 | -73% | 1 | 1 | 0% | 4,159 | 2,144 | -48% | 0 | 0 | — |
case-20 | fail→pass | 13,896 | 9,244 | -33% | 1 | 1 | 0% | 1,406 | 2,471 | +76% | 0 | 0 | — |
case-21 | fail→pass | 12,736 | 7,360 | -42% | 1 | 1 | 0% | 1,549 | 2,264 | +46% | 0 | 0 | — |
case-22 | fail→fail | 22,457 | 6,796 | -70% | 1 | 1 | 0% | 2,884 | 2,027 | -30% | 0 | 0 | — |
case-23 | pass→pass | 9,896 | 8,130 | -18% | 1 | 1 | 0% | 785 | 2,306 | +194% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 23 cases were attempted, and 17 counted toward the lift figure. The other 6 produced results that are not comparable between the two arms, so they are excluded from the headline rather than averaged into it. The headline lift of +39 percentage points is the difference between those two pass rates over the 17 comparable cases. 1 case got worse with the skill loaded, and it is included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
| Model | Method | Date | Lift |
|---|---|---|---|
| gemini-3.6-flash | verified | 7/27/2026 | +36% |
Other measured skills in the registry, with their headline benchmark lift.