Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,28 @@ All notable changes to this project will be documented in this file.

## [Unreleased]

### Changed

- Simplified distribution to a single shipped binary: `key-watch`
- Git hook installation now supports first-class global hooks via `core.hooksPath`
- Installation guidance is now cargo-first, with manual GitHub Releases setup documented step by step

### Added

- Hook uninstall support for local and global Git hooks
- `--init bash|zsh|fish|posix` to print shell aliases for `keywatch` and `kw`
- README now documents uninstall steps for both `cargo install` and manual GitHub Releases installs

### Removed

- Duplicate Cargo binary wrappers for `keywatch` and `watch`
- `scripts/install.sh` in favor of documented `cargo install` and manual release-binary setup

## [1.1.0] - 2026-05-05

### Added

- Binary aliases: `keywatch`, `watch` (in addition to `key-watch`)
- `keywatch` and `watch` aliases for `key-watch`
- Exit code modes: `--exit-mode always|critical|strict`
- Binary integrity verification: `--verify-integrity`
- Repository controls: `--allowed-repos`, `--blocked-repos`
Expand Down
13 changes: 0 additions & 13 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,6 @@ homepage = "https://github.com/pixincreate/KeyWatch"
keywords = ["secret-scanner", "security", "credentials", "lint"]

license = "GPL-3.0-only"
license-file = "LICENSE"

[[bin]]
name = "key-watch"
path = "src/main.rs"

[[bin]]
name = "keywatch"
path = "src/bin/keywatch.rs"

[[bin]]
name = "watch"
path = "src/bin/watch.rs"

[dependencies]
clap = { version = "4.6.1", features = ["derive"] }
Expand Down
119 changes: 107 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,51 +4,135 @@ A fast secret scanner for files and directories.

## Install

### Recommended: cargo install

```sh
# Recommended
cargo install --git https://github.com/pixincreate/KeyWatch.git
cargo install key-watch
key-watch --version

# Enable aliases for your current shell session
eval "$(key-watch --init bash)"
```

# Or use the install script
./scripts/install.sh
To make aliases persistent, add the init line to your shell config file:

```sh
# bash
echo 'eval "$(key-watch --init bash)"' >> ~/.bashrc

# Manual: download binary, add to PATH
# zsh
echo 'eval "$(key-watch --init zsh)"' >> ~/.zshrc
```

### Manual install from GitHub Releases

1. Download the correct binary for your OS/architecture from GitHub Releases.
2. Move it to a directory on your `PATH`, for example `~/.local/bin`.
3. Make it executable.
4. Verify it runs.
5. Enable aliases with `--init`.

```sh
mkdir -p ~/.local/bin
mv ~/Downloads/key-watch ~/.local/bin/key-watch
chmod +x ~/.local/bin/key-watch
~/.local/bin/key-watch --version

# Enable aliases for current shell session
eval "$(~/.local/bin/key-watch --init bash)"
```

Requires Rust 1.85+ (edition 2024) when building from source.

The canonical command is `key-watch`.
`keywatch` and `kw` are optional shell aliases exposed via `key-watch --init ...`.

## Uninstall

### If installed with `cargo install`

```sh
cargo uninstall key-watch
```

If you added aliases to your shell config, remove the init line you added earlier, for example:

```sh
# bash
sed -i.bak '/key-watch --init bash/d' ~/.bashrc

# zsh
sed -i.bak '/key-watch --init zsh/d' ~/.zshrc
```

### If installed manually from GitHub Releases

1. Remove the `key-watch` binary from your `PATH` directory.
2. Remove any shell init line you added for aliases.
3. Restart your shell or reload your shell config.

```sh
rm -f ~/.local/bin/key-watch

# If you added aliases for the current shell config, remove that line manually
# then reload your shell config, for example:
source ~/.bashrc
```

## Usage

```sh
# Scan a file
keywatch --file secrets.txt
key-watch --file secrets.txt

# Scan a directory
keywatch --dir .
key-watch --dir .

# Verbose output (JSON)
keywatch --file secrets.txt --verbose
key-watch --file secrets.txt --verbose

# Install git hook
keywatch --install-hook pre-commit
keywatch --install-hook pre-push
key-watch --install-hook pre-commit
key-watch --install-hook pre-push

# Remove git hook
key-watch --uninstall-hook pre-commit
key-watch --uninstall-hook pre-push

# Install git hook globally via core.hooksPath
key-watch --install-hook pre-commit --global
key-watch --install-hook pre-push --global

# Remove global hook
key-watch --uninstall-hook pre-commit --global
key-watch --uninstall-hook pre-push --global

# Print shell aliases
eval "$(key-watch --init bash)"
```

## Options

- `--file <path>` - Scan a single file
- `--file <path>` - Scan one or more files (repeat the flag)
- `--dir <path>` - Scan a directory recursively
- `--output <path>` - Save report to file
- `--verbose` - Print full JSON output
- `--exclude <patterns>` - Comma-separated glob patterns to exclude
- `--exit-mode <mode>` - Exit behavior: `always` (always pass), `critical` (fail on HIGH only), `strict` (fail on any finding, default)
- `--install-hook <type>` - Install pre-commit or pre-push hook
- `--uninstall-hook <type>` - Remove pre-commit or pre-push hook
- `--global` - Use the global `core.hooksPath` directory for hook install/uninstall
- `--init <shell>` - Print shell aliases for `keywatch` and `kw`
- `--verify-integrity` - Check binary hasn't been tampered with
- `--allowed-repos <urls>` - Whitelist repos (pre-push)
- `--blocked-repos <urls>` - Block repos (pre-push)

## Aliases

`key-watch`, `keywatch`, `watch` are equivalent.
- `key-watch` is the only shipped binary.
- `keywatch` and `kw` are optional aliases.
- `key-watch --init bash|zsh|fish|posix` prints shell aliases you can eval in your shell.
- `watch` is intentionally not used, to avoid colliding with the standard Unix `watch` command.

## Exit Codes

Expand All @@ -63,6 +147,17 @@ keywatch --install-hook pre-push
- **Repos**: All allowed (no restrictions)
- **Exit mode**: strict (fail on any finding)

## Git Hooks

- `--install-hook pre-commit|pre-push` installs a repo-local hook into `.git/hooks/`
- `--uninstall-hook pre-commit|pre-push` removes a KeyWatch hook from the same target
- `--install-hook ... --global` installs into Git's global hooks directory
- `--uninstall-hook ... --global` removes the hook from Git's global hooks directory
- If `core.hooksPath` is already configured, KeyWatch installs into that directory
- Otherwise KeyWatch creates a managed hooks directory and configures `git config --global core.hooksPath`
- KeyWatch refuses to overwrite a non-KeyWatch global hook file
- KeyWatch also refuses to remove a non-KeyWatch global hook file

## Development

```sh
Expand Down
60 changes: 0 additions & 60 deletions scripts/install.sh

This file was deleted.

6 changes: 0 additions & 6 deletions src/bin/keywatch.rs

This file was deleted.

6 changes: 0 additions & 6 deletions src/bin/watch.rs

This file was deleted.

27 changes: 25 additions & 2 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,12 @@ use clap::{ArgGroup, Parser};
ArgGroup::new("target")
.required(true)
.multiple(false)
.args(&["file", "dir", "install_hook"]),
.args(&["file", "dir", "install_hook", "uninstall_hook", "init"]),
))]
#[command(group(
ArgGroup::new("hook_action")
.multiple(false)
.args(&["install_hook", "uninstall_hook"]),
))]
pub struct CliOptions {
/// Scan specific file(s) - supports multiple --file flags
Expand Down Expand Up @@ -43,9 +48,27 @@ pub struct CliOptions {

/// Install KeyWatch as a git hook
/// Options: pre-push, pre-commit
#[arg(long, value_parser = ["pre-push", "pre-commit"])]
#[arg(long, value_parser = ["pre-push", "pre-commit"], conflicts_with = "uninstall_hook")]
pub install_hook: Option<String>,

/// Remove a KeyWatch git hook
/// Options: pre-push, pre-commit
#[arg(long, value_parser = ["pre-push", "pre-commit"], conflicts_with = "install_hook")]
pub uninstall_hook: Option<String>,

/// Install the selected hook globally using git core.hooksPath
#[arg(
long,
conflicts_with_all = ["file", "dir", "init"],
requires = "hook_action",
default_value_t = false
)]
pub global: bool,

/// Print shell aliases for keywatch and kw
#[arg(long, value_parser = ["bash", "zsh", "fish", "posix"])]
pub init: Option<String>,

/// Exit code behavior
/// Options:
/// - always: Always exit 0 (bypass)
Expand Down
Loading
Loading