From 4a8a5620c3b24cabf4b222179e9a44502015e469 Mon Sep 17 00:00:00 2001 From: John Lindquist Date: Wed, 19 Mar 2025 23:05:56 -0600 Subject: [PATCH] feat: change CLI command name to 'cwt' BREAKING CHANGE: The CLI command has been renamed from 'cursor-worktree' to 'cwt' for better usability. All users will need to update their commands to use 'cwt' instead of 'cursor-worktree'. --- README.md | 16 +++++++------- TESTING.md | 47 ++++++++++++++++++++++++++++++++++++++++-- dist/commands/new.js | 32 +++++++++++++++++++--------- dist/index.js | 6 ++++-- package.json | 2 +- src/commands/remove.ts | 9 +++++--- src/index.ts | 3 ++- 7 files changed, 88 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 25dae61..6fb7806 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ pnpm install -g . ### Create a new worktree ```bash -cursor-worktree new [options] +cwt new [options] ``` Options: @@ -22,27 +22,27 @@ Options: Example: ```bash -cursor-worktree new feature/login -cursor-worktree new feature/chat --checkout -cursor-worktree new feature/auth -p ./auth-worktree +cwt new feature/login +cwt new feature/chat --checkout +cwt new feature/auth -p ./auth-worktree ``` ### List worktrees ```bash -cursor-worktree list +cwt list ``` ### Remove a worktree ```bash -cursor-worktree remove +cwt remove ``` You can remove a worktree by either its path or branch name: ```bash -cursor-worktree remove ./feature/login-worktree -cursor-worktree remove feature/chat +cwt remove ./feature/login-worktree +cwt remove feature/chat ``` ## Requirements diff --git a/TESTING.md b/TESTING.md index e993cc1..3d4f9e6 100644 --- a/TESTING.md +++ b/TESTING.md @@ -14,7 +14,50 @@ 1. In a test repository, run: ```bash - cursor-worktree new editor + cwt new editor ``` 2. Verify that a new sibling directory named `editor` is created. -3. Confirm that the worktree is added to the Git repository and that the Cursor editor opens the new directory. \ No newline at end of file +3. Confirm that the worktree is added to the Git repository and that the Cursor editor opens the new directory. + +## Manual Test for CLI Command Name Change + +1. Install the package globally: + ```bash + pnpm install -g . + ``` +2. Run the command help to verify the new command: + ```bash + cwt --help + ``` +3. Optionally, test additional commands: + - Create a new worktree: + ```bash + cwt new feature/test + ``` + - List worktrees: + ```bash + cwt list + ``` + - Remove a worktree: + ```bash + cwt remove feature/test + ``` + +## Remove Worktree Force Flag Test + +1. Create a test worktree: + ```bash + cwt new test-branch + ``` +2. Make some changes in the worktree that would prevent normal removal +3. Try removing the worktree without the force flag: + ```bash + cwt remove test-branch + ``` + This should fail if there are uncommitted changes +4. Try removing the worktree with the force flag: + ```bash + cwt remove --force test-branch + ``` + This should succeed and remove the worktree regardless of its state +5. Verify that the worktree directory is removed and the Git worktree reference is cleaned up diff --git a/dist/commands/new.js b/dist/commands/new.js index 9ef8881..4c7d89d 100644 --- a/dist/commands/new.js +++ b/dist/commands/new.js @@ -1,17 +1,25 @@ import { execa } from "execa"; import chalk from "chalk"; -import { resolve } from "node:path"; +import { resolve, join, dirname, basename } from "node:path"; export async function newWorktreeHandler(branchName = "main", options) { try { // 1. Validate we're in a git repo await execa("git", ["rev-parse", "--is-inside-work-tree"]); // 2. Build final path for the new worktree - const folderName = options.path ?? `./${branchName}-worktree`; + let folderName; + if (options.path) { + folderName = options.path; + } + else { + const currentDir = process.cwd(); + const parentDir = dirname(currentDir); + const currentDirName = basename(currentDir); + // Create a sibling directory: current directory name concatenated with branchName + folderName = join(parentDir, `${currentDirName}-${branchName}`); + } const resolvedPath = resolve(folderName); // 3. (Optional) checkout new local branch if it doesn't exist yet - // This step is only run if user passes `--checkout` if (options.checkout) { - // Check if branch already exists const { stdout } = await execa("git", ["branch", "--list", branchName]); if (!stdout) { console.log(chalk.yellow(`Branch "${branchName}" doesn't exist locally. Creating...`)); @@ -22,17 +30,21 @@ export async function newWorktreeHandler(branchName = "main", options) { } } else { - // Ensure the branch is present, or you might want to skip this check console.log(chalk.gray(`Using branch "${branchName}". Make sure it exists (local or remote).`)); } // 4. Create the new worktree console.log(chalk.blue(`Creating new worktree for branch "${branchName}" at: ${resolvedPath}`)); await execa("git", ["worktree", "add", resolvedPath, branchName]); - // 5. Open in Cursor editor - // (Assuming "cursor " is how you open a folder in Cursor) - console.log(chalk.blue(`Opening ${resolvedPath} in Cursor...`)); - await execa("cursor", [resolvedPath], { stdio: "inherit" }); - console.log(chalk.green(`Worktree created and opened in Cursor successfully!`)); + // 5. (Optional) Install dependencies if --install flag is provided + if (options.install) { + console.log(chalk.blue(`Installing dependencies using ${options.install} in ${resolvedPath}...`)); + await execa(options.install, ["install"], { cwd: resolvedPath, stdio: "inherit" }); + } + // 6. Open in the specified editor (or default to "cursor") + const editorCommand = options.editor || "cursor"; + console.log(chalk.blue(`Opening ${resolvedPath} in ${editorCommand}...`)); + await execa(editorCommand, [resolvedPath], { stdio: "inherit" }); + console.log(chalk.green(`Worktree created, dependencies installed (if specified), and opened in ${editorCommand} successfully!`)); } catch (error) { if (error instanceof Error) { diff --git a/dist/index.js b/dist/index.js index 954c062..3c924bb 100755 --- a/dist/index.js +++ b/dist/index.js @@ -5,7 +5,7 @@ import { listWorktreesHandler } from "./commands/list.js"; import { removeWorktreeHandler } from "./commands/remove.js"; const program = new Command(); program - .name("cursor-worktree") + .name("cwt") .description("Manage git worktrees and open them in the Cursor editor.") .version("1.0.0"); program @@ -13,7 +13,9 @@ program .argument("[branchName]", "Name of the branch to base this worktree on") .option("-p, --path ", "Relative path/folder name for new worktree") .option("-c, --checkout", "Create new branch if it doesn't exist and checkout automatically", false) - .description("Create a new worktree for the specified branch and open it in Cursor.") + .option("-i, --install ", "Package manager to use for installing dependencies (npm, pnpm, bun, etc.)") + .option("-e, --editor ", "Editor to use for opening the worktree (e.g., code, webstorm, windsurf, etc.)") + .description("Create a new worktree for the specified branch, install dependencies if specified, and open in editor.") .action(newWorktreeHandler); program .command("list") diff --git a/package.json b/package.json index 084ea6f..fe7ad5a 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "license": "MIT", "type": "module", "bin": { - "cursor-worktree": "dist/index.js" + "cwt": "dist/index.js" }, "scripts": { "build": "tsc", diff --git a/src/commands/remove.ts b/src/commands/remove.ts index 6dd4f62..0a0ba30 100644 --- a/src/commands/remove.ts +++ b/src/commands/remove.ts @@ -3,7 +3,10 @@ import chalk from "chalk"; import { stat, rm } from "node:fs/promises"; import { resolve } from "node:path"; -export async function removeWorktreeHandler(pathOrBranch: string = "") { +export async function removeWorktreeHandler( + pathOrBranch: string = "", + options: { force?: boolean } +) { try { await execa("git", ["rev-parse", "--is-inside-work-tree"]); @@ -47,8 +50,8 @@ export async function removeWorktreeHandler(pathOrBranch: string = "") { console.log(chalk.blue(`Removing worktree: ${targetPath}`)); - // Remove from Git's perspective - await execa("git", ["worktree", "remove", targetPath]); + // Pass the "--force" flag to Git if specified + await execa("git", ["worktree", "remove", ...(options.force ? ["--force"] : []), targetPath]); // Optionally also remove the physical directory if it still exists try { diff --git a/src/index.ts b/src/index.ts index aee336a..d8ec76b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -9,7 +9,7 @@ import { removeWorktreeHandler } from "./commands/remove.js"; const program = new Command(); program - .name("cursor-worktree") + .name("cwt") .description("Manage git worktrees and open them in the Cursor editor.") .version("1.0.0"); @@ -31,6 +31,7 @@ program program .command("remove") .argument("[pathOrBranch]", "Path of the worktree or branch to remove.") + .option("-f, --force", "Force removal of worktree and deletion of the folder", false) .description("Remove a specified worktree. Cleans up the .git/worktrees references.") .action(removeWorktreeHandler);