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