Package optimization: per-platform fields, tags, validator + interactive picker#52
Merged
Merged
Conversation
Let priority/optional/environment accept a per-platform object (like install_command already did), resolved via new prfor/optfor/envfor jq defs in lib/core.sh; envok gains a $plat arg. Proven backward-compatible: old vs new engine produce byte-identical dry-run output on all four platforms. Merge the 7 duplicate entries that only existed to vary these fields per platform (git, git-xet, hf, llama.cpp, discord, notion, octave) into single entries. Add a required 'tags' category array (18-tag controlled vocabulary, metadata only) to all 97 entries. Add scripts/validate-packages.sh (jq) enforcing the platform vocabulary, the tag set, environment shape, and the no-silent-drop rule (every targeted platform must resolve a valid priority tier + boolean optional). Wired into pre-commit and CI (lint.yml). Also migrate poetry -> uv (uv bumped to high priority; poetry removed) across packages.json, README.md, and the macOS docs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Bare 'setup.sh' on a TTY (no selection flag) now prompts the user to choose which categories to install; base essentials are always included. Adds a tagok() filter to the engine selection (CORE_JQ_DEFS) that reads TAG_FILTER_ACTIVE/SELECTED_TAGS from the environment — base (high-priority packages) and enabled work/personal apps bypass the filter, everything else must match a selected tag. New flags: --base (base only) and --tags <csv> (base + named categories) for non-interactive use. The prompt is skipped on the server profile and in non-interactive/CI runs; the filter is inactive by default so all flag-driven and CI runs are byte-identical (smoke gate unchanged: 79/31/19/39). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
On a fresh Ubuntu desktop (no curl preinstalled) the nvm step — which pipes the curl installer into a shell via eval at the top of the Linux flow — failed under 'set -euo pipefail' and aborted the whole run before rust_toolchain_step and set_default_shell could execute. That is why the Rust default toolchain and the zsh login-shell switch never happened either; both steps run later in the flow. apt_bootstrap now installs ca-certificates plus any missing curl/wget/jq right after 'apt update', before any install script or apt-repo key fetch runs. wget is bootstrapped too since the eza/obs repo setup needs it. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…tion
Adversarial review of the branch surfaced these (all medium/low):
validator (scripts/validate-packages.sh):
- detect duplicate package names (group_by .name) — this caught a real one:
jq was still two entries, so merge it like the other per-platform duplicates
(macOS brew low/optional, apt/yay medium/required). 97 -> 96 entries.
- guard keys-walking on object type so a non-object/missing package_manager
yields the clean diagnostic instead of crashing jq and masking other errors
- flag empty {} package_manager (targets zero platforms)
- flag missing/empty name
- assert the document top-level is a JSON array
selection (lib/core.sh, setup.sh):
- --tags warns on tokens matching no category and on an empty result instead
of silently degrading to base-only
- apt_bootstrap prints its intent under --dry-run (command -v probes the host,
so the bootstrap was invisible in dry-run/smoke)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ction These handled_by_setup custom packages ran as unconditional steps, so --base and --tags installed them regardless of the picker (e.g. --tags development still pulled in tailscale + claude-code). Add a pkg_selected predicate that mirrors the tier/envok/tagok selection and guard each step with it: --base -> none of the three --tags networking -> tailscale; --tags ai-coding -> claude-code --tags containers --optional -> docker tailscale/claude-code gate inside their step functions; the desktop docker call gates at its (already --optional) call site. The server profile keeps the filter inactive, so all three still install there (server docker stays unconditional, since docker has no server entry in packages.json and that path is essential). Addresses the low-severity tag-bypass findings from the branch review. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
platform_docker_optional hardcoded the same get.docker.com command that docker's packages.json install_command already held, leaving the field dead and misleading. Add the missing "server":"custom" key (the server installs docker but had no entry for it) and read the command via custom_cmd docker, mirroring tailscale/pyenv/nvm/claude-code. The post-install group step stays in code since no install_command can express it. Resolves the docker dead-data follow-up. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ulises-c
marked this pull request as ready for review
June 24, 2026 09:48
The tag-vocabulary jq query was duplicated in core_prompt_selection and core_validate_tag_selection; extract a single core_tag_vocabulary helper both call. Cosmetic DRY cleanup from the max-effort review — no behavior change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Package optimization
Cleans up
packages.json, hardens its schema, and adds an interactive category picker on top — plus fresh-install and review-driven fixes. 12 files, ~+510/−130,packages.jsondown to 96 entries (8 duplicate pairs merged).1. Per-platform field forms + merged duplicates
priority,optional, andenvironmentnow accept a scalar or a per-platform object (likeinstall_commandalready did), resolved via newprfor/optfor/envforjq defs. This lets one entry serve platforms that differ in tier/optionality/gating, so the duplicate entries that only existed for that reason are merged:git,git-xet,hf,llama.cpp,discord,notion,octave, andjq.2. Category
tags+ schema validatortagsarray (18-tag controlled vocabulary, metadata only).scripts/validate-packages.sh(jq) enforces the platform vocabulary, the tag set, environment shape, and the no-silent-drop rule — every platform a package targets must resolve a valid priority tier and a boolean optional. Also catches duplicate names, non-object/empty/missingpackage_manager, missingname, and non-array documents without crashing jq. Wired into pre-commit and CI.3. Interactive category picker
Running
setup.shbare on a TTY (no selection flag) now prompts the user to choose what to install; base essentials always install, picked categories and enabled work/personal apps layer on top.--base(base only) and--tags <csv>(base + named categories), with warnings on unknown/empty tags.tagok()filter that readsTAG_FILTER_ACTIVE/SELECTED_TAGSfrom the env — inactive by default, so all flag-driven and CI runs are unchanged. Prompt is skipped on the server profile and in non-interactive/CI runs.tailscale,claude-code,docker) gate on apkg_selectedpredicate so they honor the selection too; the server profile keeps the filter inactive, so they install as before there.4. Fresh-install fix
On a fresh Ubuntu desktop, the nvm installer (piped curl) ran before
curlwas installed and aborted the whole run underset -euo pipefail— which also prevented the later Rust-default and zsh-shell-switch steps from executing.apt_bootstrapnow installsca-certificates+ any missingcurl/wget/jqright afterapt update, before any install script or apt-repo key fetch.5. docker as source of truth
platform_docker_optionalhardcoded the same command docker'sinstall_commandalready held (dead data). Added the missingserverentry and routed install throughcustom_cmd docker, matching tailscale/pyenv/nvm/claude-code; post-install group step stays in code.Also
poetry→uv(uv bumped to high priority; poetry removed) acrosspackages.json,README.md, and the macOS docs.Review & verification
/code-review): zero confirmed correctness defects; all findings either fixed or verified as intended behavior. The duplicate-name validator check caught the un-mergedjqentry during review.Worth a live check before relying on it
Dry-run on this host can't fully exercise the fresh-Ubuntu path (host already has curl/docker). Best validated on a VM/container: the curl bootstrap and the now-reachable Rust-default + zsh-shell-switch, and the picker + docker install on a machine without docker preinstalled.
🤖 Generated with Claude Code