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

Issue:
🐛 Bug: Git tags should be cleared after creating a new repository
### 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

When a new repository is created (`--mode create`), it should have no [Git tags](https://git-scm.com/book/en/v2/Git-Basics-Tagging) to start.

### Actual

...instead, they have the full list of git tags from this `JoshuaKGoldberg/create-typescript-app` repo 😂. Example from https://github.com/JoshuaKGoldberg/release-it-action: 

```plaintext
~/repos/release-it-action $ git tag --list 
v0.1.0
v0.1.1
v0.2.0
v0.2.1
v1.0.0
v1.1.0
v1.1.1
v1.1.2
...
```

### Additional Info

Tags are auto-created by https://github.com/release-it/release-it during publishing (`.github/workflows/release.yml`). So it's good that a repo can create them. But they shouldn't be copied from the template to a new repository.

https://stackoverflow.com/questions/19542301/delete-all-tags-from-a-git-repository

Pull request:
fix: delete any existing Git tags in --mode create
## PR Checklist

- [x] Addresses an existing open issue: fixes #926
- [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

At the end of `createWithOptions`, prepends a nice little Git command before the other commands. Includes a small refactor of `createCleanupCommands` to not care about the mode.

💖 

Patch:
diff --git a/src/create/createWithOptions.ts b/src/create/createWithOptions.ts
index c8a9fd9..6320b7e 100644
--- a/src/create/createWithOptions.ts
+++ b/src/create/createWithOptions.ts
@@ -5,6 +5,7 @@ import { createCleanupCommands } from "../shared/createCleanupCommands.js";
 import { doesRepositoryExist } from "../shared/doesRepositoryExist.js";
 import { GitHubAndOptions } from "../shared/options/readOptions.js";
 import { addToolAllContributors } from "../steps/addToolAllContributors.js";
+import { clearLocalGitTags } from "../steps/clearLocalGitTags.js";
 import { finalizeDependencies } from "../steps/finalizeDependencies.js";
 import { initializeGitHubRepository } from "../steps/initializeGitHubRepository/index.js";
 import { populateCSpellDictionary } from "../steps/populateCSpellDictionary.js";
@@ -46,9 +47,11 @@ export async function createWithOptions({ github, options }: GitHubAndOptions) {
 			);
 		}
 
-		await runCleanup(createCleanupCommands(options), options.mode);
+		await runCleanup(createCleanupCommands(options.bin), options.mode);
 	}
 
+	await withSpinner("Clearing any local Git tags", clearLocalGitTags);
+
 	const sendToGitHub =
 		github && (await doesRepositoryExist(github.octokit, options));
 
diff --git a/src/initialize/initializeWithOptions.ts b/src/initialize/initializeWithOptions.ts
index 73f6592..2460a18 100644
--- a/src/initialize/initializeWithOptions.ts
+++ b/src/initialize/initializeWithOptions.ts
@@ -61,5 +61,8 @@ export async function initializeWithOptions({
 		);
 	}
 
-	await runCleanup(createCleanupCommands(options), options.mode);
+	await runCleanup(
+		createCleanupCommands(options.bin, "pnpm dedupe --offline"),
+		options.mode,
+	);
 }
diff --git a/src/migrate/migrateWithOptions.ts b/src/migrate/migrateWithOptions.ts
index 27fa80c..0e6ce16 100644
--- a/src/migrate/migrateWithOptions.ts
+++ b/src/migrate/migrateWithOptions.ts
@@ -66,5 +66,5 @@ export async function migrateWithOptions({
 		await withSpinner("Populating CSpell dictionary", populateCSpellDictionary);
 	}
 
-	await runCleanup(createCleanupCommands(options), options.mode);
+	await runCleanup(createCleanupCommands(options.bin), options.mode);
 }
diff --git a/src/shared/createCleanupCommands.ts b/src/shared/createCleanupCommands.ts
index 7eef5a8..d48c647 100644
--- a/src/shared/createCleanupCommands.ts
+++ b/src/shared/createCleanupCommands.ts
@@ -1,12 +1,9 @@
-import { Options } from "./types.js";
-
-export function createCleanupCommands({
-	bin,
-	mode,
-}: Pick<Options, "bin" | "mode">) {
+export function createCleanupCommands(
+	bin: string | undefined,
+	...prependedCommands: string[]
+) {
 	return [
-		// There's no need to dedupe when initializing from the fixed template
-		...(mode === "initialize" ? [] : ["pnpm dedupe --offline"]),
+		...prependedCommands,
 		// n/no-missing-import rightfully reports on a missing the bin .js file
 		...(bin ? ["pnpm build"] : []),
 		"pnpm lint --fix",
diff --git a/src/steps/clearLocalGitTags.ts b/src/steps/clearLocalGitTags.ts
new file mode 100644
index 0000000..c7c9d00
--- /dev/null
+++ b/src/steps/clearLocalGitTags.ts
@@ -0,0 +1,7 @@
+import { execaCommand } from "execa";
+
+export async function clearLocalGitTags() {
+	const tags = await execaCommand("git tag -l");
+
+	await execaCommand(`git tag -d ${tags.stdout.replaceAll("\n", " ")}`);
+}


Test patch:
diff --git a/src/create/createWithOptions.test.ts b/src/create/createWithOptions.test.ts
index 6e7156a..6f34c38 100644
--- a/src/create/createWithOptions.test.ts
+++ b/src/create/createWithOptions.test.ts
@@ -63,6 +63,8 @@ vi.mock("../steps/finalizeDependencies.js");
 
 vi.mock("../steps/populateCSpellDictionary.js");
 
+vi.mock("../steps/clearLocalGitTags.js");
+
 vi.mock("../steps/runCleanup.js");
 
 vi.mock("../shared/doesRepositoryExist.js", () => ({
diff --git a/src/shared/createCleanupCommands.test.ts b/src/shared/createCleanupCommands.test.ts
index 4fddbb9..8c2117f 100644
--- a/src/shared/createCleanupCommands.test.ts
+++ b/src/shared/createCleanupCommands.test.ts
@@ -3,23 +3,27 @@ import { describe, expect, it } from "vitest";
 import { createCleanupCommands } from "./createCleanupCommands.js";
 
 describe("createCleanupCommands", () => {
-	it("only lints and formats when bin is not enabled and mode is initialize", () => {
-		const actual = createCleanupCommands({
-			bin: undefined,
-			mode: "initialize",
-		});
+	it("only lints and formats when bin is not enabled and there are no prepended commands", () => {
+		const actual = createCleanupCommands(undefined);
 
 		expect(actual).toEqual(["pnpm lint --fix", "pnpm format --write"]);
 	});
 
-	it("runs dedupe and build before it lints and formats when bin is enabled and mode is create", () => {
-		const actual = createCleanupCommands({
-			bin: "bin/index.js",
-			mode: "create",
-		});
+	it("runs prepended commands before it lints and formats when bin is not enabled", () => {
+		const actual = createCleanupCommands(undefined, "prepended");
 
 		expect(actual).toEqual([
-			"pnpm dedupe --offline",
+			"prepended",
+			"pnpm lint --fix",
+			"pnpm format --write",
+		]);
+	});
+
+	it("runs prepended commands before it builds, lints, and formats when bin is not enabled", () => {
+		const actual = createCleanupCommands("bin/index.js", "prepended");
+
+		expect(actual).toEqual([
+			"prepended",
 			"pnpm build",
 			"pnpm lint --fix",
 			"pnpm format --write",