codebyplan 1.11.2 → 1.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +590 -405
- package/package.json +1 -1
- package/templates/hooks/README.md +1 -13
- package/templates/hooks/cbp-statusline.mjs +44 -0
- package/templates/hooks/cbp-statusline.py +24 -2
- package/templates/hooks/cbp-statusline.sh +22 -2
- package/templates/hooks/cbp-test-coverage-gate.sh +8 -0
- package/templates/hooks/cbp-test-hooks.sh +0 -42
- package/templates/hooks/hooks.json +0 -9
- package/templates/rules/README.md +8 -1
- package/templates/rules/supabase-branch-lifecycle.md +99 -0
- package/templates/settings.project.base.json +1 -2
- package/templates/skills/cbp-build-cc-settings/reference/cbp-conventions.md +1 -2
- package/templates/skills/cbp-checkpoint-create/SKILL.md +2 -0
- package/templates/skills/cbp-checkpoint-end/SKILL.md +27 -5
- package/templates/skills/cbp-checkpoint-start/SKILL.md +2 -2
- package/templates/skills/cbp-git-worktree-remove/SKILL.md +17 -1
- package/templates/skills/cbp-session-start/SKILL.md +28 -3
- package/templates/skills/{cbp-e2e-setup → cbp-setup-e2e}/SKILL.md +1 -1
- package/templates/skills/cbp-setup-eslint/SKILL.md +199 -0
- package/templates/skills/cbp-setup-eslint/reference/base.md +82 -0
- package/templates/skills/cbp-setup-eslint/reference/cli.md +56 -0
- package/templates/skills/cbp-setup-eslint/reference/e2e.md +68 -0
- package/templates/skills/cbp-setup-eslint/reference/jest.md +59 -0
- package/templates/skills/cbp-setup-eslint/reference/nestjs.md +69 -0
- package/templates/skills/cbp-setup-eslint/reference/nextjs.md +63 -0
- package/templates/skills/cbp-setup-eslint/reference/node.md +74 -0
- package/templates/skills/cbp-setup-eslint/reference/react-native.md +60 -0
- package/templates/skills/cbp-setup-eslint/reference/react.md +82 -0
- package/templates/skills/cbp-setup-eslint/reference/tailwind.md +64 -0
- package/templates/skills/cbp-setup-eslint/reference/testing-react.md +57 -0
- package/templates/skills/cbp-setup-eslint/reference/vitest.md +62 -0
- package/templates/skills/cbp-ship-main/SKILL.md +13 -0
- package/templates/skills/cbp-supabase-branch-check/SKILL.md +12 -5
- package/templates/skills/cbp-supabase-migrate/SKILL.md +139 -9
- package/templates/skills/cbp-supabase-migrate/reference/preflight-dry-run.md +1 -1
- package/templates/skills/cbp-supabase-setup/SKILL.md +13 -7
- package/templates/skills/cbp-supabase-setup/reference/branching-setup.md +2 -2
- package/templates/skills/cbp-task-complete/SKILL.md +1 -3
- package/templates/skills/cbp-task-start/SKILL.md +5 -3
- package/templates/hooks/cbp-mcp-worktree-inject.sh +0 -76
- /package/templates/skills/{cbp-e2e-setup → cbp-setup-e2e}/reference/maestro.md +0 -0
- /package/templates/skills/{cbp-e2e-setup → cbp-setup-e2e}/reference/playwright.md +0 -0
- /package/templates/skills/{cbp-e2e-setup → cbp-setup-e2e}/reference/tauri.md +0 -0
- /package/templates/skills/{cbp-e2e-setup → cbp-setup-e2e}/reference/vscode.md +0 -0
- /package/templates/skills/{cbp-e2e-setup → cbp-setup-e2e}/reference/xcuitest.md +0 -0
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
---
|
|
2
|
+
scope: org-shared
|
|
3
|
+
name: cbp-setup-eslint
|
|
4
|
+
description: Detect each app's tech stack, resolve matching DB ESLint presets, confirm which to enable per app, run `codebyplan eslint init` to generate eslint.config.mjs, and write/refresh .codebyplan/eslint.json. Interactive, idempotent.
|
|
5
|
+
argument-hint: "[--force]"
|
|
6
|
+
model: sonnet
|
|
7
|
+
effort: xhigh
|
|
8
|
+
allowed-tools: Read, Write, Edit, Bash(cat *), Bash(jq *), Bash(test *), Bash(ls *), Bash(mkdir *), Bash(cp *), Bash(echo *), Bash(mv *), Bash(npx codebyplan eslint *), AskUserQuestion, mcp__codebyplan__get_repos, mcp__codebyplan__get_eslint_presets
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# ESLint Setup
|
|
12
|
+
|
|
13
|
+
Configure ESLint flat config (`eslint.config.mjs`) across the repo's apps: detect each
|
|
14
|
+
app's tech stack, resolve the DB ESLint presets that match it, generate per-app configs via
|
|
15
|
+
`codebyplan eslint init`, and record the enabled presets in `.codebyplan/eslint.json` so the
|
|
16
|
+
setup is reproducible and refreshes idempotently.
|
|
17
|
+
|
|
18
|
+
Invoke at any time. Already-configured apps are preserved unless `--force` is passed.
|
|
19
|
+
Pass `--force` to re-ask every app and re-resolve presets from scratch.
|
|
20
|
+
|
|
21
|
+
The DB ESLint presets are the source of truth for rule bodies; `.codebyplan/eslint.json`
|
|
22
|
+
only records *which* presets are enabled per app (the rules live in the generated
|
|
23
|
+
`eslint.config.mjs` and the DB). See `reference/*.md` for the latest official flat-config
|
|
24
|
+
setup per stack — including stacks that have **no preset yet** (manual-config guidance).
|
|
25
|
+
|
|
26
|
+
## Arguments
|
|
27
|
+
|
|
28
|
+
Inspect `$ARGUMENTS` for `--force`. If present, set `force_mode = true`.
|
|
29
|
+
Absent: idempotent mode — preserve existing `apps[*]` entries in `.codebyplan/eslint.json`,
|
|
30
|
+
skip re-asking already-configured apps.
|
|
31
|
+
|
|
32
|
+
## Step 1 — Detect apps + per-app tech
|
|
33
|
+
|
|
34
|
+
Run two detection signals and merge:
|
|
35
|
+
|
|
36
|
+
**Signal A — DB tech_stack** via `mcp__codebyplan__get_repos` (match `repo_id` from
|
|
37
|
+
`.codebyplan/repo.json`). Read the repo's `tech_stack`:
|
|
38
|
+
- `tech_stack.apps[]` — each `{ name, path, stack[] }` is one app/package (monorepo).
|
|
39
|
+
- `tech_stack.flat[]` — the deduped repo-wide stack (single-app repos, or when `apps[]` is empty).
|
|
40
|
+
|
|
41
|
+
**Signal B — Filesystem fallback** (when `apps[]` is empty or stale). Discover workspaces the
|
|
42
|
+
same way `tech-detect.ts` does, then probe each app's `package.json`:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
# workspace globs → app dirs that contain a package.json
|
|
46
|
+
cat pnpm-workspace.yaml 2>/dev/null # packages: [...] globs
|
|
47
|
+
jq -r '.workspaces[]?' package.json 2>/dev/null # or package.json#workspaces
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
For each app dir, read `package.json` dependencies to infer the stack (next → Next.js,
|
|
51
|
+
react → React, @nestjs/* → NestJS, vitest → Vitest, jest → Jest, @playwright/test →
|
|
52
|
+
Playwright, tailwindcss → Tailwind, expo → Expo, etc.). A single-app repo is one target
|
|
53
|
+
with `source_path: "."` and `app: "root"`.
|
|
54
|
+
|
|
55
|
+
**Signal C — Read existing `.codebyplan/eslint.json`** for the idempotent merge:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
cat .codebyplan/eslint.json 2>/dev/null || echo '{}'
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
An app already present under `apps[<source_path>]` is "configured"; in non-`--force` mode it
|
|
62
|
+
is skipped in Step 3 (presets preserved verbatim).
|
|
63
|
+
|
|
64
|
+
## Step 2 — Resolve matched presets
|
|
65
|
+
|
|
66
|
+
Call `mcp__codebyplan__get_eslint_presets` with `repo_id`. The response gives:
|
|
67
|
+
- `presets[]` — all system presets (base, nextjs, react, node, cli, testing, testing-react, testing-e2e).
|
|
68
|
+
- `matched[]` — the subset whose `tech_match` (requires / excludes / requires_capabilities) the
|
|
69
|
+
repo's DB tech_stack satisfies. **Use this — do not re-implement the matching client-side.**
|
|
70
|
+
|
|
71
|
+
`matched[]` is repo-wide. Attribute presets to apps by intersecting each app's detected tech
|
|
72
|
+
with each preset's `tech_match.requires` (e.g. `nextjs` → apps with Next.js; `node`/`cli` →
|
|
73
|
+
the Node/CLI packages; `testing-e2e` → apps with Playwright). The `base` preset applies to
|
|
74
|
+
every TypeScript app.
|
|
75
|
+
|
|
76
|
+
**Gap stacks have NO preset.** NestJS, React Native/Expo, Jest, Tailwind, and
|
|
77
|
+
WebdriverIO/Mocha are real stacks with no system preset. When an app's tech includes one,
|
|
78
|
+
flag it in Step 3 and point at its `reference/*.md` for manual setup — never silently drop it.
|
|
79
|
+
|
|
80
|
+
## Step 3 — Confirm presets per app
|
|
81
|
+
|
|
82
|
+
For each detected app NOT already configured (or every app in `--force` mode), present its
|
|
83
|
+
matched presets and ask via AskUserQuestion (one batch per app, matched pre-checked):
|
|
84
|
+
|
|
85
|
+
```
|
|
86
|
+
App: apps/web (Next.js + React + SCSS + Playwright + Vitest)
|
|
87
|
+
Enable which ESLint presets?
|
|
88
|
+
[x] base (TypeScript + security + Prettier)
|
|
89
|
+
[x] nextjs (Core Web Vitals + jsx-a11y + import order)
|
|
90
|
+
[x] testing (Vitest)
|
|
91
|
+
[x] testing-react (Testing Library + jest-dom)
|
|
92
|
+
[x] testing-e2e (Playwright)
|
|
93
|
+
Gap stacks with no preset (manual setup — see reference/):
|
|
94
|
+
· tailwind → reference/tailwind.md (if this app uses Tailwind)
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Record the confirmed preset-name list per app. Toggling is allowed; an app may opt out of a
|
|
98
|
+
matched preset or in to an unmatched one.
|
|
99
|
+
|
|
100
|
+
## Step 4 — Generate config (`codebyplan eslint init`)
|
|
101
|
+
|
|
102
|
+
Offer to run the CLI, which detects tech, resolves presets, generates each
|
|
103
|
+
`eslint.config.mjs`, installs missing deps, and saves the DB config:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
npx codebyplan eslint init
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
**Degrade gracefully.** The CLI is sometimes unavailable (OAuth/MCP env, stale bin). If it
|
|
110
|
+
errors or is absent, do NOT hard-fail — print the command for the user to run manually and
|
|
111
|
+
continue to Step 5 (the `.codebyplan/eslint.json` record is written regardless). For gap
|
|
112
|
+
stacks, point the user at the matching `reference/*.md` to hand-author the config.
|
|
113
|
+
|
|
114
|
+
## Step 5 — Write `.codebyplan/eslint.json`
|
|
115
|
+
|
|
116
|
+
Build the payload conforming to the `EslintLocalConfig` schema
|
|
117
|
+
(`packages/codebyplan-package/src/lib/types.ts`): an `apps` map keyed by `source_path`, each
|
|
118
|
+
value an `EslintAppConfig` `{ app, enabled_presets[], rule_overrides?, config_path, reference_docs? }`.
|
|
119
|
+
|
|
120
|
+
Idempotency rule: deep-merge the new per-app entries onto the existing `apps` map so apps
|
|
121
|
+
skipped by the Step 3 gate keep their prior entry. Assemble in the shell, then write
|
|
122
|
+
atomically (tmp + mv) so the file is never left partial:
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
EXISTING_APPS=$(jq -c '.apps // {}' .codebyplan/eslint.json 2>/dev/null || echo '{}')
|
|
126
|
+
APPS_JSON=$(echo "$EXISTING_APPS" | jq -c --argjson new "$NEW_APPS_JSON" '. * $new')
|
|
127
|
+
jq -n --argjson apps "$APPS_JSON" '{apps: $apps}' \
|
|
128
|
+
> .codebyplan/eslint.json.tmp && mv .codebyplan/eslint.json.tmp .codebyplan/eslint.json
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Example populated entry:
|
|
132
|
+
|
|
133
|
+
```json
|
|
134
|
+
{
|
|
135
|
+
"apps": {
|
|
136
|
+
"apps/web": {
|
|
137
|
+
"app": "web",
|
|
138
|
+
"enabled_presets": ["base", "nextjs", "testing", "testing-react", "testing-e2e"],
|
|
139
|
+
"config_path": "apps/web/eslint.config.mjs",
|
|
140
|
+
"reference_docs": ["base", "nextjs", "testing-react", "e2e"]
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Only the `apps` field is written — `EslintLocalConfig` defines no other. Never store rule
|
|
147
|
+
bodies here; they live in the DB presets and the generated `eslint.config.mjs`.
|
|
148
|
+
|
|
149
|
+
## Step 6 — Verify and report
|
|
150
|
+
|
|
151
|
+
Re-read `.codebyplan/eslint.json` and emit a per-app summary:
|
|
152
|
+
|
|
153
|
+
```
|
|
154
|
+
ESLint Setup — Complete
|
|
155
|
+
|
|
156
|
+
App | Presets | Config | Gaps
|
|
157
|
+
---------------------------- | ------------------------------------ | ------------------- | --------
|
|
158
|
+
apps/web | base, nextjs, testing, testing-react | apps/web/eslint…mjs | —
|
|
159
|
+
packages/codebyplan-package | base, node, cli, testing | …/eslint.config.mjs | —
|
|
160
|
+
|
|
161
|
+
eslint.json written to .codebyplan/eslint.json
|
|
162
|
+
|
|
163
|
+
Per-stack setup references — see reference docs:
|
|
164
|
+
base/typescript → reference/base.md nextjs → reference/nextjs.md
|
|
165
|
+
react → reference/react.md node → reference/node.md
|
|
166
|
+
nestjs → reference/nestjs.md cli → reference/cli.md
|
|
167
|
+
tailwind → reference/tailwind.md expo → reference/react-native.md
|
|
168
|
+
vitest → reference/vitest.md jest → reference/jest.md
|
|
169
|
+
testing-react → reference/testing-react.md e2e → reference/e2e.md
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
If `codebyplan eslint init` was skipped (CLI unavailable), say so and restate the manual
|
|
173
|
+
command + which apps still need their `eslint.config.mjs` generated.
|
|
174
|
+
|
|
175
|
+
## Key Rules
|
|
176
|
+
|
|
177
|
+
- DB presets are the source of truth for rule bodies — `.codebyplan/eslint.json` records only
|
|
178
|
+
which presets are enabled per app
|
|
179
|
+
- `get_eslint_presets` `matched[]` drives preset resolution — never re-implement tech matching
|
|
180
|
+
- Gap stacks (NestJS, Expo, Jest, Tailwind, WebdriverIO/Mocha) have no preset — point at
|
|
181
|
+
`reference/*.md`, never silently skip
|
|
182
|
+
- `codebyplan eslint init` failure is non-fatal — print the manual command and still write eslint.json
|
|
183
|
+
- Atomic write (tmp + mv) — never leave eslint.json partial
|
|
184
|
+
- Reference docs track the **latest official** flat-config setup and flag where CBP's DB
|
|
185
|
+
presets diverge (e.g. ESLint v10 + `eslint-plugin-react-hooks@7` bundled compiler rules)
|
|
186
|
+
|
|
187
|
+
## Additional resources
|
|
188
|
+
|
|
189
|
+
- TypeScript foundation (ESLint v10 flat config): [reference/base.md](reference/base.md)
|
|
190
|
+
- Next.js: [reference/nextjs.md](reference/nextjs.md) · React (+ Storybook): [reference/react.md](reference/react.md)
|
|
191
|
+
- Node / Hono / Express: [reference/node.md](reference/node.md) · NestJS: [reference/nestjs.md](reference/nestjs.md)
|
|
192
|
+
- CLI tools: [reference/cli.md](reference/cli.md) · Tailwind CSS: [reference/tailwind.md](reference/tailwind.md)
|
|
193
|
+
- React Native / Expo: [reference/react-native.md](reference/react-native.md)
|
|
194
|
+
- Vitest: [reference/vitest.md](reference/vitest.md) · Jest: [reference/jest.md](reference/jest.md)
|
|
195
|
+
- Testing Library + jest-dom: [reference/testing-react.md](reference/testing-react.md)
|
|
196
|
+
- E2E (Playwright / WebdriverIO / Mocha): [reference/e2e.md](reference/e2e.md)
|
|
197
|
+
- ESLint local-config schema: `packages/codebyplan-package/src/lib/types.ts` (`EslintLocalConfig`)
|
|
198
|
+
- DB presets + generator: `mcp__codebyplan__get_eslint_presets`, `codebyplan eslint init`
|
|
199
|
+
- VS Code extension lint is not yet covered by a reference doc — use `reference/base.md` + `reference/node.md`.
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# base — TypeScript foundation (ESLint v10 flat config)
|
|
2
|
+
|
|
3
|
+
The foundation every TypeScript app shares: ESLint flat config, type-checked
|
|
4
|
+
`typescript-eslint` rules, security scanning, and Prettier. Maps to the CBP **`base`** DB
|
|
5
|
+
preset (`tech_match.requires: ["TypeScript"]`).
|
|
6
|
+
|
|
7
|
+
> **Verified 2026-05-31.** ESLint is on **v10** — flat config (`eslint.config.mjs`) is the
|
|
8
|
+
> only native format (legacy `.eslintrc*` was removed). `defineConfig` from `eslint/config`
|
|
9
|
+
> is the canonical wrapper for every stack below.
|
|
10
|
+
|
|
11
|
+
## Packages
|
|
12
|
+
|
|
13
|
+
| Package | Latest | Purpose |
|
|
14
|
+
| ------- | ------ | ------- |
|
|
15
|
+
| `eslint` | `10.4.1` | the linter (flat config only) |
|
|
16
|
+
| `@eslint/js` | `10.0.1` | `js.configs.recommended` |
|
|
17
|
+
| `typescript-eslint` | `8.60.0` | unified parser + plugin + presets |
|
|
18
|
+
| `eslint-config-prettier` | `10.x` | turns off formatting rules (subpath `/flat`) |
|
|
19
|
+
| `eslint-plugin-security` | `^3` | OWASP-style heuristics |
|
|
20
|
+
| `eslint-plugin-no-secrets` | `^2.1` | high-entropy string detection |
|
|
21
|
+
| `globals` | `17.6.0` | env global definitions |
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pnpm add -D eslint @eslint/js typescript-eslint eslint-config-prettier \
|
|
25
|
+
eslint-plugin-security eslint-plugin-no-secrets globals
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Flat config
|
|
29
|
+
|
|
30
|
+
```js
|
|
31
|
+
// eslint.config.mjs
|
|
32
|
+
import { defineConfig } from "eslint/config";
|
|
33
|
+
import js from "@eslint/js";
|
|
34
|
+
import tseslint from "typescript-eslint";
|
|
35
|
+
import security from "eslint-plugin-security";
|
|
36
|
+
import noSecrets from "eslint-plugin-no-secrets";
|
|
37
|
+
import prettier from "eslint-config-prettier/flat";
|
|
38
|
+
|
|
39
|
+
export default defineConfig([
|
|
40
|
+
js.configs.recommended,
|
|
41
|
+
...tseslint.configs.recommendedTypeChecked, // type-aware: no-floating-promises, etc.
|
|
42
|
+
security.configs.recommended,
|
|
43
|
+
{ plugins: { "no-secrets": noSecrets }, rules: { "no-secrets/no-secrets": ["warn", { tolerance: 4.5 }] } },
|
|
44
|
+
{
|
|
45
|
+
languageOptions: {
|
|
46
|
+
parserOptions: {
|
|
47
|
+
projectService: true, // current type-info mechanism
|
|
48
|
+
tsconfigRootDir: import.meta.dirname,
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
{ rules: { "security/detect-object-injection": "off" } }, // famously noisy
|
|
53
|
+
prettier, // MUST be last
|
|
54
|
+
]);
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Gotchas
|
|
58
|
+
|
|
59
|
+
- **`recommended` vs `recommendedTypeChecked`.** The type-checked preset is what enables the
|
|
60
|
+
genuinely valuable rules (`no-floating-promises`, `no-misused-promises`, `no-unsafe-*`) but
|
|
61
|
+
needs type info and is **much slower** — scope it to `**/*.ts`/`**/*.tsx` and disable on JS
|
|
62
|
+
config files with `tseslint.configs.disableTypeChecked`.
|
|
63
|
+
- **`projectService: true`** replaces the old `project: "./tsconfig.json"`. It auto-finds the
|
|
64
|
+
nearest `tsconfig.json` per file and lints out-of-project files (like `eslint.config.mjs`)
|
|
65
|
+
without a `tsconfig.eslint.json`. Always pair it with `tsconfigRootDir: import.meta.dirname`.
|
|
66
|
+
- **`tseslint.config()` is deprecated** in favour of core `defineConfig` from `eslint/config`
|
|
67
|
+
(a near-exact clone). The `tseslint.configs.*` presets are unchanged.
|
|
68
|
+
- **Prettier last.** `eslint-config-prettier/flat` must come after every rule-defining config
|
|
69
|
+
so it can switch off conflicting stylistic rules.
|
|
70
|
+
|
|
71
|
+
## CBP preset divergence
|
|
72
|
+
|
|
73
|
+
CBP's `base` DB preset currently pins `eslint: ^9.0.0` / `typescript-eslint: ^8.0.0` and
|
|
74
|
+
keeps several `@typescript-eslint/no-unsafe-*` rules at `warn`. The setup above reflects the
|
|
75
|
+
**latest** (`eslint@10`); both are flat-config and interoperate. Bumping the preset to v10 is
|
|
76
|
+
out of scope for the skill (no DB/preset changes) — adopt v10 per-repo when you regenerate.
|
|
77
|
+
|
|
78
|
+
## Official docs
|
|
79
|
+
|
|
80
|
+
- Configuration files: https://eslint.org/docs/latest/use/configure/configuration-files
|
|
81
|
+
- Typed linting: https://typescript-eslint.io/getting-started/typed-linting/
|
|
82
|
+
- `projectService`: https://typescript-eslint.io/blog/project-service/
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# cli — Node CLI tool overrides
|
|
2
|
+
|
|
3
|
+
For Node CLI packages with a `bin` (the CBP `codebyplan` package). Layers a small **override
|
|
4
|
+
block** on top of [base](base.md) + [node](node.md). Maps to the CBP **`cli`** DB preset
|
|
5
|
+
(`tech_match.requires_capabilities: ["cli-bin"]`).
|
|
6
|
+
|
|
7
|
+
> **Verified 2026-05-31.** There is no "CLI" ESLint plugin — the convention is a scoped
|
|
8
|
+
> rule-override block.
|
|
9
|
+
|
|
10
|
+
## What it does
|
|
11
|
+
|
|
12
|
+
A CLI legitimately (a) talks to the user via `console`, and (b) operates on user-supplied
|
|
13
|
+
filesystem paths and dynamic keys — so the `eslint-plugin-security` heuristics that flag those
|
|
14
|
+
produce noise, not findings. Relax them, **scoped narrowly** to the CLI surface.
|
|
15
|
+
|
|
16
|
+
## Flat config (override block)
|
|
17
|
+
|
|
18
|
+
```js
|
|
19
|
+
// eslint.config.mjs — appended after base + node + security configs
|
|
20
|
+
export default [
|
|
21
|
+
// ...base, node, security.configs.recommended, etc.
|
|
22
|
+
{
|
|
23
|
+
files: ["bin/**/*.{js,ts,mjs}", "src/cli/**/*.{js,ts}"],
|
|
24
|
+
rules: {
|
|
25
|
+
"no-console": "off", // console output IS the CLI's UI
|
|
26
|
+
|
|
27
|
+
// CLIs operate on user paths and dynamic keys by design:
|
|
28
|
+
"security/detect-non-literal-fs-filename": "off",
|
|
29
|
+
"security/detect-object-injection": "off",
|
|
30
|
+
|
|
31
|
+
// optional, if eslint-plugin-n is in use (CLIs call process.exit):
|
|
32
|
+
// "n/no-process-exit": "off",
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
];
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Gotchas
|
|
39
|
+
|
|
40
|
+
- **Scope via `files`** — do NOT disable these repo-wide; the same rules are valuable in
|
|
41
|
+
server/library code.
|
|
42
|
+
- The two `security/*` lines require `eslint-plugin-security` to be installed and registered
|
|
43
|
+
(otherwise ESLint errors "rule not found"). If the project has no security plugin, only the
|
|
44
|
+
`no-console: "off"` line applies.
|
|
45
|
+
|
|
46
|
+
## CBP preset divergence
|
|
47
|
+
|
|
48
|
+
The CBP `cli` preset matches the rule set above exactly (`no-console`,
|
|
49
|
+
`security/detect-non-literal-fs-filename`, `security/detect-object-injection` all off) and is
|
|
50
|
+
capability-gated on `cli-bin`. The repo's own `packages/codebyplan-package/eslint.config.mjs`
|
|
51
|
+
already applies this. No divergence — this doc just documents the convention.
|
|
52
|
+
|
|
53
|
+
## Official docs
|
|
54
|
+
|
|
55
|
+
- Convention (no upstream doc); rules: https://eslint.org/docs/latest/rules/no-console and
|
|
56
|
+
https://github.com/eslint-community/eslint-plugin-security
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# e2e — Playwright / WebdriverIO / Mocha test rules
|
|
2
|
+
|
|
3
|
+
For end-to-end specs. Three runners appear across CBP repos: **Playwright** (web), **WebdriverIO**
|
|
4
|
+
(Tauri desktop), and **Mocha** (the framework WDIO drives). Each is a **directory-scoped**
|
|
5
|
+
override. Playwright maps to the CBP **`testing-e2e`** preset
|
|
6
|
+
(`tech_match.requires: ["Playwright"]`); WebdriverIO + Mocha are **gap stacks — no preset**.
|
|
7
|
+
|
|
8
|
+
> **Verified 2026-05-31.** Version traps: Playwright is **2.10.4** (not the 0.15.x search
|
|
9
|
+
> sometimes returns); Mocha 11.x uses **`configs.recommended`** — NOT `configs.flat.recommended`.
|
|
10
|
+
|
|
11
|
+
## Packages
|
|
12
|
+
|
|
13
|
+
| Package | Latest | Flat key |
|
|
14
|
+
| ------- | ------ | -------- |
|
|
15
|
+
| `eslint-plugin-playwright` | `2.10.4` | `configs['flat/recommended']` |
|
|
16
|
+
| `eslint-plugin-wdio` | `9.27.2` | `configs['flat/recommended']` |
|
|
17
|
+
| `eslint-plugin-mocha` | `11.3.0` | `configs.recommended` (NOT `.flat`) |
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pnpm add -D eslint-plugin-playwright # web
|
|
21
|
+
pnpm add -D eslint-plugin-wdio eslint-plugin-mocha # Tauri / WebdriverIO desktop
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Flat config
|
|
25
|
+
|
|
26
|
+
```js
|
|
27
|
+
// eslint.config.mjs
|
|
28
|
+
import playwright from "eslint-plugin-playwright";
|
|
29
|
+
// desktop: import { configs as wdioConfigs } from "eslint-plugin-wdio";
|
|
30
|
+
// desktop: import mocha from "eslint-plugin-mocha";
|
|
31
|
+
|
|
32
|
+
export default [
|
|
33
|
+
// Playwright — scope to the web e2e dir
|
|
34
|
+
{
|
|
35
|
+
...playwright.configs["flat/recommended"],
|
|
36
|
+
files: ["e2e/**", "**/*.e2e.{ts,js}"],
|
|
37
|
+
rules: { ...playwright.configs["flat/recommended"].rules, "no-console": "off" },
|
|
38
|
+
},
|
|
39
|
+
|
|
40
|
+
// WebdriverIO + Mocha (Tauri desktop) — scope to the wdio spec dir
|
|
41
|
+
// { ...wdioConfigs["flat/recommended"], files: ["test/**", "e2e/**"] },
|
|
42
|
+
// { ...mocha.configs.recommended, files: ["test/**/*.{ts,js}"] },
|
|
43
|
+
];
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Gotchas
|
|
47
|
+
|
|
48
|
+
- **Scope every e2e override via `files`** — Playwright/WDIO/Mocha rules false-positive on
|
|
49
|
+
unit tests, and Testing-Library rules false-positive on e2e specs.
|
|
50
|
+
- **Mocha 11.x = `mocha.configs.recommended`** — `mocha.configs.flat.recommended` is
|
|
51
|
+
`undefined` in 11.x (it only exists on the unreleased 12.x main branch) and crashes config
|
|
52
|
+
load. Don't copy a `.flat.recommended` example.
|
|
53
|
+
- **WebdriverIO**: `eslint-plugin-wdio@9` requires ESLint 9+; when `typescript-eslint` is
|
|
54
|
+
present its recommended config auto-swaps `wdio/await-expect` → `wdio/no-floating-promise`.
|
|
55
|
+
- The CBP web app keeps Playwright specs in `e2e/**` with a Vitest sibling override for
|
|
56
|
+
`e2e/**/__tests__/**` — mirror that split if you co-locate unit tests under `e2e/`.
|
|
57
|
+
|
|
58
|
+
## CBP preset divergence
|
|
59
|
+
|
|
60
|
+
CBP's `testing-e2e` preset covers **Playwright only** (`eslint-plugin-playwright: ^2.0.0`,
|
|
61
|
+
`no-console: off`). There is **no** preset for WebdriverIO or Mocha — the Tauri desktop e2e
|
|
62
|
+
config is manual. The skill does not add presets for the gap runners.
|
|
63
|
+
|
|
64
|
+
## Official docs
|
|
65
|
+
|
|
66
|
+
- Playwright: https://github.com/mskelton/eslint-plugin-playwright
|
|
67
|
+
- WebdriverIO: https://github.com/webdriverio/webdriverio/tree/main/packages/eslint-plugin-wdio
|
|
68
|
+
- Mocha: https://github.com/lo1tuma/eslint-plugin-mocha
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# jest — Jest test-file rules
|
|
2
|
+
|
|
3
|
+
For apps that test with Jest (CBP `apps/backend` NestJS specs, `livebyplan` Expo). **Gap
|
|
4
|
+
stack — no CBP DB preset** (the `testing` preset is Vitest-only). Layers a test-scoped
|
|
5
|
+
override on top of [base](base.md).
|
|
6
|
+
|
|
7
|
+
> **Verified 2026-05-31.** `eslint-plugin-jest` uses the **bracketed** flat-config keys
|
|
8
|
+
> `configs['flat/recommended']` / `['flat/style']` (the un-prefixed keys are legacy eslintrc).
|
|
9
|
+
|
|
10
|
+
## Packages
|
|
11
|
+
|
|
12
|
+
| Package | Latest | Purpose |
|
|
13
|
+
| ------- | ------ | ------- |
|
|
14
|
+
| `eslint-plugin-jest` | `29.15.2` | Jest rules |
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pnpm add -D eslint-plugin-jest
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Peers: `eslint ^8.57 || ^9 || ^10`, `@typescript-eslint/eslint-plugin ^8`, `jest`.
|
|
21
|
+
|
|
22
|
+
## Flat config
|
|
23
|
+
|
|
24
|
+
```js
|
|
25
|
+
// eslint.config.mjs
|
|
26
|
+
import jest from "eslint-plugin-jest";
|
|
27
|
+
|
|
28
|
+
export default [
|
|
29
|
+
{
|
|
30
|
+
files: ["**/*.{test,spec}.{ts,tsx,js,jsx}", "**/__tests__/**/*.{ts,tsx,js,jsx}"],
|
|
31
|
+
...jest.configs["flat/recommended"],
|
|
32
|
+
rules: {
|
|
33
|
+
...jest.configs["flat/recommended"].rules,
|
|
34
|
+
...jest.configs["flat/style"].rules, // optional stylistic rules
|
|
35
|
+
// type-aware: hand the unbound-method check to the jest-aware version
|
|
36
|
+
"@typescript-eslint/unbound-method": "off",
|
|
37
|
+
"jest/unbound-method": "error",
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
];
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Gotchas
|
|
44
|
+
|
|
45
|
+
- Keys are **`configs['flat/recommended']`** / **`['flat/style']`** (bracketed) — the
|
|
46
|
+
unprefixed `configs.recommended` is the legacy eslintrc preset.
|
|
47
|
+
- **`jest/unbound-method`** extends `@typescript-eslint/unbound-method`; turn the base rule
|
|
48
|
+
off on test files and enable the jest version (needs `@typescript-eslint/parser` + type
|
|
49
|
+
info). Type-aware jest rules safely no-op when type info is absent.
|
|
50
|
+
- Avoid pinning `flat/all` long-term — it enables every rule and can break on minor releases.
|
|
51
|
+
|
|
52
|
+
## CBP preset divergence
|
|
53
|
+
|
|
54
|
+
There is **no** CBP `testing-jest` preset. NestJS/Expo Jest specs are currently linted only by
|
|
55
|
+
the base rules. Add the override above per-repo; the skill does not add a preset.
|
|
56
|
+
|
|
57
|
+
## Official docs
|
|
58
|
+
|
|
59
|
+
- eslint-plugin-jest: https://github.com/jest-community/eslint-plugin-jest
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# nestjs — NestJS back-end flat config
|
|
2
|
+
|
|
3
|
+
For NestJS apps (the CBP `apps/backend`). **Gap stack — no CBP DB preset.** Use this doc to
|
|
4
|
+
hand-author `eslint.config.mjs`; the `base` + `node` presets cover most of it, but the
|
|
5
|
+
official Nest starter has a specific shape worth matching.
|
|
6
|
+
|
|
7
|
+
> **Verified 2026-05-31.** There is **no first-party NestJS ESLint plugin** — the official
|
|
8
|
+
> `nest new` starter uses plain type-checked `typescript-eslint` + Prettier.
|
|
9
|
+
|
|
10
|
+
## Packages
|
|
11
|
+
|
|
12
|
+
| Package | Latest | Purpose |
|
|
13
|
+
| ------- | ------ | ------- |
|
|
14
|
+
| `typescript-eslint` | `8.60.0` | parser + type-checked preset |
|
|
15
|
+
| `@eslint/js` | `10.0.1` | `eslint.configs.recommended` |
|
|
16
|
+
| `eslint-plugin-prettier` | `^5` | `/recommended` subpath |
|
|
17
|
+
| `globals` | `17.6.0` | `globals.node`, `globals.jest` |
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pnpm add -D typescript-eslint @eslint/js eslint-plugin-prettier eslint-config-prettier globals
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Flat config (official `nest new` starter, verbatim)
|
|
24
|
+
|
|
25
|
+
```js
|
|
26
|
+
// @ts-check
|
|
27
|
+
import eslint from "@eslint/js";
|
|
28
|
+
import eslintPluginPrettierRecommended from "eslint-plugin-prettier/recommended";
|
|
29
|
+
import globals from "globals";
|
|
30
|
+
import tseslint from "typescript-eslint";
|
|
31
|
+
|
|
32
|
+
export default tseslint.config(
|
|
33
|
+
{ ignores: ["eslint.config.mjs"] },
|
|
34
|
+
eslint.configs.recommended,
|
|
35
|
+
...tseslint.configs.recommendedTypeChecked,
|
|
36
|
+
eslintPluginPrettierRecommended,
|
|
37
|
+
{
|
|
38
|
+
languageOptions: {
|
|
39
|
+
globals: { ...globals.node, ...globals.jest },
|
|
40
|
+
sourceType: "commonjs", // "module" for an ESM Nest app
|
|
41
|
+
parserOptions: { projectService: true, tsconfigRootDir: import.meta.dirname },
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
rules: {
|
|
46
|
+
"@typescript-eslint/no-explicit-any": "off",
|
|
47
|
+
"@typescript-eslint/no-floating-promises": "warn",
|
|
48
|
+
"@typescript-eslint/no-unsafe-argument": "warn",
|
|
49
|
+
"prettier/prettier": ["error", { endOfLine: "auto" }],
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
);
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Gotchas
|
|
56
|
+
|
|
57
|
+
- Nest relaxes three type-checked rules (`no-explicit-any` off; `no-floating-promises` and
|
|
58
|
+
`no-unsafe-argument` to `warn`) because its DI/decorator patterns trip them. Keep these.
|
|
59
|
+
- `{ ignores: ["eslint.config.mjs"] }` keeps type-aware linting off the config file itself.
|
|
60
|
+
- `sourceType: "commonjs"` matches Nest's default CJS build — flip to `"module"` for ESM.
|
|
61
|
+
- The starter still uses `tseslint.config(...)`; you may swap it for `defineConfig` from
|
|
62
|
+
`eslint/config` (deprecation note in [base.md](base.md)) — the presets are identical.
|
|
63
|
+
- Optional community add-on: `@darraghor/eslint-plugin-nestjs-typed` adds
|
|
64
|
+
decorator/Swagger/DTO-aware rules. Not part of the official starter.
|
|
65
|
+
- Nest tests use **Jest** — add the [jest.md](jest.md) override scoped to `**/*.spec.ts`.
|
|
66
|
+
|
|
67
|
+
## Official docs
|
|
68
|
+
|
|
69
|
+
- Starter config: https://github.com/nestjs/typescript-starter/blob/master/eslint.config.mjs
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# nextjs — Next.js (App Router) flat config
|
|
2
|
+
|
|
3
|
+
For Next.js apps. Layers on top of [base](base.md). Maps to the CBP **`nextjs`** DB preset
|
|
4
|
+
(`tech_match.requires: ["Next.js"]`, `requires_capabilities: ["jsx"]`).
|
|
5
|
+
|
|
6
|
+
> **Verified 2026-05-31.** As of **Next.js 16**, `eslint-config-next` ships **native flat
|
|
7
|
+
> config**, `next lint` is **removed** (run `eslint .` directly), and the `eslint` key in
|
|
8
|
+
> `next.config.js` is gone. No `FlatCompat` shim is needed.
|
|
9
|
+
|
|
10
|
+
## Packages
|
|
11
|
+
|
|
12
|
+
| Package | Latest | Purpose |
|
|
13
|
+
| ------- | ------ | ------- |
|
|
14
|
+
| `eslint-config-next` | `16.2.6` | bundles `@next/eslint-plugin-next` + react/react-hooks/import |
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pnpm add -D eslint-config-next
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Flat config
|
|
21
|
+
|
|
22
|
+
`eslint-config-next` exposes spreadable **array** subpath exports:
|
|
23
|
+
|
|
24
|
+
```js
|
|
25
|
+
// eslint.config.mjs
|
|
26
|
+
import { defineConfig, globalIgnores } from "eslint/config";
|
|
27
|
+
import nextVitals from "eslint-config-next/core-web-vitals";
|
|
28
|
+
import nextTs from "eslint-config-next/typescript";
|
|
29
|
+
|
|
30
|
+
export default defineConfig([
|
|
31
|
+
...nextVitals, // base + Core-Web-Vitals rules promoted warn→error
|
|
32
|
+
...nextTs, // typescript-eslint rules (based on @typescript-eslint/recommended)
|
|
33
|
+
globalIgnores([".next/**", "out/**", "build/**", "next-env.d.ts"]),
|
|
34
|
+
]);
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Subpath exports:
|
|
38
|
+
- `eslint-config-next` — base (Next + react + react-hooks recommended).
|
|
39
|
+
- `eslint-config-next/core-web-vitals` — base + CWV rules at error (**recommended default**).
|
|
40
|
+
- `eslint-config-next/typescript` — adds typescript-eslint rules; spread alongside a base.
|
|
41
|
+
|
|
42
|
+
## Gotchas
|
|
43
|
+
|
|
44
|
+
- **`globalIgnores([...])` is required** when you spread the config — re-assert the default
|
|
45
|
+
ignores (`.next/`, etc.) per the official docs example.
|
|
46
|
+
- **Monorepo**: point the plugin at the app with `settings: { next: { rootDir: "apps/web" } }`
|
|
47
|
+
(path, glob, or array).
|
|
48
|
+
- **Prettier**: add `import prettier from "eslint-config-prettier/flat"` last.
|
|
49
|
+
- `next lint` removal landed in Next 16.0.0 — old `.eslintrc.json` configs should migrate to
|
|
50
|
+
`eslint.config.mjs` (a codemod exists).
|
|
51
|
+
|
|
52
|
+
## CBP preset divergence
|
|
53
|
+
|
|
54
|
+
CBP's `nextjs` preset pins `eslint-config-next: ^15.0.0` and registers
|
|
55
|
+
`eslint-plugin-react-compiler` explicitly (see [react.md](react.md) — that plugin is now
|
|
56
|
+
**superseded**). The repo's own `apps/web/eslint.config.mjs` already uses the
|
|
57
|
+
`createRequire` + `eslint-config-next/core-web-vitals` + `eslint-config-next/typescript`
|
|
58
|
+
pattern. Adopt the native spread form above when regenerating; presets are not changed by
|
|
59
|
+
this skill.
|
|
60
|
+
|
|
61
|
+
## Official docs
|
|
62
|
+
|
|
63
|
+
- ESLint config: https://nextjs.org/docs/app/api-reference/config/eslint
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# node — Node/TypeScript service (Hono, Express, plain Node) flat config
|
|
2
|
+
|
|
3
|
+
For Node back-end services and server-side packages (the CBP MCP server uses Hono; any
|
|
4
|
+
`node-server` capability). Layers on [base](base.md). Maps to the CBP **`node`** DB preset
|
|
5
|
+
(`tech_match.requires: ["Node.js"]`, `requires_capabilities: ["node-server"]`).
|
|
6
|
+
|
|
7
|
+
> **Verified 2026-05-31.** The valuable async-safety rules (`no-floating-promises`,
|
|
8
|
+
> `no-misused-promises`) are **typescript-eslint** rules that need type info — they come from
|
|
9
|
+
> `recommendedTypeChecked`, not from a node plugin.
|
|
10
|
+
|
|
11
|
+
## Packages
|
|
12
|
+
|
|
13
|
+
| Package | Latest | Purpose |
|
|
14
|
+
| ------- | ------ | ------- |
|
|
15
|
+
| `globals` | `17.6.0` | `globals.node` |
|
|
16
|
+
| `typescript-eslint` | `8.60.0` | type-aware promise rules |
|
|
17
|
+
| `eslint-plugin-n` | `18.0.1` | **optional** — unsupported-Node-API / import / engines checks |
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pnpm add -D globals typescript-eslint
|
|
21
|
+
# optional (libraries / dual ESM-CJS packages):
|
|
22
|
+
pnpm add -D eslint-plugin-n
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Flat config
|
|
26
|
+
|
|
27
|
+
```js
|
|
28
|
+
// eslint.config.mjs
|
|
29
|
+
import { defineConfig } from "eslint/config";
|
|
30
|
+
import js from "@eslint/js";
|
|
31
|
+
import tseslint from "typescript-eslint";
|
|
32
|
+
import globals from "globals";
|
|
33
|
+
// optional: import n from "eslint-plugin-n";
|
|
34
|
+
|
|
35
|
+
export default defineConfig([
|
|
36
|
+
js.configs.recommended,
|
|
37
|
+
...tseslint.configs.recommendedTypeChecked, // enables the promise rules below
|
|
38
|
+
{
|
|
39
|
+
files: ["**/*.{ts,mts,cts}"],
|
|
40
|
+
languageOptions: {
|
|
41
|
+
globals: globals.node,
|
|
42
|
+
sourceType: "module", // "commonjs" for a CJS service
|
|
43
|
+
parserOptions: { projectService: true, tsconfigRootDir: import.meta.dirname },
|
|
44
|
+
},
|
|
45
|
+
rules: {
|
|
46
|
+
"@typescript-eslint/no-floating-promises": "error",
|
|
47
|
+
"@typescript-eslint/no-misused-promises": "error",
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
// optional eslint-plugin-n:
|
|
51
|
+
// { files: ["**/*.{ts,mts,cts}"], plugins: { n }, extends: ["n/recommended-module"] },
|
|
52
|
+
]);
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Gotchas
|
|
56
|
+
|
|
57
|
+
- **`eslint-plugin-n` is optional.** For a pure internal TS service you can skip it — its main
|
|
58
|
+
value is catching unsupported Node APIs, missing imports, and `package.json#engines` issues
|
|
59
|
+
in **libraries** and dual ESM/CJS packages. Pick the right preset: `n/recommended-module`
|
|
60
|
+
(ESM), `n/recommended-script` (CJS), `n/recommended` (mixed).
|
|
61
|
+
- The promise rules **require** a type-checked config + `projectService` — they silently do
|
|
62
|
+
nothing without type info.
|
|
63
|
+
- A CJS service uses `sourceType: "commonjs"`; an ESM service uses `"module"`.
|
|
64
|
+
|
|
65
|
+
## CBP preset divergence
|
|
66
|
+
|
|
67
|
+
CBP's `node` preset ships only the two `@typescript-eslint` promise rules at `error` (no
|
|
68
|
+
`eslint-plugin-n`). The repo's MCP server / CLI configs follow this minimal shape. Add
|
|
69
|
+
`eslint-plugin-n` per-repo if you publish a library; the skill does not modify the preset.
|
|
70
|
+
|
|
71
|
+
## Official docs
|
|
72
|
+
|
|
73
|
+
- eslint-plugin-n: https://github.com/eslint-community/eslint-plugin-n
|
|
74
|
+
- no-floating-promises: https://typescript-eslint.io/rules/no-floating-promises/
|