1pub 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_DOCKER_LONG: &str = "\
256What the engine is holding, in its own words: images, containers, local volumes and build cache, each with a count, a size, and how much of that size it believes it could give back. Then the commands that would give it back, narrowest first.
257
258Read-only, permanently. dev-prune deletes only what a lockfile proves it can rebuild, and nothing here clears that bar: an image's registry tag can be retagged or deleted, the Dockerfile that built it may not be on this disk, and a named volume is the one thing on the machine that is not reproducible at all. So this prints the prune commands and never runs them, with or without `--yes`.
259
260The numbers come from the engine's own `system df` rather than from a directory walk. On Docker Desktop and Podman the store lives inside a VM disk image the host cannot see, and `~/.docker` is configuration rather than data — a size taken off the filesystem would be wrong by orders of magnitude, in the reassuring direction. Asking the engine is also the only way to learn what is *reclaimable*, which is the figure that decides anything: 40 GB of images with 38 GB dangling is a different situation from 40 GB with 2 GB dangling.
261
262An engine that is installed with its daemon stopped is reported as exactly that, in the engine's own words, rather than as an absence.";
263
264pub const CACHES_CONTAINERS_LONG: &str = "\
265The same read-only report as `devp caches docker`, for every container engine on this machine — docker, podman and nerdctl — or for the one you name.
266
267Local Kubernetes clusters are listed by name and deliberately not sized. kind, k3d and minikube run their nodes as containers, or as a VM disk belonging to an engine already in the table, so their disk is counted there. A figure beside the cluster name would be the same gigabytes twice. Delete one with its own tool — `kind delete cluster`, `minikube delete`, `k3d cluster delete` — which is what actually releases the space. The cluster list is read out of your kubeconfig with `kubectl config get-contexts`, which contacts nothing: a context pointing at a production cluster is filtered out by name here rather than by being dialled.";
268
269pub const CACHES_CONTAINERS_EXAMPLES: &str = "\
270EXAMPLES:
271 devp caches docker Images, containers, volumes, build cache
272 devp caches podman The same, for Podman
273 devp caches containers Every engine installed, plus local clusters
274 devp caches containers nerdctl Just that one
275 devp caches docker --json | jq '.summary.reclaimable_bytes'
276 Machine-readable
277
278Nothing here deletes anything. The prune commands are printed for you to run.";
279
280pub const CACHES_CLEAR_LONG: &str = "\
281Empty one manager's cache, or every one of them. What is about to go is listed and \
282sized first, and unless `--yes` answers for you, it asks.
283
284This is a convenience, not automation. No scheduler, no Git hook and no `devp run` \
285will ever clear a cache — this only runs when you type it.
286
287Wherever the manager ships its own subcommand, that is what runs: `npm cache clean \
288--force`, `pnpm store prune`, `go clean -modcache`. The manager knows what is still \
289referenced, which a directory delete cannot work out, and its own bookkeeping stays \
290consistent. cargo, gradle, vcpkg and hex ship nothing equivalent, so those \
291are cleared by removing the directory this command resolved and sized — never a string \
292handed to a shell.
293
294Maven is reported and never cleared. `~/.m2/repository` is an install target as \
295well as a download cache — `mvn install:install-file` puts artifacts there that no \
296remote can hand back — so dev-prune sizes it and prints `rm -rf ~/.m2/repository` \
297for you to run. `clear maven` says so and stops; `clear all` skips it.
298
299Two flags narrow what `all` means, so you do not have to pick the caches by hand. \
300`--over-cap` keeps only the managers that have outgrown the ceiling you set in \
301`cache_max_gb`; with no cap set anywhere it clears nothing and says so. `--unused` keeps \
302only the managers that no registered repository uses — a cache with nothing behind it \
303was filled for projects that are not on this disk any more. It counts only repositories \
304dev-prune knows about, so `devp link` anything you keep outside the registry first, and \
305it refuses to run at all when there are no registered repositories to check against.
306
307Nothing else in a cache is lost; every manager re-downloads what it needs. What it costs \
308is time, in every project on the machine, on the next install and the next `devp \
309restore`. The freed size reported afterwards is measured rather than assumed, because \
310a `prune` keeps what is still in use.";
311
312pub const CACHES_CLEAR_EXAMPLES: &str = "\
313EXAMPLES:
314 devp caches clear npm One manager, after confirming
315 devp caches clear cargo Both cargo rows: the registry cache and its sources
316 devp caches clear all --dry-run Everything that would go, and nothing touched
317 devp caches clear all --over-cap
318 Only the ones past their cache_max_gb
319 devp caches clear all --unused Only the ones no registered repository uses
320 devp caches clear all --yes No prompt, for a script
321 devp caches clear go --json --yes
322 Machine-readable (`--json` requires `--yes`)
323
324Exit code 1 if any cache could not be cleared; the rows are printed either way.
325Exit code 2 for `maven`, which is reported but never cleared.";
326
327pub const TRUST_LONG: &str = "\
328What dev-prune is allowed to do on this machine, on one screen. Read-only — it reads \
329the registry and the OS and changes nothing.
330
331Two sections, and the split is the point. The first is guaranteed by the code: the \
332seven safety invariants plus the two questions asked as often as any of them — there \
333is no telemetry endpoint, and build output is never deleted. Those rows read the same \
334on every machine and have no setting and no flag behind them. The second is read live \
335off this machine: whether the scheduler is installed, whether the Git hooks register \
336repositories on their own, how many repositories are registered, and the settings that \
337widen what may happen without you asking for it.
338
339There is no letter grade. A report that says `trust level: MEDIUM` tells you nothing \
340you can act on, so the widened settings are named instead — `devp config show` has \
341every one of them, and `devp config set <key> <value>` puts one back.
342
343The long form of the guarantees is docs/SAFETY_INVARIANTS.md.";
344
345pub const TRUST_EXAMPLES: &str = "\
346EXAMPLES:
347 devp trust The report
348 devp trust --json | jq '.summary.widened'
349 Just the settings that widen what may happen
350 devp trust --json | jq -e '.summary.widened_count == 0'
351 Exit 1 from jq if this machine has widened anything
352
353Run interactively with a terminal, `--json` also copies the document to the clipboard.";
354
355pub const CONFIG_LONG: &str = "\
356Everything configurable lives under here: global settings (get/set/show/wizard), the \
357per-repository `.devprune.json` (project), the OS background scheduler (daemon), the \
358global Git auto-registration hooks (hook), and the file-manager icon registration \
359(icon).
360
361SHORTHANDS — `daemon`, `hook` and `icon` work without the leading `config`, and the \
362action words people reach for are accepted:
363 devp hook install = devp config hook enable
364 devp hook uninstall = devp config hook disable
365 devp daemon on / off = devp config daemon enable / disable
366 devp icon = devp config icon
367Accepted action words: enable/install/on, disable/uninstall/remove/off, status/show. \
368Anything else is rejected — a mistyped action never silently degrades into a status \
369report.";
370
371pub const CONFIG_EXAMPLES: &str = "\
372EXAMPLES:
373 devp config show Every global setting and its value
374 devp config get idle_days One setting
375 devp config set idle_days 30 Change it (rejects out-of-range values)
376 devp config wizard Walk through every setting, Enter keeps the current
377 devp config project . Inspect or create this repo's .devprune.json
378 devp config daemon status Is the background pass scheduled?
379 devp config . daemon disable Opt this repository out of the background pass
380 devp config hook enable --chain Take core.hooksPath, forwarding to the tool holding it
381 devp config icon Register the .devprune.json icon and schema";
382
383pub const CONFIG_GET_LONG: &str = "\
384Print one global setting's current value. The keys, defaults and meanings:
385
386 idle_days 15 Days untouched before a repo is a candidate
387 min_size_mb 0 Smallest directory worth deleting (0 = no floor)
388 scan_depth 6 Directory levels below a repo root discovery descends (1-32)
389 require_confirmation true Whether a prune pass asks before deleting
390 allow_manifest_rewrite false Whether verification may repair a drifted lockfile
391 command_timeout_secs 600 Ceiling on any one package-manager command
392 auto_setup true Whether the integration pass may run unattended
393 auto_daemon true …and may register the OS scheduler
394 check_interval_days 2 How often the scheduler runs a pass
395 auto_hooks true …and may install the global Git hooks
396 auto_hooks_chain false …and may chain onto another tool's core.hooksPath
397 update_check true Whether the periodic release check runs
398 update_check_interval_days 7 Minimum gap between two release checks
399 update_check_timeout_secs 5 How long that one request may hang
400
401Three have a per-repository override in `.devprune.json`, where they win for that \
402tree only: `idle_days` (spelled `override_idle_days` there), `min_size_mb` and \
403`scan_depth`. The rest are deliberately global — a committed `.devprune.json` must \
404not be able to grant a repository `allow_manifest_rewrite` over its own manifests.";
405
406pub const CONFIG_GET_EXAMPLES: &str = "\
407EXAMPLES:
408 devp config get idle_days
409 devp config get update_check";
410
411pub const CONFIG_SET_LONG: &str = "\
412Change one global setting. A value outside the accepted range is rejected with the \
413range in the message, never silently clamped — `scan_depth 0` and `scan_depth 40` are \
414both refused outright. Keys are the same list `devp config get --help` shows.";
415
416pub const CONFIG_SET_EXAMPLES: &str = "\
417EXAMPLES:
418 devp config set idle_days 30
419 devp config set min_size_mb 50
420 devp config set command_timeout_secs 1200
421 devp config set update_check false Turn the release check off for good";
422
423pub const CONFIG_SHOW_LONG: &str = "\
424Print every global setting with its current value. With `--update`, also run a sync \
425pass across all registered repositories, refreshing each one's `.devprune.json` \
426scaffolding without touching values you have changed.";
427
428pub const CONFIG_SHOW_EXAMPLES: &str = "\
429EXAMPLES:
430 devp config show
431 devp config show --update";
432
433pub const CONFIG_PROJECT_LONG: &str = "\
434Inspect a repository's `.devprune.json`, or create it when missing. The file holds \
435the per-repository overrides — `override_idle_days`, `min_size_mb`, `scan_depth`, \
436`ignore`, `disable_daemon`, `disable_hooks` — and carries a `$schema` line so any \
437editor with JSON Schema support validates and completes it.
438
439`--update` refreshes the file's scaffolding (schema pointer, missing keys) while \
440keeping every value you have set. A file that does not parse is refused, not reset — \
441fix it, or pass `--update` deliberately.
442
443Writing the file also records it in the repository's `.git/info/exclude`, so the \
444config — one machine's preference, not part of the project — never shows up in \
445`git status`. The shared, tracked `.gitignore` is never modified.";
446
447pub const CONFIG_PROJECT_EXAMPLES: &str = "\
448EXAMPLES:
449 devp config project . Show (or create) this repository's config
450 devp config project ~/Code/api
451 devp config project . --update Refresh scaffolding, keep your values";
452
453pub const CONFIG_DAEMON_LONG: &str = "\
454The OS background scheduler — Task Scheduler on Windows, launchd on macOS, systemd \
455timers on Linux — which runs `devp run --daemon` every `check_interval_days` days.
456
457Globally: `enable` registers the schedule, `disable` removes it, `status` reports \
458it. With a path first, the same words act on one repository via `disable_daemon` in \
459its `.devprune.json`: the machine-wide pass keeps running, that repository sits it \
460out.";
461
462pub const CONFIG_DAEMON_EXAMPLES: &str = "\
463EXAMPLES:
464 devp daemon status Machine-wide scheduler state
465 devp daemon enable Register the schedule (also: install, on)
466 devp daemon disable Remove it (also: uninstall, remove, off)
467 devp config . daemon disable This repository opts out of the background pass
468 devp config ~/Code/api daemon enable";
469
470pub const CONFIG_HOOK_LONG: &str = "\
471The global Git hooks (via `core.hooksPath`) that auto-register any repository you \
472commit in — `devp link --quiet`, silent, honouring opt-outs. `enable` installs them, \
473`disable` removes them (restoring what was there), `status` reports them. With a \
474path first, the same words act on one repository via `disable_hooks`.
475
476Git has exactly one global `core.hooksPath` and no way to chain two, so a tool that \
477holds it (husky, pre-commit, lefthook) shuts every other one out. `--chain` is the \
478way through: dev-prune takes the slot and writes, per hook, a shim that does its own \
479work and then execs the same-named hook in the displaced directory — their hooks \
480keep firing, their exit codes still block commits. `devp hook uninstall` puts the \
481original back. The chain snapshots the other tool's hooks at install time; `devp \
482hook status` reports any that have drifted, and `devp hook install --chain` rebuilds.";
483
484pub const CONFIG_HOOK_EXAMPLES: &str = "\
485EXAMPLES:
486 devp hook status Installed? Chained? Drifted?
487 devp hook install Take the free core.hooksPath slot
488 devp hook install --chain Take a slot husky/pre-commit/lefthook holds, forwarding
489 devp hook uninstall Restore the previous core.hooksPath
490 devp config . hook disable This repository opts out of auto-registration";
491
492pub const CONFIG_ICON_LONG: &str = "\
493Register `*.devprune.json` with the OS file manager and write the icon files and the \
494JSON Schema into the config directory. On Linux this is a complete registration \
495(shared-mime-info plus hicolor icons — Nautilus, Dolphin, Thunar, Nemo, PCManFM). On \
496Windows, Explorer resolves icons by last extension only, so the config folder gets \
497its own icon instead of hijacking every `.json` on the machine. On macOS a UTI must \
498come from an application bundle, which a single binary is not.
499
500It also prints an editor snippet to paste yourself — it never edits your editor \
501settings, your PATH, or your shell startup files.";
502
503pub const CONFIG_ICON_EXAMPLES: &str = "\
504EXAMPLES:
505 devp icon Same command, without the leading `config`";
506
507pub const CONFIG_WIZARD_LONG: &str = "\
508Open every global setting in a full-screen configurator, with the `devp trust` \
509declaration in front of it: what this tool is allowed to do is on screen before any \
510of it is configurable.
511
512Arrows move; Space changes the highlighted setting — a toggle flips, a number opens \
513a field, `disabled_adapters` opens the adapter checklist; `r` puts one back; `y` \
514accepts everything as shown. The last screen lists exactly what will be written, \
515before it is written. `q` leaves without saving anything.
516
517It runs itself once on a first install — so the defaults are something you agreed \
518to, not something you inherited — and again after an upgrade adds a setting this \
519machine has never been shown, which it marks NEW and opens on. Settings you have \
520already confirmed are never re-asked.
521
522It never runs unattended: no TTY means skipped, not guessed at. `--no-tui`, and the \
523DEV_PRUNE_NO_TUI environment variable, ask one question per line instead — for \
524terminals the full-screen view cannot drive, and for agents, which hold a real \
525terminal and will never press a key. To configure this tool from a script, use \
526`devp config set <key> <value>`, which needs no terminal at all.";
527
528pub const CONFIG_WIZARD_EXAMPLES: &str = "\
529EXAMPLES:
530 devp config wizard
531 devp config wizard --no-tui One question per line
532 devp config set disabled_adapters go Leave Go projects alone entirely
533 devp config set disabled_adapters - Every adapter active again";
534
535pub const RESTORE_LONG: &str = "\
536Put dependencies back: detect each project's lockfile and run its manager's install \
537(`npm ci`, `pnpm install`, `uv sync`, `cargo fetch`, …). Mirrors pruning — every \
538project in the tree is restored, each by its own manager, so a monorepo comes back \
539whole.
540
541`--last-run` restores exactly what the most recent prune pass deleted, across every \
542repository it touched, and nothing else: the undo for a `devp run`. Each prune \
543records what it removed (a dry run records nothing), and the flag fails cleanly if \
544no pass has been recorded yet. It cannot be combined with a path — silently ignoring \
545the path would restore the wrong thing.";
546
547pub const RESTORE_EXAMPLES: &str = "\
548EXAMPLES:
549 devp restore Restore the current directory's projects
550 devp restore ~/Code/my-app
551 devp restore --last-run Undo the last prune pass, everywhere it acted";
552
553pub const UPDATE_LONG: &str = "\
554Print the installed version, ask GitHub's public API for the latest release, and \
555show the upgrade command for how this copy was installed. `--install` runs the upgrade through the \
556package manager that owns this copy (cargo, npm, uv, pipx, or the installer \
557script). `auto_update` is on by default and does the verified-download half by \
558itself at the end of a prune pass when a newer release is known — never the \
559package-manager half, and nothing at all on WinGet, Scoop and Homebrew, where the \
560manager owns the upgrade; `devp config set auto_update false` stops it. An upgrade never interrupts the \
561scheduler: the scheduled pass runs a managed copy that refreshes itself from the \
562new binary on its next run.
563
564`devp config set version_lock true` outranks all of it. While the pin is on this \
565copy stays on the version it is: `auto_update` does not run however it is set, \
566`--install` refuses, `devp install --channel` refuses because moving channels \
567installs the latest release, and the install scripts leave the binary alone. \
568No flag bypasses it -- `devp config set version_lock false` is the way back.
569
570The same check also runs quietly from `devp run` and `devp status`, at most once \
571every `update_check_interval_days` (7 by default), printing one line only when a \
572newer version exists. It is the only thing in dev-prune that opens a network \
573connection, sends no body and no identifier, and `devp config set update_check \
574false` turns it off for good; `--offline` skips it for one run without changing the \
575setting.";
576
577pub const UPDATE_EXAMPLES: &str = "\
578EXAMPLES:
579 devp update Version, latest release, upgrade command
580 devp update --install Upgrade now, through the owning channel
581 devp update --offline No network this run";
582
583pub const INSTALL_LONG: &str = "\
584Move this installation from one package manager to another.
585
586`devp update` always upgrades the copy that is running, through whichever channel \
587installed it. This command changes *which* channel owns it: it installs through the \
588manager you name, then removes the old copy through the manager that put it there — in \
589that order, so a failed install leaves the working copy untouched.
590
591Removing the old copy through its own manager, rather than deleting the file, is the \
592point: uv, pipx, npm, cargo and the rest each keep a record of what they installed, and \
593a manager whose record still says dev-prune is there will put the old binary back.
594
595Nothing is migrated, because nothing needs to be. Settings, the repository registry and \
596the undo history live in the config directory, which no package manager owns.
597
598With no `--channel` it prints which manager installed this copy and what `--channel` \
599accepts. `--dry-run` prints the commands without running any of them.";
600
601pub const INSTALL_EXAMPLES: &str = "\
602EXAMPLES:
603 devp install Which channel owns this copy
604 devp install --channel winget --dry-run Print the plan, change nothing
605 devp install --channel uv Move onto uv, and remove the old copy
606 devp install --channel cargo --yes Skip the confirmation prompt";
607
608pub const SKILL_LONG: &str = "\
609Teach your AI assistant this tool. Exports SKILL.md — the full agent-facing manual: \
610every command, the JSON contracts, the safety invariants, the troubleshooting tree — \
611into the config directory, installs it into any detected agent skills directory \
612(`~/.claude/skills/dev-prune/`, the same install `devp setup` performs), and prints \
613ready-to-copy onboarding prompts for assistants without one (Gemini Antigravity, \
614Cursor, Windsurf, Copilot, OpenClaw).
615
616`--agent <EDITOR>` instead writes per-repository rules into the current repository, in \
617the file that editor's agent reads. Ten editors get a file of their own — cursor, \
618windsurf, antigravity, cline, roo, kilocode, continue, amazon-q, kiro, trae — and six \
619share a file with other tools, so dev-prune owns a marked block inside it: agents-md \
620(`AGENTS.md`, the cross-tool convention Codex, Jules, Amp and OpenCode read), copilot \
621(`.github/copilot-instructions.md`), gemini (`GEMINI.md`), junie \
622(`.junie/guidelines.md`), zed (`.rules`) and aider (`CONVENTIONS.md`, the one file its \
623editor does not read by finding it — writing it prints the `read: CONVENTIONS.md` line \
624that makes Aider load it). Every byte outside the markers is left as found. \
625`devp skill --help` lists each value with its exact path. Claude Code needs no \
626per-repository file — its skill installs globally.";
627
628pub const SKILL_EXAMPLES: &str = "\
629EXAMPLES:
630 devp skill Export SKILL.md, print onboarding prompts
631 devp skill --agent cursor Write .cursor/rules/dev-prune.mdc here
632 devp skill --agent agents-md Upsert the marked block in AGENTS.md";
633
634pub const SETUP_LONG: &str = "\
635Install whatever integration is missing and leave the rest alone: the `devp` alias, \
636the managed binary directory on your PATH (a user PATH entry on Windows, \
637`~/.local/bin` symlinks elsewhere — what keeps `devp` working after the venv or npx \
638cache it came from disappears), the exported SKILL.md and its agent-directory \
639install, the file-manager icon registration, the global Git hooks, and the OS \
640scheduler. Safe to run repeatedly: it is the same pass the install scripts run, the \
641same one `devp init` runs, and the same one that runs by itself on the first command \
642after an upgrade.
643
644It skips rather than forces: Git hooks when `git` is missing or another tool holds \
645`core.hooksPath` (take the slot with `devp hook install --chain`), the alias when \
646the running process is `devp` itself on Windows, and anything switched off by \
647`auto_setup`, `auto_hooks`, `auto_daemon` or `DEV_PRUNE_NO_AUTO_SETUP=1`.
648
649When a VS Code-family editor is on your PATH (VS Code, VSCodium, Cursor, Windsurf, \
650Positron, Kiro, or an Insiders build) and the dev-prune extension is not installed, \
651one run also asks — once ever, only at a terminal — whether to install it into each \
652editor found. Each editor installs from its own registry; when a fork's registry does \
653not carry the extension, the `.vsix` from the latest GitHub release is installed \
654instead. Decline and it never asks again; install it yourself later with \
655`code --install-extension VKrishna04.dev-prune`.";
656
657pub const SETUP_EXAMPLES: &str = "\
658EXAMPLES:
659 devp setup Install what is missing, report what was skipped
660 devp setup --status Report only; change nothing
661 DEV_PRUNE_NO_AUTO_SETUP=1 devp init ~/Code
662 Register repositories, install nothing";
663
664pub const DOCTOR_LONG: &str = "\
665One read-only pass that answers \"why is this not doing what I expect\". Without a \
666path it checks the installation: binary and twin, PATH, registry health, every \
667stored setting revalidated, SKILL.md, icons, hooks, scheduler, the package-manager \
668binaries your repositories actually need, and the release-check state. With a path \
669it checks that repository and ends by naming the single reason a prune would or \
670would not touch it.
671
672`--fix` is diagnosis first, then treatment — and it mends installed-but-broken only: \
673a stale or missing `devp` twin, a missing SKILL.md export, hooks or a scheduler \
674whose recorded binary moved, a drifted hook chain, and registry entries whose \
675repository is gone. Each repair is the corresponding `devp setup` pass re-run, so a \
676repair can never do more than setup itself would. It never performs a first-time \
677install, and never touches an unreadable registry — a parse failure is for you to \
678look at, not for a tool to guess at.
679
680EXIT CODES: 0 when everything works, warnings included — a missing scheduler should \
681not fail a script. 1 only for genuine breakage: an unreadable registry, an \
682out-of-range setting, a dead registered path, a directory that is not a Git \
683repository. `--fix` exits 1 when any repair failed or was out of reach.";
684
685pub const DOCTOR_EXAMPLES: &str = "\
686EXAMPLES:
687 devp doctor Check the installation
688 devp doctor . Why would a prune touch (or skip) this repository?
689 devp doctor ~/Code/api
690 devp doctor --fix Repair what the installation check finds broken";
691
692pub const UNINSTALL_LONG: &str = "\
693Remove dev-prune from the machine: the OS scheduler, the global Git hooks (only if \
694`core.hooksPath` still points at dev-prune), the file-type icons, the installed \
695agent skill, the PATH entry (or `~/.local/bin` symlinks), and the binaries — the \
696managed pair, the copy you invoked, and, with your confirmation, every other copy \
697found on PATH or in the well-known install directories (`~/.cargo/bin`, \
698`~/.local/bin`, npm's global directory, pip's Scripts directories). A copy owned by \
699a package manager is listed with its manager, and after removal the manager's own \
700uninstall command is printed so its records can be cleared too.
701
702On Windows, where a running executable cannot delete itself, a detached helper \
703removes the last files a few seconds after the command exits — no reboot, no closed \
704terminal.
705
706Without `--deep` the configuration survives, so a reinstall picks up where you left \
707off. With `--deep` the global config folder and every registered repository's \
708`.devprune.json` go too; that asks for confirmation, and refuses outright with no \
709terminal to ask on unless `-y` is passed. Exits 1 if anything could not be removed, \
710naming each leftover.";
711
712pub const UNINSTALL_EXAMPLES: &str = "\
713EXAMPLES:
714 devp uninstall Remove the program; keep config for a reinstall
715 devp uninstall --deep Also wipe config and per-repo .devprune.json (asks)
716 devp uninstall --deep -y Non-interactive; also confirms the stray-copy sweep";