Skip to content

Conversation

@hayaishi
Copy link

@hayaishi hayaishi commented Dec 21, 2025

Thanks for developing and maintaining this tool β€” it has made my git worktree workflows much smoother compared to using git worktree directly πŸ™‡β€β™‚οΈ

This PR introduces pre_remove / post_remove hooks to .wtp.yml, enabling users to run cleanup and lifecycle automation around git worktree remove.


✨ Motivation

When removing a worktree, users often want to automate tasks such as:

  • backing up important files
  • cleaning caches / generated artifacts
  • resetting local / preview environments
  • notifying external tools or services
  • performing project housekeeping workflows

These hooks allow these workflows to be handled consistently within wtp.


πŸ”§ How to Use

hooks:
  pre_remove:
    - type: copy
      from: ".env"
      to: ".env.backup"

    - type: command
      command: "echo cleanup"

  post_remove:
    - type: command
      command: "echo after remove"

πŸ“Œ Behavior β€” Path Resolution

pre_remove

  • Runs before git worktree remove
  • from paths are resolved relative to the worktree
  • to paths are resolved relative to the repository root
  • work_dir defaults to repository root
  • Security check ensures paths do not escape the repository root

post_remove

  • Runs after git worktree remove
  • Relative paths (from, to, and work_dir) are resolved relative to the repository root
  • Consistent cleanup behavior together with pre_remove

post_create (unchanged)

  • from paths are resolved relative to the repository root
  • to paths are resolved relative to the worktree
  • Existing setup semantics remain as-is

πŸ” Base Path Summary

Hook from Base Path to Base Path
post_create repo root worktree
pre_remove worktree repo root
post_remove repo root repo root

Happy to adjust naming, behavior, or docs if you prefer πŸ™
If this does not align with the project's direction, please feel free to close the PR.

Summary by CodeRabbit

  • New Features

    • Added pre_remove and post_remove hook support to run custom commands before and after worktree removal, with sensible working-directory defaults and execution feedback.
  • Documentation

    • Updated configuration examples and architecture docs to describe the new hooks, resolution rules, and expected behavior.
  • Tests

    • Added integration and unit tests covering pre-remove and post-remove execution, defaults, error paths, and cross-platform command handling.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Dec 21, 2025

Walkthrough

Adds pre-remove and post-remove hook support: configuration fields and validation, executor refactor to support base-path-aware hook execution, integration of pre-remove hooks before git worktree removal and post-remove hooks after removal, updated init template and docs, and corresponding tests.

Changes

Cohort / File(s) Summary
Configuration Schema & Tests
internal/config/config.go, internal/config/config_test.go
Added PreRemove and PostRemove fields to Hooks; added validateHooks(name, ...); replaced HasHooks() with HasPostCreateHooks(), HasPreRemoveHooks(), HasPostRemoveHooks(); updated tests to cover new hook types and validation messages.
Hook Executor & Tests
internal/hooks/executor.go, internal/hooks/executor_test.go
Refactored executor to base-path-aware helpers (executeHooksWithWriter, executeHookWithWriter, etc.); added ExecutePreRemoveHooks() and ExecutePostRemoveHooks(); copy/command resolution now accepts separate source/destination/command bases; introduced synchronized writer for streamed command output and per-hook completion logs; extended tests for pre/post flows and path-resolution.
Remove Command & Tests
cmd/wtp/remove.go, cmd/wtp/remove_test.go
Integrated pre-remove hook execution before git worktree remove and post-remove execution after removal; added config loading and error propagation around hooks; extensive tests for default workdir resolution, failure paths, and emitted command assertions.
Add Command Adjustment
cmd/wtp/add.go
Replaced cfg.HasHooks() check with cfg.HasPostCreateHooks() to use the new specific accessor.
Init Template & Tests
cmd/wtp/init.go, cmd/wtp/init_test.go
Added pre_remove and post_remove sections (with commented examples) to the generated .wtp.yml template; tests updated to assert presence and comments.
Docs
README.md, docs/architecture.md
Documented pre-remove and post-remove hook semantics, path-resolution behavior, default workdir rules, and generalized hooks section/title.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant RemoveCmd as Remove Command
    participant Config as Config Loader
    participant PreHook as Pre-Remove Executor
    participant Git as Git Worktree
    participant PostHook as Post-Remove Executor

    User->>RemoveCmd: wtp remove <name>
    RemoveCmd->>Config: Load repo config (.wtp.yml)
    Config-->>RemoveCmd: Config (hooks)

    alt has pre_remove
        RemoveCmd->>PreHook: Execute pre-remove hooks
        PreHook->>PreHook: Resolve paths relative to worktree
        PreHook-->>RemoveCmd: Pre-remove hooks completed / error
    end

    RemoveCmd->>Git: git worktree remove <path>
    Git-->>RemoveCmd: Worktree removed / error

    alt has post_remove
        RemoveCmd->>PostHook: Execute post-remove hooks
        PostHook->>PostHook: Resolve paths relative to repo root
        PostHook-->>RemoveCmd: Post-remove hooks completed / error
    end

    RemoveCmd-->>User: Report success or error
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

  • Focus areas:
    • internal/hooks/executor.go β€” base-path refactor, path resolution for copy/command hooks, synchronized writer and streamed output handling.
    • cmd/wtp/remove.go and cmd/wtp/remove_test.go β€” ensure pre-remove errors prevent removal, post-remove behavior after deletion, and test coverage correctness.
    • internal/config/config.go β€” validation refactor, error messages, and new Has*Hooks accessors; check all call sites updated (e.g., add.go).
    • Cross-file consistency β€” ensure new hook fields are parsed, validated, and referenced consistently in tests and docs.

Possibly related PRs

Suggested labels

codex

Poem

🐰 I hopped through configs, tidy and neat,
Pre-hooks saved treasures before retreat,
Git waved goodbye as post-hooks pat the ground,
Echoes of commands β€” a soft, proud sound,
My little paws left no loose end to meet. πŸ₯•

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 5.88% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
βœ… Passed checks (2 passed)
Check name Status Explanation
Description Check βœ… Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check βœ… Passed The title clearly and specifically summarizes the main change: introducing two new hook types (pre_remove and post_remove) for the worktree removal lifecycle.
✨ Finishing touches
  • πŸ“ Generate docstrings
πŸ§ͺ Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❀️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
cmd/wtp/remove.go (1)

198-229: Consider moving work_dir resolution into the executor.

The current approach creates a modified copy of the config just to adjust work_dir paths before calling the executor. This adds complexity at the command level. Consider whether this path resolution logic would be better encapsulated within the executor's ExecutePostRemoveHooks method, where it can handle the base-path semantics internally.

This would:

  • Reduce duplication if other commands need similar behavior
  • Keep path resolution logic centralized in the hooks module
  • Simplify the command-level code
internal/config/config_test.go (1)

449-481: Consider adding an empty slice test case for consistency.

TestHasPostCreateHooks includes a test case for an empty hooks slice (lines 429-436), but TestHasPreRemoveHooks and TestHasPostRemoveHooks don't include this edge case. For completeness and consistency, consider adding similar test cases.

πŸ”Ž Suggested addition
 		{
 			name:     "config without pre-remove hooks",
 			config:   &Config{Hooks: Hooks{}},
 			expected: false,
 		},
+		{
+			name: "config with empty pre-remove hooks slice",
+			config: &Config{
+				Hooks: Hooks{
+					PreRemove: []Hook{},
+				},
+			},
+			expected: false,
+		},
 	}
πŸ“œ Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

πŸ“₯ Commits

Reviewing files that changed from the base of the PR and between e9095d3 and 8b937e5.

πŸ“’ Files selected for processing (11)
  • README.md (1 hunks)
  • cmd/wtp/add.go (1 hunks)
  • cmd/wtp/init.go (1 hunks)
  • cmd/wtp/init_test.go (2 hunks)
  • cmd/wtp/remove.go (4 hunks)
  • cmd/wtp/remove_test.go (3 hunks)
  • docs/architecture.md (1 hunks)
  • internal/config/config.go (3 hunks)
  • internal/config/config_test.go (6 hunks)
  • internal/hooks/executor.go (7 hunks)
  • internal/hooks/executor_test.go (2 hunks)
🧰 Additional context used
πŸ““ Path-based instructions (2)
**/*.go

πŸ“„ CodeRabbit inference engine (AGENTS.md)

**/*.go: Follow standard Go style with tabs and gofmt; package names must be short and lowercase
Keep import groups tidy; use goimports for organization with local prefix following module path github.com/satococoa/wtp/v2
Adhere to linting rules in .golangci.yml (vet, staticcheck, gosec, mnd, lll=120)
Wrap errors with context; avoid ignoring errors in Go code
Use snake_case for Go filenames (e.g., remove.go, shell_integration.go)
Document exported items in Go when non-trivial; follow godoc conventions

Files:

  • cmd/wtp/remove.go
  • cmd/wtp/init_test.go
  • cmd/wtp/remove_test.go
  • cmd/wtp/init.go
  • internal/hooks/executor_test.go
  • internal/config/config.go
  • internal/hooks/executor.go
  • cmd/wtp/add.go
  • internal/config/config_test.go
**/*_test.go

πŸ“„ CodeRabbit inference engine (AGENTS.md)

**/*_test.go: Unit tests must be placed alongside packages as *_test.go files in the same directory
Target 70% code coverage with unit tests focusing on fast feedback and mocked git interactions using table-driven test cases

Files:

  • cmd/wtp/init_test.go
  • cmd/wtp/remove_test.go
  • internal/hooks/executor_test.go
  • internal/config/config_test.go
🧠 Learnings (10)
πŸ““ Common learnings
Learnt from: CR
Repo: satococoa/wtp PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T13:33:48.693Z
Learning: Applies to .wtp.yml : Project hooks must be defined in `.wtp.yml`; keep commands deterministic and safe, avoiding destructive steps by default
Learnt from: CR
Repo: satococoa/wtp PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T13:33:48.693Z
Learning: Shell integration must separate completion and hooks for clarity; `wtp cd` outputs absolute worktree path with no side effects
Learnt from: CR
Repo: satococoa/wtp PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T13:33:48.693Z
Learning: Worktree naming convention: main worktree is rendered as `@`; non-main worktrees display path relative to `base_dir` (e.g., `.worktrees/feat/hoge` β†’ `feat/hoge`)
Learnt from: CR
Repo: satococoa/wtp PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T13:33:48.693Z
Learning: Run commands from inside a worktree to mimic real usage (e.g., `go run ../cmd/wtp add feature/new-feature`)
πŸ“š Learning: 2025-12-02T13:33:48.693Z
Learnt from: CR
Repo: satococoa/wtp PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T13:33:48.693Z
Learning: Applies to .wtp.yml : Project hooks must be defined in `.wtp.yml`; keep commands deterministic and safe, avoiding destructive steps by default

Applied to files:

  • docs/architecture.md
  • cmd/wtp/remove.go
  • cmd/wtp/init_test.go
  • README.md
  • cmd/wtp/remove_test.go
  • cmd/wtp/init.go
πŸ“š Learning: 2025-12-02T13:33:48.693Z
Learnt from: CR
Repo: satococoa/wtp PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T13:33:48.693Z
Learning: Run commands from inside a worktree to mimic real usage (e.g., `go run ../cmd/wtp add feature/new-feature`)

Applied to files:

  • cmd/wtp/remove.go
  • cmd/wtp/init_test.go
  • README.md
  • cmd/wtp/remove_test.go
  • cmd/wtp/init.go
  • internal/hooks/executor.go
πŸ“š Learning: 2025-12-02T13:33:48.693Z
Learnt from: CR
Repo: satococoa/wtp PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T13:33:48.693Z
Learning: CLI entrypoint is located at `cmd/wtp`; new command implementations must integrate with the main CLI structure

Applied to files:

  • cmd/wtp/remove.go
  • cmd/wtp/remove_test.go
  • cmd/wtp/init.go
  • internal/hooks/executor.go
  • cmd/wtp/add.go
πŸ“š Learning: 2025-12-02T13:33:48.693Z
Learnt from: CR
Repo: satococoa/wtp PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T13:33:48.693Z
Learning: Shell integration must separate completion and hooks for clarity; `wtp cd` outputs absolute worktree path with no side effects

Applied to files:

  • cmd/wtp/remove.go
  • cmd/wtp/remove_test.go
  • cmd/wtp/init.go
  • internal/hooks/executor.go
πŸ“š Learning: 2025-12-02T13:33:48.693Z
Learnt from: CR
Repo: satococoa/wtp PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T13:33:48.693Z
Learning: Applies to **/*_test.go : Target 70% code coverage with unit tests focusing on fast feedback and mocked git interactions using table-driven test cases

Applied to files:

  • cmd/wtp/remove.go
  • cmd/wtp/init_test.go
  • cmd/wtp/remove_test.go
  • internal/hooks/executor_test.go
πŸ“š Learning: 2025-12-02T13:33:48.693Z
Learnt from: CR
Repo: satococoa/wtp PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T13:33:48.693Z
Learning: The root module is `github.com/satococoa/wtp/v2` running on Go 1.24; maintain module path consistency

Applied to files:

  • cmd/wtp/remove.go
  • cmd/wtp/remove_test.go
πŸ“š Learning: 2025-12-02T13:33:48.693Z
Learnt from: CR
Repo: satococoa/wtp PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T13:33:48.693Z
Learning: Applies to test/e2e/**/*.go : End-to-end tests must be placed in `test/e2e` and exercise real git workflows using the built binary

Applied to files:

  • cmd/wtp/init_test.go
  • cmd/wtp/remove_test.go
πŸ“š Learning: 2025-12-02T13:33:48.693Z
Learnt from: CR
Repo: satococoa/wtp PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T13:33:48.693Z
Learning: Applies to **/*.go : Keep import groups tidy; use goimports for organization with local prefix following module path `github.com/satococoa/wtp/v2`

Applied to files:

  • cmd/wtp/remove_test.go
  • cmd/wtp/init.go
πŸ“š Learning: 2025-12-02T13:33:48.693Z
Learnt from: CR
Repo: satococoa/wtp PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T13:33:48.693Z
Learning: Use `go run ./cmd/wtp <args>` for rapid feedback during development instead of building binaries

Applied to files:

  • cmd/wtp/remove_test.go
🧬 Code graph analysis (5)
cmd/wtp/remove.go (3)
internal/config/config.go (5)
  • LoadConfig (56-96)
  • ConfigFileName (43-43)
  • Config (13-17)
  • Hook (32-39)
  • Hooks (25-29)
internal/errors/errors.go (1)
  • ConfigLoadFailed (198-224)
internal/hooks/executor.go (1)
  • NewExecutor (29-34)
cmd/wtp/remove_test.go (2)
internal/command/types.go (2)
  • Result (11-15)
  • Command (4-8)
internal/config/config.go (5)
  • Config (13-17)
  • Defaults (20-22)
  • Hooks (25-29)
  • Hook (32-39)
  • HookTypeCommand (51-51)
internal/hooks/executor_test.go (3)
internal/hooks/executor.go (1)
  • NewExecutor (29-34)
internal/config/config.go (5)
  • Config (13-17)
  • Hooks (25-29)
  • Hook (32-39)
  • HookTypeCopy (49-49)
  • HookTypeCommand (51-51)
internal/command/types.go (1)
  • Command (4-8)
internal/hooks/executor.go (1)
internal/config/config.go (4)
  • Hooks (25-29)
  • Hook (32-39)
  • HookTypeCopy (49-49)
  • HookTypeCommand (51-51)
internal/config/config_test.go (2)
internal/config/config.go (4)
  • Hooks (25-29)
  • Hook (32-39)
  • HookTypeCommand (51-51)
  • Config (13-17)
internal/command/types.go (1)
  • Command (4-8)
πŸ”‡ Additional comments (19)
cmd/wtp/init_test.go (1)

144-145: LGTM! Test assertions properly validate new hook sections.

The test correctly verifies that both pre_remove: and post_remove: sections, along with their descriptive comments, are present in the generated configuration file.

Also applies to: 159-160

cmd/wtp/init.go (1)

103-111: LGTM! Hook templates are well-structured and safe.

The new hook sections follow the established pattern and use safe example commands (echo statements), aligning with the project's guideline to keep commands deterministic and safe by default. Based on learnings, project hooks must be defined in .wtp.yml and should avoid destructive steps by default.

README.md (1)

226-232: LGTM! Documentation clearly explains hook behavior and path resolution.

The documentation effectively describes the path resolution semantics and working directory defaults for both pre_remove and post_remove hooks, with clear reasoning (e.g., "the worktree is gone" for post_remove).

cmd/wtp/remove_test.go (2)

302-347: LGTM! Comprehensive integration test for pre-remove hooks.

The test properly creates a realistic scenario with temporary directories, config file, and mock executor. It verifies both the hook execution messaging and the actual hook output.


396-430: Good cross-platform test coverage.

The test correctly handles platform differences by selecting pwd for Unix-like systems and cd for Windows. This ensures the test validates working directory resolution across different operating systems.

docs/architecture.md (1)

116-122: LGTM! Documentation accurately reflects the expanded hook system.

The heading change from "Post-create hooks support" to "Hooks support" appropriately reflects the broader scope now that pre-remove and post-remove hooks are included.

Also applies to: 128-128

cmd/wtp/add.go (1)

336-336: LGTM! API update makes hook type checking more explicit.

The change from cfg.HasHooks() to cfg.HasPostCreateHooks() improves clarity by explicitly indicating which hook type is being checked, aligning with the introduction of multiple hook types (post-create, pre-remove, post-remove).

internal/hooks/executor_test.go (2)

77-126: Excellent integration test validating pre-remove path semantics.

This test effectively validates the key behavior described in the PR objectives:

  • Copy hook's from path resolves relative to the worktree (source of "original.file")
  • Copy hook's to path resolves relative to the repository root (destination "backup.file")
  • Command hook's work_dir defaults to running inside the worktree

The test provides strong evidence that the path resolution semantics are working as designed.


27-32: LGTM! Comprehensive edge case coverage for new hook types.

The nil config and empty hooks tests ensure that both ExecutePreRemoveHooks and ExecutePostRemoveHooks handle edge cases gracefully without errors.

Also applies to: 46-56, 58-75

internal/config/config_test.go (2)

40-74: LGTM!

Test cases appropriately extended to cover the new pre_remove and post_remove hooks. The YAML content and assertions are consistent with the new hook structure.


176-187: LGTM!

Valid configuration test case appropriately extended to include PreRemove and PostRemove hooks, ensuring comprehensive validation coverage.

internal/config/config.go (3)

24-29: LGTM!

Clean extension of the Hooks struct with new PreRemove and PostRemove fields. The YAML tags follow the existing convention with omitempty for optional fields.


130-150: LGTM!

The validateHooks helper is a clean refactor that eliminates code duplication and provides consistent, informative error messages including the hook name and 1-based index.


176-189: LGTM!

The three Has*Hooks methods provide a clean, consistent API for checking hook presence. The implementations are simple and correct.

internal/hooks/executor.go (5)

36-49: LGTM!

Clean refactoring of ExecutePostCreateHooks to use the generalized base-path-aware helper. The path resolution correctly reflects the PR description: from = repo root, to = worktree.


51-65: LGTM!

ExecutePreRemoveHooks correctly implements the path resolution described in the PR: from paths resolve from the worktree (to extract files before removal), and to paths resolve from the repository root.


82-107: LGTM!

Clean generalized hook execution loop with helpful progress logging. The error wrapping includes the hook index for easier debugging.


127-151: LGTM!

The copy hook implementation correctly handles separate source and destination base paths with appropriate security checks via ensureWithinBase. The path traversal prevention aligns with the learning about keeping hooks deterministic and safe.


225-228: Verify environment variable semantics for post-remove hooks.

GIT_WTP_WORKTREE_PATH is set to basePath, which for post-remove hooks would point to the (now removed) worktree directory. This is potentially useful for scripts needing to know which worktree was removed, but the variable name implies an existing path.

Consider documenting this behavior or, if the path resolution fix above is applied, reviewing whether this variable should still reference the original worktree path or the current working directory.

@hayaishi
Copy link
Author

@coderabbitai generate docstrings

@coderabbitai
Copy link

coderabbitai bot commented Dec 21, 2025

βœ… Actions performed

Initiated docstring generation; will generate only if new commits exist.

@coderabbitai
Copy link

coderabbitai bot commented Dec 21, 2025

Note

Docstrings generation - SUCCESS
Generated docstrings for this pull request at #69

coderabbitai bot added a commit that referenced this pull request Dec 21, 2025
Docstrings generation was requested by @hayaishi.

* #68 (comment)

The following files were modified:

* `cmd/wtp/add.go`
* `cmd/wtp/init.go`
* `cmd/wtp/remove.go`
* `internal/config/config.go`
* `internal/hooks/executor.go`
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
internal/hooks/executor_test.go (1)

90-92: Consider adding cleanup for the created file.

The file srcFile is created with 0644 permissions. While t.TempDir() handles cleanup, explicitly using the octal literal 0o644 would be more consistent with the directoryPermissions constant style used elsewhere.

πŸ”Ž Suggested change
-	err = os.WriteFile(srcFile, []byte("test"), 0644)
+	err = os.WriteFile(srcFile, []byte("test"), 0o644)
cmd/wtp/remove_test.go (1)

404-407: Minor: Variable shadows imported package name.

The variable command shadows the imported github.com/satococoa/wtp/v2/internal/command package. While this works since the package isn't used after this point, renaming improves clarity.

πŸ”Ž Suggested rename
-	command := "pwd"
+	cmdStr := "pwd"
 	if runtime.GOOS == "windows" {
-		command = "cd"
+		cmdStr = "cd"
 	}
 
 	cfg := &config.Config{
 		Defaults: config.Defaults{
 			BaseDir: "../worktrees",
 		},
 		Hooks: config.Hooks{
 			PostRemove: []config.Hook{
 				{
 					Type:    config.HookTypeCommand,
-					Command: command,
+					Command: cmdStr,
 					WorkDir: "scripts",
 				},
 			},
 		},
 	}
πŸ“œ Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

πŸ“₯ Commits

Reviewing files that changed from the base of the PR and between 8b937e5 and b750efb.

πŸ“’ Files selected for processing (4)
  • cmd/wtp/remove.go
  • cmd/wtp/remove_test.go
  • internal/hooks/executor.go
  • internal/hooks/executor_test.go
🧰 Additional context used
πŸ““ Path-based instructions (2)
**/*.go

πŸ“„ CodeRabbit inference engine (AGENTS.md)

**/*.go: Follow standard Go style with tabs and gofmt; package names must be short and lowercase
Keep import groups tidy; use goimports for organization with local prefix following module path github.com/satococoa/wtp/v2
Adhere to linting rules in .golangci.yml (vet, staticcheck, gosec, mnd, lll=120)
Wrap errors with context; avoid ignoring errors in Go code
Use snake_case for Go filenames (e.g., remove.go, shell_integration.go)
Document exported items in Go when non-trivial; follow godoc conventions

Files:

  • internal/hooks/executor_test.go
  • internal/hooks/executor.go
  • cmd/wtp/remove_test.go
  • cmd/wtp/remove.go
**/*_test.go

πŸ“„ CodeRabbit inference engine (AGENTS.md)

**/*_test.go: Unit tests must be placed alongside packages as *_test.go files in the same directory
Target 70% code coverage with unit tests focusing on fast feedback and mocked git interactions using table-driven test cases

Files:

  • internal/hooks/executor_test.go
  • cmd/wtp/remove_test.go
🧠 Learnings (10)
πŸ““ Common learnings
Learnt from: CR
Repo: satococoa/wtp PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T13:33:48.693Z
Learning: Applies to .wtp.yml : Project hooks must be defined in `.wtp.yml`; keep commands deterministic and safe, avoiding destructive steps by default
Learnt from: CR
Repo: satococoa/wtp PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T13:33:48.693Z
Learning: Shell integration must separate completion and hooks for clarity; `wtp cd` outputs absolute worktree path with no side effects
πŸ“š Learning: 2025-12-02T13:33:48.693Z
Learnt from: CR
Repo: satococoa/wtp PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T13:33:48.693Z
Learning: Applies to **/*_test.go : Target 70% code coverage with unit tests focusing on fast feedback and mocked git interactions using table-driven test cases

Applied to files:

  • internal/hooks/executor_test.go
  • cmd/wtp/remove_test.go
  • cmd/wtp/remove.go
πŸ“š Learning: 2025-12-02T13:33:48.693Z
Learnt from: CR
Repo: satococoa/wtp PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T13:33:48.693Z
Learning: Run commands from inside a worktree to mimic real usage (e.g., `go run ../cmd/wtp add feature/new-feature`)

Applied to files:

  • internal/hooks/executor.go
  • cmd/wtp/remove_test.go
  • cmd/wtp/remove.go
πŸ“š Learning: 2025-12-02T13:33:48.693Z
Learnt from: CR
Repo: satococoa/wtp PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T13:33:48.693Z
Learning: Shell integration must separate completion and hooks for clarity; `wtp cd` outputs absolute worktree path with no side effects

Applied to files:

  • internal/hooks/executor.go
  • cmd/wtp/remove_test.go
  • cmd/wtp/remove.go
πŸ“š Learning: 2025-12-02T13:33:48.693Z
Learnt from: CR
Repo: satococoa/wtp PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T13:33:48.693Z
Learning: CLI entrypoint is located at `cmd/wtp`; new command implementations must integrate with the main CLI structure

Applied to files:

  • internal/hooks/executor.go
  • cmd/wtp/remove_test.go
  • cmd/wtp/remove.go
πŸ“š Learning: 2025-12-02T13:33:48.693Z
Learnt from: CR
Repo: satococoa/wtp PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T13:33:48.693Z
Learning: Applies to test/e2e/**/*.go : End-to-end tests must be placed in `test/e2e` and exercise real git workflows using the built binary

Applied to files:

  • cmd/wtp/remove_test.go
πŸ“š Learning: 2025-12-02T13:33:48.693Z
Learnt from: CR
Repo: satococoa/wtp PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T13:33:48.693Z
Learning: Applies to .wtp.yml : Project hooks must be defined in `.wtp.yml`; keep commands deterministic and safe, avoiding destructive steps by default

Applied to files:

  • cmd/wtp/remove_test.go
  • cmd/wtp/remove.go
πŸ“š Learning: 2025-12-02T13:33:48.693Z
Learnt from: CR
Repo: satococoa/wtp PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T13:33:48.693Z
Learning: Applies to **/*.go : Keep import groups tidy; use goimports for organization with local prefix following module path `github.com/satococoa/wtp/v2`

Applied to files:

  • cmd/wtp/remove_test.go
πŸ“š Learning: 2025-12-02T13:33:48.693Z
Learnt from: CR
Repo: satococoa/wtp PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T13:33:48.693Z
Learning: The root module is `github.com/satococoa/wtp/v2` running on Go 1.24; maintain module path consistency

Applied to files:

  • cmd/wtp/remove_test.go
  • cmd/wtp/remove.go
πŸ“š Learning: 2025-12-02T13:33:48.693Z
Learnt from: CR
Repo: satococoa/wtp PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T13:33:48.693Z
Learning: Use `go run ./cmd/wtp <args>` for rapid feedback during development instead of building binaries

Applied to files:

  • cmd/wtp/remove_test.go
🧬 Code graph analysis (4)
internal/hooks/executor_test.go (3)
internal/hooks/executor.go (1)
  • NewExecutor (29-34)
internal/config/config.go (5)
  • Config (13-17)
  • Hooks (25-29)
  • Hook (32-39)
  • HookTypeCopy (49-49)
  • HookTypeCommand (51-51)
internal/command/types.go (1)
  • Command (4-8)
internal/hooks/executor.go (1)
internal/config/config.go (4)
  • Hooks (25-29)
  • Hook (32-39)
  • HookTypeCopy (49-49)
  • HookTypeCommand (51-51)
cmd/wtp/remove_test.go (2)
internal/command/types.go (2)
  • Result (11-15)
  • Command (4-8)
internal/config/config.go (5)
  • Config (13-17)
  • Defaults (20-22)
  • Hooks (25-29)
  • Hook (32-39)
  • HookTypeCommand (51-51)
cmd/wtp/remove.go (3)
internal/config/config.go (3)
  • LoadConfig (56-96)
  • ConfigFileName (43-43)
  • Config (13-17)
internal/errors/errors.go (1)
  • ConfigLoadFailed (198-224)
internal/hooks/executor.go (1)
  • NewExecutor (29-34)
πŸ”‡ Additional comments (18)
internal/hooks/executor.go (5)

36-49: LGTM! ExecutePostCreateHooks properly resolves paths.

The base-path model correctly resolves:

  • Copy source from repo root
  • Copy destination to worktree
  • Commands execute in worktree context

51-65: LGTM! ExecutePreRemoveHooks aligns with PR objectives.

Pre-remove hooks correctly:

  • Copy from paths resolved relative to worktree (source)
  • Copy to paths resolved relative to repo root (destination)
  • Commands execute in worktree context (before removal)

This matches the documented behavior in the PR description.


67-101: LGTM! Post-remove path resolution issue has been addressed.

The implementation now correctly:

  1. Resolves all paths relative to e.repoRoot (lines 97-99)
  2. Validates WorkDir doesn't escape repository root (lines 85-89)
  3. Defaults empty WorkDir to repo root (lines 77-79)

This aligns with the PR objectives where post_remove runs after the worktree is deleted, so all paths must be repo-relative.


246-249: Verify GIT_WTP_WORKTREE_PATH semantics for post-remove hooks.

For post-remove hooks, basePath will be e.repoRoot (not the removed worktree path). This means GIT_WTP_WORKTREE_PATH will equal GIT_WTP_REPO_ROOT for post-remove hooks.

Consider whether this is the intended behavior, or if post-remove hooks should not set GIT_WTP_WORKTREE_PATH at all (since the worktree no longer exists).


306-319: LGTM! Synchronized writer prevents output interleaving.

The synchronizedWriter correctly uses a mutex to prevent interleaved stdout/stderr output from concurrent goroutines streaming command output.

internal/hooks/executor_test.go (4)

27-32: LGTM! Nil config handling test for PreRemove.

Correctly verifies that ExecutePreRemoveHooks returns no error when the executor is created with a nil config.


46-56: LGTM! No-hooks scenario test for PreRemove.

Verifies no-op behavior when PreRemove hooks slice is empty.


58-75: LGTM! Nil config and no-hooks tests for PostRemove.

Tests correctly verify that ExecutePostRemoveHooks handles nil config and empty hook slices gracefully.


77-126: LGTM! Comprehensive pre-remove path resolution test.

This test validates the critical pre-remove behavior:

  • from paths resolve relative to worktree
  • to paths resolve relative to repo root
  • WorkDir resolves relative to worktree

The test creates actual files and directories, verifies the backup file lands in the repo root, and confirms command output contains the expected worktree-relative path.

cmd/wtp/remove.go (4)

134-143: LGTM! Config loading and pre-remove hook execution.

The implementation correctly:

  1. Loads config from main repo path
  2. Returns a descriptive error if config loading fails
  3. Executes pre-remove hooks before the actual removal

163-165: LGTM! Post-remove hooks executed after successful removal.

Post-remove hooks are correctly executed after the worktree is removed and the success message is printed.


177-196: LGTM! executePreRemoveHooks helper function.

Clean implementation that:

  • Short-circuits if no pre-remove hooks are configured
  • Prints user-friendly status messages
  • Properly propagates errors from hook execution

198-217: LGTM! executePostRemoveHooks helper function.

The function signature correctly omits the worktree path since it no longer exists when post-remove hooks run. The work_dir validation has been moved to ExecutePostRemoveHooks in executor.go, addressing the previous review comment.

cmd/wtp/remove_test.go (5)

302-347: LGTM! Pre-remove hook execution test.

This test validates the full integration:

  • Creates a real .wtp.yml config file with a pre-remove command hook
  • Uses a mock command executor for git operations
  • Verifies the expected output messages appear

349-394: LGTM! Post-remove hook execution test.

Mirrors the pre-remove test structure and validates that post-remove hooks are executed after the worktree removal.


396-430: LGTM! WorkDir resolution test for post-remove hooks.

This test validates that relative WorkDir paths are correctly resolved against the repository root. Cross-platform command selection (pwd vs cd) ensures the test works on Windows.


432-459: LGTM! Path traversal security test.

This test is critical for validating the security fix. It confirms that:

  • Relative work_dir values like ../.. are rejected
  • The specific error message is returned
  • The command is never executed (verified by output not containing the echo text)

555-581: LGTM! Config load failure test.

Validates that malformed YAML in .wtp.yml is properly detected and a descriptive error is returned.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
cmd/wtp/remove_test.go (1)

432-459: Important security test for path traversal prevention.

This test validates a critical security boundaryβ€”ensuring hooks cannot escape the repository root via .. path components. The error message assertion uses filepath.Join("..", "..") on both sides, ensuring cross-platform consistency.

One minor observation: consider adding an additional test case with absolute paths outside the repo (e.g., /etc or C:\Windows) to strengthen the security validation.

πŸ“œ Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

πŸ“₯ Commits

Reviewing files that changed from the base of the PR and between b750efb and fe77994.

πŸ“’ Files selected for processing (2)
  • cmd/wtp/remove_test.go
  • internal/hooks/executor_test.go
🧰 Additional context used
πŸ““ Path-based instructions (2)
**/*.go

πŸ“„ CodeRabbit inference engine (AGENTS.md)

**/*.go: Follow standard Go style with tabs and gofmt; package names must be short and lowercase
Keep import groups tidy; use goimports for organization with local prefix following module path github.com/satococoa/wtp/v2
Adhere to linting rules in .golangci.yml (vet, staticcheck, gosec, mnd, lll=120)
Wrap errors with context; avoid ignoring errors in Go code
Use snake_case for Go filenames (e.g., remove.go, shell_integration.go)
Document exported items in Go when non-trivial; follow godoc conventions

Files:

  • internal/hooks/executor_test.go
  • cmd/wtp/remove_test.go
**/*_test.go

πŸ“„ CodeRabbit inference engine (AGENTS.md)

**/*_test.go: Unit tests must be placed alongside packages as *_test.go files in the same directory
Target 70% code coverage with unit tests focusing on fast feedback and mocked git interactions using table-driven test cases

Files:

  • internal/hooks/executor_test.go
  • cmd/wtp/remove_test.go
🧠 Learnings (7)
πŸ““ Common learnings
Learnt from: CR
Repo: satococoa/wtp PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T13:33:48.693Z
Learning: Applies to .wtp.yml : Project hooks must be defined in `.wtp.yml`; keep commands deterministic and safe, avoiding destructive steps by default
Learnt from: CR
Repo: satococoa/wtp PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T13:33:48.693Z
Learning: Shell integration must separate completion and hooks for clarity; `wtp cd` outputs absolute worktree path with no side effects
Learnt from: CR
Repo: satococoa/wtp PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T13:33:48.693Z
Learning: Worktree naming convention: main worktree is rendered as `@`; non-main worktrees display path relative to `base_dir` (e.g., `.worktrees/feat/hoge` β†’ `feat/hoge`)
πŸ“š Learning: 2025-12-02T13:33:48.693Z
Learnt from: CR
Repo: satococoa/wtp PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T13:33:48.693Z
Learning: Applies to **/*_test.go : Target 70% code coverage with unit tests focusing on fast feedback and mocked git interactions using table-driven test cases

Applied to files:

  • internal/hooks/executor_test.go
  • cmd/wtp/remove_test.go
πŸ“š Learning: 2025-12-02T13:33:48.693Z
Learnt from: CR
Repo: satococoa/wtp PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T13:33:48.693Z
Learning: CLI entrypoint is located at `cmd/wtp`; new command implementations must integrate with the main CLI structure

Applied to files:

  • cmd/wtp/remove_test.go
πŸ“š Learning: 2025-12-02T13:33:48.693Z
Learnt from: CR
Repo: satococoa/wtp PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T13:33:48.693Z
Learning: Run commands from inside a worktree to mimic real usage (e.g., `go run ../cmd/wtp add feature/new-feature`)

Applied to files:

  • cmd/wtp/remove_test.go
πŸ“š Learning: 2025-12-02T13:33:48.693Z
Learnt from: CR
Repo: satococoa/wtp PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T13:33:48.693Z
Learning: Applies to test/e2e/**/*.go : End-to-end tests must be placed in `test/e2e` and exercise real git workflows using the built binary

Applied to files:

  • cmd/wtp/remove_test.go
πŸ“š Learning: 2025-12-02T13:33:48.693Z
Learnt from: CR
Repo: satococoa/wtp PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T13:33:48.693Z
Learning: The root module is `github.com/satococoa/wtp/v2` running on Go 1.24; maintain module path consistency

Applied to files:

  • cmd/wtp/remove_test.go
πŸ“š Learning: 2025-12-02T13:33:48.693Z
Learnt from: CR
Repo: satococoa/wtp PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T13:33:48.693Z
Learning: Use `go run ./cmd/wtp <args>` for rapid feedback during development instead of building binaries

Applied to files:

  • cmd/wtp/remove_test.go
🧬 Code graph analysis (1)
cmd/wtp/remove_test.go (2)
internal/command/types.go (2)
  • Result (11-15)
  • Command (4-8)
internal/config/config.go (5)
  • Config (13-17)
  • Defaults (20-22)
  • Hooks (25-29)
  • Hook (32-39)
  • HookTypeCommand (51-51)
πŸ”‡ Additional comments (8)
cmd/wtp/remove_test.go (4)

302-347: Well-structured integration test for pre-remove hooks.

Good test coverage that validates:

  • Config file loading from disk
  • Pre-remove hook execution messaging
  • Hook output capture in the buffer

The test properly uses t.TempDir() for automatic cleanup and creates realistic directory structures.


349-394: LGTM for post-remove hook integration test.

Mirrors the pre-remove test structure appropriately. The test validates that post-remove hooks execute after worktree removal with correct messaging.


396-430: Good cross-platform handling for work_dir test.

The use of runtime.GOOS to select pwd vs cd ensures the test works on both Unix and Windows platforms. The test correctly validates that work_dir is resolved relative to the repository root for post-remove hooks.


555-581: Good error handling test for malformed config.

The test validates that YAML parsing errors are properly propagated and the removal operation fails gracefully when the config file is invalid. The malformed YAML at line 562 (invalid at the end without proper structure) is a realistic error case.

internal/hooks/executor_test.go (4)

27-32: LGTM for nil config safety test.

This test ensures ExecutePreRemoveHooks handles nil config gracefully without panicking, which is important for defensive programming.


46-56: Good edge case coverage for empty hooks array.

Tests that an empty hooks slice (as opposed to nil) is handled correctly, returning no error without attempting any operations.


58-75: LGTM for post-remove nil/empty config tests.

Note that ExecutePostRemoveHooks correctly doesn't require a worktree path parameter since the worktree is already removed at that point.


77-126: Comprehensive integration test correctly validating pre-remove path resolution semantics.

This test effectively validates the key behavior:

  • from paths resolved relative to the worktree (line 104: "original.file" from worktree)
  • to paths resolved relative to repository root (line 105: "backup.file" to repo root)
  • work_dir resolved relative to the worktree for command execution (lines 110, 124)

The cross-platform handling at lines 94-97 is correct for validating the working directory via pwd/cd. Per the documentation, command hooks in pre_remove default to running inside the worktree, which this test correctly validates.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant