diff --git a/_typos.toml b/_typos.toml
index 4af054d17..4152afe98 100644
--- a/_typos.toml
+++ b/_typos.toml
@@ -4,6 +4,7 @@ extend-exclude = [
"ecosystem/**",
"scripts/**/*.in",
"crates/uv-build-frontend/src/pipreqs/mapping",
+ "crates/uv/src/commands/tool/top_packages.txt",
]
ignore-hidden = false
diff --git a/crates/uv/tests/it/show_settings.rs b/crates/uv/tests/it/show_settings.rs
index 890f693fe..b92072c05 100644
--- a/crates/uv/tests/it/show_settings.rs
+++ b/crates/uv/tests/it/show_settings.rs
@@ -7831,7 +7831,7 @@ fn preview_features() {
show_settings: true,
preview: Preview {
flags: PreviewFeatures(
- PYTHON_INSTALL_DEFAULT | PYTHON_UPGRADE | JSON_OUTPUT | PYLOCK | ADD_BOUNDS | PACKAGE_CONFLICTS | EXTRA_BUILD_DEPENDENCIES | DETECT_MODULE_CONFLICTS | FORMAT | NATIVE_AUTH | S3_ENDPOINT | CACHE_SIZE | INIT_PROJECT_FLAG | WORKSPACE_METADATA | WORKSPACE_DIR,
+ PYTHON_INSTALL_DEFAULT | PYTHON_UPGRADE | JSON_OUTPUT | PYLOCK | ADD_BOUNDS | PACKAGE_CONFLICTS | EXTRA_BUILD_DEPENDENCIES | DETECT_MODULE_CONFLICTS | FORMAT | NATIVE_AUTH | S3_ENDPOINT | CACHE_SIZE | INIT_PROJECT_FLAG | WORKSPACE_METADATA | WORKSPACE_DIR | TOOL_INSTALL_CONFIRMATION,
),
},
python_preference: Managed,
@@ -8059,7 +8059,7 @@ fn preview_features() {
show_settings: true,
preview: Preview {
flags: PreviewFeatures(
- PYTHON_INSTALL_DEFAULT | PYTHON_UPGRADE | JSON_OUTPUT | PYLOCK | ADD_BOUNDS | PACKAGE_CONFLICTS | EXTRA_BUILD_DEPENDENCIES | DETECT_MODULE_CONFLICTS | FORMAT | NATIVE_AUTH | S3_ENDPOINT | CACHE_SIZE | INIT_PROJECT_FLAG | WORKSPACE_METADATA | WORKSPACE_DIR,
+ PYTHON_INSTALL_DEFAULT | PYTHON_UPGRADE | JSON_OUTPUT | PYLOCK | ADD_BOUNDS | PACKAGE_CONFLICTS | EXTRA_BUILD_DEPENDENCIES | DETECT_MODULE_CONFLICTS | FORMAT | NATIVE_AUTH | S3_ENDPOINT | CACHE_SIZE | INIT_PROJECT_FLAG | WORKSPACE_METADATA | WORKSPACE_DIR | TOOL_INSTALL_CONFIRMATION,
),
},
python_preference: Managed,
diff --git a/docs/reference/cli.md b/docs/reference/cli.md
index 59272a6fc..fa433d004 100644
--- a/docs/reference/cli.md
+++ b/docs/reference/cli.md
@@ -2362,7 +2362,9 @@ uv tool run [OPTIONS] [COMMAND]
Can be provided multiple times.
Expects to receive either a hostname (e.g., localhost), a host-port pair (e.g., localhost:8080), or a URL (e.g., https://localhost).
WARNING: Hosts included in this list will not be verified against the system's certificate store. Only use --allow-insecure-host in a secure network with verified sources, as it bypasses SSL verification and could expose you to MITM attacks.
-May also be set with the UV_INSECURE_HOST environment variable.
--build-constraints, --build-constraint, -b build-constraintsConstrain build dependencies using the given requirements files when building source distributions.
+May also be set with the UV_INSECURE_HOST environment variable.
--approve-all-tool-installsAutomatically approve all tool installations without prompting.
+When enabled, skips confirmation prompts for installing uncached packages. This is useful for CI/CD environments or when you trust all packages.
+--build-constraints, --build-constraint, -b build-constraintsConstrain build dependencies using the given requirements files when building source distributions.
Constraints files are requirements.txt-like files that only control the version of a requirement that's installed. However, including a package in a constraints file will not trigger the installation of that package.
May also be set with the UV_BUILD_CONSTRAINT environment variable.
--cache-dir cache-dirPath to the cache directory.
Defaults to $XDG_CACHE_HOME/uv or $HOME/.cache/uv on macOS and Linux, and %LOCALAPPDATA%\uv\cache on Windows.
diff --git a/docs/reference/settings.md b/docs/reference/settings.md
index 2073d8244..c880fd081 100644
--- a/docs/reference/settings.md
+++ b/docs/reference/settings.md
@@ -2204,6 +2204,73 @@ Accepts both standalone package names (`ruff`) and version specifiers (`ruff<0.5
---
+### `install-prompt`
+
+Settings for tool install confirmation prompts.
+
+#### [`approve-all-heuristics`](#install-prompt_approve-all-heuristics) {: #install-prompt_approve-all-heuristics }
+
+
+A list of heuristics to use when deciding whether to show a confirmation prompt.
+
+Each heuristic checks a different condition. If all enabled heuristics pass (i.e., the
+package matches all checks), the prompt is skipped. Available heuristics:
+- `top-packages`: Skip prompt if package is in the top Python packages list
+- `previously-installed`: Skip prompt if package has been previously approved (not yet implemented)
+- `user-allowlist`: Skip prompt if package is in user's allowlist file (not yet implemented)
+
+Defaults to `["top-packages"]`.
+
+**Default value**: `["top-packages"]`
+
+**Type**: `list[str]`
+
+**Example usage**:
+
+=== "pyproject.toml"
+
+ ```toml
+ [tool.uv.install-prompt]
+ approve-all-heuristics = ["top-packages", "previously-installed"]
+ ```
+=== "uv.toml"
+
+ ```toml
+ [install-prompt]
+ approve-all-heuristics = ["top-packages", "previously-installed"]
+ ```
+
+---
+
+#### [`approve-all-tool-installs`](#install-prompt_approve-all-tool-installs) {: #install-prompt_approve-all-tool-installs }
+
+
+Automatically approve all tool installations without prompting.
+
+When enabled, `uvx` and `uv tool run` will skip confirmation prompts for installing
+uncached packages. This is useful for CI/CD environments or when you trust all packages.
+
+**Default value**: `false`
+
+**Type**: `bool`
+
+**Example usage**:
+
+=== "pyproject.toml"
+
+ ```toml
+ [tool.uv.install-prompt]
+ approve-all-tool-installs = true
+ ```
+=== "uv.toml"
+
+ ```toml
+ [install-prompt]
+ approve-all-tool-installs = true
+ ```
+
+---
+
### `pip`
Settings that are specific to the `uv pip` command-line interface.
diff --git a/scripts/uvx_usage_on_gh/fetch_uvx_usage.py b/scripts/uvx_usage_on_gh/fetch_uvx_usage.py
index 54e84a635..eaf26b321 100755
--- a/scripts/uvx_usage_on_gh/fetch_uvx_usage.py
+++ b/scripts/uvx_usage_on_gh/fetch_uvx_usage.py
@@ -128,7 +128,12 @@ def extract_package_name(match_text: str) -> Optional[str]:
package = package.split("@")[0]
# Validation checks
- if package.startswith("--") or "/" in package or "\\" in package or len(package) < 2:
+ if (
+ package.startswith("--")
+ or "/" in package
+ or "\\" in package
+ or len(package) < 2
+ ):
return None
return package
@@ -303,7 +308,9 @@ async def wait_for_rate_limit(rate_limit: RateLimitInfo) -> None:
await asyncio.sleep(RATE_LIMIT_DELAY)
-def build_size_query(base_query: str, start_bytes: int, end_bytes: Optional[int]) -> str:
+def build_size_query(
+ base_query: str, start_bytes: int, end_bytes: Optional[int]
+) -> str:
"""Build a GitHub Code Search query with size filter."""
if end_bytes is None:
return f"{base_query} size:>={start_bytes}"
@@ -365,6 +372,7 @@ async def check_packages_batch(
Returns:
Dictionary mapping package names to their existence status
"""
+
async def check_one(package: str) -> tuple[str, bool]:
async with semaphore:
async with httpx.AsyncClient() as client:
@@ -422,7 +430,7 @@ async def search_uvx_usage(
current_rate_limit = RateLimitInfo(None, None)
# Size buckets to work around GitHub's 1000 result limit
- # It would be way smarter to do this dynamically (query a given size range and do a
+ # It would be way smarter to do this dynamically (query a given size range and do a
# binary/proportional split on the number of results) but I already got this far
# so I'm not going to change it for now.
markdown_size_buckets = [
@@ -500,7 +508,9 @@ async def search_uvx_usage(
packages_to_check = list(set(unknown_packages_queue))
unknown_packages_queue.clear()
- logger.info(f"Checking {len(packages_to_check)} unknown packages against PyPI...")
+ logger.info(
+ f"Checking {len(packages_to_check)} unknown packages against PyPI..."
+ )
results = await check_packages_batch(packages_to_check, pypi_cache, semaphore)
# Update valid package counts based on results
@@ -509,7 +519,9 @@ async def search_uvx_usage(
count = all_package_counts.get(package, 0)
if count > 0:
valid_package_counts[package] = count
- logger.debug(f"Added {package} to valid packages ({count} occurrences)")
+ logger.debug(
+ f"Added {package} to valid packages ({count} occurrences)"
+ )
else:
logger.warning(f"Package {package} validated but has no count")
@@ -639,9 +651,7 @@ def write_top_packages(
)
# Sort by count descending, then alphabetically
- sorted_packages = sorted(
- packages.items(), key=lambda x: (-x[1], x[0])
- )
+ sorted_packages = sorted(packages.items(), key=lambda x: (-x[1], x[0]))
for package, count in sorted_packages:
f.write(f"{package}\n")
diff --git a/uv.schema.json b/uv.schema.json
index b475bbd7a..f0e18ef33 100644
--- a/uv.schema.json
+++ b/uv.schema.json
@@ -321,6 +321,16 @@
}
]
},
+ "install-prompt": {
+ "anyOf": [
+ {
+ "$ref": "#/definitions/InstallPromptOptions"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
"keyring-provider": {
"description": "Attempt to use `keyring` for authentication for index URLs.\n\nAt present, only `--keyring-provider subprocess` is supported, which configures uv to\nuse the `keyring` CLI to handle authentication.",
"anyOf": [
@@ -1137,6 +1147,37 @@
"description": "The URL of an index to use for fetching packages (e.g., `https://pypi.org/simple`), or a local path.",
"type": "string"
},
+ "InstallPromptHeuristic": {
+ "type": "string",
+ "enum": [
+ "top-packages",
+ "previously-installed",
+ "user-allowlist"
+ ]
+ },
+ "InstallPromptOptions": {
+ "description": "Settings for tool install confirmation prompts.",
+ "type": "object",
+ "properties": {
+ "approve-all-heuristics": {
+ "description": "A list of heuristics to use when deciding whether to show a confirmation prompt.\n\nEach heuristic checks a different condition. If all enabled heuristics pass (i.e., the\npackage matches all checks), the prompt is skipped. Available heuristics:\n- `top-packages`: Skip prompt if package is in the top Python packages list\n- `previously-installed`: Skip prompt if package has been previously approved (not yet implemented)\n- `user-allowlist`: Skip prompt if package is in user's allowlist file (not yet implemented)\n\nDefaults to `[\"top-packages\"]`.",
+ "type": [
+ "array",
+ "null"
+ ],
+ "items": {
+ "$ref": "#/definitions/InstallPromptHeuristic"
+ }
+ },
+ "approve-all-tool-installs": {
+ "description": "Automatically approve all tool installations without prompting.\n\nWhen enabled, `uvx` and `uv tool run` will skip confirmation prompts for installing\nuncached packages. This is useful for CI/CD environments or when you trust all packages.",
+ "type": [
+ "boolean",
+ "null"
+ ]
+ }
+ }
+ },
"KeyringProviderType": {
"description": "Keyring provider type to use for credential lookup.",
"oneOf": [