Skip to content

fix(audit): aggregate manual removal commands and fix go/cargo uninstalls - #134

Merged
CybotTM merged 1 commit into
mainfrom
fix/reconcile-aggregate-manual-commands
Jul 30, 2026
Merged

fix(audit): aggregate manual removal commands and fix go/cargo uninstalls#134
CybotTM merged 1 commit into
mainfrom
fix/reconcile-aggregate-manual-commands

Conversation

@CybotTM

@CybotTM CybotTM commented Jul 30, 2026

Copy link
Copy Markdown
Member

Problem

Follow-up to #133. With failures now visible, a full make reconcile-all run surfaced three remaining issues:

  1. Dozens of individual sudo hints — the user has to collect sudo apt remove X / sudo rm <path> commands one by one from the output.
  2. ~/go/bin binaries (golangci-lint, shfmt) fell through the generic /bin heuristic to system, producing nonsense guidance: sudo system remove golangci-lint. They are user-owned go install binaries — removal is deleting the file, no sudo needed.
  3. cargo uninstall delta / watchexec failed with "package ID specification did not match any packages": the owning crates are git-delta and watchexec-cli.

Fix

  • --reconcile --all --apply now ends with one copy-paste command per remedy: a single sudo apt remove <pkg…> per system package manager and a single sudo rm -f <path…> for unmanaged binaries (deduped, sorted; only for conflicts the user confirmed).
  • Path heuristic classifies /go/bin as go (before the generic /bin fallback); go binaries are removed like manual installs and rank tier 2 alongside cargo/pip/npm. Shell-side capability.sh already handled this.
  • Cargo uninstall maps binary → owning crate via cargo install --list (fallback: tool name).
  • system-classified binaries get sudo rm <path> guidance instead of the nonexistent sudo system remove <tool>.

Test plan

  • 5 new tests (written first, watched fail): GOPATH classification, go-method file removal, system-method rm guidance, cargo crate mapping, and the aggregated command lines in --all --apply output.
  • Full suite: 781 passed, 1 skipped; smoke test OK; pre-commit (flake8, isort, black) green.
  • Real-machine spot checks: classify_install_method('/home/sme/go/bin/shfmt') == 'go'; _cargo_package_for('delta') == 'git-delta'; _cargo_package_for('watchexec') == 'watchexec-cli'.

https://claude.ai/code/session_01MH3EaniXCnJdwqNvrMB4Ym

…alls

Follow-up to #133: errors are now visible, but a full `make reconcile-all`
run still left the user with dozens of per-tool sudo hints and two new
defect classes:

- Aggregate confirmed-but-failed removals at the end of `--all --apply`
  into one copy-paste command per remedy: a single
  `sudo apt remove <pkg...>` per system package manager and a single
  `sudo rm -f <path...>` for unmanaged binaries.
- Classify GOPATH bin (`~/go/bin`) as 'go' instead of falling through to
  the generic '/bin' → 'system' branch, which produced nonsense
  `sudo system remove <tool>` guidance. go-installed binaries are removed
  by deleting the file (user-owned, no sudo). Shell-side capability.sh
  already did this.
- `cargo uninstall <binary>` fails when the crate name differs from the
  binary (git-delta installs `delta`, watchexec-cli installs `watchexec`).
  Map binary → owning crate via `cargo install --list`, falling back to
  the tool name.
- 'system'-classified binaries now get `sudo rm <path>` guidance instead
  of the nonexistent `sudo system remove <tool>` command.

Claude-Session: https://claude.ai/code/session_01MH3EaniXCnJdwqNvrMB4Ym
Signed-off-by: Sebastian Mendel <github@sebastianmendel.de>
Copilot AI review requested due to automatic review settings July 30, 2026 10:22
@github-actions

Copy link
Copy Markdown
Contributor

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Scanned Files

None

@sonarqubecloud

Copy link
Copy Markdown

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@CybotTM
CybotTM merged commit afc4d62 into main Jul 30, 2026
24 of 25 checks passed
@CybotTM
CybotTM deleted the fix/reconcile-aggregate-manual-commands branch July 30, 2026 10:31
CybotTM added a commit that referenced this pull request Jul 30, 2026
…ics, survivor validation (#135)

## Problems

Three issues from real `make reconcile-all` runs after #134:

1. **Ctrl-C hung the run.** Prompts executed inside ThreadPool workers;
Ctrl-C killed the main thread while workers kept prompting, then
Python's atexit hung joining a worker blocked in `input()` — repeated
Ctrl-C required.
2. **`make reconcile-all` ended in `Error 1` even when everything worked
as designed** — removals that need sudo are refused on purpose and
handed over as copy-paste commands, yet still counted as failures for
the exit code.
3. **A kept "duplicate" can be silently broken by the removal.** Real
incident: reconcile kept a stray copy of byobu's launcher script in
`~/.local/bin` and removed the apt package that owned `/usr/lib/byobu` —
the survivor sourced those files and broke.

## Fixes (one commit each)

- **Main-thread prompting + a/q answers.** `bulk_reconcile` now detects
in parallel (workers, no stdin) and prompts/removes sequentially on the
main thread. Ctrl-C acts as quit: current prompt declined, remaining
tools marked aborted, clean summary, no traceback. Prompt is now
`[y/N/a=all/q=quit]` with sticky all/quit semantics.
- **`manual_required` outcome.** Failures that only need a manual sudo
command classify as `manual_required`: shown with ⚠, counted in a new
"Manual action required" summary line, included in the aggregated
command block, and excluded from the non-zero exit. Genuine errors still
exit 1.
- **Survivor validation.** After a successful removal, the kept
installation is probed with its version command; if it no longer runs,
the result fails with guidance to reinstall the removed package or
delete the broken survivor.

## Test plan

- 9 new tests, written first and watched fail: sticky a/q/Ctrl-C prompt
semantics, prompts-run-in-MainThread assertion, manual_required
classification (all-manual vs mixed), exit-0 CLI behavior with summary
line, broken/working survivor probes.
- One existing test updated for changed behavior
(`test_reconcile_aggressive_mode` now patches the survivor probe).
- Full suite: 789 passed, 1 skipped; smoke test OK; pre-commit (flake8,
isort, black) green.

https://claude.ai/code/session_01MH3EaniXCnJdwqNvrMB4Ym
CybotTM added a commit to netresearch/cli-tools-skill that referenced this pull request Jul 30, 2026
…cile binary (#42)

## Problem

Field failures from `coding_agent_cli_toolset` reconcile runs (see
netresearch/coding_agent_cli_toolset#134 / #135) exist identically in
this skill's `scripts/lib/reconcile.sh`:

1. `cargo uninstall "$tool"` uses the binary name, but cargo needs the
**crate** name — `git-delta` installs `delta`, `watchexec-cli` installs
`watchexec` — and the `|| true` swallowed the failure silently, leaving
the old install behind.
2. The post-reconcile verify only checks `command -v`. Presence is not
function: a surviving wrapper script can depend on files the removed
package owned (real incident: byobu's launcher script sourcing
`/usr/lib/byobu`, broken by `apt remove byobu`).

## Fix

- Map binary → owning crate via `cargo install --list` (awk two-state
parse; fallback to tool name), and warn on uninstall failure instead of
hiding it.
- After reconciliation, probe `--version`/`version`; warn when the
binary is on PATH but no longer runs.

Note: the other removal branches share the `|| true` silent-swallow
pattern — candidate for a follow-up sweep.

## Test plan

- `bash -n` + shellcheck clean (only pre-existing SC1091 info).
- awk mapping verified against real `cargo install --list` format:
`delta → git-delta`, `watchexec → watchexec-cli`, absent packages fall
back to the tool name.

## Quality gate note

The `Smoke Test` check fails with `✗ github_release_binary.sh fd install
failed:` (empty error). This failure is pre-existing on `main` —
identical signature in [run
30123292238](https://github.com/netresearch/cli-tools-skill/actions/runs/30123292238)
(2026-07-24, before this branch existed) — and the check is non-required
(`mergeStateStatus: UNSTABLE`). This PR touches only
`scripts/lib/reconcile.sh`; the failing path is the
`github_release_binary.sh` installer. Tracked separately.
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.

2 participants