Skip to main content

dev_prune/
help.rs

1// Copyright 2026 VKrishna04
2// SPDX-License-Identifier: Apache-2.0
3
4// The long-form help text: what `devp <command> --help` prints.
5//
6// `-h` stays short — one line per flag, no scrolling. `--help` (and `devp help
7// <command>`) gets these: the full description, the behaviour that is not obvious from
8// the flag list, and worked examples for every command and subcommand. The text is
9// kept in one file rather than inline in the derive so the CLI definition in `lib.rs`
10// stays readable, and because these paragraphs are documentation first — they carry
11// the same facts as `docs/CLI_REFERENCE.md`, and changing one means changing both.
12
13pub const INIT_LONG: &str = "\
14Crawl the given directory trees for Git repositories and register every one found, up \
15to 8 levels deep. Registration is one entry in dev-prune's own registry file — nothing \
16in the repository is created, changed or deleted.
17
18After registering, it runs the same integration pass as `devp setup` (installing \
19whatever is missing, reporting whatever it skipped) and the same quiet release check \
20as `devp update`.
21
22Registration is what makes a repository visible to `devp run`, `devp status` and the \
23background pass. Repositories are never auto-discovered at prune time: what dev-prune \
24touches is exactly what you registered.";
25
26pub const INIT_EXAMPLES: &str = "\
27EXAMPLES:
28  devp init                       Register repositories under the current directory
29  devp init ~/Code                Register everything under ~/Code
30  devp init ~/Code ~/Work/oss     Multiple trees in one pass
31  devp scan ~/Code                Same command — `scan` and `onboard` are aliases
32  DEV_PRUNE_NO_AUTO_SETUP=1 devp init ~/Code
33                                  Register repositories, install nothing
34
35UNDO:
36  devp undo                       Reverts the most recent init or link";
37
38pub const LINK_LONG: &str = "\
39Register one Git repository for pruning. The path defaults to `.`, so inside a \
40repository `devp link` is the whole command. Registration writes one entry to \
41dev-prune's registry; the repository itself is untouched.
42
43`--quiet` is the form the global Git hook invokes: it prints nothing (a hook fires \
44inside someone's commit, whose terminal is not dev-prune's to write to) and it skips \
45repositories whose `.devprune.json` sets `disable_hooks`, so a workspace that opted \
46out of auto-registration stays out.";
47
48pub const LINK_EXAMPLES: &str = "\
49EXAMPLES:
50  devp link                       Register the current directory
51  devp link ~/Code/my-app         Register a repository by path
52  devp link . --quiet             What the Git hook runs; silent, honours opt-outs
53
54UNDO:
55  devp undo                       Reverts the most recent init or link
56  devp unlink                     Unregister (keeps every file on disk)";
57
58pub const UNLINK_LONG: &str = "\
59Remove a repository from dev-prune's registry. This deletes the registry entry and \
60nothing else — no workspace file is touched, and the repository's `.devprune.json`, \
61if it has one, stays where it is.
62
63`--missing` removes every registered path whose directory no longer exists, instead \
64of one named repository. Deleted clones, reformatted drives and moved workspaces all \
65leave dead entries behind; `devp doctor` counts them in one warning and points here \
66rather than printing one `devp unlink` line per dead path.";
67
68pub const UNLINK_EXAMPLES: &str = "\
69EXAMPLES:
70  devp unlink                     Unregister the current directory
71  devp unlink ~/Code/old-app      Unregister a repository by path
72  devp unlink --missing           Drop every entry whose path no longer exists";
73
74pub const UNDO_LONG: &str = "\
75Revert the most recent `devp init` or `devp link`: whatever repositories that one \
76action registered are unregistered again. Only registration is undone — `undo` never \
77deletes files, and it is not the undo for a prune (that is `devp restore --last-run`).";
78
79pub const UNDO_EXAMPLES: &str = "\
80EXAMPLES:
81  devp undo                       Unregister whatever the last init/link registered
82
83RELATED:
84  devp restore --last-run         The undo for a prune pass — reinstalls what it deleted";
85
86pub const RUN_LONG: &str = "\
87Execute a prune pass: across every registered repository with no path, or on one \
88repository with `devp run <path>`. Each repository goes through the same gauntlet, \
89and a directory is deleted only when every check passes:
90
91  1. `ignore.devprune.json` in the root, or `\"ignore\": true` — skipped instantly.
92  2. Idle check: last commit and newest source mtime, against `idle_days` (15 by
93     default). `--ignore-idle` lifts this one check and nothing else.
94  3. Project discovery: the root and up to `scan_depth` levels below it (6 by
95     default), so a monorepo's every package is found.
96  4. The package manager's own binary must be present — a directory whose manager
97     is missing cannot be verified, so it is not touched.
98  5. Lockfile verification: the manager itself confirms the lockfile can rebuild
99     the directory. No flag bypasses this, and none ever will.
100  6. Size floor (`min_size_mb` / `--min-size`), symlink refusal, nested-repository
101     refusal.
102
103Interactively, a selection TUI shows what would be deleted before anything is; `-y` \
104skips the confirmation, and `--dry-run` reports what a pass would do without deleting \
105anything at all. Adapter names for `--only`/`--skip` are: npm, pnpm, yarn, bun, uv, \
106poetry, pdm, pipenv, venv, cargo, go, composer, bundler, cocoapods, mix, mix_build, \
107gradle, maven, swift, terraform, dart, vcpkg, cmake_build — an unknown name is an \
108error listing the valid ones, not a silently empty pass. cargo, gradle, maven, \
109swift, dart, mix_build, vcpkg and cmake_build are opt-in (`devp config set \
110enable_cargo true`) and idle-gated separately by `build_idle_days`, because a \
111build directory takes far longer to get back than a dependency directory. \
112`devp config wizard` switches them on by language, and can give any one adapter \
113its own idle window.
114
115`--except` is the safe spelling of \"clean up but keep the API project\": the named \
116repositories are never verified, never deleted and never restored, which beats \
117pruning them and downloading everything back. Entries match by full path or by \
118directory name, case-insensitively, `~` expanded.
119
120`--explain` answers \"why was that repository not pruned?\": every repository and \
121directory is listed with its verdict, including the states a normal pass keeps quiet \
122about — still active (with the actual age), opted out, under the size floor. It is \
123read-only and cannot be combined with `--json`.";
124
125pub const RUN_EXAMPLES: &str = "\
126EXAMPLES:
127  devp run --dry-run              What would be pruned, and why the rest would not
128  devp run                        Prune across all registered repositories (asks first)
129  devp run -y                     Same, no confirmation prompt
130  devp run .                      Prune only the current repository
131  devp run ~/Code/my-app --ignore-idle -y
132                                  Prune it even though it was touched recently
133  devp run --except api-service,~/Code/playground
134                                  Everything except the ones you name
135  devp run --only cargo,uv --dry-run
136                                  Only these package managers
137  devp run --skip venv --min-size 50
138                                  Skip venvs; ignore directories under 50 MiB
139  devp run --json --dry-run       One JSON document on stdout (schema in CLI_REFERENCE)
140  devp run --explain              Why each repository would or would not be pruned
141
142Run interactively with a terminal, `--json` also copies the document to the clipboard.
143A run in which any repository failed exits 1, even if others succeeded.";
144
145pub const STATUS_LONG: &str = "\
146The dashboard: every registered repository with its state (Candidate, Active, \
147Ignored, No Bloat, Path Missing, or an unreadable `.devprune.json`), its reclaimable \
148space, and its last activity. In a terminal this is an interactive TUI; piped or \
149redirected it prints a plain table; `--json` replaces either with one document and \
150changes nothing at all.
151
152Sizes are what deleting the directory actually gives back: bytes hardlinked into a \
153pnpm or bun store are measured per file and excluded, because the store keeps them.
154
155TUI KEYS:
156  Up/Down, j/k      Move           PgUp/PgDn        Jump ten rows
157  Home/End, g/G     First/last     p                Prune-select mode (candidates pre-selected)
158  Space             Toggle row     a                Toggle all candidates
159  Enter             Prune the selection             i    Toggle ignore in .devprune.json
160  Esc               Leave mode / exit               q, Ctrl-C   Exit
161
162SHORTCUTS:
163  devp status daemon              = devp config daemon status
164  devp status . hook              = devp config hook . status";
165
166pub const STATUS_EXAMPLES: &str = "\
167EXAMPLES:
168  devp status                     The dashboard (TUI in a terminal, table when piped)
169  devp status --top 10            Only the ten biggest repositories; totals unaffected
170  devp status --drift             What would a prune refuse on, and how to record it
171  devp status --json | jq '.totals.reclaimable_bytes'
172                                  Machine-readable; stdout carries the document only
173  devp status --top 5 --json      The trim is reported as a top-level \"top\" field
174
175Run interactively with a terminal, `--json` also copies the document to the clipboard.";
176
177pub const STATS_LONG: &str = "\
178What dev-prune has already done, as opposed to what it could do next: lifetime space \
179reclaimed, how many prune passes there have been, the most recent pass and how to \
180undo it, the last passes, and the repositories that have given back the most. \
181Read-only — it reads the registry and touches nothing.";
182
183pub const STATS_EXAMPLES: &str = "\
184EXAMPLES:
185  devp stats                      The report
186  devp stats --json | jq '.lifetime.bytes_freed'
187                                  Lifetime bytes as a number
188
189Run interactively with a terminal, `--json` also copies the document to the clipboard.";
190
191pub const MAN_LONG: &str = "Render the manual, from the same clap definitions `--help` prints, so the manual cannot describe a flag the program does not have.
192
193`devp man` at a terminal prints the contents page: every command grouped by what it is for, one line each, plus the flags that go before the command and the exit codes. `devp man <command>` prints that one command's page — the same text `devp <command> --help` prints, because they are the same definition.
194
195The roff source is something `man` formats, not something a person reads, and on Windows there is no `man` to hand it to, so it appears only where something can use it: redirect or pipe the output and it is roff again, so `devp man > devp.1` and `devp man | man -l -` are unchanged. `--roff` forces roff at a terminal too.
196
197`--dir` writes the full set (`devp.1`, `dev-prune.1`, and one `devp-<command>.1` per subcommand) into a directory, ready to copy onto `manpath`.";
198
199pub const MAN_EXAMPLES: &str = "EXAMPLES:
200  devp man                        The contents page, on any platform
201  devp man run                    One command's page
202  devp man | man -l -             Read it formatted by man (Linux/macOS)
203  devp man --roff > devp.1        Save the roff source
204  devp man run --roff > devp-run.1   Save one page's roff source
205  devp man --dir ./man            Write the full set into ./man
206  sudo cp man/*.1 /usr/local/share/man/man1/   Install them system-wide";
207
208pub const COMPLETIONS_LONG: &str = "\
209Print a shell completion script to stdout — the script and nothing else, because the \
210output is meant to be redirected or eval'd and anything extra becomes a shell error \
211on every new terminal.
212
213The script is generated from the same argument definition the binary parses with, so \
214a flag cannot exist in one and be missing from the other. It completes whichever name \
215invoked it: `devp completions bash` completes `devp`, `dev-prune completions bash` \
216completes `dev-prune` — generate one for each name you actually type.";
217
218pub const COMPLETIONS_EXAMPLES: &str = "\
219EXAMPLES:
220  source <(devp completions bash)                       Bash, current shell
221  devp completions bash > ~/.local/share/bash-completion/completions/devp
222  devp completions zsh  > ~/.zfunc/_devp                Zsh (a directory on $fpath)
223  devp completions fish > ~/.config/fish/completions/devp.fish
224  devp completions powershell | Out-File -Append -Encoding utf8 $PROFILE
225                                                        PowerShell, permanently";
226
227pub const CACHES_LONG: &str = "\
228Find every package-manager cache and store on the machine, size each one, and print \
229the command that clears it — largest first, with a total. On its own it deletes \
230nothing, and nothing that runs on a schedule ever will: a cache is shared by every \
231repository, so no single lockfile can prove its contents recoverable, which is the \
232bar every dev-prune deletion has to clear. Clearing one also turns the next `devp \
233restore` into a download.
234
235`devp caches clear <manager>` runs the command this table prints, after showing you \
236what goes and asking.
237
238Covered: npm, pnpm, yarn, bun, uv, pip, conda, cargo, go, maven, gradle, nuget, vcpkg, \
239conan, composer, cocoapods and hex. Each manager is asked where its cache is (`npm \
240config get cache`, `go env GOMODCACHE`, …) rather than assumed, with read-only queries \
241run from your home directory; a manager that is not installed falls back to the \
242conventional location, because a cache left behind by an uninstalled manager is \
243exactly the multi-gigabyte directory nobody remembers.";
244
245pub const CACHES_EXAMPLES: &str = "\
246EXAMPLES:
247  devp caches                     The table, largest first, with clear commands
248  devp caches --json | jq '.summary.total_bytes'
249                                  Machine-readable
250  devp caches clear npm           Empty one, after asking
251  devp caches clear all --dry-run What would go, and nothing touched
252
253Run interactively with a terminal, `--json` also copies the document to the clipboard.";
254
255pub const CACHES_CLEAR_LONG: &str = "\
256Empty one manager's cache, or every one of them. What is about to go is listed and \
257sized first, and unless `--yes` answers for you, it asks.
258
259This is a convenience, not automation. No scheduler, no Git hook and no `devp run` \
260will ever clear a cache — this only runs when you type it.
261
262Wherever the manager ships its own subcommand, that is what runs: `npm cache clean \
263--force`, `pnpm store prune`, `go clean -modcache`. The manager knows what is still \
264referenced, which a directory delete cannot work out, and its own bookkeeping stays \
265consistent. cargo, gradle, vcpkg and hex ship nothing equivalent, so those \
266are cleared by removing the directory this command resolved and sized — never a string \
267handed to a shell.
268
269Maven is reported and never cleared. `~/.m2/repository` is an install target as \
270well as a download cache — `mvn install:install-file` puts artifacts there that no \
271remote can hand back — so dev-prune sizes it and prints `rm -rf ~/.m2/repository` \
272for you to run. `clear maven` says so and stops; `clear all` skips it.
273
274Two flags narrow what `all` means, so you do not have to pick the caches by hand. \
275`--over-cap` keeps only the managers that have outgrown the ceiling you set in \
276`cache_max_gb`; with no cap set anywhere it clears nothing and says so. `--unused` keeps \
277only the managers that no registered repository uses — a cache with nothing behind it \
278was filled for projects that are not on this disk any more. It counts only repositories \
279dev-prune knows about, so `devp link` anything you keep outside the registry first, and \
280it refuses to run at all when there are no registered repositories to check against.
281
282Nothing else in a cache is lost; every manager re-downloads what it needs. What it costs \
283is time, in every project on the machine, on the next install and the next `devp \
284restore`. The freed size reported afterwards is measured rather than assumed, because \
285a `prune` keeps what is still in use.";
286
287pub const CACHES_CLEAR_EXAMPLES: &str = "\
288EXAMPLES:
289  devp caches clear npm           One manager, after confirming
290  devp caches clear cargo         Both cargo rows: the registry cache and its sources
291  devp caches clear all --dry-run Everything that would go, and nothing touched
292  devp caches clear all --over-cap
293                                  Only the ones past their cache_max_gb
294  devp caches clear all --unused  Only the ones no registered repository uses
295  devp caches clear all --yes     No prompt, for a script
296  devp caches clear go --json --yes
297                                  Machine-readable (`--json` requires `--yes`)
298
299Exit code 1 if any cache could not be cleared; the rows are printed either way.
300Exit code 2 for `maven`, which is reported but never cleared.";
301
302pub const TRUST_LONG: &str = "\
303What dev-prune is allowed to do on this machine, on one screen. Read-only — it reads \
304the registry and the OS and changes nothing.
305
306Two sections, and the split is the point. The first is guaranteed by the code: the \
307seven safety invariants plus the two questions asked as often as any of them — there \
308is no telemetry endpoint, and build output is never deleted. Those rows read the same \
309on every machine and have no setting and no flag behind them. The second is read live \
310off this machine: whether the scheduler is installed, whether the Git hooks register \
311repositories on their own, how many repositories are registered, and the settings that \
312widen what may happen without you asking for it.
313
314There is no letter grade. A report that says `trust level: MEDIUM` tells you nothing \
315you can act on, so the widened settings are named instead — `devp config show` has \
316every one of them, and `devp config set <key> <value>` puts one back.
317
318The long form of the guarantees is docs/SAFETY_INVARIANTS.md.";
319
320pub const TRUST_EXAMPLES: &str = "\
321EXAMPLES:
322  devp trust                      The report
323  devp trust --json | jq '.summary.widened'
324                                  Just the settings that widen what may happen
325  devp trust --json | jq -e '.summary.widened_count == 0'
326                                  Exit 1 from jq if this machine has widened anything
327
328Run interactively with a terminal, `--json` also copies the document to the clipboard.";
329
330pub const CONFIG_LONG: &str = "\
331Everything configurable lives under here: global settings (get/set/show/wizard), the \
332per-repository `.devprune.json` (project), the OS background scheduler (daemon), the \
333global Git auto-registration hooks (hook), and the file-manager icon registration \
334(icon).
335
336SHORTHANDS — `daemon`, `hook` and `icon` work without the leading `config`, and the \
337action words people reach for are accepted:
338  devp hook install       = devp config hook enable
339  devp hook uninstall     = devp config hook disable
340  devp daemon on / off    = devp config daemon enable / disable
341  devp icon               = devp config icon
342Accepted action words: enable/install/on, disable/uninstall/remove/off, status/show. \
343Anything else is rejected — a mistyped action never silently degrades into a status \
344report.";
345
346pub const CONFIG_EXAMPLES: &str = "\
347EXAMPLES:
348  devp config show                Every global setting and its value
349  devp config get idle_days       One setting
350  devp config set idle_days 30    Change it (rejects out-of-range values)
351  devp config wizard              Walk through every setting, Enter keeps the current
352  devp config project .           Inspect or create this repo's .devprune.json
353  devp config daemon status       Is the background pass scheduled?
354  devp config . daemon disable    Opt this repository out of the background pass
355  devp config hook enable --chain Take core.hooksPath, forwarding to the tool holding it
356  devp config icon                Register the .devprune.json icon and schema";
357
358pub const CONFIG_GET_LONG: &str = "\
359Print one global setting's current value. The keys, defaults and meanings:
360
361  idle_days                  15     Days untouched before a repo is a candidate
362  min_size_mb                0      Smallest directory worth deleting (0 = no floor)
363  scan_depth                 6      Directory levels below a repo root discovery descends (1-32)
364  require_confirmation       true   Whether a prune pass asks before deleting
365  allow_manifest_rewrite     false  Whether verification may repair a drifted lockfile
366  command_timeout_secs       600    Ceiling on any one package-manager command
367  auto_setup                 true   Whether the integration pass may run unattended
368  auto_daemon                true   …and may register the OS scheduler
369  check_interval_days        2      How often the scheduler runs a pass
370  auto_hooks                 true   …and may install the global Git hooks
371  auto_hooks_chain           false  …and may chain onto another tool's core.hooksPath
372  update_check               true   Whether the periodic release check runs
373  update_check_interval_days 7      Minimum gap between two release checks
374  update_check_timeout_secs  5      How long that one request may hang
375
376Three have a per-repository override in `.devprune.json`, where they win for that \
377tree only: `idle_days` (spelled `override_idle_days` there), `min_size_mb` and \
378`scan_depth`. The rest are deliberately global — a committed `.devprune.json` must \
379not be able to grant a repository `allow_manifest_rewrite` over its own manifests.";
380
381pub const CONFIG_GET_EXAMPLES: &str = "\
382EXAMPLES:
383  devp config get idle_days
384  devp config get update_check";
385
386pub const CONFIG_SET_LONG: &str = "\
387Change one global setting. A value outside the accepted range is rejected with the \
388range in the message, never silently clamped — `scan_depth 0` and `scan_depth 40` are \
389both refused outright. Keys are the same list `devp config get --help` shows.";
390
391pub const CONFIG_SET_EXAMPLES: &str = "\
392EXAMPLES:
393  devp config set idle_days 30
394  devp config set min_size_mb 50
395  devp config set command_timeout_secs 1200
396  devp config set update_check false    Turn the release check off for good";
397
398pub const CONFIG_SHOW_LONG: &str = "\
399Print every global setting with its current value. With `--update`, also run a sync \
400pass across all registered repositories, refreshing each one's `.devprune.json` \
401scaffolding without touching values you have changed.";
402
403pub const CONFIG_SHOW_EXAMPLES: &str = "\
404EXAMPLES:
405  devp config show
406  devp config show --update";
407
408pub const CONFIG_PROJECT_LONG: &str = "\
409Inspect a repository's `.devprune.json`, or create it when missing. The file holds \
410the per-repository overrides — `override_idle_days`, `min_size_mb`, `scan_depth`, \
411`ignore`, `disable_daemon`, `disable_hooks` — and carries a `$schema` line so any \
412editor with JSON Schema support validates and completes it.
413
414`--update` refreshes the file's scaffolding (schema pointer, missing keys) while \
415keeping every value you have set. A file that does not parse is refused, not reset — \
416fix it, or pass `--update` deliberately.
417
418Writing the file also records it in the repository's `.git/info/exclude`, so the \
419config — one machine's preference, not part of the project — never shows up in \
420`git status`. The shared, tracked `.gitignore` is never modified.";
421
422pub const CONFIG_PROJECT_EXAMPLES: &str = "\
423EXAMPLES:
424  devp config project .           Show (or create) this repository's config
425  devp config project ~/Code/api
426  devp config project . --update  Refresh scaffolding, keep your values";
427
428pub const CONFIG_DAEMON_LONG: &str = "\
429The OS background scheduler — Task Scheduler on Windows, launchd on macOS, systemd \
430timers on Linux — which runs `devp run --daemon` every `check_interval_days` days.
431
432Globally: `enable` registers the schedule, `disable` removes it, `status` reports \
433it. With a path first, the same words act on one repository via `disable_daemon` in \
434its `.devprune.json`: the machine-wide pass keeps running, that repository sits it \
435out.";
436
437pub const CONFIG_DAEMON_EXAMPLES: &str = "\
438EXAMPLES:
439  devp daemon status              Machine-wide scheduler state
440  devp daemon enable              Register the schedule (also: install, on)
441  devp daemon disable             Remove it (also: uninstall, remove, off)
442  devp config . daemon disable    This repository opts out of the background pass
443  devp config ~/Code/api daemon enable";
444
445pub const CONFIG_HOOK_LONG: &str = "\
446The global Git hooks (via `core.hooksPath`) that auto-register any repository you \
447commit in — `devp link --quiet`, silent, honouring opt-outs. `enable` installs them, \
448`disable` removes them (restoring what was there), `status` reports them. With a \
449path first, the same words act on one repository via `disable_hooks`.
450
451Git has exactly one global `core.hooksPath` and no way to chain two, so a tool that \
452holds it (husky, pre-commit, lefthook) shuts every other one out. `--chain` is the \
453way through: dev-prune takes the slot and writes, per hook, a shim that does its own \
454work and then execs the same-named hook in the displaced directory — their hooks \
455keep firing, their exit codes still block commits. `devp hook uninstall` puts the \
456original back. The chain snapshots the other tool's hooks at install time; `devp \
457hook status` reports any that have drifted, and `devp hook install --chain` rebuilds.";
458
459pub const CONFIG_HOOK_EXAMPLES: &str = "\
460EXAMPLES:
461  devp hook status                Installed? Chained? Drifted?
462  devp hook install               Take the free core.hooksPath slot
463  devp hook install --chain       Take a slot husky/pre-commit/lefthook holds, forwarding
464  devp hook uninstall             Restore the previous core.hooksPath
465  devp config . hook disable      This repository opts out of auto-registration";
466
467pub const CONFIG_ICON_LONG: &str = "\
468Register `*.devprune.json` with the OS file manager and write the icon files and the \
469JSON Schema into the config directory. On Linux this is a complete registration \
470(shared-mime-info plus hicolor icons — Nautilus, Dolphin, Thunar, Nemo, PCManFM). On \
471Windows, Explorer resolves icons by last extension only, so the config folder gets \
472its own icon instead of hijacking every `.json` on the machine. On macOS a UTI must \
473come from an application bundle, which a single binary is not.
474
475It also prints an editor snippet to paste yourself — it never edits your editor \
476settings, your PATH, or your shell startup files.";
477
478pub const CONFIG_ICON_EXAMPLES: &str = "\
479EXAMPLES:
480  devp icon                       Same command, without the leading `config`";
481
482pub const CONFIG_WIZARD_LONG: &str = "\
483Open every global setting in a full-screen configurator, with the `devp trust` \
484declaration in front of it: what this tool is allowed to do is on screen before any \
485of it is configurable.
486
487Arrows move; Space changes the highlighted setting — a toggle flips, a number opens \
488a field, `disabled_adapters` opens the adapter checklist; `r` puts one back; `y` \
489accepts everything as shown. The last screen lists exactly what will be written, \
490before it is written. `q` leaves without saving anything.
491
492It runs itself once on a first install — so the defaults are something you agreed \
493to, not something you inherited — and again after an upgrade adds a setting this \
494machine has never been shown, which it marks NEW and opens on. Settings you have \
495already confirmed are never re-asked.
496
497It never runs unattended: no TTY means skipped, not guessed at. `--no-tui`, and the \
498DEV_PRUNE_NO_TUI environment variable, ask one question per line instead — for \
499terminals the full-screen view cannot drive, and for agents, which hold a real \
500terminal and will never press a key. To configure this tool from a script, use \
501`devp config set <key> <value>`, which needs no terminal at all.";
502
503pub const CONFIG_WIZARD_EXAMPLES: &str = "\
504EXAMPLES:
505  devp config wizard
506  devp config wizard --no-tui              One question per line
507  devp config set disabled_adapters go     Leave Go projects alone entirely
508  devp config set disabled_adapters -      Every adapter active again";
509
510pub const RESTORE_LONG: &str = "\
511Put dependencies back: detect each project's lockfile and run its manager's install \
512(`npm ci`, `pnpm install`, `uv sync`, `cargo fetch`, …). Mirrors pruning — every \
513project in the tree is restored, each by its own manager, so a monorepo comes back \
514whole.
515
516`--last-run` restores exactly what the most recent prune pass deleted, across every \
517repository it touched, and nothing else: the undo for a `devp run`. Each prune \
518records what it removed (a dry run records nothing), and the flag fails cleanly if \
519no pass has been recorded yet. It cannot be combined with a path — silently ignoring \
520the path would restore the wrong thing.";
521
522pub const RESTORE_EXAMPLES: &str = "\
523EXAMPLES:
524  devp restore                    Restore the current directory's projects
525  devp restore ~/Code/my-app
526  devp restore --last-run         Undo the last prune pass, everywhere it acted";
527
528pub const UPDATE_LONG: &str = "\
529Print the installed version, ask GitHub's public API for the latest release, and \
530show the upgrade command for how this copy was installed. `--install` runs the upgrade through the \
531package manager that owns this copy (cargo, npm, uv, pipx, or the installer \
532script). `auto_update` is on by default and does the verified-download half by \
533itself at the end of a prune pass when a newer release is known — never the \
534package-manager half, and nothing at all on WinGet, Scoop and Homebrew, where the \
535manager owns the upgrade; `devp config set auto_update false` stops it. An upgrade never interrupts the \
536scheduler: the scheduled pass runs a managed copy that refreshes itself from the \
537new binary on its next run.
538
539`devp config set version_lock true` outranks all of it. While the pin is on this \
540copy stays on the version it is: `auto_update` does not run however it is set, \
541`--install` refuses, `devp install --channel` refuses because moving channels \
542installs the latest release, and the install scripts leave the binary alone. \
543No flag bypasses it -- `devp config set version_lock false` is the way back.
544
545The same check also runs quietly from `devp run` and `devp status`, at most once \
546every `update_check_interval_days` (7 by default), printing one line only when a \
547newer version exists. It is the only thing in dev-prune that opens a network \
548connection, sends no body and no identifier, and `devp config set update_check \
549false` turns it off for good; `--offline` skips it for one run without changing the \
550setting.";
551
552pub const UPDATE_EXAMPLES: &str = "\
553EXAMPLES:
554  devp update                     Version, latest release, upgrade command
555  devp update --install           Upgrade now, through the owning channel
556  devp update --offline           No network this run";
557
558pub const INSTALL_LONG: &str = "\
559Move this installation from one package manager to another.
560
561`devp update` always upgrades the copy that is running, through whichever channel \
562installed it. This command changes *which* channel owns it: it installs through the \
563manager you name, then removes the old copy through the manager that put it there — in \
564that order, so a failed install leaves the working copy untouched.
565
566Removing the old copy through its own manager, rather than deleting the file, is the \
567point: uv, pipx, npm, cargo and the rest each keep a record of what they installed, and \
568a manager whose record still says dev-prune is there will put the old binary back.
569
570Nothing is migrated, because nothing needs to be. Settings, the repository registry and \
571the undo history live in the config directory, which no package manager owns.
572
573With no `--channel` it prints which manager installed this copy and what `--channel` \
574accepts. `--dry-run` prints the commands without running any of them.";
575
576pub const INSTALL_EXAMPLES: &str = "\
577EXAMPLES:
578  devp install                              Which channel owns this copy
579  devp install --channel winget --dry-run   Print the plan, change nothing
580  devp install --channel uv                 Move onto uv, and remove the old copy
581  devp install --channel cargo --yes        Skip the confirmation prompt";
582
583pub const SKILL_LONG: &str = "\
584Teach your AI assistant this tool. Exports SKILL.md — the full agent-facing manual: \
585every command, the JSON contracts, the safety invariants, the troubleshooting tree — \
586into the config directory, installs it into any detected agent skills directory \
587(`~/.claude/skills/dev-prune/`, the same install `devp setup` performs), and prints \
588ready-to-copy onboarding prompts for assistants without one (Gemini Antigravity, \
589Cursor, Windsurf, Copilot, OpenClaw).
590
591`--agent <EDITOR>` instead writes per-repository rules into the current repository, in \
592the file that editor's agent reads. Ten editors get a file of their own — cursor, \
593windsurf, antigravity, cline, roo, kilocode, continue, amazon-q, kiro, trae — and six \
594share a file with other tools, so dev-prune owns a marked block inside it: agents-md \
595(`AGENTS.md`, the cross-tool convention Codex, Jules, Amp and OpenCode read), copilot \
596(`.github/copilot-instructions.md`), gemini (`GEMINI.md`), junie \
597(`.junie/guidelines.md`), zed (`.rules`) and aider (`CONVENTIONS.md`, the one file its \
598editor does not read by finding it — writing it prints the `read: CONVENTIONS.md` line \
599that makes Aider load it). Every byte outside the markers is left as found. \
600`devp skill --help` lists each value with its exact path. Claude Code needs no \
601per-repository file — its skill installs globally.";
602
603pub const SKILL_EXAMPLES: &str = "\
604EXAMPLES:
605  devp skill                      Export SKILL.md, print onboarding prompts
606  devp skill --agent cursor       Write .cursor/rules/dev-prune.mdc here
607  devp skill --agent agents-md    Upsert the marked block in AGENTS.md";
608
609pub const SETUP_LONG: &str = "\
610Install whatever integration is missing and leave the rest alone: the `devp` alias, \
611the managed binary directory on your PATH (a user PATH entry on Windows, \
612`~/.local/bin` symlinks elsewhere — what keeps `devp` working after the venv or npx \
613cache it came from disappears), the exported SKILL.md and its agent-directory \
614install, the file-manager icon registration, the global Git hooks, and the OS \
615scheduler. Safe to run repeatedly: it is the same pass the install scripts run, the \
616same one `devp init` runs, and the same one that runs by itself on the first command \
617after an upgrade.
618
619It skips rather than forces: Git hooks when `git` is missing or another tool holds \
620`core.hooksPath` (take the slot with `devp hook install --chain`), the alias when \
621the running process is `devp` itself on Windows, and anything switched off by \
622`auto_setup`, `auto_hooks`, `auto_daemon` or `DEV_PRUNE_NO_AUTO_SETUP=1`.
623
624When a VS Code-family editor is on your PATH (VS Code, VSCodium, Cursor, Windsurf, \
625Positron, Kiro, or an Insiders build) and the dev-prune extension is not installed, \
626one run also asks — once ever, only at a terminal — whether to install it into each \
627editor found. Each editor installs from its own registry; when a fork's registry does \
628not carry the extension, the `.vsix` from the latest GitHub release is installed \
629instead. Decline and it never asks again; install it yourself later with \
630`code --install-extension VKrishna04.dev-prune`.";
631
632pub const SETUP_EXAMPLES: &str = "\
633EXAMPLES:
634  devp setup                      Install what is missing, report what was skipped
635  devp setup --status             Report only; change nothing
636  DEV_PRUNE_NO_AUTO_SETUP=1 devp init ~/Code
637                                  Register repositories, install nothing";
638
639pub const DOCTOR_LONG: &str = "\
640One read-only pass that answers \"why is this not doing what I expect\". Without a \
641path it checks the installation: binary and twin, PATH, registry health, every \
642stored setting revalidated, SKILL.md, icons, hooks, scheduler, the package-manager \
643binaries your repositories actually need, and the release-check state. With a path \
644it checks that repository and ends by naming the single reason a prune would or \
645would not touch it.
646
647`--fix` is diagnosis first, then treatment — and it mends installed-but-broken only: \
648a stale or missing `devp` twin, a missing SKILL.md export, hooks or a scheduler \
649whose recorded binary moved, a drifted hook chain, and registry entries whose \
650repository is gone. Each repair is the corresponding `devp setup` pass re-run, so a \
651repair can never do more than setup itself would. It never performs a first-time \
652install, and never touches an unreadable registry — a parse failure is for you to \
653look at, not for a tool to guess at.
654
655EXIT CODES: 0 when everything works, warnings included — a missing scheduler should \
656not fail a script. 1 only for genuine breakage: an unreadable registry, an \
657out-of-range setting, a dead registered path, a directory that is not a Git \
658repository. `--fix` exits 1 when any repair failed or was out of reach.";
659
660pub const DOCTOR_EXAMPLES: &str = "\
661EXAMPLES:
662  devp doctor                     Check the installation
663  devp doctor .                   Why would a prune touch (or skip) this repository?
664  devp doctor ~/Code/api
665  devp doctor --fix               Repair what the installation check finds broken";
666
667pub const UNINSTALL_LONG: &str = "\
668Remove dev-prune from the machine: the OS scheduler, the global Git hooks (only if \
669`core.hooksPath` still points at dev-prune), the file-type icons, the installed \
670agent skill, the PATH entry (or `~/.local/bin` symlinks), and the binaries — the \
671managed pair, the copy you invoked, and, with your confirmation, every other copy \
672found on PATH or in the well-known install directories (`~/.cargo/bin`, \
673`~/.local/bin`, npm's global directory, pip's Scripts directories). A copy owned by \
674a package manager is listed with its manager, and after removal the manager's own \
675uninstall command is printed so its records can be cleared too.
676
677On Windows, where a running executable cannot delete itself, a detached helper \
678removes the last files a few seconds after the command exits — no reboot, no closed \
679terminal.
680
681Without `--deep` the configuration survives, so a reinstall picks up where you left \
682off. With `--deep` the global config folder and every registered repository's \
683`.devprune.json` go too; that asks for confirmation, and refuses outright with no \
684terminal to ask on unless `-y` is passed. Exits 1 if anything could not be removed, \
685naming each leftover.";
686
687pub const UNINSTALL_EXAMPLES: &str = "\
688EXAMPLES:
689  devp uninstall                  Remove the program; keep config for a reinstall
690  devp uninstall --deep           Also wipe config and per-repo .devprune.json (asks)
691  devp uninstall --deep -y        Non-interactive; also confirms the stray-copy sweep";