From ed631f788f35fdd36c9142d814291e890dcc316b Mon Sep 17 00:00:00 2001 From: Sebastian Mendel Date: Wed, 25 Feb 2026 15:19:37 +0100 Subject: [PATCH 1/3] docs: update README, add ADR-007 and ADR-008 Fix outdated README claim about missing tests (493 tests exist). Update Python version requirement to 3.14+ per pyproject.toml. Update installation section to document generic make install- pattern for all 89 cataloged tools. Document upgrade-%, uninstall-%, and reconcile-% pattern targets with fallback chain. Add ADR-007 documenting the generic tool installation architecture (catalog JSON + generic installers + install_tool.sh orchestrator). Add ADR-008 documenting the Makefile pattern target fallback chain (dedicated script -> install_tool.sh -> error). Update ADR index and catalog/README.md to reflect current architecture. Signed-off-by: Sebastian Mendel --- README.md | 45 +++--- catalog/README.md | 16 +- ...-generic-tool-installation-architecture.md | 143 ++++++++++++++++++ ...DR-008-makefile-pattern-target-fallback.md | 133 ++++++++++++++++ docs/adr/README.md | 12 +- 5 files changed, 319 insertions(+), 30 deletions(-) create mode 100644 docs/adr/ADR-007-generic-tool-installation-architecture.md create mode 100644 docs/adr/ADR-008-makefile-pattern-target-fallback.md diff --git a/README.md b/README.md index dcea53e..1b61d03 100644 --- a/README.md +++ b/README.md @@ -99,7 +99,7 @@ Example (abridged): Note: Not all of these are expected to be installed globally; the report simply surfaces what is present and how it compares upstream. ## Requirements -- Python 3.9+ +- Python 3.14+ - Network access to query GitHub/PyPI/crates.io/npm ## Quick Start @@ -451,19 +451,29 @@ If `source_kind` is `skip`, upstream lookup is disabled for that tool. python3 -m pyflakes cli_audit.py ``` -- Run tests (n/a): This repo currently ships without tests. PRs welcome. +- Run tests: +```bash +make test # Run all 493+ tests +make test-unit # Unit tests only +make test-coverage # With coverage report +make test-parallel # Parallel via pytest-xdist +``` ## Installation scripts -Language-agnostic core tools and language-specific stacks are provided under `scripts/`: +All 89 cataloged tools can be installed, upgraded, uninstalled, or reconciled via generic Make targets: ```bash -make scripts-perms +# Generic pattern targets - work for ANY cataloged tool +make install- # e.g., make install-jq, make install-semgrep +make upgrade- # e.g., make upgrade-ripgrep +make uninstall- # e.g., make uninstall-bat +make reconcile- # e.g., make reconcile-node -# Core simple tools (fd, fzf, ripgrep, jq, yq, bat, delta, just) -make install-core +# Group install by catalog tag +make install-core # Core tools (fd, fzf, ripgrep, jq, yq, bat, delta, just) -# Language stacks +# Language stacks (dedicated scripts with full lifecycle management) make install-python make install-node make install-go @@ -478,7 +488,7 @@ make install-brew make install-rust ``` -These scripts prefer the most up-to-date sources (e.g., nvm for Node, vendor installers for AWS CLI and kubectl) when feasible. +The pattern targets use a fallback chain: if a dedicated script exists (`scripts/install_.sh`), it is used; otherwise, the generic installer (`scripts/install_tool.sh`) reads the tool's catalog JSON entry and delegates to the appropriate method-specific installer. This means adding support for a new tool only requires creating a `catalog/.json` file. ### Role-focused quick checks (local-only) @@ -520,20 +530,19 @@ The audit attempts to identify how a tool was installed by inspecting the resolv When ambiguous, the audit may report a generic bucket (e.g., `~/.local/bin`). The JSON output includes `installed_path_resolved` and `classification_reason` to aid debugging. -### Actions: install, update, uninstall, reconcile +### Actions: install, upgrade, uninstall, reconcile -All scripts accept an action argument. Defaults to `install`. +All pattern targets (`install-%`, `upgrade-%`, `uninstall-%`, `reconcile-%`) work for any of the 89 cataloged tools. They use a three-step fallback: dedicated script, then generic installer, then error. ```bash -# Update existing toolchains -make update-core -make update-python -make update-node -make update-go -make update-aws - -# Uninstall +# Upgrade any tool +make upgrade-fd +make upgrade-python +make upgrade-node + +# Uninstall any tool make uninstall-node +make uninstall-semgrep # Reconcile preferred method # Example: remove distro Node and switch to nvm-managed diff --git a/catalog/README.md b/catalog/README.md index b8770d5..149d894 100644 --- a/catalog/README.md +++ b/catalog/README.md @@ -68,9 +68,11 @@ Install tools via system package managers (apt, brew, dnf, pacman). ## Adding a New Tool 1. Create `catalog/.json` with appropriate metadata -2. The tool will automatically use the generic installer for its method +2. The tool will automatically be available via `make install-`, `make upgrade-`, `make uninstall-`, and `make reconcile-` 3. No need to create a custom install script! +Currently **89 tools** are cataloged. + ## Environment Variables - `INSTALL_STRATEGY`: Where to install tools (USER, GLOBAL, CURRENT, PROJECT) @@ -92,14 +94,8 @@ INSTALL_STRATEGY=GLOBAL scripts/install_tool.sh terraform make upgrade ``` -## Migration Status - -Tools with catalog entries use the new system. Tools without catalog entries fall back to legacy `install_core.sh`. +## Architecture -**Migrated:** -- kubectl -- terraform -- aws -- semgrep +All 89 tools have catalog entries. The generic installer (`scripts/install_tool.sh`) reads a tool's catalog JSON and delegates to the appropriate method-specific installer under `scripts/installers/`. Tools with complex installation needs (python, node, docker, rust, etc.) use `install_method: "dedicated_script"` to route to their existing bespoke scripts. -**To migrate:** Add catalog entries for remaining tools from `install_core.sh` +See [ADR-007](../docs/adr/ADR-007-generic-tool-installation-architecture.md) for the full architectural decision record. diff --git a/docs/adr/ADR-007-generic-tool-installation-architecture.md b/docs/adr/ADR-007-generic-tool-installation-architecture.md new file mode 100644 index 0000000..8ba0909 --- /dev/null +++ b/docs/adr/ADR-007-generic-tool-installation-architecture.md @@ -0,0 +1,143 @@ +# ADR-007: Generic Tool Installation Architecture + +**Status:** Accepted +**Date:** 2026-02-25 +**Deciders:** AI CLI Preparation Team +**Tags:** installation, catalog, generic-installer, architecture + +## Context + +The project originally required a dedicated shell script (`scripts/install_.sh`) for each tool it managed. As the catalog grew to 89+ tools, this approach did not scale: + +- Each new tool required writing a bespoke install script, even for tools that follow identical installation patterns (e.g., downloading a GitHub release binary, or `uv tool install `). +- Only ~15 tools had dedicated scripts. The remaining 75+ catalog-only tools could not be installed, upgraded, or uninstalled via Make targets. +- Duplicated logic across scripts (downloading, extracting, verifying, installing to PATH) was difficult to maintain. + +**Problem:** How do we support install/upgrade/uninstall for all 89+ cataloged tools without writing and maintaining a dedicated script for each one? + +## Decision + +Implement a **three-tier generic installation architecture**: + +### Tier 1: Catalog JSON entries + +Each tool is described by a `catalog/.json` file containing metadata and installation parameters: + +```json +{ + "name": "fd", + "install_method": "github_release_binary", + "binary_name": "fd", + "github_repo": "sharkdp/fd", + "download_url_template": "https://github.com/sharkdp/fd/releases/download/v{version}/fd-v{version}-{arch}-unknown-linux-gnu.tar.gz", + "tags": ["core", "search"] +} +``` + +The `install_method` field determines which generic installer handles the tool. + +### Tier 2: Method-specific installers + +Generic installer scripts under `scripts/installers/` implement each installation method: + +| Installer | Method | Example tools | +|-----------|--------|---------------| +| `github_release_binary.sh` | Download binary from GitHub releases | fd, ripgrep, bat, delta | +| `hashicorp_zip.sh` | Download from releases.hashicorp.com | terraform | +| `aws_installer.sh` | AWS official installer | aws | +| `uv_tool.sh` | `uv tool install` for Python packages | semgrep, ruff, black | +| `npm_global.sh` | `npm install -g` | eslint, prettier | +| `package_manager.sh` | System package managers (apt/brew/dnf) | sponge, pipx | +| `dedicated_script.sh` | Delegate to a tool-specific script | python, node, docker | + +### Tier 3: Orchestrator + +`scripts/install_tool.sh` is the central orchestrator that: + +1. Reads `catalog/.json` to determine the install method +2. Validates the catalog entry has required fields +3. Delegates to the appropriate method-specific installer in `scripts/installers/` +4. Handles universal concerns (uninstall detection, status reporting) + +For tools with `install_method: "auto"`, the orchestrator uses the reconciliation system (`scripts/lib/reconcile.sh`) to detect existing installations and choose the best approach. + +## Rationale + +### Why a catalog-driven approach? + +- **Declarative over imperative**: Tool metadata is data, not code. Adding a tool is a JSON file, not a shell script. +- **Consistent behavior**: All tools using the same method share the same installer logic (download, extract, verify, install). +- **Testable**: Catalog entries can be validated programmatically. Installers can be tested independently of individual tools. + +### Why keep dedicated scripts? + +Some tools have genuinely complex installation requirements that cannot be captured in a simple JSON entry: + +- **Python** (`install_python.sh`): Manages uv installation, Python version selection, virtual environments +- **Node** (`install_node.sh`): Manages nvm, node version selection, corepack +- **Docker** (`install_docker.sh`): Repository setup, GPG keys, daemon configuration +- **Rust** (`install_rust.sh`): Rustup installer, toolchain management + +These tools use `install_method: "dedicated_script"` in their catalog entry, which routes to the existing script. + +## Consequences + +### Positive + +- **Scalability**: Adding a new tool only requires creating a `catalog/.json` file +- **Consistency**: All tools of the same method type behave identically +- **Reduced maintenance**: Bug fixes in a method installer benefit all tools using that method +- **Complete coverage**: All 89 cataloged tools now support install, upgrade, uninstall, and reconcile operations + +### Negative + +- **Abstraction cost**: Some tools may need special handling that does not fit neatly into a generic installer +- **jq dependency**: The orchestrator requires `jq` to parse catalog JSON files + +### Neutral + +- **Coexistence**: Both dedicated scripts and generic installers coexist; the Makefile pattern targets handle the fallback transparently +- **Migration path**: Tools can start with a dedicated script and migrate to catalog-only as patterns stabilize, or vice versa + +## Implementation Notes + +### Directory structure + +``` +catalog/ + fd.json + ripgrep.json + semgrep.json + ... # 89 tool definitions +scripts/ + install_tool.sh # Orchestrator + install_python.sh # Dedicated script (complex tools) + install_node.sh + ... + installers/ + github_release_binary.sh + hashicorp_zip.sh + uv_tool.sh + ... # 10 method-specific installers +``` + +### Adding a new tool + +1. Create `catalog/.json` with `name`, `install_method`, and method-specific fields +2. Optionally add `tags` for group installation (`make install-core`, etc.) +3. The tool is immediately available via `make install-`, `make upgrade-`, etc. + +## References + +- **[ADR-008](ADR-008-makefile-pattern-target-fallback.md)** - Makefile pattern target fallback chain +- **[ADR-001](ADR-001-context-aware-installation.md)** - Context-aware installation modes +- **[ADR-002](ADR-002-package-manager-hierarchy.md)** - Package manager preference hierarchy +- **[catalog/README.md](../../catalog/README.md)** - Catalog documentation + +--- + +**Revision History:** + +| Version | Date | Changes | +|---------|------|---------| +| 1.0 | 2026-02-25 | Initial decision accepted | diff --git a/docs/adr/ADR-008-makefile-pattern-target-fallback.md b/docs/adr/ADR-008-makefile-pattern-target-fallback.md new file mode 100644 index 0000000..f3063bb --- /dev/null +++ b/docs/adr/ADR-008-makefile-pattern-target-fallback.md @@ -0,0 +1,133 @@ +# ADR-008: Makefile Pattern Target Fallback Chain + +**Status:** Accepted +**Date:** 2026-02-25 +**Deciders:** AI CLI Preparation Team +**Tags:** makefile, installation, pattern-targets, fallback + +## Context + +The project uses GNU Make as the primary user interface. Historically, tool installation targets were either: + +1. **Explicit targets** (e.g., `install-python`, `install-node`) with hardcoded commands +2. **Pattern targets** (e.g., `install-%`) that expanded to `./scripts/install_$*.sh` + +The pattern target approach only worked for the ~15 tools that had dedicated install scripts. Running `make install-jq` or `make install-semgrep` would fail because no `scripts/install_jq.sh` or `scripts/install_semgrep.sh` existed, even though these tools had full catalog entries with all metadata needed for generic installation. + +This meant 75+ catalog-only tools could not be managed via Make, despite having complete installation metadata in `catalog/*.json`. + +**Problem:** How do we make `make install-`, `make upgrade-`, `make uninstall-`, and `make reconcile-` work for all 89 cataloged tools, while preserving backward compatibility with existing dedicated scripts? + +## Decision + +All four pattern targets (`install-%`, `upgrade-%`, `uninstall-%`, `reconcile-%`) use a **three-step fallback chain**: + +```makefile +install-%: scripts-perms + @if [ -f "./scripts/install_$*.sh" ]; then \ + ./scripts/install_$*.sh; \ + elif [ -f "./catalog/$*.json" ]; then \ + ./scripts/install_tool.sh "$*" install; \ + else \ + echo "Error: No installer found for '$*'" >&2; exit 1; \ + fi +``` + +### Fallback steps + +1. **Dedicated script** (`./scripts/install_$*.sh`): If a tool-specific script exists, use it. This preserves existing behavior for complex tools like python, node, docker, rust, etc. + +2. **Catalog entry** (`./catalog/$*.json`): If no dedicated script exists but a catalog entry does, delegate to the generic installer `scripts/install_tool.sh`, which reads the catalog metadata and routes to the appropriate method-specific installer. + +3. **Error with helpful message**: If neither exists, print an error. This catches typos and requests for unknown tools. + +### Explicit targets take precedence + +GNU Make resolves explicit targets before pattern targets. Important tools retain their explicit targets for clarity in `make help` output and to allow tool-specific Make-level options: + +```makefile +install-python: scripts-perms ## Install Python toolchain via uv + ./scripts/install_python.sh + +install-node: scripts-perms ## Install Node.js via nvm + ./scripts/install_node.sh + +# Generic fallback for everything else +install-%: scripts-perms + @if [ -f "./scripts/install_$*.sh" ]; then ... +``` + +## Rationale + +### Why a fallback chain (not just catalog-only)? + +- **Backward compatibility**: Existing dedicated scripts continue to work unchanged +- **Graceful migration**: Tools can be migrated from dedicated scripts to catalog-only entries at any pace +- **Complex tools need scripts**: Python, Node, Docker, and Rust installations involve multi-step logic (version managers, repository setup, GPG keys) that does not fit in a JSON catalog entry + +### Why check dedicated script first? + +- Dedicated scripts may implement tool-specific logic beyond what the generic installer supports +- When both a dedicated script and a catalog entry exist, the script is authoritative +- This matches user expectations: if someone writes `install_foo.sh`, they expect it to be used + +### Why not use Make's `$(wildcard)` or `$(shell)` for detection? + +These are evaluated at Makefile parse time, not at target execution time. The `if [ -f ... ]` check in the recipe ensures the detection happens when the target is actually invoked, reflecting the current filesystem state. + +## Consequences + +### Positive + +- **Complete coverage**: All 89 cataloged tools now work with `make install-`, `make upgrade-`, `make uninstall-`, and `make reconcile-` +- **Backward compatible**: No changes needed to existing dedicated scripts or explicit targets +- **Discoverable**: `make help` shows both explicit targets (with descriptions) and the pattern target +- **Consistent error handling**: Unknown tools get a clear error message + +### Negative + +- **Implicit behavior**: Users may not realize the fallback chain exists; `make install-jq` working "magically" could be surprising +- **Debugging**: When something fails, users need to understand whether the dedicated script or the generic installer was invoked + +### Neutral + +- **Dual maintenance**: Some tools have both a dedicated script and a catalog entry (the script takes precedence for Make targets, the catalog entry is used by `install_tool.sh` and the audit system) + +## Implementation Notes + +### Affected targets in `Makefile.d/user.mk` + +| Pattern | Action passed to script | +|---------|------------------------| +| `install-%` | `install` (or no argument for dedicated scripts) | +| `upgrade-%` | `update` | +| `uninstall-%` | `uninstall` | +| `reconcile-%` | `reconcile` | + +### Testing the fallback + +```bash +# Tool with dedicated script (uses install_python.sh) +make install-python + +# Tool with catalog entry only (uses install_tool.sh → github_release_binary.sh) +make install-fd + +# Unknown tool (error) +make install-nonexistent +# Error: No installer found for 'nonexistent' +``` + +## References + +- **[ADR-007](ADR-007-generic-tool-installation-architecture.md)** - Generic tool installation architecture +- **[Makefile.d/user.mk](../../Makefile.d/user.mk)** - Pattern target definitions +- **[scripts/install_tool.sh](../../scripts/install_tool.sh)** - Generic installer orchestrator + +--- + +**Revision History:** + +| Version | Date | Changes | +|---------|------|---------| +| 1.0 | 2026-02-25 | Initial decision accepted | diff --git a/docs/adr/README.md b/docs/adr/README.md index 5baab3b..a807fbd 100644 --- a/docs/adr/README.md +++ b/docs/adr/README.md @@ -102,6 +102,8 @@ ADRs are **immutable** once accepted. To change a decision: | [004](ADR-004-always-latest-version-policy.md) | Always-Latest Version Policy | Accepted | 2025-10-09 | versioning, upgrades | | [005](ADR-005-environment-detection.md) | Environment Detection Logic | Accepted | 2025-10-09 | environment-detection, context-aware | | [006](ADR-006-configuration-file-format.md) | Configuration File Format | Accepted | 2025-10-09 | configuration, yaml | +| [007](ADR-007-generic-tool-installation-architecture.md) | Generic Tool Installation Architecture | Accepted | 2026-02-25 | installation, catalog, generic-installer | +| [008](ADR-008-makefile-pattern-target-fallback.md) | Makefile Pattern Target Fallback Chain | Accepted | 2026-02-25 | makefile, installation, pattern-targets | --- @@ -113,6 +115,8 @@ ADRs are **immutable** once accepted. To change a decision: - ADR-001: Context-Aware Installation Modes - ADR-002: Package Manager Preference Hierarchy - ADR-003: Parallel Installation Approach +- ADR-007: Generic Tool Installation Architecture +- ADR-008: Makefile Pattern Target Fallback Chain **Version Management:** - ADR-004: Always-Latest Version Policy @@ -123,12 +127,16 @@ ADRs are **immutable** once accepted. To change a decision: ### By Tag -**installation:** ADR-001, ADR-002, ADR-003 +**installation:** ADR-001, ADR-002, ADR-003, ADR-007, ADR-008 **environment-detection:** ADR-001, ADR-005 **versioning:** ADR-004 **configuration:** ADR-006 **reconciliation:** ADR-003 **package-managers:** ADR-002 +**catalog:** ADR-007 +**makefile:** ADR-008 +**pattern-targets:** ADR-008 +**generic-installer:** ADR-007 --- @@ -169,5 +177,5 @@ ADRs are **immutable** once accepted. To change a decision: --- -**Last Updated:** 2025-10-09 +**Last Updated:** 2026-02-25 **Maintainer:** AI CLI Preparation Team From 3aae7cb0d3f820ceb1550badae40b2f263f5940b Mon Sep 17 00:00:00 2001 From: Sebastian Mendel Date: Wed, 25 Feb 2026 15:28:46 +0100 Subject: [PATCH 2/3] docs: address review feedback for README and ADRs - Fix test count from 493+ to 490+ in README - Fix fd.json example in ADR-007 to use shfmt (actual github_release_binary user) - Add missing installers to ADR-007 Tier 2 table (docker_plugin, github_clone, npm_self_update) - Document auto/reconciliation install method in ADR-007 - Add upgrade/update naming note to ADR-008 Signed-off-by: Sebastian Mendel --- README.md | 2 +- ...-generic-tool-installation-architecture.md | 19 ++++++++++++++----- ...DR-008-makefile-pattern-target-fallback.md | 2 ++ 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 1b61d03..8d29d29 100644 --- a/README.md +++ b/README.md @@ -453,7 +453,7 @@ python3 -m pyflakes cli_audit.py - Run tests: ```bash -make test # Run all 493+ tests +make test # Run all 490+ tests make test-unit # Unit tests only make test-coverage # With coverage report make test-parallel # Parallel via pytest-xdist diff --git a/docs/adr/ADR-007-generic-tool-installation-architecture.md b/docs/adr/ADR-007-generic-tool-installation-architecture.md index 8ba0909..bdf00a7 100644 --- a/docs/adr/ADR-007-generic-tool-installation-architecture.md +++ b/docs/adr/ADR-007-generic-tool-installation-architecture.md @@ -25,12 +25,16 @@ Each tool is described by a `catalog/.json` file containing metadata and i ```json { - "name": "fd", + "name": "shfmt", "install_method": "github_release_binary", - "binary_name": "fd", - "github_repo": "sharkdp/fd", - "download_url_template": "https://github.com/sharkdp/fd/releases/download/v{version}/fd-v{version}-{arch}-unknown-linux-gnu.tar.gz", - "tags": ["core", "search"] + "binary_name": "shfmt", + "github_repo": "mvdan/sh", + "download_url_template": "https://github.com/mvdan/sh/releases/download/{version}/shfmt_{version}_linux_{arch}", + "arch_map": { + "x86_64": "amd64", + "aarch64": "arm64", + "armv7l": "arm" + } } ``` @@ -48,8 +52,13 @@ Generic installer scripts under `scripts/installers/` implement each installatio | `uv_tool.sh` | `uv tool install` for Python packages | semgrep, ruff, black | | `npm_global.sh` | `npm install -g` | eslint, prettier | | `package_manager.sh` | System package managers (apt/brew/dnf) | sponge, pipx | +| `docker_plugin.sh` | Docker plugins | compose | +| `github_clone.sh` | Clone and build from source | rbenv, ruby-build | +| `npm_self_update.sh` | NPM self-update | npm | | `dedicated_script.sh` | Delegate to a tool-specific script | python, node, docker | +**Note on `auto` install method:** 11 tools (including fd, ripgrep, bat, and hyperfine) use `"install_method": "auto"` instead of specifying a fixed installer. The `auto` method uses the reconciliation system (`scripts/lib/reconcile.sh`) to detect existing installations and choose the best available method from the tool's `available_methods` list in the catalog. This allows the system to adapt to the user's environment (e.g., preferring a GitHub binary release over cargo over apt, depending on what is already installed and available). + ### Tier 3: Orchestrator `scripts/install_tool.sh` is the central orchestrator that: diff --git a/docs/adr/ADR-008-makefile-pattern-target-fallback.md b/docs/adr/ADR-008-makefile-pattern-target-fallback.md index f3063bb..4f85fe1 100644 --- a/docs/adr/ADR-008-makefile-pattern-target-fallback.md +++ b/docs/adr/ADR-008-makefile-pattern-target-fallback.md @@ -104,6 +104,8 @@ These are evaluated at Makefile parse time, not at target execution time. The `i | `uninstall-%` | `uninstall` | | `reconcile-%` | `reconcile` | +**Note on upgrade/update naming:** The user-facing Make target is `upgrade-%`, but it passes `update` as the action parameter to the underlying scripts. This is for historical compatibility -- the installer scripts originally used `update` as the action name. The Makefile uses the more intuitive `upgrade` terminology for the user interface. + ### Testing the fallback ```bash From 122418af5b44bb11c4ebdd834eed1c0b4357c1d9 Mon Sep 17 00:00:00 2001 From: Sebastian Mendel Date: Wed, 25 Feb 2026 15:35:02 +0100 Subject: [PATCH 3/3] docs: note planned go_install method in ADR-007 Signed-off-by: Sebastian Mendel --- docs/adr/ADR-007-generic-tool-installation-architecture.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/adr/ADR-007-generic-tool-installation-architecture.md b/docs/adr/ADR-007-generic-tool-installation-architecture.md index bdf00a7..1467ea1 100644 --- a/docs/adr/ADR-007-generic-tool-installation-architecture.md +++ b/docs/adr/ADR-007-generic-tool-installation-architecture.md @@ -56,6 +56,7 @@ Generic installer scripts under `scripts/installers/` implement each installatio | `github_clone.sh` | Clone and build from source | rbenv, ruby-build | | `npm_self_update.sh` | NPM self-update | npm | | `dedicated_script.sh` | Delegate to a tool-specific script | python, node, docker | +| *(planned)* `go_install.sh` | Install via `go install` | templ | **Note on `auto` install method:** 11 tools (including fd, ripgrep, bat, and hyperfine) use `"install_method": "auto"` instead of specifying a fixed installer. The `auto` method uses the reconciliation system (`scripts/lib/reconcile.sh`) to detect existing installations and choose the best available method from the tool's `available_methods` list in the catalog. This allows the system to adapt to the user's environment (e.g., preferring a GitHub binary release over cargo over apt, depending on what is already installed and available).