Repository: JoshuaKGoldberg/create-typescript-app (ts)

Issue:
🐛 Bug: ESLint failures creating a new repo with `blockESLintPlugin`
### Bug Report Checklist

- [x] I have tried restarting my IDE and the issue persists.
- [x] I have pulled the latest `main` branch of the repository.
- [x] I have [searched for related issues](https://github.com/JoshuaKGoldberg/create-typescript-app/issues?q=is%3Aissue) and found none that matched my issue.

### Expected

Creating a new ESLint plugin built on CTA's _Everything_ preset should succeed:

```shell
npx create-typescript-app --preset everything --add-eslint-plugin --exclude-example-files --directory eslint-plugin-cta-example --title eslint-plugin-cta-example --description "Example ESLint plugin built with create-typescript-app. 🔌"
```

### Actual

```plaintext
┌  ✨ create-typescript-app@2.38.0 ✨
│
●  Learn more on:
│    https://github.com/JoshuaKGoldberg/create-typescript-app
│
◇  Running with mode --setup using the template:
│    create-typescript-app
│
◇  Inferred default options from system
│
◇  Created repository on GitHub
│
◇  Ran the Create TypeScript App template
│
◇  Prepared local repository
│
│  You've got a new repository ready to use in:
│    ./eslint-plugin-cta-example
│  
│  It's also pushed to GitHub on:
│    https://github.com/bingo-examples/eslint-plugin-cta-example
│
■  The pnpm lint --fix script failed. You should re-run it and fix its complaints.
│  ExecaError: Command failed with exit code 1: pnpm lint --fix
│  
│  
│  > eslint-plugin-cta-example@0.0.0 lint /Users/josh/repos/eslint-plugin-cta-example
│  > eslint . --max-warnings 0 --fix
│  
│  
│  /Users/josh/repos/eslint-plugin-cta-example/src/rules/example.test.ts
│    1:22  error  Can't resolve './enums.js' in '/Users/josh/repos/eslint-plugin-cta-example/src/rules'  n/no-missing-import
│    4:1   error  Unsafe call of a(n) `error` type typed value                                           @typescript-eslint/no-unsafe-call
│    4:12  error  Unsafe member access .run on an `error` typed value                                    @typescript-eslint/no-unsafe-member-access
│  
│  /Users/josh/repos/eslint-plugin-cta-example/src/rules/example.ts
│    3:14  error  Unsafe assignment of an error typed value       @typescript-eslint/no-unsafe-assignment
│    3:21  error  Unsafe call of a(n) `error` type typed value    @typescript-eslint/no-unsafe-call
│    7:5   error  Unsafe call of a(n) `any` typed value           @typescript-eslint/no-unsafe-call
│    7:13  error  Unsafe member access .report on an `any` value  @typescript-eslint/no-unsafe-member-access
│    9:6   error  Unsafe assignment of an `any` value             @typescript-eslint/no-unsafe-assignment
│  
│  /Users/josh/repos/eslint-plugin-cta-example/src/rules/index.ts
│    4:2  error  Unsafe assignment of an error typed value  @typescript-eslint/no-unsafe-assignment
│  
│  /Users/josh/repos/eslint-plugin-cta-example/src/rules/ruleTester.ts
│    1:28  error  Can't resolve '@typescript-eslint/rule-tester' in '/Users/josh/repos/eslint-plugin-cta-example/src/rules'  n/no-missing-import
│    4:12  error  Unsafe member access .afterAll on an `error` typed value                                                   @typescript-eslint/no-unsafe-member-access
│    5:12  error  Unsafe member access .it on an `error` typed value                                                         @typescript-eslint/no-unsafe-member-access
│    6:12  error  Unsafe member access .itOnly on an `error` typed value                                                     @typescript-eslint/no-unsafe-member-access
│    7:12  error  Unsafe member access .describe on an `error` typed value                                                   @typescript-eslint/no-unsafe-member-access
│    9:14  error  Unsafe assignment of an error typed value                                                                  @typescript-eslint/no-unsafe-assignment
│    9:27  error  Unsafe construction of a(n) `error` type typed value                                                       @typescript-eslint/no-unsafe-call
│  
│  /Users/josh/repos/eslint-plugin-cta-example/src/utils.ts
│    1:29  error  Can't resolve '@typescript-eslint/utils' in '/Users/josh/repos/eslint-plugin-cta-example/src'  n/no-missing-import
│    3:14  error  Unsafe assignment of an error typed value                                                      @typescript-eslint/no-unsafe-assignment
│    3:27  error  Unsafe call of a(n) `error` type typed value                                                   @typescript-eslint/no-unsafe-call
│    3:39  error  Unsafe member access .RuleCreator on an `error` typed value                                    @typescript-eslint/no-unsafe-member-access
│    5:87  error  Invalid type "any" of template literal expression                                              @typescript-eslint/restrict-template-expressions
│  
│  ✖ 21 problems (21 errors, 0 warnings)
│  
│  \u2009ELIFECYCLE\u2009 Command failed with exit code 1.
│      at getFinalError (file:///Users/josh/repos/create-typescript-app/node_modules/.pnpm/execa@9.5.2/node_modules/execa/lib/return/final-error.js:6:9)
│      at makeError (file:///Users/josh/repos/create-typescript-app/node_modules/.pnpm/execa@9.5.2/node_modules/execa/lib/return/result.js:108:16)
│      at getAsyncResult (file:///Users/josh/repos/create-typescript-app/node_modules/.pnpm/execa@9.5.2/node_modules/execa/lib/methods/main-async.js:167:4)
│      at handlePromise (file:///Users/josh/repos/create-typescript-app/node_modules/.pnpm/execa@9.5.2/node_modules/execa/lib/methods/main-async.js:150:17)
│      at async runCommand (file:///Users/josh/repos/create-typescript-app/node_modules/.pnpm/bingo@0.5.15/node_modules/bingo/lib/runners/applyScriptsToSystem.js:10:24)
│      at async file:///Users/josh/repos/create-typescript-app/node_modules/.pnpm/bingo@0.5.15/node_modules/bingo/lib/runners/applyScriptsToSystem.js:22:17
│      at async Promise.all (index 1)
│      at async applyScriptsToSystem (file:///Users/josh/repos/create-typescript-app/node_modules/.pnpm/bingo@0.5.15/node_modules/bingo/lib/runners/applyScriptsToSystem.js:20:9)
│      at async Promise.all (index 0)
│      at async runCreation (file:///Users/josh/repos/create-typescript-app/node_modules/.pnpm/bingo@0.5.15/node_modules/bingo/lib/runners/runCreation.js:8:5)
│
└  Thanks for using create-typescript-app! 💝

Be sure to:

- enable the GitHub apps on https://github.com/bingo-examples/eslint-plugin-cta-example/settings/installations:
   - Codecov (https://github.com/apps/codecov)
   - Renovate (https://github.com/apps/renovate)
- populate the secrets on https://github.com/bingo-examples/eslint-plugin-cta-example/settings/secrets/actions:
   - ACCESS_TOKEN (a GitHub PAT with repo and workflow permissions)
   - NPM_TOKEN (an npm access token with automation permissions)
```

### Additional Info

🎁 

Pull request description:
fix: corrected setup for blockESLintPlugin
## PR Checklist

- [x] Addresses an existing open issue: fixes #2195
- [x] That issue was marked as [`status: accepting prs`](https://github.com/JoshuaKGoldberg/create-typescript-app/issues?q=is%3Aopen+is%3Aissue+label%3A%22status%3A+accepting+prs%22)
- [x] Steps in [CONTRIBUTING.md](https://github.com/JoshuaKGoldberg/create-typescript-app/blob/main/.github/CONTRIBUTING.md) were taken

## Overview

Switches the default `README.md` usage to a `defaultUsage` Addon, then pipes `blockTypeScript`'s example files through `blockExample`. That way `--exclude-example-files` gets them out of the way so that `--add-eslint-plugin` can swap in the eslint-doc-generator table.

🎁 

Patch:
diff --git a/src/base.ts b/src/base.ts
index d629664..8cb20e5 100644
--- a/src/base.ts
+++ b/src/base.ts
@@ -289,7 +289,7 @@ export const base = createBase({
 		);
 
 		const getReadmeUsage = lazyValue(
-			async () => await readReadmeUsage(getEmoji, getReadme, getRepository),
+			async () => await readReadmeUsage(getReadme),
 		);
 
 		const getRepository = lazyValue(
diff --git a/src/blocks/blockESLintPlugin.ts b/src/blocks/blockESLintPlugin.ts
index eba752a..e41ec71 100644
--- a/src/blocks/blockESLintPlugin.ts
+++ b/src/blocks/blockESLintPlugin.ts
@@ -4,10 +4,12 @@ import { blockDevelopmentDocs } from "./blockDevelopmentDocs.js";
 import { blockESLint } from "./blockESLint.js";
 import { blockGitHubActionsCI } from "./blockGitHubActionsCI.js";
 import { blockPackageJson } from "./blockPackageJson.js";
+import { blockREADME } from "./blockREADME.js";
 import { blockVitest } from "./blockVitest.js";
 import { blockESLintPluginIntake } from "./eslint/blockESLintPluginIntake.js";
 import { zConfigEmoji } from "./eslint/schemas.js";
 import { intakeFile } from "./intake/intakeFile.js";
+import { CommandPhase } from "./phases.js";
 
 export const blockESLintPlugin = base.createBlock({
 	about: {
@@ -28,6 +30,9 @@ export const blockESLintPlugin = base.createBlock({
 	produce({ addons, options }) {
 		const { configEmoji } = addons;
 		const configFileName = `.eslint-doc-generatorrc.${options.type === "commonjs" ? "mjs" : "js"}`;
+		const pluginName = options.repository
+			.replace(/^eslint-plugin-/, "")
+			.replaceAll(/-\w/g, (matched) => matched[1].toUpperCase());
 
 		return {
 			addons: [
@@ -83,9 +88,37 @@ pnpm build:docs
 						},
 					],
 				}),
+				blockREADME({
+					defaultUsage: [
+						`Add this plugin to the list of plugins in your [ESLint configuration file](https://eslint.org/docs/latest/use/configure/configuration-files):
+
+\`\`\`shell
+npm i ${options.repository} -D
+\`\`\`
+
+\`\`\`ts
+import ${pluginName} from "${options.repository}";
+
+export default [
+	// (other plugins)
+	${pluginName}.configs.recommended, // 👈
+];
+\`\`\`
+
+### Rules
+
+These are all set to \`"error"\` in the recommended config:
+
+<!-- begin auto-generated rules list --><!-- end auto-generated rules list -->`,
+					],
+				}),
 				blockPackageJson({
 					properties: {
+						dependencies: {
+							"@typescript-eslint/utils": "^8.29.0",
+						},
 						devDependencies: {
+							"@typescript-eslint/rule-tester": "8.29.1",
 							"eslint-doc-generator": "2.1.0",
 							"eslint-plugin-eslint-plugin": "6.4.0",
 						},
@@ -117,6 +150,16 @@ const config = {
 export default config;
 `,
 			},
+			scripts: [
+				{
+					commands: ["pnpm build"],
+					phase: CommandPhase.Build,
+				},
+				{
+					commands: ["pnpm eslint-doc-generator --init-rule-docs"],
+					phase: CommandPhase.Process,
+				},
+			],
 		};
 	},
 	setup({ options }) {
@@ -159,7 +202,7 @@ export { rules };
 export default plugin;
 `,
 					rules: {
-						"example.test.ts": `import { rule } from "./enums.js";
+						"enums.test.ts": `import { rule } from "./enums.js";
 import { ruleTester } from "./ruleTester.js";
 
 ruleTester.run("enums", rule, {
@@ -180,7 +223,7 @@ ruleTester.run("enums", rule, {
 	valid: [\`const Values = {};\`, \`const Values = {} as const;\`],
 });
 `,
-						"example.ts": `import { createRule } from "../utils.js";
+						"enums.ts": `import { createRule } from "../utils.js";
 
 export const rule = createRule({
 	create(context) {
@@ -207,10 +250,10 @@ export const rule = createRule({
 	name: "enums",
 });
 `,
-						"index.ts": `import { rule as example } from "./example.js";
+						"index.ts": `import { rule as enums } from "./enums.js";
 
 export const rules = {
-	example,
+	enums,
 };
 `,
 						"ruleTester.ts": `import { RuleTester } from "@typescript-eslint/rule-tester";
diff --git a/src/blocks/blockExampleFiles.ts b/src/blocks/blockExampleFiles.ts
index f04a9aa..4c4ccf8 100644
--- a/src/blocks/blockExampleFiles.ts
+++ b/src/blocks/blockExampleFiles.ts
@@ -1,6 +1,7 @@
 import { z } from "zod";
 
 import { base } from "../base.js";
+import { blockREADME } from "./blockREADME.js";
 
 export const blockExampleFiles = base.createBlock({
 	about: {
@@ -8,9 +9,17 @@ export const blockExampleFiles = base.createBlock({
 	},
 	addons: {
 		files: z.record(z.string()).default({}),
+		usage: z.array(z.string()).default([]),
 	},
 	setup({ addons }) {
+		const { usage } = addons;
+
 		return {
+			addons: [
+				blockREADME({
+					defaultUsage: usage,
+				}),
+			],
 			files: {
 				src: addons.files,
 			},
diff --git a/src/blocks/blockREADME.ts b/src/blocks/blockREADME.ts
index a6c9692..bfa7e48 100644
--- a/src/blocks/blockREADME.ts
+++ b/src/blocks/blockREADME.ts
@@ -29,11 +29,12 @@ export const blockREADME = base.createBlock({
 	},
 	addons: {
 		badges: z.array(zBadge).default([]),
+		defaultUsage: z.array(z.string()).default([]),
 		notices: z.array(z.string()).default([]),
 		sections: z.array(z.string()).default([]),
 	},
 	produce({ addons, options }) {
-		const { badges, notices, sections } = addons;
+		const { badges, defaultUsage, notices, sections } = addons;
 
 		const explainer =
 			options.documentation.readme.explainer &&
@@ -60,7 +61,7 @@ ${formatBadges(badges)}
 ${[logo, explainer].filter(Boolean).join("")}
 ## Usage
 
-${options.documentation.readme.usage}
+${options.documentation.readme.usage ?? defaultUsage.join("\n\n")}
 
 ## Development
 
diff --git a/src/blocks/blockTypeScript.ts b/src/blocks/blockTypeScript.ts
index b67aa2c..6475983 100644
--- a/src/blocks/blockTypeScript.ts
+++ b/src/blocks/blockTypeScript.ts
@@ -87,6 +87,16 @@ export * from "./types.js";
 	}
 	`,
 					},
+					usage: [
+						`\`\`\`shell
+npm i ${options.repository}
+\`\`\`
+\`\`\`ts
+import { greet } from "${options.repository}";
+
+greet("Hello, world! ${options.emoji}");
+\`\`\``,
+					],
 				}),
 				blockGitignore({
 					ignores: ["/lib"],
diff --git a/src/options/readDocumentation.ts b/src/options/readDocumentation.ts
index d828f34..5c6984c 100644
--- a/src/options/readDocumentation.ts
+++ b/src/options/readDocumentation.ts
@@ -5,7 +5,7 @@ export async function readDocumentation(
 	getReadmeAdditional: () => Promise<string | undefined>,
 	getReadmeExplainer: () => Promise<string | undefined>,
 	getReadmeFootnotes: () => Promise<string | undefined>,
-	getReadmeUsage: () => Promise<string>,
+	getReadmeUsage: () => Promise<string | undefined>,
 ): Promise<Documentation> {
 	const [additional, explainer, footnotes, development, usage] =
 		await Promise.all([
diff --git a/src/options/readReadmeUsage.ts b/src/options/readReadmeUsage.ts
index 3efbc84..97cf6c3 100644
--- a/src/options/readReadmeUsage.ts
+++ b/src/options/readReadmeUsage.ts
@@ -1,19 +1,22 @@
-import { readUsageFromReadme } from "./readUsageFromReadme.js";
+const startUsage = "## Usage";
 
-export async function readReadmeUsage(
-	getEmoji: () => Promise<string>,
-	getReadme: () => Promise<string>,
-	getRepository: () => Promise<string | undefined>,
-) {
-	return (
-		readUsageFromReadme(await getReadme()) ??
-		`\`\`\`shell
-npm i ${await getRepository()}
-\`\`\`
-\`\`\`ts
-import { greet } from "${await getRepository()}";
+export async function readReadmeUsage(getReadme: () => Promise<string>) {
+	const readme = await getReadme();
 
-greet("Hello, world! ${await getEmoji()}");
-\`\`\``
-	);
+	const indexOfUsage = readme.indexOf(startUsage);
+	if (indexOfUsage === -1) {
+		return undefined;
+	}
+
+	const offset = indexOfUsage + startUsage.length;
+	const indexOfNextKnownHeading = readme
+		.slice(offset)
+		.search(/## (?:Development|Contributing|Contributors)/);
+	if (indexOfNextKnownHeading === -1) {
+		return readme.slice(offset) || undefined;
+	}
+
+	const usage = readme.slice(offset, indexOfNextKnownHeading + offset).trim();
+
+	return usage || undefined;
 }
diff --git a/src/options/readUsageFromReadme.ts b/src/options/readUsageFromReadme.ts
deleted file mode 100644
index d3bcddf..0000000
--- a/src/options/readUsageFromReadme.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-const startUsage = "## Usage";
-
-export function readUsageFromReadme(readme: string) {
-	const indexOfUsage = readme.indexOf(startUsage);
-	if (indexOfUsage === -1) {
-		return undefined;
-	}
-
-	const offset = indexOfUsage + startUsage.length;
-	const indexOfNextKnownHeading = readme
-		.slice(offset)
-		.search(/## (?:Development|Contributing|Contributors)/);
-	if (indexOfNextKnownHeading === -1) {
-		return readme.slice(offset);
-	}
-
-	const usage = readme.slice(offset, indexOfNextKnownHeading + offset).trim();
-
-	return usage ? usage : undefined;
-}
diff --git a/src/schemas.ts b/src/schemas.ts
index 8415021..3b16861 100644
--- a/src/schemas.ts
+++ b/src/schemas.ts
@@ -14,7 +14,7 @@ export const zReadme = z.object({
 	additional: z.string().optional(),
 	explainer: z.string().optional(),
 	footnotes: z.string().optional(),
-	usage: z.string(),
+	usage: z.string().optional(),
 });
 
 export type Readme = z.infer<typeof zReadme>;


Test patch:
diff --git a/src/blocks/blockESLintPlugin.test.ts b/src/blocks/blockESLintPlugin.test.ts
index e78964c..5cb690f 100644
--- a/src/blocks/blockESLintPlugin.test.ts
+++ b/src/blocks/blockESLintPlugin.test.ts
@@ -88,10 +88,41 @@ describe("blockESLintPlugin", () => {
 			      },
 			      "block": [Function],
 			    },
+			    {
+			      "addons": {
+			        "defaultUsage": [
+			          "Add this plugin to the list of plugins in your [ESLint configuration file](https://eslint.org/docs/latest/use/configure/configuration-files):
+
+			\`\`\`shell
+			npm i test-repository -D
+			\`\`\`
+
+			\`\`\`ts
+			import testRepository from "test-repository";
+
+			export default [
+				// (other plugins)
+				testRepository.configs.recommended, // 👈
+			];
+			\`\`\`
+
+			### Rules
+
+			These are all set to \`"error"\` in the recommended config:
+
+			<!-- begin auto-generated rules list --><!-- end auto-generated rules list -->",
+			        ],
+			      },
+			      "block": [Function],
+			    },
 			    {
 			      "addons": {
 			        "properties": {
+			          "dependencies": {
+			            "@typescript-eslint/utils": "^8.29.0",
+			          },
 			          "devDependencies": {
+			            "@typescript-eslint/rule-tester": "8.29.1",
 			            "eslint-doc-generator": "2.1.0",
 			            "eslint-plugin-eslint-plugin": "6.4.0",
 			          },
@@ -131,6 +162,20 @@ describe("blockESLintPlugin", () => {
 			export default config;
 			",
 			  },
+			  "scripts": [
+			    {
+			      "commands": [
+			        "pnpm build",
+			      ],
+			      "phase": 2,
+			    },
+			    {
+			      "commands": [
+			        "pnpm eslint-doc-generator --init-rule-docs",
+			      ],
+			      "phase": 3,
+			    },
+			  ],
 			}
 		`);
 	});
@@ -221,10 +266,41 @@ describe("blockESLintPlugin", () => {
 			      },
 			      "block": [Function],
 			    },
+			    {
+			      "addons": {
+			        "defaultUsage": [
+			          "Add this plugin to the list of plugins in your [ESLint configuration file](https://eslint.org/docs/latest/use/configure/configuration-files):
+
+			\`\`\`shell
+			npm i test-repository -D
+			\`\`\`
+
+			\`\`\`ts
+			import testRepository from "test-repository";
+
+			export default [
+				// (other plugins)
+				testRepository.configs.recommended, // 👈
+			];
+			\`\`\`
+
+			### Rules
+
+			These are all set to \`"error"\` in the recommended config:
+
+			<!-- begin auto-generated rules list --><!-- end auto-generated rules list -->",
+			        ],
+			      },
+			      "block": [Function],
+			    },
 			    {
 			      "addons": {
 			        "properties": {
+			          "dependencies": {
+			            "@typescript-eslint/utils": "^8.29.0",
+			          },
 			          "devDependencies": {
+			            "@typescript-eslint/rule-tester": "8.29.1",
 			            "eslint-doc-generator": "2.1.0",
 			            "eslint-plugin-eslint-plugin": "6.4.0",
 			          },
@@ -264,6 +340,20 @@ describe("blockESLintPlugin", () => {
 			export default config;
 			",
 			  },
+			  "scripts": [
+			    {
+			      "commands": [
+			        "pnpm build",
+			      ],
+			      "phase": 2,
+			    },
+			    {
+			      "commands": [
+			        "pnpm eslint-doc-generator --init-rule-docs",
+			      ],
+			      "phase": 3,
+			    },
+			  ],
 			}
 		`);
 	});
@@ -352,10 +442,41 @@ describe("blockESLintPlugin", () => {
 			      },
 			      "block": [Function],
 			    },
+			    {
+			      "addons": {
+			        "defaultUsage": [
+			          "Add this plugin to the list of plugins in your [ESLint configuration file](https://eslint.org/docs/latest/use/configure/configuration-files):
+
+			\`\`\`shell
+			npm i test-repository -D
+			\`\`\`
+
+			\`\`\`ts
+			import testRepository from "test-repository";
+
+			export default [
+				// (other plugins)
+				testRepository.configs.recommended, // 👈
+			];
+			\`\`\`
+
+			### Rules
+
+			These are all set to \`"error"\` in the recommended config:
+
+			<!-- begin auto-generated rules list --><!-- end auto-generated rules list -->",
+			        ],
+			      },
+			      "block": [Function],
+			    },
 			    {
 			      "addons": {
 			        "properties": {
+			          "dependencies": {
+			            "@typescript-eslint/utils": "^8.29.0",
+			          },
 			          "devDependencies": {
+			            "@typescript-eslint/rule-tester": "8.29.1",
 			            "eslint-doc-generator": "2.1.0",
 			            "eslint-plugin-eslint-plugin": "6.4.0",
 			          },
@@ -429,7 +550,7 @@ describe("blockESLintPlugin", () => {
 			export default plugin;
 			",
 			      "rules": {
-			        "example.test.ts": "import { rule } from "./enums.js";
+			        "enums.test.ts": "import { rule } from "./enums.js";
 			import { ruleTester } from "./ruleTester.js";
 
 			ruleTester.run("enums", rule, {
@@ -450,7 +571,7 @@ describe("blockESLintPlugin", () => {
 				valid: [\`const Values = {};\`, \`const Values = {} as const;\`],
 			});
 			",
-			        "example.ts": "import { createRule } from "../utils.js";
+			        "enums.ts": "import { createRule } from "../utils.js";
 
 			export const rule = createRule({
 				create(context) {
@@ -477,10 +598,10 @@ describe("blockESLintPlugin", () => {
 				name: "enums",
 			});
 			",
-			        "index.ts": "import { rule as example } from "./example.js";
+			        "index.ts": "import { rule as enums } from "./enums.js";
 
 			export const rules = {
-				example,
+				enums,
 			};
 			",
 			        "ruleTester.ts": "import { RuleTester } from "@typescript-eslint/rule-tester";
@@ -503,6 +624,20 @@ describe("blockESLintPlugin", () => {
 			",
 			    },
 			  },
+			  "scripts": [
+			    {
+			      "commands": [
+			        "pnpm build",
+			      ],
+			      "phase": 2,
+			    },
+			    {
+			      "commands": [
+			        "pnpm eslint-doc-generator --init-rule-docs",
+			      ],
+			      "phase": 3,
+			    },
+			  ],
 			}
 		`);
 	});
@@ -596,10 +731,41 @@ describe("blockESLintPlugin", () => {
 			      },
 			      "block": [Function],
 			    },
+			    {
+			      "addons": {
+			        "defaultUsage": [
+			          "Add this plugin to the list of plugins in your [ESLint configuration file](https://eslint.org/docs/latest/use/configure/configuration-files):
+
+			\`\`\`shell
+			npm i test-repository -D
+			\`\`\`
+
+			\`\`\`ts
+			import testRepository from "test-repository";
+
+			export default [
+				// (other plugins)
+				testRepository.configs.recommended, // 👈
+			];
+			\`\`\`
+
+			### Rules
+
+			These are all set to \`"error"\` in the recommended config:
+
+			<!-- begin auto-generated rules list --><!-- end auto-generated rules list -->",
+			        ],
+			      },
+			      "block": [Function],
+			    },
 			    {
 			      "addons": {
 			        "properties": {
+			          "dependencies": {
+			            "@typescript-eslint/utils": "^8.29.0",
+			          },
 			          "devDependencies": {
+			            "@typescript-eslint/rule-tester": "8.29.1",
 			            "eslint-doc-generator": "2.1.0",
 			            "eslint-plugin-eslint-plugin": "6.4.0",
 			          },
@@ -640,6 +806,20 @@ describe("blockESLintPlugin", () => {
 			export default config;
 			",
 			  },
+			  "scripts": [
+			    {
+			      "commands": [
+			        "pnpm build",
+			      ],
+			      "phase": 2,
+			    },
+			    {
+			      "commands": [
+			        "pnpm eslint-doc-generator --init-rule-docs",
+			      ],
+			      "phase": 3,
+			    },
+			  ],
 			}
 		`);
 	});
diff --git a/src/blocks/blockExampleFiles.test.ts b/src/blocks/blockExampleFiles.test.ts
index 089d6d0..983c9eb 100644
--- a/src/blocks/blockExampleFiles.test.ts
+++ b/src/blocks/blockExampleFiles.test.ts
@@ -40,6 +40,14 @@ describe("blockExampleFiles", () => {
 
 		expect(creation).toMatchInlineSnapshot(`
 			{
+			  "addons": [
+			    {
+			      "addons": {
+			        "defaultUsage": undefined,
+			      },
+			      "block": [Function],
+			    },
+			  ],
 			  "files": {
 			    "src": {
 			      "index.ts": "console.log('Hello, world!');",
diff --git a/src/blocks/blockTypeScript.test.ts b/src/blocks/blockTypeScript.test.ts
index 813b808..efe2291 100644
--- a/src/blocks/blockTypeScript.test.ts
+++ b/src/blocks/blockTypeScript.test.ts
@@ -64,6 +64,16 @@ describe("blockTypeScript", () => {
 				}
 				",
 			        },
+			        "usage": [
+			          "\`\`\`shell
+			npm i test-repository
+			\`\`\`
+			\`\`\`ts
+			import { greet } from "test-repository";
+
+			greet("Hello, world! 💖");
+			\`\`\`",
+			        ],
 			      },
 			      "block": [Function],
 			    },
@@ -224,6 +234,16 @@ describe("blockTypeScript", () => {
 				}
 				",
 			        },
+			        "usage": [
+			          "\`\`\`shell
+			npm i test-repository
+			\`\`\`
+			\`\`\`ts
+			import { greet } from "test-repository";
+
+			greet("Hello, world! 💖");
+			\`\`\`",
+			        ],
 			      },
 			      "block": [Function],
 			    },
@@ -382,6 +402,16 @@ describe("blockTypeScript", () => {
 				}
 				",
 			        },
+			        "usage": [
+			          "\`\`\`shell
+			npm i test-repository
+			\`\`\`
+			\`\`\`ts
+			import { greet } from "test-repository";
+
+			greet("Hello, world! 💖");
+			\`\`\`",
+			        ],
 			      },
 			      "block": [Function],
 			    },
@@ -549,6 +579,16 @@ describe("blockTypeScript", () => {
 				}
 				",
 			        },
+			        "usage": [
+			          "\`\`\`shell
+			npm i test-repository
+			\`\`\`
+			\`\`\`ts
+			import { greet } from "test-repository";
+
+			greet("Hello, world! 💖");
+			\`\`\`",
+			        ],
 			      },
 			      "block": [Function],
 			    },
diff --git a/src/options/readReadmeUsage.test.ts b/src/options/readReadmeUsage.test.ts
index 086eb33..cf0f640 100644
--- a/src/options/readReadmeUsage.test.ts
+++ b/src/options/readReadmeUsage.test.ts
@@ -1,46 +1,51 @@
-import { describe, expect, it, vi } from "vitest";
+import { describe, expect, it } from "vitest";
 
 import { readReadmeUsage } from "./readReadmeUsage.js";
 
-const mockReadUsageFromReadme = vi.fn();
+describe(readReadmeUsage, () => {
+	it("returns undefined when ## Usage is not found", async () => {
+		const actual = await readReadmeUsage(() => Promise.resolve("## Other"));
 
-vi.mock("./readUsageFromReadme.js", () => ({
-	get readUsageFromReadme() {
-		return mockReadUsageFromReadme;
-	},
-}));
+		expect(actual).toBeUndefined();
+	});
 
-describe(readReadmeUsage, () => {
-	it("returns the existing usage when readUsageFromReadme provides one", async () => {
-		const existing = "Use it.";
+	it("returns existing content when ## Usage is found and a next important heading is not found", async () => {
+		const actual = await readReadmeUsage(() =>
+			Promise.resolve("## Usage\n\nContents."),
+		);
 
-		mockReadUsageFromReadme.mockReturnValueOnce(existing);
+		expect(actual).toBe(`\n\nContents.`);
+	});
 
-		const usage = await readReadmeUsage(
-			() => Promise.resolve("💖"),
-			() => Promise.resolve(""),
-			() => Promise.resolve(undefined),
+	it("returns undefined when there is no content between ## Usage and ## Development", async () => {
+		const actual = await readReadmeUsage(() =>
+			Promise.resolve("## Usage\n\n  \n## Development"),
 		);
 
-		expect(usage).toBe(existing);
+		expect(actual).toBeUndefined();
 	});
 
-	it("returns sample usage when readUsageFromReadme doesn't provide usage", async () => {
-		mockReadUsageFromReadme.mockReturnValueOnce(undefined);
+	it("returns the content when content exists between ## Usage and ## Development", async () => {
+		const actual = await readReadmeUsage(() =>
+			Promise.resolve("## Usage\n\n  Content.\n## Development"),
+		);
 
-		const usage = await readReadmeUsage(
-			() => Promise.resolve("💖"),
-			() => Promise.resolve(""),
-			() => Promise.resolve("test-repository"),
+		expect(actual).toBe("Content.");
+	});
+
+	it("returns the content when content exists between ## Usage and ## Contributing", async () => {
+		const actual = await readReadmeUsage(() =>
+			Promise.resolve("## Usage\n\n  Content.\n## Contributing"),
 		);
 
-		expect(usage).toBe(`\`\`\`shell
-npm i test-repository
-\`\`\`
-\`\`\`ts
-import { greet } from "test-repository";
+		expect(actual).toBe("Content.");
+	});
+
+	it("returns the content when content exists between ## Usage and ## Contributors", async () => {
+		const actual = await readReadmeUsage(() =>
+			Promise.resolve("## Usage\n\n  Content.\n## Contributors"),
+		);
 
-greet("Hello, world! 💖");
-\`\`\``);
+		expect(actual).toBe("Content.");
 	});
 });
diff --git a/src/options/readUsageFromReadme.test.ts b/src/options/readUsageFromReadme.test.ts
deleted file mode 100644
index 2775768..0000000
--- a/src/options/readUsageFromReadme.test.ts
+++ /dev/null
@@ -1,45 +0,0 @@
-import { describe, expect, it } from "vitest";
-
-import { readUsageFromReadme } from "./readUsageFromReadme.js";
-
-describe(readUsageFromReadme, () => {
-	it("returns undefined when ## Usage is not found", () => {
-		const usage = readUsageFromReadme("## Other");
-
-		expect(usage).toBeUndefined();
-	});
-
-	it("returns existing content when ## Usage is found and a next important heading is not found", () => {
-		const usage = readUsageFromReadme("## Usage\n\nContents.");
-
-		expect(usage).toBe(`\n\nContents.`);
-	});
-
-	it("returns undefined when there is no content between ## Usage and ## Development", () => {
-		const usage = readUsageFromReadme("## Usage\n\n  \n## Development");
-
-		expect(usage).toBeUndefined();
-	});
-
-	it("returns the content when content exists between ## Usage and ## Development", () => {
-		const usage = readUsageFromReadme("## Usage\n\n  Content.\n## Development");
-
-		expect(usage).toBe("Content.");
-	});
-
-	it("returns the content when content exists between ## Usage and ## Contributing", () => {
-		const usage = readUsageFromReadme(
-			"## Usage\n\n  Content.\n## Contributing",
-		);
-
-		expect(usage).toBe("Content.");
-	});
-
-	it("returns the content when content exists between ## Usage and ## Contributors", () => {
-		const usage = readUsageFromReadme(
-			"## Usage\n\n  Content.\n## Contributors",
-		);
-
-		expect(usage).toBe("Content.");
-	});
-});