repowise-dev / repowise-dev/repowise

[Bug] repowise uninstall never removes the post-commit hook that init installs

Open Beginner friendly
#2,469 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug help wanted
Dominant language
Python
Stars
6.7k
Forks
711
Avg merge
1d 13h
Merged PRs (30d)
439

Description

Summary

repowise uninstall does not list or remove the git post-commit hook, although repowise init is one of the two things that installs it.

packages/cli/src/repowise/cli/uninstall/inventory.py:20-34 declares the four groups the plan enumerates:

class Group(StrEnum):
    AGENTS = "agents"
    REPO_FILES = "repo-files"
    INDEX = "index"
    GLOBAL = "global"

.git/hooks/post-commit is in none of them. Grepping the module for post-commit returns nothing.

repowise.cli.hooks.uninstall has exactly one caller in the tree:

packages/cli/src/repowise/cli/commands/hook_cmd.py:102
    from repowise.cli.hooks import uninstall

which is the explicit repowise hook uninstall. Meanwhile init installs it at packages/cli/src/repowise/cli/commands/init_cmd/_interactive.py:239.

Consequence

A user runs repowise init, accepts the post-commit hook when offered, later runs repowise uninstall --all, and the marker block stays in .git/hooks/post-commit. Nothing in the output mentions it.

The block is inert once the index is gone, because it gates on [ -f "$ROOT/.repowise/state.json" ] (packages/cli/src/repowise/cli/hooks.py:74), so nothing breaks. The problem is the claim rather than the behaviour. The module docstring at inventory.py:1-9 is explicit about what it is for:

The full inventory of what repowise has written, computed without writing. This module is the trust claim. [...] So the plan enumerates every path in every group, present or not, and carries for each either the action a run would take or the reason it would not.

A path that install writes and uninstall neither removes nor names is the one outcome that design rules out. Item already carries a blocked field for exactly the case where a path is listed and deliberately not touched, so the shape for an honest answer is already there.

The pattern this belongs to

Three uninstall paths currently leave behind something their install wrote. The other two are #2470 (the Claude Code target leaves an env pin and two permission rules) and #2471 (hook rewrite uninstall leaves a repo config flag and, on some machines, an AGENTS.md block). They are separate files and separate commands, but a contributor picking up one may want to read the other two, since the question in each case is the same: what did install write that uninstall does not account for?

Done looks like

.git/hooks/post-commit appears in the plan under REPO_FILES, with either the removal a run would perform or the reason it would not. Removing the repowise block while preserving any other hook content is already implemented in hooks.uninstall (hooks.py:366-391, including the "removed (other hook content preserved)" branch), so this is wiring rather than new logic.

Worth deciding in the same pass: whether removal belongs under REPO_FILES or under a group of its own. The hook lives in .git/, which is neither the repo's own tracked files nor .repowise/, and a user who wants the index gone but the auto-sync hook kept is not an obviously wrong user.

Scope note

Verified against current main by reading the inventory groups, grepping for every caller of hooks.uninstall, and confirming init installs it. I did not run repowise uninstall, and I did not check whether the workspace uninstall path differs.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with packages/cli/src/repowise/cli/uninstall/inventory.py:20-34 and compare its groups with init installation at commands/init_cmd/_interactive.py:239. Read hooks.py:74 and 366-391, plus the hook uninstall caller in commands/hook_cmd.py:102. Done means the post-commit path is represented in the uninstall plan with its removal action or a stated reason it is blocked, while preserving other hook content.

Written by the indexing model from the issue text.

Assessment

Tech stack
git, python
Domain
cli, tooling
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
74/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.