Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ When installer runs, `resolve_tool_selection(args, registry)` performs:
5. Walk soft `recommends` -- silently drop skipped/unknown.
6. Filter by current platform (`linux`/`macos`/`windows`).

Group expansion (`expand_groups`) recursive, cycle-detected. Synthetic groups expand at runtime (not stored in `packages.json`): `@shared` -> every non-group, **non-`optional`** package whose `kind != env` (everything for a shared/read-only tree); `@envs` -> its complement (every `kind == env` config bundle). `all` is also accepted as a name and expands to every non-group, non-`optional` package. **`optional: true` is the only gating field** (there is no `default` field): packages so flagged are skipped by the `all` / `@shared` / `@engineering-loadout` sweeps and install only when named explicitly or pulled by a group that lists them in `members` -- e.g. `surfer` (name-only) and the `@rust` trio (`rust`, `rust-crate-store`, `env-cargo`, reachable via `@rust`). Tools and env config bundles are **fully decoupled** -- no cross-`recommends` in either direction (a tool never recommends its env bundle, an env bundle never recommends its tool) -- so `./loadout install @shared --dest-dir ...` (shared tree) and `./loadout install @envs` (per-user config) are clean complements needing no `--skip`/`--no-deps`. Both synthetic groups surface in `list --groups` and `describe <@group>` (driven by `_SYNTHETIC_GROUPS`) despite not being registry entries.
Group expansion (`expand_groups`) recursive, cycle-detected. Synthetic groups expand at runtime (not stored in `packages.json`): `@shared` -> every non-group, **non-`optional`** package whose `kind != env` (everything for a shared/read-only tree); `@shared-all` -> the same but with optionals folded back in (every non-group `kind != env` package regardless of `optional` -- the full shared tree incl. `surfer`/`cicwave`/`rust`/`rust-crate-store` in one name); `@envs` -> the complement of `@shared` (every `kind == env` config bundle). `all` is also accepted as a name and expands to every non-group, non-`optional` package. **`optional: true` is the only gating field** (there is no `default` field): packages so flagged are skipped by the `all` / `@shared` / `@engineering-loadout` sweeps and install only when named explicitly, via `@shared-all`, or pulled by a group that lists them in `members` -- e.g. `surfer` (name-only) and the `@rust` trio (`rust`, `rust-crate-store`, `env-cargo`, reachable via `@rust`; note `env-cargo` is `kind == env` so it stays on the `@envs` side, NOT in `@shared-all`). Tools and env config bundles are **fully decoupled** -- no cross-`recommends` in either direction (a tool never recommends its env bundle, an env bundle never recommends its tool) -- so `./loadout install @shared --dest-dir ...` (shared tree) and `./loadout install @envs` (per-user config) are clean complements needing no `--skip`/`--no-deps`. All three synthetic groups surface in `list --groups` and `describe <@group>` (driven by `_SYNTHETIC_GROUPS`) despite not being registry entries.

### Subcommands

Expand Down
2 changes: 1 addition & 1 deletion envs/bash/global/completions/loadout.bash
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ _loadout_complete() {

local verbs="install reinstall upgrade update list search info describe resolve doctor snapshot clean completion"
local pkgs="agent-deck amux bash bash-docs bat biome broot btm btop bzip2 choose cicwave clang cloc dasel delta duf dust env-autohotkey env-bash env-cargo env-editorconfig env-helix env-nvim env-pip env-powershell env-starship env-tmux env-vim env-wezterm env-zsh expect eza fd fio firefox fish flameshot font-cascadiacode font-dejavusansmono font-envycoder font-firacode font-hack font-inconsolata font-iosevkaterm font-jetbrainsmono font-meslolg font-robotomono font-sourcecodepro font-ubuntumono fresh fzf glow gnu-coreutils gnuplot gobject-typelibs gocheat gopls gping gui_libs gvim htop hx hyperfine jq jupyterlab just keyb lazygit lefdef-tools liberty-filter liberty-tools llvm-bolt lua-language-server mate-terminal meld mesa3d_libs micro miller modules ncdu nedit-ng ngspice ninja nodejs numr nvim nvim-catalog-plugins nvim-qt octave p7zip patchelf pdftotext pigz portable-python procs pv pygwalker pyright rg rsync ruff rust rust-analyzer rust-crate-store scc sd shellcheck shfmt st starship stylua surfer tcl text-serdes time-plot tk tkdiff tldr tldr-data tmux tmux-path-store tokei tree-sitter treesitter-parsers ty urxvt uv vcd-toggle-profiler vim visidata wezterm xsel xterm yank yara yazi yq zellij zoxide zsh"
local groups="@all-environments @compression @core-cli @core-environments @data-bundles @dev-tools @editor-cli @editor-gui @editor-suite @engineering-loadout @envs @fonts-all @fonts-default @gui-suite @python-stack @python-tools-extra @rust @scientific @security @shared @shells-optional @system-monitor all"
local groups="@all-environments @compression @core-cli @core-environments @data-bundles @dev-tools @editor-cli @editor-gui @editor-suite @engineering-loadout @envs @fonts-all @fonts-default @gui-suite @python-stack @python-tools-extra @rust @scientific @security @shared @shared-all @shells-optional @system-monitor all"
local tags="agent automation benchmark browser build cache clipboard compat compiler compress core data def dev diff disk docs document eda editor elf env file font format git gobject gui hotkeys hpc javascript lang lef liberty library lint llvm lsp lua markdown math monitor multiplexer nav network notebook nvim opengl optimizer parser pkg plot profiler prompt python reference runtime rust scientific screenshot scripting search security shell simulator spice sync system terminal text tmux toolchain tui vcd viewer visualization waveform"

# Option that takes an argument -> complete the argument, not a new word.
Expand Down
11 changes: 9 additions & 2 deletions loadout_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@ def _parse_namelist(raw):
# `list --groups` and `describe`.
_SYNTHETIC_GROUPS = {
"@shared": "Every non-env, non-optional package -- install set for a shared/read-only tree.",
"@shared-all": "@shared plus every non-env optional package (surfer, cicwave, rust, ...).",
"@envs": "Every per-user env config bundle (the complement of @shared).",
}

Expand All @@ -252,8 +253,10 @@ def expand_groups(names, registry, _stack=None):
`--only @shared --skip @envs` to keep env recommends out of a shared tree.
`all` (and the @shared / @engineering-loadout sweeps that build on it) skip
packages flagged "optional": true -- those install only when named explicitly
or pulled in by a group that lists them (e.g. surfer, the @rust trio). Cycles
in the group graph raise ResolverError.
or pulled in by a group that lists them (e.g. surfer, the @rust trio).
@shared-all is @shared with those optionals folded back in (every non-env
package regardless of "optional") -- the full shared/read-only tree in one
name. Cycles in the group graph raise ResolverError.
Unknown package names produce a warning and are dropped.
"""
if _stack is None:
Expand All @@ -270,6 +273,10 @@ def expand_groups(names, registry, _stack=None):
if not n.startswith("@") and e.get("kind") != "env" and not e.get("optional")
}
continue
if name == "@shared-all":
# @shared but optionals INCLUDED -- the full shared/read-only tree.
out |= {n for n, e in registry.items() if not n.startswith("@") and e.get("kind") != "env"}
continue
if name == "@envs":
out |= {n for n, e in registry.items() if not n.startswith("@") and e.get("kind") == "env"}
continue
Expand Down
Loading