.agents/skills/lint-new/SKILL.md
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/eslint/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/eslint/eslintPluginScraps/src/rules/$ARGUMENTS.tsstatic/eslint/eslintPluginScraps/src/rules/$ARGUMENTS.spec.tsimport {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.
import {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:
pnpm test-ci "static/eslint/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)context.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/eslint/eslintPluginScraps/src/rules/index.ts:
import {$RULE_NAME} from './$ARGUMENTS';
export const rules = {
// existing rules...
$ARGUMENTS: $RULE_NAME,
};
Add to eslint.config.ts inside the name: 'plugin/@sentry/scraps' block:
'@sentry/scraps/$ARGUMENTS': 'error',
// or with options:
'@sentry/scraps/$ARGUMENTS': ['error', { /* options */ }],
pnpm test-ci "static/eslint/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)