rsconstruct 0.9.124

Rust based fast build system
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
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
# Command Reference

## Global Flags

These flags can be used with any command:

| Flag | Description |
|------|-------------|
| `--verbose`, `-v` | Show skip/restore/cache messages during build |
| `--output-display`, `-O` | What to show for output files (`none`, `basename`, `path`; default: `none`) |
| `--input-display`, `-I` | What to show for input files (`none`, `source`, `all`; default: `source`) |
| `--path-format`, `-P` | Path format for displayed files (`basename`, `path`; default: `path`) |
| `--show-child-processes` | Print each child process command before execution |
| `--show-output` | Show tool output even on success (default: only show on failure) |
| `--json` | Output in JSON Lines format (machine-readable) |
| `--quiet`, `-q` | Suppress all output except errors (useful for CI) |
| `--phases` | Show build phase messages (discover, add_dependencies, etc.) |

Example:

```bash
rsconstruct --phases build                    # Show phase messages during build
rsconstruct --show-child-processes build      # Show each command being executed
rsconstruct --show-output build               # Show compiler/linter output even on success
rsconstruct --phases --show-child-processes build # Show both phases and commands
rsconstruct -O path build                     # Show output file paths in build messages
rsconstruct -I all build                      # Show all input files (including headers)
```

## `rsconstruct build`

**Requires config.** (no subcommands)

Incremental build — only rebuilds products whose inputs have changed.

```bash
rsconstruct build                              # Incremental build
rsconstruct build --force                      # Force full rebuild
rsconstruct build -j4                          # Build with 4 parallel jobs
rsconstruct build --dry-run                    # Show what would be built without executing (honors -p/-x/-t)
rsconstruct build --keep-going                 # Continue after errors
rsconstruct build --timings                    # Show per-product and total timing info
rsconstruct build --stop-after discover        # Stop after product discovery
rsconstruct build --stop-after add-dependencies # Stop after dependency scanning
rsconstruct build --stop-after resolve         # Stop after graph resolution
rsconstruct build --stop-after classify        # Stop after classifying products
rsconstruct build --show-output                # Show compiler/linter output even on success
rsconstruct build --auto-add-words             # Add misspelled words to .zspell-words instead of failing
rsconstruct build --auto-add-words -p processor.checker.zspell   # Run only zspell and auto-add words
rsconstruct build -p processor.checker.ruff,processor.checker.pylint               # Run only specific processors
rsconstruct build --explain                    # Show why each product is skipped/restored/rebuilt
rsconstruct build --retry 3                    # Retry failed products up to 3 times
rsconstruct build --no-mtime                   # Disable mtime pre-check, always compute checksums
rsconstruct build --no-summary                 # Suppress the build summary
rsconstruct build --batch-size 10              # Limit batch size for batch-capable processors
rsconstruct build --verify-tool-versions       # Verify tool versions against .tools.versions
rsconstruct build -t "src/*.c"                 # Only build products matching this glob pattern
rsconstruct build -d src                       # Only build products under this directory
rsconstruct build --show-all-config-changes    # Show all config changes, not just output-affecting
```

By default, tool output (compiler messages, linter output) is only shown when a command fails. Use `--show-output` to see all output.

### Selecting processors (`-p`, `-x`)

`-p` runs the named processors **and the processors that generate their
inputs**, transitively — selecting a checker of generated files also runs
the generator, so the checker has something to check even on a clean
checkout. (`-t`/`-d` do the same for the products they select.)

`-x` excludes a processor, and with it every product that needs its
outputs: those could only run against files nobody is producing. An
excluded processor is never pulled back in, even when a `-p` selection
depends on it.

### Incremental recovery and batch behavior

Batch-capable processors run their rebuilding products in a single tool invocation, with or without `--keep-going`. A batch-capable checker waits until every one of its products is ready (its files that depend on a generated file included) and then runs once over all of them; a batch-capable generator's products run as they become ready, since one of them may feed another. If the tool fails, all products in the batch are marked failed and must be rebuilt. Use `--batch-size N` to limit batch chunks and improve recovery granularity, or `--batch-size -1` to run every product on its own; successfully completed products are cached as soon as their invocation finishes, so after a failure or an interruption the next run only rebuilds what wasn't completed.

### Processor Shortcuts (`@` aliases)

The `-p` flag supports `@`-prefixed shortcuts that expand to groups of processors:

**By type:**
- `@checker` — all checker processors (ruff, pylint, shellcheck, etc.)
- `@generator` — all generator processors (tera, cc_single_file, etc.)
- `@creator` — all creator processors (pip, npm, cargo, etc.)
- `@explicit` — all explicit processors
- `@mass_generator` — all mass generator processors
- `@lua` — all Lua plugin processors

**By tool or name** — any other `@x` selects both of these:
- every processor that requires tool `x` (`@python3`, `@node`; matched
  against each processor's `required_tools()`)
- every processor whose short name is `x`, with all its instances
  (`@tera` → `processor.generator.tera` and e.g.
  `processor.generator.tera.docs`; `@generic` → the generic processor of
  every type)

An `@x` that selects nothing is reported as an unknown processor.

Examples:

```bash
rsconstruct build -p @checker              # Run only checker processors
rsconstruct build -p @generator            # Run only generator processors
rsconstruct build -p @python3               # Run all Python-based processors
rsconstruct build -p @checker,tera         # Mix shortcuts with processor names
```

The `--stop-after` flag allows stopping the build at a specific phase:
- `discover` — stop after discovering products (before dependency scanning)
- `add-dependencies` — stop after adding dependencies (before resolving graph)
- `resolve` — stop after resolving the dependency graph (before execution)
- `classify` — stop after classifying products (show skip/restore/build counts)
- `build` — run the full build (default)

## `rsconstruct clean`

Clean build artifacts. When run without a subcommand, removes build output files (same as `rsconstruct clean outputs`).

| Subcommand | Config required? |
|------------|-----------------|
| `outputs` | Yes |
| `all` | Yes |
| `git` | Yes |
| `unknown` | Yes |

```bash
rsconstruct clean                # Remove build output files (preserves cache) [default]
rsconstruct clean outputs        # Remove build output files (preserves cache)
rsconstruct clean outputs -p processor.generator.tera,processor.generator.tags    # Only clean outputs from named processors
rsconstruct clean outputs --no-empty-dirs # Skip the empty-directory sweep
rsconstruct clean all            # Remove out/ and .rsconstruct/ directories
rsconstruct clean git            # Hard clean using git clean -qffxd (requires git repository)
rsconstruct clean unknown        # Remove files not tracked by git and not known as build outputs
rsconstruct clean unknown --dry-run      # Show what would be removed without deleting
rsconstruct clean unknown --no-gitignore # Include gitignored files as unknown
```

### `rsconstruct clean outputs`

Removes build output files. The cache (`.rsconstruct/`) is preserved.

What gets removed depends on each product's processor type:

- **Checkers** (ruff, cppcheck, shellcheck, terms, …) — produce no outputs, so nothing to delete.
- **Generators** (tera, tags, mako, jinja2, mermaid, pdflatex, pdfunite, …) — declare concrete output files. Each declared output file is removed individually with `fs::remove_file`. **Directories are never recursed into.**
- **Creators** (mdbook, sphinx, jekyll, cargo, npm, gem, cc, …) — invoke an external build that produces an unknown set of files inside a declared output directory (e.g. `book/`, `_site/`, `target/`). The whole declared `output_dir` is removed recursively. This is the only path that recursively deletes a directory, and it only ever affects directories the creator itself declared.
- **Explicit** — declares both `output_files` and `output_dirs` directly in `rsconstruct.toml`. Files are removed individually; declared `output_dirs` are removed recursively (because the user asked for that).
- **Mass generators** — every file the tool's plan predicted is a declared output and is removed individually. The `output_dirs` a mass generator declares only bound where predicted files may live and are never removed.
- **Lua plugins** — call the plugin's own `clean()` function if defined; otherwise fall back to file-only removal.

#### Empty-directory sweep

After every product's `clean()` has run, `clean outputs` walks every directory that previously contained an output file (and the parents of every removed `output_dir`), bottom-up, and removes any that are now empty. The sweep:

- Tries `fs::remove_dir` (non-recursive) — only succeeds when the directory is empty.
- Walks upward through parents until it hits a non-empty directory or the project root.
- Never removes a directory that still contains files — empty dirs only.
- Does not special-case `out/`. Any path ancestor of a cleaned output is eligible.

Pass `--no-empty-dirs` to skip the sweep entirely. Files still get cleaned; empty parent directories are left in place.

#### Filtering by processor

`-p` / `--processor` accepts a comma-separated list of processor instance names (inames), e.g. `-p processor.generator.tera,processor.creator.cargo`. Only products of exactly those instances are cleaned: a named instance is its own iname, so cleaning both `[processor.generator.marp.slides]` and `[processor.generator.marp.docs]` takes `-p processor.generator.marp.slides,processor.generator.marp.docs`.

#### Other clean variants

- `clean all` — removes `out/` and `.rsconstruct/` outright (cache included). Use when you want a full reset.
- `clean git` — runs `git clean -qffxd`. Requires a git repository.
- `clean unknown` — finds files in the working tree that are neither git-tracked nor declared as a build output. Defaults to dry-run unless you pass without `--dry-run`. `--no-gitignore` includes gitignored files in the search.

## `rsconstruct status`

**Requires config.** (no subcommands)

Show product status — whether each product is up-to-date, stale, or restorable from cache.

```bash
rsconstruct status                     # Show per-processor and total summary
rsconstruct status -v                  # Show per-product status
rsconstruct status --breakdown         # Show source file counts by processor and extension
rsconstruct --json status              # Same summary as JSON
```

The per-processor table (and each entry of `--json`'s `processor` array) carries
two flags after the counts: `native`, whether the processor is pure Rust inside
rsconstruct, and `rust`, whether the code that does the work is written in Rust at
all — always so for a native processor, and for an external one only when the tool
it runs is (ruff, taplo, rumdl, clippy, cargo, mdbook, pyrefly, rustc). Processors
that run a user-supplied command (`script`, `explicit`, `generator`, `creator`,
`mass_generator`) report `rust: false`, since the language is unknown.

## `rsconstruct smart auto`

Auto-detect relevant processors and add them to `rsconstruct.toml`. Scans the project for files matching each processor's conventions and checks that the required tools are installed. Only adds new sections — existing processor sections are preserved. **Requires config.**

```bash
rsconstruct smart auto
```

Example output:

```text
Added 3 processor(s): pylint, ruff, shellcheck
```

## `rsconstruct init`

**No config needed.** (no subcommands)

Initialize a new rsconstruct project in the current directory.

```bash
rsconstruct init
```

## `rsconstruct watch`

**Requires config.** (no subcommands)

Watch source files and auto-rebuild on changes.

```bash
rsconstruct watch                              # Watch and rebuild on changes
rsconstruct watch --auto-add-words             # Watch with zspell auto-add words
rsconstruct watch -j4                          # Watch with 4 parallel jobs
rsconstruct watch -p processor.checker.ruff                      # Watch and only run the ruff processor
```

The watch command accepts the same build flags as `rsconstruct build` (e.g., `--jobs`, `--keep-going`, `--timings`, `--processor`, `--batch-size`, `--explain`, `--retry`, `--no-mtime`, `--no-summary`).

## `rsconstruct graph`

Display the build dependency graph.

| Subcommand | Config required? |
|------------|-----------------|
| `show` | Yes |
| `view` | Yes |
| `stats` | Yes |

```bash
rsconstruct graph show                    # Default SVG format
rsconstruct graph show --format dot       # Graphviz DOT format
rsconstruct graph show --format mermaid   # Mermaid format
rsconstruct graph show --format json      # JSON format
rsconstruct graph show --format text      # Plain text hierarchical view
rsconstruct graph show --format svg       # SVG format (requires Graphviz dot)
rsconstruct graph view                    # Open as SVG (default viewer)
rsconstruct graph view --viewer mermaid   # Open as HTML with Mermaid in browser
rsconstruct graph view --viewer svg       # Generate and open SVG using Graphviz dot
rsconstruct graph stats                   # Show graph statistics (products, processors, dependencies)
```

## `rsconstruct cache`

Manage the build cache.

| Subcommand | Config required? |
|------------|-----------------|
| `clear` | No |
| `size` | Yes |
| `trim` | Yes |
| `list` | Yes |
| `stale` | Yes |
| `stats` | Yes |
| `remove-stale` | Yes |

```bash
rsconstruct cache clear         # Clear the entire cache
rsconstruct cache size          # Show cache size
rsconstruct cache trim          # Remove unreferenced objects
rsconstruct cache list          # List all cache entries and their status
rsconstruct cache stale         # Show which cache entries are stale vs current
rsconstruct cache stats         # Show per-processor cache statistics
rsconstruct cache remove-stale  # Remove stale index entries not matching any current product
```

## `rsconstruct webcache`

Manage the web request cache. Schemas fetched by `iyamlschema` (and any future processors that fetch URLs) are cached in `.rsconstruct/webcache.redb`.

| Subcommand | Config required? |
|------------|-----------------|
| `clear` | No |
| `stats` | No |
| `list` | No |

```bash
rsconstruct webcache clear   # Clear all cached web responses
rsconstruct webcache stats   # Show cache size and entry count
rsconstruct webcache list    # List all cached URLs and their sizes
```

## `rsconstruct analyzer`

Manage dependency analyzers and inspect the dependency cache they fill. An analyzer scans source files for dependencies (C/C++ `#include`s, Python imports, Sass `@use`s, Markdown and Tera references) and records them in `.rsconstruct/deps.redb`; see [Dependency Analyzers](analyzers.md). Analyzers are referred to by pname (the type: `cpp`, `icpp`, `markdown`, `python`, `sass`, `tera`) or by iname (the instance as declared in `rsconstruct.toml`, e.g. `cpp` or `cpp.kernel`).

| Subcommand | Config required? |
|------------|-----------------|
| `add` | Yes (`--dry-run`: no) |
| `build` | Yes |
| `clean` | Yes |
| `config` | Yes |
| `defconfig` | No |
| `delete` | Yes |
| `disable` | Yes |
| `enable` | Yes |
| `list` | No |
| `show` | Yes |
| `stats` | Yes |
| `used` | Yes |

```bash
rsconstruct analyzer list                        # List all available analyzers
rsconstruct analyzer used                        # Show which analyzers are enabled and detected
rsconstruct analyzer defconfig cpp               # Show the default configuration of an analyzer type
rsconstruct analyzer config                      # Show the resolved configuration of every declared analyzer
rsconstruct analyzer config cpp                  # ...of one analyzer instance
rsconstruct analyzer add cpp                     # Append [analyzer.cpp] to rsconstruct.toml with comments
rsconstruct analyzer add cpp --dry-run           # Print the snippet instead of writing it
rsconstruct analyzer enable cpp                  # Set enabled = true on the stanza
rsconstruct analyzer disable cpp                 # Set enabled = false on the stanza
rsconstruct analyzer delete cpp                  # Remove the stanza entirely
rsconstruct analyzer build                       # Run dependency analysis without building
rsconstruct analyzer show all                    # Show all cached dependencies
rsconstruct analyzer show files src/main.c       # Show dependencies for a specific file
rsconstruct analyzer show files src/a.c src/b.c  # ...for several files
rsconstruct analyzer show files --hash-pieces src/main.c  # Also show each analyzer's hash pieces
rsconstruct analyzer show analyzers cpp          # Show dependencies found by the C/C++ analyzer
rsconstruct analyzer show analyzers cpp python   # ...by several analyzers
rsconstruct analyzer stats                       # Show statistics by analyzer
rsconstruct analyzer clean                       # Clear the entire dependency cache
rsconstruct analyzer clean --analyzer cpp        # Clear only entries from the cpp analyzer
```

Example output for `rsconstruct analyzer show all`:

```text
src/main.c: [icpp] (no dependencies)
src/test.c: [icpp]
  src/utils.h
  src/config.h
```

Example output for `rsconstruct analyzer stats`:

```text
╭──────────┬───────┬──────────────╮
│ Analyzer │ Files │ Dependencies │
├──────────┼───────┼──────────────┤
│ icpp     │ 2     │ 2            │
├──────────┼───────┼──────────────┤
│ Total    │ 2     │ 2            │
╰──────────┴───────┴──────────────╯
```

`show`, `stats` and `clean` read the dependency cache directly. If it is empty, run `rsconstruct analyzer build` (or a build) first to populate it.

These commands are useful for:
- Debugging why a file is being rebuilt
- Understanding the include/import structure of your project
- Verifying that dependency analyzers are finding the right files
- Clearing dependencies for a specific analyzer without affecting others

## `rsconstruct smart`

Smart config manipulation commands for managing processor sections in `rsconstruct.toml`.

| Subcommand | Config required? |
|------------|-----------------|
| `disable-all` | No |
| `enable-all` | No |
| `enable` | No |
| `disable` | No |
| `only` | No |
| `reset` | No |
| `enable-detected` | Yes |
| `enable-if-available` | Yes |
| `minimal` | Yes |
| `auto` | Yes |
| `remove-no-file-processors` | Yes |

```bash
rsconstruct smart enable pylint          # Add [processor.checker.pylint] section
rsconstruct smart disable pylint         # Remove [processor.checker.pylint] section
rsconstruct smart enable-all             # Add sections for all builtin processors
rsconstruct smart disable-all            # Remove all processor sections
rsconstruct smart enable-detected        # Add sections for auto-detected processors
rsconstruct smart enable-if-available    # Add sections for detected processors with tools installed
rsconstruct smart minimal                # Remove all, then add only detected processors
rsconstruct smart only ruff pylint       # Remove all, then add only listed processors
rsconstruct smart reset                  # Remove all processor sections
rsconstruct smart remove-no-file-processors  # Remove processors that don't match any files (skips enabled = false)
```

## `rsconstruct processor`

| Subcommand | Config required? |
|------------|-----------------|
| `add` | Yes (`--dry-run`: no) |
| `allowlist` | Yes |
| `config` | Yes |
| `defconfig` | No |
| `delete` | Yes |
| `disable` | Yes |
| `enable` | Yes |
| `files` | Yes |
| `graph` | Yes |
| `info` | No |
| `list` | No |
| `names` | Yes |
| `recommend` | No |
| `search` | No |
| `types` | No |
| `used` | Yes |

```bash
rsconstruct processor list              # List all built-in processors with type and description
rsconstruct processor list --type checker  # ...only one processor type
rsconstruct --json processor list       # Same list as JSON
rsconstruct processor info processor.checker.ruff         # Describe a processor and every config field
rsconstruct processor info              # ...every processor
rsconstruct --json processor info       # Same as JSON
rsconstruct processor types             # List the processor types
rsconstruct processor search yaml       # Search processors by name, description or keywords
rsconstruct processor recommend         # Show the recommended processor for each file extension
rsconstruct processor used              # Show which processors are enabled and detected
rsconstruct processor names             # Print the iname of every enabled processor, one per line
rsconstruct processor files             # Show source and target files for each declared processor
rsconstruct processor files processor.checker.ruff        # Show files for a specific processor
rsconstruct processor config processor.checker.ruff       # Show resolved configuration for a processor
rsconstruct processor config --diff     # Show only fields that differ from defaults
rsconstruct processor defconfig processor.checker.ruff    # Show default configuration for a processor
rsconstruct processor add processor.checker.ruff          # Append [processor.checker.ruff] to rsconstruct.toml (fields pre-populated + comments)
rsconstruct processor add processor.checker.ruff --dry-run  # Preview the snippet without writing
rsconstruct processor enable processor.checker.ruff       # Set enabled = true on the stanza
rsconstruct processor disable processor.checker.ruff      # Set enabled = false on the stanza
rsconstruct processor delete processor.checker.ruff       # Remove the stanza entirely
rsconstruct processor allowlist         # Show the current processor allowlist
rsconstruct processor graph             # Show inter-processor dependencies
rsconstruct processor graph --format dot    # Graphviz DOT format
rsconstruct processor graph --format mermaid # Mermaid format
rsconstruct processor files --headers   # Show files with processor headers
```

`list` prints one row per processor with its type and three flags: `Native`
(pure Rust inside rsconstruct, or `external` when it runs another program),
`Rust` (whether the code that does the work is written in Rust — every native
processor, plus external ones whose tool is Rust: ruff, taplo, rumdl, clippy,
cargo, mdbook, pyrefly, rustc) and `Fix` (has a built-in fixer for
`rsconstruct fix`; currently `false` for every processor, see
[`rsconstruct fix`](#rsconstruct-fix)). The JSON
form carries the same as `native`, `rust` and `fix` booleans. `rust` is what to
read when the question is "how much of this project's toolchain is Rust";
`native` is what to read when the question is "which processors need no tool
installed".

A fourth column, `Rust alternative`, names the Rust processors that can
replace a non-Rust one (`ishellcheck` for `shellcheck`, `iluacheck` and
`selene` for `luacheck`); the JSON form has them as a `rust_alternatives` array, empty
where there is none. Each Rust processor declares what it replaces in its own
file, and its alternative's docs page links it.

`recommend` maps file extensions to the processor for them. Where a Rust
processor can replace the classic recommendation, the Rust one is listed and
the classic one moves to the `Also` column (`also` in the JSON, next to
`processors`, every Rust option, and `processor`, the first of them).

`info` is the long form of one processor (or, without a name, of every
processor): its description, type, keywords, the same flags as `list` plus
batch support, the `max_jobs` cap and the implementation version, then a table
of every config field it accepts — type, default, whether it is required,
whether it is part of the cache key (`Checksum`), and what it does. It is
`defconfig -v` with the processor's own description on top, and reads only
the processors' built-in metadata, so it needs no `rsconstruct.toml`.

## `rsconstruct tool`

List or check external tools required by declared processors. All subcommands use config if available; without config, they operate on all built-in processors.

| Subcommand | Config required? |
|------------|-----------------|
| `list` | Uses config if available |
| `check` | Uses config if available |
| `lock` | Uses config if available |
| `install` | Uses config if available |
| `install-deps` | Uses config if available |
| `stats` | Uses config if available |
| `graph` | Uses config if available |

```bash
rsconstruct tool list              # List all tools known to rsconstruct (from the central registry)
rsconstruct tool list -M           # ...with every install method for each tool
rsconstruct tool list-configured   # List tools this project's processors require, and which needs them
rsconstruct tool list-configured -a   # Include tools from disabled processors
rsconstruct tool check             # Verify tool versions against .tools.versions lock file
rsconstruct tool lock              # Lock tool versions to .tools.versions
rsconstruct tool install           # Install missing external tools for enabled processors
rsconstruct tool install ruff      # Install a specific tool by name
rsconstruct tool install --all     # Install every tool in the registry, ignoring the config (no config needed)
rsconstruct tool install -i        # Ask for confirmation first (default: install without asking)
rsconstruct tool install --no-eatmydata        # Don't wrap apt/dnf/pacman with eatmydata
rsconstruct tool install-deps      # Install declared [dependencies] (pip set from uv.lock, npm set from package-lock.json by default) in fixed order: system → pip → npm → gem → cargo
rsconstruct tool install-deps -i   # Ask for confirmation first (default: install without asking)
rsconstruct tool install-deps --no-eatmydata   # Don't wrap apt/dnf/pacman with eatmydata
rsconstruct tool stats             # Show tool availability and language runtime breakdown
rsconstruct tool stats --json      # Show tool stats in JSON format
rsconstruct tool graph             # Show tool-to-processor dependency graph (DOT format)
rsconstruct tool graph --format mermaid  # Mermaid format
rsconstruct tool graph --view      # Open tool graph in browser
```

`install --all` walks the central registry instead of the project config, so it
needs no `rsconstruct.toml`. It never skips: a tool with no automatable install
method is a hard error, not a warning. This makes it the intended way to
provision CI — one command covers the whole matrix, and a registry entry that
loses its install method fails the provisioning step instead of quietly
shrinking the matrix. `--all` cannot be combined with a tool name.

### Ruby gems without a writable gem dir

Only system package managers (apt/dnf/pacman/snap) are sudo-wrapped; `pip`,
`npm`, `cargo`, and `gem` run unprivileged. With a distro ruby, that would
make `gem install` die on the root-owned system gem dir (`/var/lib/gems` on
Debian/Ubuntu), so when the default gem dir isn't writable, gem installs get
`--user-install` appended automatically. To make the resulting executables
resolvable, every rsconstruct invocation appends the user gem bin dirs that
exist (`$GEM_HOME/bin`, `~/.gem/ruby/*/bin`, `~/.local/share/gem/ruby/*/bin`)
to its own `PATH` at startup — tool probes and spawned processors see them
without any workflow- or shell-level `GEM_HOME`/`PATH` setup.

### Node.js packages from the project's `node_modules`

The same startup step prepends the project's `node_modules/.bin`, when it
exists, so the packages `install-deps` put there from `package.json` (with
`npm ci`, see `[dependencies]` in the configuration reference) resolve ahead
of any global copy. `tool install` therefore finds a locked tool already
installed and does not add a global one on top.

## `rsconstruct tag`

Search and query frontmatter tags from markdown files.

| Subcommand | Config required? |
|------------|-----------------|
| `list` | Yes |
| `count` | Yes |
| `tree` | Yes |
| `stats` | Yes |
| `files` | Yes |
| `grep` | Yes |
| `for-file` | Yes |
| `frontmatter` | Yes |
| `unused` | Yes |
| `validate` | Yes |
| `matrix` | Yes |
| `coverage` | Yes |
| `orphans` | Yes |
| `check` | Yes |
| `suggest` | Yes |
| `merge` | Yes |
| `collect` | Yes |

```bash
rsconstruct tag list                        # List all unique tags
rsconstruct tag count                       # Show each tag with file count, sorted by frequency
rsconstruct tag tree                        # Show tags grouped by prefix/category
rsconstruct tag stats                       # Show statistics about the tags database
rsconstruct tag files docker                # List files matching a tag (AND semantics)
rsconstruct tag files docker --or k8s       # List files matching any tag (OR semantics)
rsconstruct tag files level:advanced        # Match key:value tags
rsconstruct tag grep deploy                 # Search for tags containing a substring
rsconstruct tag grep deploy -i              # Case-insensitive tag search
rsconstruct tag for-file src/main.md        # List all tags for a specific file
rsconstruct tag frontmatter src/main.md     # Show raw frontmatter for a file
rsconstruct tag validate                    # Validate tags against tags_dir allowlist
rsconstruct tag unused                      # List tags in tags_dir not used by any file
rsconstruct tag unused --strict             # Exit with error if unused tags found (CI)
rsconstruct tag check                       # Run all tag validations without building
rsconstruct tag suggest src/main.md         # Suggest tags for a file based on similarity
rsconstruct tag coverage                    # Show percentage of files with each tag category
rsconstruct tag matrix                      # Show coverage matrix of tag categories per file
rsconstruct tag orphans                     # Find markdown files with no tags
rsconstruct tag merge ../other/tags         # Merge tags from another project
rsconstruct tag collect                     # Add missing tags from source files to tag collection
```

## `rsconstruct complete`

Generate shell completions. No config needed when shell is specified as argument; uses config to read default shells if no argument given.

```bash
rsconstruct complete bash    # Generate bash completions
rsconstruct complete zsh     # Generate zsh completions
rsconstruct complete fish    # Generate fish completions
```

## `rsconstruct term`

Manage term checking and fixing in markdown files.

| Subcommand | Config required? |
|------------|-----------------|
| `fix` | Yes |
| `merge` | Yes |
| `stats` | Yes |

### `rsconstruct term fix`

Add backticks around terms from the terms directory that appear unquoted in markdown files.

```bash
rsconstruct term fix
rsconstruct term fix --remove-non-terms   # also remove backticks from non-terms
```

### `rsconstruct term merge`

Merge terms from another project's terms directory. Unions matching files and copies missing files in both directions.

```bash
rsconstruct term merge ../other-project/terms
```

## `rsconstruct doctor`

**Requires config.** (no subcommands)

Diagnose build environment — checks config, tools, versions, and declared
`[dependencies]` (Python deps declared in `pyproject.toml` are checked too;
see the `[dependencies]` section in Configuration). Tools are probed by
running them; `system` dependencies are
packages, not tools, so they are probed through the platform's package manager
(dpkg-query/rpm/pacman/brew) — a binary-less package like `aspell-en` counts
as installed even though no binary carries its name.

```bash
rsconstruct doctor
```

## `rsconstruct info`

Show project information.

| Subcommand | Config required? |
|------------|-----------------|
| `source` | Yes |

```bash
rsconstruct info source          # Show source file counts by extension
```

## `rsconstruct page`

Query GitHub Pages publishing settings from the [`[pages]`](configuration.md#pages) config section.

| Subcommand | Config required? |
|------------|-----------------|
| `dir` | Yes |

```bash
rsconstruct page dir            # Print the published directory ([pages].dir)
rsconstruct --json page dir      # {"configured": true, "dir": "out/web"}
```

`page dir` prints nothing and still exits 0 when `[pages]` is not configured — CI branches on the output being empty, not on exit codes. See [GitHub Actions](github-actions.md#github-pages-deployment) for the workflow pattern this enables.

## `rsconstruct sloc`

**No config needed.** (no subcommands)

Count source lines of code (SLOC) by language, with optional COCOMO effort/cost estimation.

```bash
rsconstruct sloc                 # Show SLOC by language
rsconstruct sloc --cocomo        # Include COCOMO effort/cost estimation
rsconstruct sloc --cocomo --salary 80000  # Custom annual salary for COCOMO
```

## `rsconstruct toml`

```bash
rsconstruct toml check   # validate rsconstruct.toml (unknown fields, types, required fields)
rsconstruct toml files   # list every config file rsconstruct may read and whether it exists
```

`toml files` needs no config. It prints the merge chain lowest precedence
first — the user config (`$XDG_CONFIG_HOME/rsconstruct/config.toml`, see
[User config](configuration.md#user-config-configrsconstructconfigtoml)),
`rsconstruct.toml` and `rsconstruct.local.toml` — followed by the other
project files rsconstruct reads (`.rsconstructignore`, `.tools.versions`),
each with its resolved path and whether it is present. Use it to find out
which file a setting is coming from, or where the user config lives on a
given machine. With `--json` the same list is emitted as an array of
`{precedence, role, path, exists, note}` objects.

## `rsconstruct error`

**No config needed.** (no subcommands)

List every exit code rsconstruct can return and its name. Errors are printed as `Error [EXIT_...]: ...`, so the name in a message maps back to the code here.

```bash
rsconstruct error
```

| Code | Name |
|------|------|
| 0 | `EXIT_SUCCESS` |
| 1 | `EXIT_BUILD_ERROR` |
| 2 | `EXIT_CONFIG_ERROR` |
| 3 | `EXIT_TOOL_ERROR` |
| 4 | `EXIT_GRAPH_ERROR` |
| 5 | `EXIT_IO_ERROR` |
| 130 | `EXIT_INTERRUPTED` |

## `rsconstruct fix`

Run fixers (auto-format, auto-fix) on source files, modifying them in place.

| Subcommand | Config required? |
|------------|-----------------|
| `list` | Yes |
| `run` | Yes |

```bash
rsconstruct fix list                    # List the fix-capable processors declared in this project
rsconstruct fix run processor.checker.script.format    # Run one fixer (by full iname)
rsconstruct fix run processor.checker.script.a,processor.checker.script.b  # Run several, comma-separated
```

`fix run` requires at least one processor name. Today the only fix-capable processors are [`script`](processor/checker/script.md) instances that set `fix_command` (and optionally `fix_args`); no built-in processor declares a fixer, so the `Fix` column of `rsconstruct processor list` is `false` for all of them.

## `rsconstruct function`

**No config needed.**

Inspect the built-in functions rsconstruct exposes to Tera templates (`load_python`, `load_lua`, `load_toml`, `toml_get`, ...).

| Subcommand | Config required? |
|------------|-----------------|
| `list` | No |

```bash
rsconstruct function list   # Signature, return value, dependency tracking and an example for each function
```

Example entry:

```text
load_toml(path: string)
  Parse a TOML file and expose its top-level table to the template.
  returns:       object (key → value, nested tables preserved)
  dep tracking:  The file at `path` is added as an input (content-tracked).
  example:       {% set cfg = load_toml(path="pyproject.toml") %}{{ cfg.project.version }}
```

## `rsconstruct hook`

**No config needed.** (no subcommands)

List the post-config hooks: built-in adjustments applied to the configuration after it is loaded, such as defaults that change when running under CI (`CI=true`).

```bash
rsconstruct hook
```

```text
╭──────────────────────┬─────────────────────────────────────────────────────────────────────╮
│ Hook                 │ Description                                                         │
├──────────────────────┼─────────────────────────────────────────────────────────────────────┤
│ eatmydata_ci_default │ When CI=true, enable eatmydata wrapping for apt/dnf/pacman installs │
│ marp_ci_cap          │ When CI=true and marp.max_jobs is unset, cap it at 2                │
╰──────────────────────┴─────────────────────────────────────────────────────────────────────╯
```

## `rsconstruct product`

**Requires config.**

Inspect a single product in the build graph: everything that goes into its cache key and whether it would be rebuilt. Use it to answer "why is this file rebuilding?".

| Subcommand | Config required? |
|------------|-----------------|
| `show` | Yes |

```bash
rsconstruct product show out/processor.generator.cc_single_file/src/main.elf  # By output path
rsconstruct product show src/main.c                                            # By primary input
```

The path is looked up as an output first; if no product owns it, products that have it as their primary input are shown. Example output:

```text
processor: processor.generator.cc_single_file
outputs:
  out/processor.generator.cc_single_file/src/test.elf
inputs:
  primary src/test.c
  analyzer [icpp]
    src/utils.h
    src/config.h
cache_key: 7b1eccc2...
  config b884aa0c...
  tool 2a4d5d7d...
hash_pieces: (none)
input_checksum: 71e9982e...
descriptor_key: 8c2a5920...
cache_state: BUILD (no cache entry)
```

## `rsconstruct symlink-install`

**Requires config.** (no subcommands)

Symlink every file under one or more source folders into target folders, recreating the directory structure. Configure the folder pairs in `rsconstruct.toml`; `sources[i]` is installed into `targets[i]`, so both arrays must have the same length. A leading `~/` in a target is expanded to `$HOME`.

```toml
[command.symlink_install]
sources = ["dotfiles", "bin"]
targets = ["~/", "~/.local/bin"]
```

```bash
rsconstruct symlink-install
```

```text
dotfiles → /home/user/
  created .bashrc
  created .config/git/config
Symlink install: 2 created, 0 updated, 0 unchanged
```

Each link points to the absolute path of the source file. Running it again is safe: links that already point to the right file are left alone (`unchanged`), and links pointing elsewhere are replaced (`updated`). **A regular file at a target path is deleted and replaced by the link**, so make sure the target folders hold nothing you want to keep under the same names.

## `rsconstruct version`

**No config needed.** (no subcommands)

Print version information.

```bash
rsconstruct version
```