Skip to main content

dev_prune/
constants.rs

1// Copyright 2026 VKrishna04
2// SPDX-License-Identifier: Apache-2.0
3
4// Centralized application constants and default configurations.
5//
6// Serves as the single source of truth for app metadata, versioning,
7// default thresholds, and file paths.
8
9/// Application crate version derived dynamically from `Cargo.toml`.
10pub const VERSION: &str = env!("CARGO_PKG_VERSION");
11
12/// Minimum supported Rust version, derived dynamically from `rust-version` in
13/// `Cargo.toml` — which is the single source of truth for the MSRV. Only `cargo
14/// install` users ever meet it; every other channel ships a prebuilt binary.
15pub const MSRV: &str = env!("CARGO_PKG_RUST_VERSION");
16
17/// Application name.
18pub const APP_NAME: &str = "dev-prune";
19
20/// Author of dev-prune.
21pub const AUTHOR: &str = "VKrishna04";
22
23/// Canonical source repository.
24///
25/// The one in `Cargo.toml` is only visible to people who already found the crate. This
26/// one is compiled into the binary, so a copy of the executable still says where it came
27/// from.
28pub const REPO_URL: &str = "https://github.com/Life-Experimentalist/dev-prune";
29
30/// Project homepage.
31pub const HOMEPAGE_URL: &str = "https://devprune.vkrishna04.me";
32
33/// The one-line credit printed under interactive output.
34///
35/// Deliberately plain text in plain sight: it is not obfuscated, not assembled at
36/// runtime, and not checked anywhere. Anyone may fork this project and change this line
37/// — the Apache-2.0 licence says so, and nothing in the code argues. It exists so that
38/// the common case, someone running the published binary, shows where it came from.
39pub const ATTRIBUTION_LINE: &str =
40    "dev-prune · made with ♥ by VKrishna04 · github.com/Life-Experimentalist/dev-prune";
41
42/// The body of `devp --version`.
43///
44/// Built at runtime rather than with `concat!`, which only takes literals and would mean
45/// spelling the author and the URL a second time. Two copies of a string are two things
46/// that can disagree, and this one exists precisely so that a stray copy of the binary
47/// can still be traced back.
48pub static LONG_VERSION: std::sync::LazyLock<String> = std::sync::LazyLock::new(|| {
49    format!(
50        "{VERSION}\n\
51         author:     {AUTHOR}\n\
52         repository: {REPO_URL}\n\
53         homepage:   {HOMEPAGE_URL}\n\
54         license:    Apache-2.0"
55    )
56});
57
58/// How many prune passes `devp stats` keeps a summary of.
59///
60/// The registry is rewritten in full on every save, so this list is a file-size decision
61/// as much as a display one. Fifty passes is roughly a year of a fortnightly schedule.
62pub const PRUNE_HISTORY_LIMIT: usize = 50;
63
64/// How many restore measurements one adapter's throughput average is worth.
65///
66/// Past this, the running totals are halved before the new sample is added, so the
67/// average follows the machine rather than remembering a disk it no longer has. A plain
68/// lifetime mean would quote a spinning-rust number years after the SSD went in.
69pub const RESTORE_RATE_SAMPLE_CAP: u32 = 20;
70
71/// The shortest restore worth learning a throughput from, in milliseconds.
72///
73/// A restore that returned in under this is a manager deciding it had nothing to do —
74/// the packages were still in its cache, or already on disk. Averaging those in would
75/// claim a rate no cold restore can reach.
76pub const RESTORE_RATE_MIN_MILLIS: u64 = 250;
77
78/// The release that started recording per-repository totals and the pass history.
79///
80/// A machine that pruned for months on 1.0.0 has a large lifetime total and no history at
81/// all, and reading that as "nothing was ever pruned here" would be wrong. Both `devp
82/// stats` and its `--json` document quote this version so the gap is explained rather than
83/// looking like data loss. It is deliberately not [`VERSION`]: it names the release the
84/// format changed in, and does not move again.
85pub const HISTORY_STARTS_AT: &str = "1.1.0";
86
87/// Default idle threshold in days before a repository is eligible for pruning.
88pub const DEFAULT_IDLE_DAYS: u64 = 15;
89
90/// Default idle threshold for *build-tree* directories, in days.
91///
92/// Applies to every adapter that answers [`crate::adapters::PackageManager::opt_in`]:
93/// cargo, gradle, maven and swift. Deliberately longer than [`DEFAULT_IDLE_DAYS`],
94/// because those directories come back by recompiling the whole project rather than by
95/// re-downloading a dependency tree, so the bar for "nobody will miss this" sits
96/// higher. The engine applies `max(build_idle_days, idle_days)`.
97///
98/// Three times the dependency window and no more: 60 days was long enough that a
99/// project touched once a quarter never became a candidate at all, which is not
100/// caution, it is the feature never firing.
101pub const DEFAULT_BUILD_IDLE_DAYS: u64 = 45;
102
103/// Shown while `devp trust` and the configurator ask the OS about the scheduler and
104/// Git hooks, then overwritten in place. Kept here because its width is what the
105/// erase writes over, so the two must not be able to drift apart.
106pub const READING_MACHINE: &str = "Reading this machine (scheduler, Git hooks)...";
107
108/// Default interval in days between background daemon prune runs.
109pub const DEFAULT_CHECK_INTERVAL_DAYS: u64 = 2;
110
111/// Whether the setup pass installs the OS scheduler.
112///
113/// On. A pruner that has to be remembered is a pruner that never runs; the scheduled
114/// pass is the product, not an extra. It is still bounded by everything an interactive
115/// run is bounded by — idle threshold, lockfile verification, per-repo opt-outs — and
116/// `devp daemon uninstall` (or `devp config set auto_daemon false`) removes it.
117pub const DEFAULT_AUTO_DAEMON: bool = true;
118
119/// Whether the setup pass installs the global Git auto-registration hooks.
120///
121/// On, but conditionally: installation is skipped, not forced, when `core.hooksPath`
122/// already belongs to husky, pre-commit or lefthook.
123pub const DEFAULT_AUTO_HOOKS: bool = true;
124
125/// Whether dev-prune installs its missing integrations by itself.
126///
127/// On. The pass runs once per installed version — on first run, and again after an
128/// upgrade — and only creates what is absent. Set to `false`, or export
129/// `DEV_PRUNE_NO_AUTO_SETUP`, to manage the integrations entirely by hand.
130///
131/// The environment variable is symmetric: with it set, `devp uninstall` leaves those
132/// same hand-managed integrations — the scheduler, the agent skills, the install
133/// directories guessed from the home folder — alone too, and says so. "Entirely by
134/// hand" has to include the removal, or the variable's promise only holds until the
135/// day you uninstall.
136pub const DEFAULT_AUTO_SETUP: bool = true;
137
138/// Default for whether `link` and `init` write a `.devprune.json` into repositories
139/// they register.
140///
141/// Off: most repositories are fine on the defaults, and a config file dropped into
142/// every repo is litter. The setting exists for people who tune repositories
143/// individually often enough that creating the file by hand every time is the chore.
144pub const DEFAULT_AUTO_CONFIG: bool = false;
145
146/// Default setting for requiring interactive confirmation before pruning.
147pub const DEFAULT_REQUIRE_CONFIRMATION: bool = true;
148
149/// Default size floor, in MiB, below which a bloat directory is left alone.
150///
151/// Zero — every recognised directory is a candidate. Raising it trades a little disk
152/// space for fewer reinstalls: deleting a 3 MiB `node_modules` costs a full `npm ci`
153/// and reclaims almost nothing.
154pub const DEFAULT_MIN_SIZE_MB: u64 = 0;
155
156/// How far below a repository root project discovery descends, by default.
157///
158/// Six covers `packages/scope/name/…` monorepo layouts with room to spare while keeping
159/// the walk bounded on repositories with deep source trees. Configurable with
160/// `devp config set scan_depth`, and per repository with `"scan_depth"` in
161/// `.devprune.json`, because "deep enough" is a property of the layout, not of the tool.
162pub const DEFAULT_SCAN_DEPTH: usize = 6;
163
164/// Upper bound accepted for `scan_depth`.
165///
166/// Not a matter of taste. The walk is breadth-first over every directory that is not
167/// excluded, so cost grows with the tree, and a repository with a deep generated tree
168/// (a Bazel `bazel-out`, a `.terraform` provider cache) can turn an unbounded walk into
169/// a multi-minute stall on a background pass nobody is watching.
170pub const MAX_SCAN_DEPTH_LIMIT: usize = 32;
171
172/// Ceiling on the threads `devp status` uses to size repositories.
173///
174/// The scan is one independent file-system walk per repository, so it is bound by the
175/// disk rather than the CPU and oversubscribing the cores is what makes it fast. The
176/// ceiling exists anyway: past this point a spinning disk spends its time seeking
177/// between trees instead of reading them, and a laptop under a background pass should
178/// still be usable.
179pub const STATUS_SCAN_MAX_THREADS: usize = 32;
180
181/// Threads per reported core for the status scan. Above one because the scan waits on
182/// the disk far more than on the CPU; well below what the disk will queue, because past
183/// that point the extra threads only take turns.
184pub const STATUS_SCAN_THREADS_PER_CORE: usize = 2;
185
186/// Overrides the computed status-scan thread count. Clamped to
187/// [`STATUS_SCAN_MAX_THREADS`]; `1` forces a sequential scan.
188pub const STATUS_SCAN_THREADS_ENV: &str = "DEV_PRUNE_SCAN_THREADS";
189
190/// How many lines of a failed command's output are relayed into a report.
191///
192/// A failing `npm ci` prints its whole usage screen. Six lines is enough for the error
193/// and its cause, and short enough that the prune report around it is still readable;
194/// the rest is reachable through the log file the condensed output still names.
195pub const TOOL_OUTPUT_MAX_LINES: usize = 6;
196
197/// Directory names that mean "the contents of this are disposable".
198///
199/// Matched against a repository's *ancestors*, never against the repository directory
200/// itself: a project may legitimately be called `cache`, but a checkout sitting inside
201/// one is something a tool put there. Editor and agent plugin managers clone into
202/// directories like `~/.claude/plugins/cache/temp_git_<id>`, which is nowhere near the OS
203/// temp directory and is deleted just as fast; twenty-eight of those reached one
204/// registry before this list existed.
205pub const EPHEMERAL_ANCESTORS: &[&str] = &["cache", ".cache", "Cache", "Caches", "tmp", ".tmp"];
206
207/// Repository directory *names* that mean the same thing, wherever they are.
208///
209/// The ancestor list above only catches a throwaway clone that a tool was polite enough
210/// to put under a directory called `cache`. These prefixes catch the clone itself:
211/// `temp_git_1787245534782` is a checkout some plugin manager made, named after the
212/// millisecond it made it, and it will be gone before the next prune pass. Registering
213/// one strands a dead entry in the registry the moment the tool cleans up after itself.
214///
215/// A prefix rather than a substring, and deliberately narrow: `temporary-fixes` and
216/// `my-temp-git-notes` are real repositories somebody named badly, and refusing to track
217/// a real workspace is the worse of the two errors.
218pub const EPHEMERAL_REPO_PREFIXES: &[&str] = &["temp_git_", "tmp_git_"];
219
220/// Whether an adapter whose sync command edits tracked manifests may run it.
221///
222/// Off. `cargo generate-lockfile` re-resolves every dependency and rewrites
223/// `Cargo.lock`; `go mod tidy` edits `go.mod` and `go.sum` and can drop requirements.
224/// A cleanup tool that silently changes files Git tracks has done something the user
225/// did not ask for, so these run read-only and this switch is the informed opt-in.
226pub const DEFAULT_ALLOW_MANIFEST_REWRITE: bool = false;
227
228/// Whether the setup pass installs the Git hooks in front of another tool's.
229///
230/// Off. Chaining is behaviour-preserving — every hook is forwarded on and
231/// `devp hook uninstall` restores the original `core.hooksPath` — but it still rewires
232/// somebody else's Git configuration, which is not a thing to do unasked. Turn it on
233/// with `devp config set auto_hooks_chain true`, or do it once with
234/// `devp hook install --chain`.
235pub const DEFAULT_AUTO_HOOKS_CHAIN: bool = false;
236
237/// GitHub releases page, shown whenever an upgrade is relevant.
238pub const RELEASES_URL: &str = "https://github.com/Life-Experimentalist/dev-prune/releases";
239
240/// The shell installer, printed as an upgrade command and re-run by `devp update
241/// --install` when the running binary came from it.
242pub const INSTALL_SH_URL: &str = "https://devprune.vkrishna04.me/install.sh";
243
244/// The PowerShell installer — same two callers as [`INSTALL_SH_URL`].
245pub const INSTALL_PS1_URL: &str = "https://devprune.vkrishna04.me/install.ps1";
246
247/// GitHub API endpoint for the latest published release.
248///
249/// Contacted by `devp update`, by the interval-gated check behind `run`/`status`/`init`
250/// (off via `update_check false` or `DEV_PRUNE_OFFLINE`), and by the one-time
251/// extension-install offer's `.vsix` fallback. See the network policy in
252/// `docs/PRIVACY.md`.
253pub const LATEST_RELEASE_API_URL: &str =
254    "https://api.github.com/repos/Life-Experimentalist/dev-prune/releases/latest";
255
256/// Where a release's assets live, with `{tag}` standing in for `v1.4.0`.
257///
258/// Used by `devp update --install` to fetch the uncompressed binary and its `.sha256`
259/// sidecar. Deliberately the plain `releases/download` path rather than an API endpoint:
260/// it needs no token, is not rate-limited per-IP the way `api.github.com` is, and is the
261/// same URL a person would click on the release page.
262pub const RELEASE_DOWNLOAD_BASE: &str =
263    "https://github.com/Life-Experimentalist/dev-prune/releases/download";
264
265/// The release-asset name for one platform, without the `.sha256` suffix.
266///
267/// This is a contract with the packaging steps in `.github/workflows/release.yml`, which
268/// build these exact names. A mismatch is not a compile error and not a test failure —
269/// it is a self-update that 404s on the day of a release — so the two are commented as
270/// referring to each other.
271///
272/// `None` on a platform the release does not build for, which is how a source build on,
273/// say, FreeBSD declines the direct route instead of downloading a Linux binary.
274pub fn release_asset_name(version: &str) -> Option<String> {
275    let os = match std::env::consts::OS {
276        "windows" => "windows",
277        "macos" => "darwin",
278        "linux" => "linux",
279        _ => return None,
280    };
281    // The release publishes `x64`/`arm64`/`x86`, not Rust's target-arch spellings.
282    let arch = match std::env::consts::ARCH {
283        "x86_64" => "x64",
284        "aarch64" => "arm64",
285        "x86" => "x86",
286        _ => return None,
287    };
288    // Only Windows ships a 32-bit build; on any other OS `x86` has no asset.
289    if arch == "x86" && os != "windows" {
290        return None;
291    }
292    let ext = if os == "windows" { ".exe" } else { "" };
293    Some(format!("dev-prune-v{version}-{os}-{arch}{ext}"))
294}
295
296/// Whether the periodic release check runs. On by default — see `Settings::update_check`.
297pub const DEFAULT_UPDATE_CHECK: bool = true;
298
299/// Whether a known-newer release installs itself at the end of a prune pass. On by
300/// default — see `Settings::auto_update`.
301pub const DEFAULT_AUTO_UPDATE: bool = true;
302
303/// Default interval, in days, between automatic release checks.
304///
305/// A week. Frequent enough that a security fix is not missed for long, rare enough that
306/// it is invisible in day-to-day use. Override with
307/// `devp config set update_check_interval_days`.
308pub const UPDATE_CHECK_INTERVAL_DAYS: i64 = 7;
309
310/// Default timeout for the release check. Short on purpose — this is a convenience,
311/// and a user waiting on a hung socket is worse than not knowing. Override with
312/// `devp config set update_check_timeout_secs` when a proxy needs longer.
313pub const UPDATE_CHECK_TIMEOUT_SECS: u64 = 5;
314
315/// Timeout for downloading a release binary in `devp update --install`.
316///
317/// Far longer than [`UPDATE_CHECK_TIMEOUT_SECS`], which only reads a few hundred bytes
318/// of JSON: this pulls several megabytes, and a slow connection is not an error.
319pub const UPDATE_DOWNLOAD_TIMEOUT_SECS: u64 = 300;
320
321/// Name of the registry JSON file.
322pub const REGISTRY_FILENAME: &str = "registry.json";
323
324/// Config directory name under user config root.
325pub const CONFIG_DIR_NAME: &str = "dev-prune";
326
327/// Global environment variable name to override config directory location.
328pub const ENV_CONFIG_DIR_OVERRIDE: &str = "DEV_PRUNE_CONFIG_DIR";
329
330/// Environment variable that suppresses the automatic setup pass entirely.
331///
332/// For images, CI and anyone who wants the binary and nothing else. `devp setup` still
333/// works when it is set — this only governs the unattended pass.
334pub const ENV_NO_AUTO_SETUP: &str = "DEV_PRUNE_NO_AUTO_SETUP";
335
336/// Environment variable that keeps the process off the network entirely — the release
337/// check and the extension-download fallback alike. Set by the test suites, useful on
338/// air-gapped machines; the durable per-user switch is
339/// `devp config set update_check false`.
340pub const ENV_OFFLINE: &str = "DEV_PRUNE_OFFLINE";
341
342/// Set to any value to keep every full-screen view from opening, so the line-by-line
343/// fallback runs instead.
344///
345/// For agents and wrappers that hold a real terminal — the terminal test alone cannot
346/// tell them apart from a person, and a full-screen view waiting on a keypress from
347/// something that will never send one is a hang.
348pub const ENV_NO_TUI: &str = "DEV_PRUNE_NO_TUI";
349
350/// Windows environment variable that carries the *machine's* architecture when the
351/// running process is emulated.
352///
353/// `std::env::consts::ARCH` is baked in at compile time and only ever describes the
354/// binary. Windows sets this one under WOW64 and under ARM64 emulation, which is the
355/// only way a 32-bit build can tell that it is running on a 64-bit machine.
356/// `scripts/install.ps1` reads the same variable to choose which asset to download.
357pub const ENV_NATIVE_ARCH: &str = "PROCESSOR_ARCHITEW6432";
358
359/// Filename that, when present in a repo root, causes dev-prune to skip that repo entirely.
360///
361/// Create this file with: `touch ignore.devprune.json`
362pub const DEVPRUNE_IGNORE_FILE: &str = "ignore.devprune.json";
363
364/// Default timeout in seconds for lockfile enforcement / CLI commands (10 minutes).
365pub const DEFAULT_COMMAND_TIMEOUT_SECS: u64 = 600;
366
367/// Timeout for the "where does your cache live?" queries `devp caches` makes.
368///
369/// Deliberately not `command_timeout_secs`. That ceiling is sized for `npm ci` and
370/// `cargo metadata`; `npm config get cache` prints one line and returns. A query that
371/// has not answered in five seconds is a broken installation, and the report is better
372/// off falling back to the conventional path than waiting ten minutes for it.
373pub const CACHE_QUERY_TIMEOUT_SECS: u64 = 5;
374
375/// Ceiling for one `devp caches clear` step.
376///
377/// Ten minutes, not the five seconds a query gets: `go clean -modcache` and deleting a
378/// multi-gigabyte `~/.gradle/caches` are genuinely slow, and on Windows every file in a
379/// module cache is read-only, so the delete is a chmod-and-unlink per file. A clear that
380/// has not finished in ten minutes is wedged, and killing it leaves a partially emptied
381/// cache the manager refills on its own.
382pub const CACHE_CLEAR_TIMEOUT_SECS: u64 = 600;
383
384/// Documentation URL for troubleshooting lockfile and pruning failures.
385pub const TROUBLESHOOTING_URL: &str = "https://devprune.vkrishna04.me/docs/troubleshooting";
386/// Name of the structured per-repository configuration file stored inside repo roots.
387pub const PER_REPO_CONFIG_FILE: &str = ".devprune.json";
388
389/// Public URL for the JSON Schema used by IDEs for .devprune.json IntelliSense.
390pub const JSON_SCHEMA_URL: &str = "https://devprune.vkrishna04.me/schemas/v1/devprune.schema.json";
391
392/// Directory under the user's home that marks a Claude Code installation.
393///
394/// Its presence is how the setup pass decides the machine has an agent to install the
395/// skill for; the directory itself is only ever created by Claude Code.
396pub const CLAUDE_HOME_DIR: &str = ".claude";
397
398/// Subdirectory of an agent's home where Agent Skills live, one directory per skill.
399pub const AGENT_SKILLS_SUBDIR: &str = "skills";
400
401/// Marketplace identifier (`publisher.name`) of the VS Code extension, as understood
402/// by `code --install-extension`.
403pub const VSCODE_EXTENSION_ID: &str = "VKrishna04.dev-prune";
404
405/// The WinGet package identifier, as published in `packaging/winget/` and in the
406/// manifests submitted to microsoft/winget-pkgs. `devp update` and `devp uninstall` name
407/// it back to the user, so a typo here sends someone to a command that does not resolve.
408pub const WINGET_PACKAGE_ID: &str = "VKrishna04.dev-prune";
409
410/// The Homebrew tap that carries the formula, as `brew tap` takes it. Not homebrew-core:
411/// plain `brew install dev-prune` resolves there, and that has a notability bar this
412/// project has not cleared. See `docs/DISTRIBUTION.md`.
413pub const HOMEBREW_TAP: &str = "Life-Experimentalist/tap";
414
415/// The Scoop bucket name and the repository behind it. `scoop bucket add` takes both,
416/// and the name is what `scoop install` then resolves `dev-prune` against.
417pub const SCOOP_BUCKET_NAME: &str = "life-experimentalist";
418/// Repository URL for [`SCOOP_BUCKET_NAME`].
419pub const SCOOP_BUCKET_URL: &str = "https://github.com/Life-Experimentalist/scoop-bucket";
420
421/// Where a person can read about the extension before installing it.
422///
423/// The offer prints all three. The two registries carry the same build — the release
424/// `.vsix` — but a machine that trusts one may not have the other, and someone who
425/// wants to read the source before letting anything into their editor needs neither.
426pub const VSCODE_MARKETPLACE_URL: &str =
427    "https://marketplace.visualstudio.com/items?itemName=VKrishna04.dev-prune";
428/// The Open VSX listing, which is what VSCodium, Cursor and Windsurf resolve against.
429pub const OPENVSX_URL: &str = "https://open-vsx.org/extension/VKrishna04/dev-prune";
430
431/// Name of the Windows Task Scheduler task the daemon registers.
432pub const WINDOWS_TASK_NAME: &str = "DevPrune";
433/// File name of the windowless scheduler binary — `dev-prune.exe` with its PE subsystem
434/// set to GUI, the same relationship `pythonw.exe` has to `python.exe`. Generated
435/// locally beside the managed binary; never shipped in any archive.
436pub const WINDOWS_HIDDEN_BIN: &str = "devpw.exe";
437/// Marker file (in the config directory) recording that this machine's Task Scheduler
438/// refused the hidden (S4U) task registration, so setup keeps the visible task instead
439/// of retrying the upgrade on every pass.
440pub const SCHEDULER_HIDDEN_REFUSED_MARKER: &str = "scheduler-hidden-refused";
441
442/// Label of the macOS LaunchAgent the daemon registers (also names its plist file).
443pub const MACOS_LAUNCHD_LABEL: &str = "com.devprune.daemon";
444
445/// Where `devp skill --agent cursor` writes the per-repository rules.
446pub const CURSOR_RULES_FILE: &str = ".cursor/rules/dev-prune.mdc";
447
448/// Where `devp skill --agent windsurf` writes the per-repository rules.
449pub const WINDSURF_RULES_FILE: &str = ".windsurf/rules/dev-prune.md";
450
451/// Where `devp skill --agent antigravity` writes the per-repository rules —
452/// Antigravity (Google) reads workspace rules from `.agent/rules/`.
453pub const ANTIGRAVITY_RULES_FILE: &str = ".agent/rules/dev-prune.md";
454
455/// Where `devp skill --agent cline` writes its rules (Cline reads every file in the
456/// `.clinerules/` directory).
457pub const CLINE_RULES_FILE: &str = ".clinerules/dev-prune.md";
458
459/// Where `devp skill --agent agents-md` writes its marked block — the cross-tool
460/// convention read by Codex, Jules, Amp, Antigravity and others.
461pub const AGENTS_MD_FILE: &str = "AGENTS.md";
462
463/// Where `devp skill --agent roo` writes its rules (Roo Code reads every file in
464/// `.roo/rules/`).
465pub const ROO_RULES_FILE: &str = ".roo/rules/dev-prune.md";
466
467/// Where `devp skill --agent kilocode` writes its rules (Kilo Code reads every file in
468/// `.kilocode/rules/`).
469pub const KILOCODE_RULES_FILE: &str = ".kilocode/rules/dev-prune.md";
470
471/// Where `devp skill --agent continue` writes its rules (Continue reads every file in
472/// `.continue/rules/`).
473pub const CONTINUE_RULES_FILE: &str = ".continue/rules/dev-prune.md";
474
475/// Where `devp skill --agent amazon-q` writes its rules (Amazon Q Developer reads every
476/// file in `.amazonq/rules/`).
477pub const AMAZON_Q_RULES_FILE: &str = ".amazonq/rules/dev-prune.md";
478
479/// Where `devp skill --agent kiro` writes its rules (Kiro reads every file in
480/// `.kiro/steering/`).
481pub const KIRO_STEERING_FILE: &str = ".kiro/steering/dev-prune.md";
482
483/// Where `devp skill --agent trae` writes its rules (Trae reads every file in
484/// `.trae/rules/`).
485pub const TRAE_RULES_FILE: &str = ".trae/rules/dev-prune.md";
486
487/// The shared file `devp skill --agent junie` owns a marked block inside — JetBrains
488/// Junie reads one guidelines file, not a directory.
489pub const JUNIE_GUIDELINES_FILE: &str = ".junie/guidelines.md";
490
491/// The shared file `devp skill --agent gemini` owns a marked block inside — the Gemini
492/// CLI reads one context file per repository.
493pub const GEMINI_MD_FILE: &str = "GEMINI.md";
494
495/// The shared file `devp skill --agent zed` owns a marked block inside. Zed reads
496/// `.rules` ahead of every other convention, so a repository that also has an
497/// `AGENTS.md` still needs this one.
498pub const ZED_RULES_FILE: &str = ".rules";
499
500/// The shared file `devp skill --agent copilot` owns a marked block inside.
501pub const COPILOT_INSTRUCTIONS_FILE: &str = ".github/copilot-instructions.md";
502
503/// Markers around the block in [`COPILOT_INSTRUCTIONS_FILE`] that dev-prune manages.
504/// Everything outside them belongs to the user and is never touched.
505pub const RULES_BLOCK_START: &str = "<!-- dev-prune:rules:start -->";
506pub const RULES_BLOCK_END: &str = "<!-- dev-prune:rules:end -->";
507
508/// The phrase Git uses when it refuses a working tree owned by another account.
509///
510/// Matched against Git's own stderr rather than parsed: the message is twelve lines
511/// long, ten of which are identical for every repository refused, and `devp run`
512/// groups every repository sharing this cause under one explanation and one fix
513/// instead of reprinting those ten lines per repository.
514pub const GIT_DUBIOUS_OWNERSHIP: &str = "detected dubious ownership";
515
516/// The phrase Git uses when the path it was pointed at is not a working tree at all.
517///
518/// Same reasoning as [`GIT_DUBIOUS_OWNERSHIP`]: one cause, one fix, one paragraph.
519pub const GIT_NOT_A_REPOSITORY: &str = "not a git repository";