Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
52b589c
Initial plan
Copilot Jan 17, 2026
635e997
feat: add dual ESLint config support for v8 CJS and v9 ESM
Copilot Jan 17, 2026
2411c62
fix: update ESLint v9 config to use .mjs extension and adjust strict …
Copilot Jan 17, 2026
a38e326
docs: add examples, changelog, and CI workflows
Copilot Jan 17, 2026
31e45e6
style: format all files with Prettier
Copilot Jan 17, 2026
3bf2ff6
chore: license and commitlint fixes, add eslint skill
anchildress1 Jan 17, 2026
529124f
chore: enforce prettier-first linting and improve ci workflow
anchildress1 Jan 17, 2026
1725b1d
chore: enforce prettier-first linting and improve ci workflow
anchildress1 Jan 17, 2026
a60d3b0
fix: align implementation with PR description - add airbnb-base, jest…
Copilot Jan 18, 2026
2c0f08a
fix: make no-warning-comments rule consistent across v8 and v9 configs
Copilot Jan 18, 2026
c712558
fix: resolve commitlint failure by commenting out unavailable plugin
Copilot Jan 18, 2026
6063cd0
chore: shorten commit message in commit.tmp to follow best practices
Copilot Jan 18, 2026
77a8866
refactor(repo): convert to Prettier-only, add cjs flat config support
anchildress1 Jan 18, 2026
46fbc04
ci: fix Codecov uploads and add CodeQL scanning
anchildress1 Jan 18, 2026
9737c3c
fix: address PR review comments
anchildress1 Jan 18, 2026
dc1c324
chore(ci): enforce coverage thresholds and align CI docs
anchildress1 Jan 18, 2026
23289cc
docs(agents): refresh repo working memory
anchildress1 Jan 18, 2026
b060e60
ci: fix CI workflow issues and remove CodeQL
anchildress1 Jan 19, 2026
67fff1b
ci: update workflows to latest actions and remove lockfile requirements
anchildress1 Jan 21, 2026
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
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* @anchildress1
238 changes: 238 additions & 0 deletions .github/skills/eslint-plugin-configuring/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,238 @@
---
status: draft
name: eslint-plugin-configs
description: Generate or update an ESLint plugin that exports rule configs compatible with ESLint v8 (eslintrc) and ESLint v9 (flat config).
---

## Role

You are the agent responsible for creating or modifying an **ESLint plugin package** that exports:

- `rules`
- `configs` (flat and/or legacy)
- optional `processors`
- recommended `meta` (`name`, `version`, `namespace`)

Your output is plugin source code plus correct consumer usage examples.

This Skill targets **ESLint v8.x and v9.x** behavior explicitly.

---

## Operating Assumptions

### ESLint Version Semantics

- **ESLint v9**
- Flat config is the default.
- `.eslintrc*` is deprecated.
- Legacy configs only apply if `ESLINT_USE_FLAT_CONFIG=false`.

- **ESLint v8**
- `.eslintrc*` is the common default.
- Flat config is opt-in via `eslint.config.js` or `ESLINT_USE_FLAT_CONFIG=true`.

You must not misstate these behaviors.

---

## Non-Goals (Hard Constraints)

- Do not claim the plugin can force configuration usage.
- Do not invent undocumented config resolution logic.
- Do not mix flat and legacy config shapes.
- Do not introduce ambiguous or colliding config names.

---

## Required Inputs (Implicit)

Assume the following exist or are derivable:

- `PACKAGE_NAME` (npm package name)
- `NAMESPACE` (rule/config prefix)
- `RULES` (map of `ruleId -> rule implementation`)
- Optional desired config set (e.g. `recommended`, `strict`)

---

## Mandatory Plugin Shape

The plugin **must** export a single object with:

- `meta`
- `name`
- `version`
- `namespace`
- `rules`
- `configs`
- optional `processors`

Preferred export is **ESM default export**.

---

## Meta Rules

- `meta.namespace` is the canonical prefix for:
- rules
- configs
- plugin registration
- All rule references must use `"<namespace>/<ruleId>"`.
- Namespace consistency is mandatory across all outputs.

---

## Config Export Strategies

Choose **exactly one** strategy and apply it consistently.

### Strategy A — New Plugin (Recommended)

Use explicit separation:

- Flat configs: `flat/<configName>`
- Legacy configs: `legacy-<configName>`

This avoids collisions and makes intent unambiguous.

### Strategy B — Existing Plugin Compatibility

If a legacy config already exists as `<configName>` and cannot be renamed:

- Preserve legacy key: `<configName>`
- Add flat variant: `flat/<configName>`

Never remove or silently rename an existing legacy config.

---

## Flat Config Requirements (ESLint v9 Primary)

Flat configs must:

- Be arrays of config objects (preferred).
- Register the plugin using object form:
- `plugins: { [namespace]: plugin }`
- Enable rules using namespaced keys.
- Optionally define `languageOptions`.

Example shape (conceptual):

- `configs["flat/recommended"] -> Array<FlatConfigObject>`

---

## Legacy Config Requirements (ESLint v8 Compatibility)

Legacy configs must:

- Be plain eslintrc-shaped objects.
- Register plugin using:
- `plugins: ["<namespace>"]`
- Enable rules using namespaced keys.
- Optionally include `globals`, `parserOptions`, etc.

Example shape (conceptual):

- `configs["legacy-recommended"] -> EslintrcObject`

---

## Self-Reference Rule (Critical)

If configs need to reference the plugin object itself:

1. Instantiate `plugin` with empty `configs`.
2. Assign configs **after** plugin creation (e.g. via `Object.assign`).

Never reference `plugin` before it exists.

---

## Consumer Mapping Rules

### Flat Config Consumers

If the plugin exports:

- `configs["flat/recommended"]`

Then consumers extend:

- `"namespace/recommended"`

The `flat/` prefix is **not** used by consumers.

### Legacy Consumers

If the plugin exports:

- `configs["legacy-recommended"]`

Then consumers extend:

- `"namespace/legacy-recommended"`

If preserving an existing legacy name:

- `configs["recommended"]` → `"namespace/recommended"`

---

## Required Consumer Examples

Unless explicitly excluded, you must output:

1. **Flat config example**
- `eslint.config.js`
- Uses `defineConfig`
- Registers plugin
- Uses `extends`

2. **Legacy config example**
- `.eslintrc` (JSON/YAML/JS)
- Uses `plugins` + `extends`

Examples must match the chosen naming strategy exactly.

---

## Repair / Migration Rules

When updating existing plugins:

- Legacy only → add `flat/<name>` if dual support is required.
- Flat only → add `legacy-<name>` if v8 support is required.
- Missing `meta.namespace` → add it and realign all rule prefixes.
- Incorrect consumer examples → regenerate to match config keys.

Never silently change public config names.

---

## Validation Checklist (Fail Fast)

Confirm all of the following:

- `plugin.meta.namespace` exists and matches all rule prefixes.
- Flat configs:
- arrays (preferred)
- register plugin via object form
- Legacy configs:
- eslintrc object shape
- plugin registered via array
- No config key collisions.
- ESLint v8 vs v9 behavior is stated correctly.
- No claim that the plugin forces config usage.

---

## Output Expectations

When executing this Skill, output:

- Plugin source code (or diffs).
- Flat consumer example.
- Legacy consumer example.
- Brief validation confirmation.
38 changes: 38 additions & 0 deletions .github/skills/eslint-plugin-configuring/examples/consumers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
## ESLint v9 Flat Config Consumer

```js
import { defineConfig } from 'eslint/config';
import myPlugin from 'eslint-plugin-yourplugin';

export default defineConfig([
{
files: ['**/*.{js,ts}'],
plugins: {
[myPlugin.meta.namespace]: myPlugin,
},
extends: [`${myPlugin.meta.namespace}/recommended`],
},
]);
```

---

## ESLint v8 Legacy Consumer (legacy-\* strategy)

```jsonc
{
"plugins": ["yourplugin-namespace"],
"extends": ["yourplugin-namespace/legacy-recommended"],
}
```

---

## ESLint v8 Legacy Consumer (preserved name strategy)

```jsonc
{
"plugins": ["yourplugin-namespace"],
"extends": ["yourplugin-namespace/recommended"],
}
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
## Plugin Skeleton (ESM)

```js
const plugin = {
meta: {
name: PACKAGE_NAME,
version: PACKAGE_VERSION,
namespace: NAMESPACE,
},
rules: RULES,
configs: {},
// processors: {}
};

export default plugin;
```

---

## Config Assignment Pattern

Use this pattern when configs need access to the plugin object:

```js
Object.assign(plugin.configs, {
'flat/recommended': [
{
plugins: {
[plugin.meta.namespace]: plugin,
},
rules: {
// "<namespace>/<ruleId>": "error"
},
},
],

'legacy-recommended': {
plugins: [plugin.meta.namespace],
rules: {
// "<namespace>/<ruleId>": "error"
},
},
});
```

---

## Strategy Selection

### Use Strategy A when

- Creating a new plugin.
- You control the public API.
- You want zero ambiguity.

### Use Strategy B when

- Legacy config names already exist.
- Breaking changes are not acceptable.

---

## Rule Keying

- Rule IDs inside the plugin must not contain `/`.
- Rule references in configs must always be `<namespace>/<ruleId>`.

---

## Prohibited Patterns

- Flat config objects exported as legacy configs.
- Legacy configs exported as arrays.
- Plugin rules referenced without namespace.
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
## Validation Checklist

### Meta

- meta.name present
- meta.version present
- meta.namespace present and consistent

### Flat Configs

- Value is an array (preferred)
- Plugin registered via object form
- Rules use `<namespace>/<ruleId>`

### Legacy Configs

- Value is eslintrc-shaped object
- Plugin registered via array
- Rules use `<namespace>/<ruleId>`

### Naming

- Strategy A or B applied consistently
- No colliding config keys

### Semantics

- ESLint v9 flat default acknowledged
- ESLint v8 legacy default acknowledged
- No claim of forced config usage
Loading