Skip to content

Commit a51e013

Browse files
Merge pull request #7 from digitaldrreamer/claude/cli-ux
feat(cli): AI-client summary report + sweep + interactive UX (v0.2.0)
2 parents 669f152 + 8a3a55a commit a51e013

8 files changed

Lines changed: 393 additions & 77 deletions

File tree

CHANGELOG.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,25 @@ All notable changes to this project are documented here. The format is based on
44
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project
55
adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

7+
## [0.2.0] — 2026-07-02
8+
9+
### Added
10+
- `broom sweep` — redact in place, equivalent to `clean --apply` (both work).
11+
- Interactive menu when `broom` is run with no arguments on a terminal, and a
12+
confirmation prompt before `sweep`/`clean --apply` (skipped with `--yes` and
13+
in non-interactive/CI use). Built on `node:readline/promises` — still zero
14+
runtime dependencies.
15+
- `--verbose` flag: the default report is now a compact summary; `--verbose`
16+
lists every occurrence with rule and line number.
17+
- "Did you mean …?" suggestion for unknown commands.
18+
19+
### Changed
20+
- Report redesigned. Instead of one line per finding (thousands of lines on a
21+
busy history), the default groups by **AI client** and shows vulnerable chats
22+
and the count of **distinct** secrets, deduped by value — the same credential
23+
echoed many times counts once (the raw occurrence count is shown alongside).
24+
- Colour output now respects `NO_COLOR` / `FORCE_COLOR` and TTY detection.
25+
726
## [0.1.1] — 2026-07-02
827

928
### Fixed
@@ -45,5 +64,6 @@ First public release.
4564
- Extended-thinking blocks are passed through the proxy unredacted to preserve
4665
their signatures — see the README Limitations section.
4766

67+
[0.2.0]: https://github.com/digitaldrreamer/broomsticks/releases/tag/v0.2.0
4868
[0.1.1]: https://github.com/digitaldrreamer/broomsticks/releases/tag/v0.1.1
4969
[0.1.0]: https://github.com/digitaldrreamer/broomsticks/releases/tag/v0.1.0

README.md

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,13 @@ broomsticks does two things: it **cleans** secrets already sitting in your local
1414
# scan every transcript for secrets (read-only)
1515
npx broomsticks scan
1616

17-
# preview redactions, then apply them (each touched file is backed up first)
17+
# preview redactions, then sweep them away (each touched file is backed up first)
1818
npx broomsticks clean
19-
npx broomsticks clean --apply
19+
npx broomsticks sweep
2020
```
2121

22+
Run `broom` with no arguments in a terminal for an interactive menu.
23+
2224
Prefer a global command? `npm install -g broomsticks` gives you `broom` (used throughout this README). Requires **Node ≥ 22.13** — it reads Cursor's SQLite store with the built-in `node:sqlite`, unflagged as of 22.13.0, so there are no native modules to build.
2325

2426
## What it does
@@ -41,15 +43,20 @@ broom scan # report secrets; exits 1 if any (CI-fri
4143
broom scan --source cursor # limit to one source (repeatable)
4244
broom sources # list discovered transcript files
4345
broom clean # preview redactions (dry-run)
44-
broom clean --apply # redact in place, backing up first
46+
broom sweep # redact in place, backing up first (= clean --apply)
47+
broom scan --verbose # list every finding, not just per-file counts
4548
broom scan --json # machine-readable output
46-
broom clean --apply --extra ./leaked.txt # also scrub your own known values
49+
broom sweep --extra ./leaked.txt # also scrub your own known values
4750
```
4851

52+
By default the report is a compact summary — grouped by AI client, showing how many chats are affected and how many **distinct** secrets are exposed (the same key echoed many times across a transcript counts once; the total occurrence count is shown alongside). Add `--verbose` to list every occurrence with its rule and line. Colour is used on a terminal and disabled automatically when piped or when `NO_COLOR` is set. `sweep` (and `clean --apply`) asks for confirmation on a terminal; pass `--yes` to skip it, and in a pipe/CI it proceeds without prompting.
53+
4954
| Flag | Meaning |
5055
| --- | --- |
5156
| `--source <id>` | Restrict to `claude-code`, `codex`, or `cursor` (repeatable; default: all) |
52-
| `--apply` | Perform redaction (`clean` only; otherwise dry-run) |
57+
| `--apply` | Perform redaction (`clean` only; `sweep` implies it) |
58+
| `--yes` | Skip the pre-redaction confirmation prompt |
59+
| `--verbose` | List every finding, not just per-file counts |
5360
| `--backup-dir <dir>` | Where backups go (default `~/.broom/backups/<timestamp>/`) |
5461
| `--no-backup` | Skip backups (discouraged) |
5562
| `--extra <file>` | Extra literal / `/regex/` secrets to redact, one per line |

bin/broom.mjs

Lines changed: 43 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { discoverTargets as codexTargets } from '../src/sources/codex.mjs'
1717
import { discoverTargets as cursorTargets } from '../src/sources/cursor.mjs'
1818
import { runInstall, isInstalled } from '../src/install.mjs'
1919
import { startProxy, installProxyEnv, uninstallProxyEnv, installDaemon, uninstallDaemon } from '../src/proxy.mjs'
20+
import { confirm, select, isInteractive, closest } from '../src/ui.mjs'
2021

2122
// ── Package metadata ──────────────────────────────────────────────────────────
2223
const pkgPath = join(dirname(fileURLToPath(import.meta.url)), '..', 'package.json')
@@ -44,11 +45,30 @@ function options(name) {
4445

4546
// ── Top-level flags ───────────────────────────────────────────────────────────
4647
if (flag('--version') || flag('-v')) { console.log(pkg.version); process.exit(0) }
47-
if (flag('--help') || flag('-h') || argv.length === 0) { printHelp(); process.exit(0) }
48+
if (flag('--help') || flag('-h')) { printHelp(); process.exit(0) }
49+
50+
const COMMANDS = ['scan', 'clean', 'sweep', 'sources', 'install', 'proxy']
51+
52+
let command = argv[0]
53+
54+
// No command: offer an interactive menu on a TTY, otherwise print help.
55+
if (!command) {
56+
if (isInteractive()) {
57+
command = await select('\n What would you like to do?', [
58+
{ label: 'scan — find secrets (read-only)', value: 'scan' },
59+
{ label: 'clean — preview redactions (dry-run)', value: 'clean' },
60+
{ label: 'sweep — redact secrets in place (backs up)', value: 'sweep' },
61+
{ label: 'sources — list discovered transcript files', value: 'sources' },
62+
])
63+
if (!command) { console.log('\n Nothing to do.\n'); process.exit(0) }
64+
} else {
65+
printHelp(); process.exit(0)
66+
}
67+
}
4868

49-
const command = argv[0]
50-
if (!['scan', 'clean', 'sources', 'install', 'proxy'].includes(command)) {
51-
console.error(`broom: unknown command '${command}'. Try 'broom --help'.`)
69+
if (!COMMANDS.includes(command)) {
70+
const hint = closest(command, COMMANDS)
71+
console.error(`broom: unknown command '${command}'.${hint ? ` Did you mean '${hint}'?` : ''} Try 'broom --help'.`)
5272
process.exit(1)
5373
}
5474

@@ -156,10 +176,11 @@ const extraFile = option('--extra')
156176
const allowFile = option('--allowlist')
157177
const jsonOut = flag('--json')
158178
const noFail = flag('--no-fail')
159-
const apply = flag('--apply')
179+
const apply = flag('--apply') || command === 'sweep' // `sweep` = clean --apply
160180
const backupDir = option('--backup-dir')
161181
const noBackup = flag('--no-backup')
162182
const noAllowlist = flag('--no-allowlist')
183+
const verbose = flag('--verbose')
163184

164185
// ── Load extra secrets from --extra file ──────────────────────────────────────
165186
let extras = []
@@ -207,7 +228,19 @@ if (targets.length === 0) {
207228
process.exit(noFail ? 0 : 1)
208229
}
209230

210-
const isClean = command === 'clean'
231+
const isClean = command === 'clean' || command === 'sweep'
232+
233+
// Confirm before writing, but only when interactive and not forced with --yes.
234+
// Non-interactive (pipes, CI) proceeds silently — running `sweep`/`--apply`
235+
// there is itself the consent.
236+
if (apply && !flag('--yes') && isInteractive()) {
237+
const ok = await confirm(
238+
`\n Redact secrets in place across ${targets.length} discovered file(s)? Originals are backed up first.`,
239+
false,
240+
)
241+
if (!ok) { console.log('\n Aborted — nothing was written.\n'); process.exit(0) }
242+
}
243+
211244
const backup = (!noBackup && apply) ? new BackupSession(backupDir) : null
212245

213246
const scanResults = []
@@ -251,7 +284,7 @@ if (backup) backup.writeManifest(scanResults)
251284
if (jsonOut) {
252285
printJsonReport(scanResults)
253286
} else {
254-
printReport(scanResults, { clean: isClean, apply })
287+
printReport(scanResults, { clean: isClean, apply, verbose })
255288
}
256289

257290
const totalFindings = scanResults.reduce((n, r) => n + r.findings.length, 0)
@@ -279,8 +312,10 @@ function printHelp() {
279312
broomsticks v${pkg.version} — sweep secrets out of AI coding-assistant transcripts
280313
281314
USAGE
315+
broom interactive menu (on a terminal)
282316
broom scan [options] find secrets (read-only, exits 1 if found)
283317
broom clean [options] preview redactions (dry-run by default)
318+
broom sweep [options] redact in place — backs up first (= clean --apply)
284319
broom clean --apply [options] redact in place — backs up first
285320
broom sources list discovered transcript files
286321
broom install install Claude Code skill + Stop hook
@@ -301,6 +336,7 @@ OPTIONS
301336
--allowlist <file> Custom allowlist file (default: ~/.broom/allowlist.txt)
302337
--no-allowlist Disable allowlist suppression (report all findings)
303338
--json Machine-readable JSON output
339+
--verbose List every finding, not just per-file counts
304340
--no-fail Exit 0 even when secrets are found (CI override)
305341
--port <n> Proxy port (default: 7777)
306342
--verbose Log redaction counts to stderr (proxy only)

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "broomsticks",
3-
"version": "0.1.1",
3+
"version": "0.2.0",
44
"description": "Sweep leaked secrets out of your AI coding-assistant chat transcripts (Claude Code, Codex, Cursor). Dry-run by default, backups before every write, zero runtime dependencies.",
55
"type": "module",
66
"bin": {

0 commit comments

Comments
 (0)