From 9e77c1e663ceba036c4007bbfb21ce707e61b933 Mon Sep 17 00:00:00 2001 From: Daniel Han Date: Mon, 13 Jul 2026 00:15:04 -0700 Subject: [PATCH] Studio: remove AGENTS.md and CLAUDE.md from install artifacts (#7096) * Studio: remove AGENTS.md from install artifacts * Studio: prune CLAUDE.md from install artifacts * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix Studio instruction cleanup edge cases * Trim Studio cleanup comments * Make Studio cleanup safe on PowerShell 5.1 * Fix Studio cleanup ownership boundaries * Simplify Windows link detection --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .../src/i18n/{AGENTS.md => README.md} | 4 +- studio/install_llama_prebuilt.py | 58 +++- studio/setup.ps1 | 38 +++ studio/setup.sh | 20 ++ .../test_install_llama_prebuilt_logic.py | 256 ++++++++++++++++++ 5 files changed, 373 insertions(+), 3 deletions(-) rename studio/frontend/src/i18n/{AGENTS.md => README.md} (94%) diff --git a/studio/frontend/src/i18n/AGENTS.md b/studio/frontend/src/i18n/README.md similarity index 94% rename from studio/frontend/src/i18n/AGENTS.md rename to studio/frontend/src/i18n/README.md index 35c025cd0b..ba8cce5d8d 100644 --- a/studio/frontend/src/i18n/AGENTS.md +++ b/studio/frontend/src/i18n/README.md @@ -1,4 +1,4 @@ -# i18n Contribution Instructions +# i18n Contribution Guide - `locales/en.ts` is the complete baseline message file. - Non-English locale files may be partial. Missing keys must fall back to English at runtime. @@ -9,4 +9,4 @@ - Keep product and technical names unchanged unless there is an established localized name, for example `Unsloth Studio`, `LoRA`, `GGUF`, and `Hugging Face`. - Keep translation changes small and reviewable. Prefer separate commits for runtime changes, UI migration, and locale text. - When adding user-facing Studio UI text, add the English message key first and add non-English overrides only when the translation is clear. -- Run `npx tsx src/i18n/check-parity.ts` before committing to ensure there are no shape mismatches or placeholder discrepancies in the non-English overlays. \ No newline at end of file +- Run `npx tsx src/i18n/check-parity.ts` before committing to ensure there are no shape mismatches or placeholder discrepancies in the non-English overlays. diff --git a/studio/install_llama_prebuilt.py b/studio/install_llama_prebuilt.py index ca1fe79efa..cf9b2cf45f 100644 --- a/studio/install_llama_prebuilt.py +++ b/studio/install_llama_prebuilt.py @@ -20,6 +20,7 @@ import re import shutil import site import socket +import stat import struct import subprocess import sys @@ -4385,6 +4386,48 @@ def copy_directory_contents(source_dir: Path, destination: Path) -> None: shutil.copy2(item, target) +def _is_link_or_junction(path: Path) -> bool: + """Return whether ``path`` redirects to another filesystem location.""" + if os.name == "nt": + try: + attributes = getattr(path.lstat(), "st_file_attributes", 0) + except OSError: + return True + return bool(attributes & stat.FILE_ATTRIBUTE_REPARSE_POINT) + try: + return path.is_symlink() + except OSError: + return True + + +def remove_agent_instruction_files(root: Path) -> int: + """Best-effort removal inside a managed tree without following links.""" + if _is_link_or_junction(root) or not root.is_dir(): + return 0 + + removed = 0 + for current_dir, dirnames, filenames in os.walk(root, topdown = True, followlinks = False): + current_path = Path(current_dir) + # followlinks=False still follows Windows junctions. + if current_path != root and _is_link_or_junction(current_path): + dirnames.clear() + continue + dirnames[:] = [ + dirname for dirname in dirnames if not _is_link_or_junction(current_path / dirname) + ] + for filename in sorted({"AGENTS.md", "CLAUDE.md"}.intersection(filenames)): + candidate = current_path / filename + try: + candidate.unlink() + except FileNotFoundError: + continue + except OSError as exc: + log(f"could not remove contributor-only instruction {candidate}: {exc}") + else: + removed += 1 + return removed + + def hydrate_source_tree( source_ref: str, install_dir: Path, @@ -4447,6 +4490,9 @@ def hydrate_source_tree( "upstream source archive was missing required repo files: " + ", ".join(missing) ) copy_directory_contents(source_root, install_dir) + removed = remove_agent_instruction_files(install_dir) + if removed: + log(f"removed {removed} contributor-only agent instruction file(s) from staged source") except PrebuiltFallback: raise except Exception as exc: @@ -6824,6 +6870,7 @@ def install_prebuilt( override_has_rocm: bool = False, override_rocm_gfx: str | None = None, force_cpu: bool = False, + instruction_cleanup_root: Path | None = None, ) -> None: host = detect_host() host = _apply_host_overrides( @@ -6836,8 +6883,15 @@ def install_prebuilt( host, published_repo, published_release_tag, force_cpu = force_cpu ) choice: AssetChoice | None = None + cleanup_root = install_dir if instruction_cleanup_root is None else instruction_cleanup_root try: with install_lock(install_lock_path(install_dir)): + if (install_dir / "UNSLOTH_PREBUILT_INFO.json").is_file(): + removed = remove_agent_instruction_files(cleanup_root) + if removed: + log( + f"removed {removed} contributor-only agent instruction file(s) from install" + ) if install_dir.exists(): log( f"existing llama.cpp install detected at {install_dir}; validating staged prebuilt update before replacement" @@ -7168,14 +7222,16 @@ def main() -> int: # Install path only: route status logs to stdout (see _LOG_TO_STDOUT note). global _LOG_TO_STDOUT _LOG_TO_STDOUT = True + install_arg = Path(args.install_dir).expanduser() install_prebuilt( - install_dir = Path(args.install_dir).expanduser().resolve(), + install_dir = install_arg.resolve(), llama_tag = args.llama_tag, published_repo = args.published_repo, published_release_tag = args.published_release_tag or "", override_has_rocm = args.has_rocm, override_rocm_gfx = args.rocm_gfx, force_cpu = args.cpu_fallback, + instruction_cleanup_root = install_arg.absolute(), ) return EXIT_SUCCESS diff --git a/studio/setup.ps1 b/studio/setup.ps1 index db01a1ecad..2c828281d2 100644 --- a/studio/setup.ps1 +++ b/studio/setup.ps1 @@ -203,6 +203,31 @@ function New-UnslothTemporaryFile { return Get-Item -LiteralPath $tempPath } +function Remove-AgentInstructionFiles { + param([string[]]$Roots) + + foreach ($root in $Roots) { + if (-not $root) { continue } + $item = Get-Item -LiteralPath $root -Force -ErrorAction SilentlyContinue + if (-not $item -or -not $item.PSIsContainer) { continue } + if ($item.Attributes -band [System.IO.FileAttributes]::ReparsePoint) { continue } + $pending = New-Object System.Collections.Stack + $pending.Push($item) + while ($pending.Count -gt 0) { + $current = $pending.Pop() + foreach ($child in @(Get-ChildItem -LiteralPath $current.FullName -Force -ErrorAction SilentlyContinue)) { + if ($child.PSIsContainer) { + if (-not ($child.Attributes -band [System.IO.FileAttributes]::ReparsePoint)) { + $pending.Push($child) + } + } elseif ($child.Name -in @("AGENTS.md", "CLAUDE.md")) { + Remove-Item -LiteralPath $child.FullName -Force -ErrorAction SilentlyContinue + } + } + } + } +} + function Get-InstalledLlamaPrebuiltRelease { param([string]$InstallDir) @@ -2314,6 +2339,8 @@ if ((Test-Path $OxcValidatorDir) -and $NodeSource -ne "skip" -and (Get-Command n substep "OXC validator runtime skipped (no npm found); code validation degrades until Node is available" "Yellow" } +Remove-AgentInstructionFiles -Roots @($FrontendDir, $OxcValidatorDir) + # ========================================================================== # PHASE 3: Python environment + dependencies # ========================================================================== @@ -3259,6 +3286,7 @@ if ($LocalLlamaCppSrc) { if ($LASTEXITCODE -ne 0) { substep "Could not create directory junction; copying instead..." "Yellow" Copy-Item -Recurse -LiteralPath $ResolvedLocal -Destination $LlamaCppDir + Remove-AgentInstructionFiles -Roots @($LlamaCppDir) } Write-Host "" step "llama.cpp" "linked local directory: $ResolvedLocal" @@ -4024,6 +4052,16 @@ if ($LocalLlamaCppLinked) { } } +$llamaCppItem = Get-Item -LiteralPath $LlamaCppDir -Force -ErrorAction SilentlyContinue +$llamaCppIsLink = $llamaCppItem -and ($llamaCppItem.Attributes -band [System.IO.FileAttributes]::ReparsePoint) +if (-not $llamaCppIsLink -and ( + -not $StudioHomeIsCustom -or + (Test-Path -LiteralPath (Join-Path $LlamaCppDir $StudioOwnedMarker) -PathType Leaf) -or + (Test-StudioOwnedAdoptable $LlamaCppDir) + )) { + Remove-AgentInstructionFiles -Roots @($LlamaCppDir) +} + # ───────────────────────────────────────────── # Footer # ───────────────────────────────────────────── diff --git a/studio/setup.sh b/studio/setup.sh index d244e3cdcf..3e81b332ad 100755 --- a/studio/setup.sh +++ b/studio/setup.sh @@ -74,6 +74,16 @@ verbose_substep() { return 0 } +_remove_agent_instruction_files() { + local _root + for _root in "$@"; do + [ -d "$_root" ] || continue + [ -L "$_root" ] && continue + find "$_root" -type f \( -name 'AGENTS.md' -o -name 'CLAUDE.md' \) \ + -exec rm -f {} + 2>/dev/null || true + done +} + # ── Corporate-mirror / proxy escape hatch for the frontend npm/bun install (#6491) ── # studio/frontend/.npmrc pins registry=https://registry.npmjs.org/ as a supply-chain # lock. A project-level pin overrides a corporate user's ~/.npmrc proxy, so the install @@ -847,6 +857,8 @@ elif [ -d "$_OXC_DIR" ] && [ "${NODE_SOURCE:-}" != skip ]; then substep "OXC validator runtime skipped (no npm found); code validation degrades until Node is available" "$C_WARN" fi +_remove_agent_instruction_files "$SCRIPT_DIR/frontend" "$_OXC_DIR" + # ── Python venv + deps ── [ -d "$REPO_ROOT/.venv" ] && rm -rf "$REPO_ROOT/.venv" @@ -1919,6 +1931,14 @@ if [ "$_LLAMA_CPP_DEGRADED" = true ] \ fi fi +if [ ! -L "$LLAMA_CPP_DIR" ] && { + [ "$_STUDIO_HOME_IS_CUSTOM" != true ] || + [ -f "$LLAMA_CPP_DIR/$_STUDIO_OWNED_MARKER" ] || + _studio_owned_adoptable "$LLAMA_CPP_DIR" +}; then + _remove_agent_instruction_files "$LLAMA_CPP_DIR" +fi + # ── Footer ── if [ "$_LLAMA_ONLY" = "1" ]; then echo "" diff --git a/tests/studio/install/test_install_llama_prebuilt_logic.py b/tests/studio/install/test_install_llama_prebuilt_logic.py index c852d7c495..e4b9c7df85 100644 --- a/tests/studio/install/test_install_llama_prebuilt_logic.py +++ b/tests/studio/install/test_install_llama_prebuilt_logic.py @@ -3,6 +3,7 @@ import importlib.util import io import json import os +import subprocess import sys import tarfile import zipfile @@ -30,6 +31,7 @@ AssetChoice = INSTALL_LLAMA_PREBUILT.AssetChoice ApprovedArtifactHash = INSTALL_LLAMA_PREBUILT.ApprovedArtifactHash ApprovedReleaseChecksums = INSTALL_LLAMA_PREBUILT.ApprovedReleaseChecksums hydrate_source_tree = INSTALL_LLAMA_PREBUILT.hydrate_source_tree +remove_agent_instruction_files = INSTALL_LLAMA_PREBUILT.remove_agent_instruction_files validate_prebuilt_choice = INSTALL_LLAMA_PREBUILT.validate_prebuilt_choice activate_install_tree = INSTALL_LLAMA_PREBUILT.activate_install_tree activate_staged_dir = INSTALL_LLAMA_PREBUILT.activate_staged_dir @@ -203,6 +205,178 @@ def test_extract_archive_rejects_zip_symlink_entry(tmp_path: Path): extract_archive(archive_path, tmp_path / "extract") +def test_remove_agent_instruction_files_does_not_follow_links(tmp_path: Path): + managed = tmp_path / "managed" + nested = managed / "nested" + external = tmp_path / "external" + nested.mkdir(parents = True) + external.mkdir() + (managed / "AGENTS.md").write_text("managed root", encoding = "utf-8") + (nested / "AGENTS.md").write_text("managed nested", encoding = "utf-8") + (managed / "CLAUDE.md").write_text("managed Claude root", encoding = "utf-8") + (nested / "CLAUDE.md").write_text("managed Claude nested", encoding = "utf-8") + (external / "AGENTS.md").write_text("user owned", encoding = "utf-8") + (external / "CLAUDE.md").write_text("user-owned Claude", encoding = "utf-8") + try: + (managed / "external-link").symlink_to(external, target_is_directory = True) + linked_root = tmp_path / "linked-root" + linked_root.symlink_to(external, target_is_directory = True) + except OSError as exc: + pytest.skip(f"directory symlinks unavailable: {exc}") + + assert remove_agent_instruction_files(managed) == 4 + assert not list(managed.rglob("AGENTS.md")) + assert not list(managed.rglob("CLAUDE.md")) + assert (external / "AGENTS.md").read_text(encoding = "utf-8") == "user owned" + assert (external / "CLAUDE.md").read_text(encoding = "utf-8") == "user-owned Claude" + + assert remove_agent_instruction_files(linked_root) == 0 + assert (external / "AGENTS.md").exists() + assert (external / "CLAUDE.md").exists() + + +@pytest.mark.skipif(os.name != "nt", reason = "Windows junction behavior") +def test_remove_agent_instruction_files_does_not_follow_windows_junctions(tmp_path: Path): + managed = tmp_path / "managed" + external = tmp_path / "external" + managed.mkdir() + external.mkdir() + (external / "AGENTS.md").write_text("user owned", encoding = "utf-8") + (external / "CLAUDE.md").write_text("user-owned Claude", encoding = "utf-8") + + nested_junction = managed / "external-junction" + root_junction = tmp_path / "linked-root" + for junction in (nested_junction, root_junction): + result = subprocess.run( + ["cmd", "/d", "/c", "mklink", "/J", str(junction), str(external)], + capture_output = True, + text = True, + check = False, + ) + if result.returncode != 0: + pytest.skip(f"directory junctions unavailable: {result.stderr or result.stdout}") + + assert remove_agent_instruction_files(managed) == 0 + assert remove_agent_instruction_files(root_junction) == 0 + assert (external / "AGENTS.md").read_text(encoding = "utf-8") == "user owned" + assert (external / "CLAUDE.md").read_text(encoding = "utf-8") == "user-owned Claude" + + +def test_remove_agent_instruction_files_prunes_linklike_directories( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +): + managed = tmp_path / "managed" + simulated_junction = managed / "simulated-junction" + simulated_junction.mkdir(parents = True) + agents = simulated_junction / "AGENTS.md" + claude = simulated_junction / "CLAUDE.md" + agents.write_text("external instructions", encoding = "utf-8") + claude.write_text("external Claude instructions", encoding = "utf-8") + real_is_link_or_junction = INSTALL_LLAMA_PREBUILT._is_link_or_junction + + monkeypatch.setattr( + INSTALL_LLAMA_PREBUILT, + "_is_link_or_junction", + lambda path: path == simulated_junction or real_is_link_or_junction(path), + ) + + assert remove_agent_instruction_files(managed) == 0 + assert agents.exists() + assert claude.exists() + + +def test_remove_agent_instruction_files_continues_after_unlink_error( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str] +): + managed = tmp_path / "managed" + managed.mkdir() + blocked = managed / "AGENTS.md" + removable = managed / "CLAUDE.md" + blocked.write_text("blocked", encoding = "utf-8") + removable.write_text("remove me", encoding = "utf-8") + real_unlink = Path.unlink + + def selective_unlink(path: Path, *args, **kwargs): + if path == blocked: + raise PermissionError(errno.EACCES, "Access is denied", str(path)) + return real_unlink(path, *args, **kwargs) + + monkeypatch.setattr(Path, "unlink", selective_unlink) + + assert remove_agent_instruction_files(managed) == 1 + assert blocked.exists() + assert not removable.exists() + captured = capsys.readouterr() + assert "could not remove contributor-only instruction" in captured.out + captured.err + + +def test_main_resolves_linked_install_path_and_preserves_cleanup_root( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +): + target = tmp_path / "target" + linked_root = tmp_path / "linked-root" + target.mkdir() + try: + linked_root.symlink_to(target, target_is_directory = True) + except OSError as exc: + pytest.skip(f"directory symlinks unavailable: {exc}") + + received = {} + monkeypatch.setattr( + sys, + "argv", + ["install_llama_prebuilt.py", "--install-dir", str(linked_root)], + ) + monkeypatch.setattr( + INSTALL_LLAMA_PREBUILT, + "install_prebuilt", + lambda **kwargs: received.update(kwargs), + ) + monkeypatch.setattr(INSTALL_LLAMA_PREBUILT, "_LOG_TO_STDOUT", False) + + assert INSTALL_LLAMA_PREBUILT.main() == 0 + assert received["install_dir"] == target.resolve() + assert received["instruction_cleanup_root"] == linked_root.absolute() + assert received["instruction_cleanup_root"].is_symlink() + + +def test_install_prebuilt_uses_explicit_instruction_cleanup_root( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +): + install_dir = tmp_path / "target" + linked_root = tmp_path / "linked-root" + install_dir.mkdir() + (install_dir / "UNSLOTH_PREBUILT_INFO.json").write_text("{}", encoding = "utf-8") + try: + linked_root.symlink_to(install_dir, target_is_directory = True) + except OSError as exc: + pytest.skip(f"directory symlinks unavailable: {exc}") + + cleanup_roots = [] + monkeypatch.setattr(INSTALL_LLAMA_PREBUILT, "detect_host", linux_host) + monkeypatch.setattr( + INSTALL_LLAMA_PREBUILT, + "remove_agent_instruction_files", + lambda root: cleanup_roots.append(root) or 0, + ) + monkeypatch.setattr( + INSTALL_LLAMA_PREBUILT, + "resolve_simple_install_release_plans", + lambda *_args, **_kwargs: (_ for _ in ()).throw(RuntimeError("stop after cleanup")), + ) + + with pytest.raises(RuntimeError, match = "stop after cleanup"): + install_prebuilt( + install_dir.resolve(), + "latest", + "unslothai/llama.cpp", + "", + instruction_cleanup_root = linked_root.absolute(), + ) + + assert cleanup_roots == [linked_root.absolute()] + + def test_hydrate_source_tree_extracts_upstream_archive_contents( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ): @@ -224,6 +398,26 @@ def test_hydrate_source_tree_extracts_upstream_archive_contents( f"llama.cpp-{upstream_tag}/gguf-py/gguf/__init__.py", b"__all__ = []\n", ) + add_bytes_to_tar( + archive, + f"llama.cpp-{upstream_tag}/AGENTS.md", + b"upstream contributor instructions\n", + ) + add_bytes_to_tar( + archive, + f"llama.cpp-{upstream_tag}/examples/AGENTS.md", + b"nested contributor instructions\n", + ) + add_bytes_to_tar( + archive, + f"llama.cpp-{upstream_tag}/CLAUDE.md", + b"Claude contributor instructions\n", + ) + add_bytes_to_tar( + archive, + f"llama.cpp-{upstream_tag}/examples/CLAUDE.md", + b"nested Claude contributor instructions\n", + ) source_urls = set(INSTALL_LLAMA_PREBUILT.upstream_source_archive_urls(upstream_tag)) @@ -244,6 +438,8 @@ def test_hydrate_source_tree_extracts_upstream_archive_contents( assert (install_dir / "convert_hf_to_gguf.py").exists() assert (install_dir / "gguf-py" / "gguf" / "__init__.py").exists() assert not (install_dir / f"llama.cpp-{upstream_tag}").exists() + assert not list(install_dir.rglob("AGENTS.md")) + assert not list(install_dir.rglob("CLAUDE.md")) def test_release_asset_download_url(): @@ -644,6 +840,29 @@ def test_activate_install_tree_restores_existing_install_after_activation_failur assert "restored previous install from rollback path" in output +def test_activate_install_tree_preserves_symlink_to_resolved_target( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +): + install_dir = tmp_path / "target" + linked_root = tmp_path / "linked-root" + staging_dir = tmp_path / "staging" + install_dir.mkdir() + staging_dir.mkdir() + (install_dir / "old.txt").write_text("old", encoding = "utf-8") + (staging_dir / "new.txt").write_text("new", encoding = "utf-8") + try: + linked_root.symlink_to(install_dir, target_is_directory = True) + except OSError as exc: + pytest.skip(f"directory symlinks unavailable: {exc}") + monkeypatch.setattr(INSTALL_LLAMA_PREBUILT, "confirm_install_tree", lambda *_args: None) + + activate_install_tree(staging_dir, linked_root.resolve(), linux_host()) + + assert linked_root.is_symlink() + assert (linked_root / "new.txt").read_text(encoding = "utf-8") == "new" + assert not (linked_root / "old.txt").exists() + + def test_activate_install_tree_cleans_all_paths_when_rollback_restore_fails( tmp_path: Path, monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str] ): @@ -2007,6 +2226,14 @@ def test_install_prebuilt_skips_download_when_existing_install_matches( approved_checksums = checksums, prebuilt_fallback_used = False, ) + (install_dir / "AGENTS.md").write_text("old root instructions", encoding = "utf-8") + nested_agents = install_dir / "examples" / "AGENTS.md" + nested_agents.parent.mkdir() + nested_agents.write_text("old nested instructions", encoding = "utf-8") + (install_dir / "CLAUDE.md").write_text("old Claude instructions", encoding = "utf-8") + (nested_agents.parent / "CLAUDE.md").write_text( + "old nested Claude instructions", encoding = "utf-8" + ) monkeypatch.setattr(INSTALL_LLAMA_PREBUILT, "detect_host", lambda: host) monkeypatch.setattr( @@ -2026,6 +2253,35 @@ def test_install_prebuilt_skips_download_when_existing_install_matches( ) install_prebuilt(install_dir, "latest", "unslothai/llama.cpp", "") + assert not list(install_dir.rglob("AGENTS.md")) + assert not list(install_dir.rglob("CLAUDE.md")) + + +def test_setup_scripts_prune_agent_files_without_shipping_a_repo_copy(): + setup_sh = (PACKAGE_ROOT / "studio" / "setup.sh").read_text(encoding = "utf-8") + setup_ps1 = (PACKAGE_ROOT / "studio" / "setup.ps1").read_text(encoding = "utf-8") + + assert '_remove_agent_instruction_files "$SCRIPT_DIR/frontend" "$_OXC_DIR"' in setup_sh + assert '_remove_agent_instruction_files "$LLAMA_CPP_DIR"' in setup_sh + assert "-name 'CLAUDE.md'" in setup_sh + assert 'if [ ! -L "$LLAMA_CPP_DIR" ] && {' in setup_sh + assert '${_LOCAL_LLAMA_CPP_LINKED:-false}" != true' not in setup_sh + assert "$LLAMA_CPP_DIR/$_STUDIO_OWNED_MARKER" in setup_sh + assert '_studio_owned_adoptable "$LLAMA_CPP_DIR"' in setup_sh + assert "Remove-AgentInstructionFiles -Roots @($FrontendDir, $OxcValidatorDir)" in setup_ps1 + assert '"CLAUDE.md"' in setup_ps1 + assert '-Include "AGENTS.md", "CLAUDE.md"' not in setup_ps1 + assert '$child.Name -in @("AGENTS.md", "CLAUDE.md")' in setup_ps1 + assert "$llamaCppIsLink" in setup_ps1 + assert "if (-not $LocalLlamaCppLinked)" not in setup_ps1 + assert "Join-Path $LlamaCppDir $StudioOwnedMarker" in setup_ps1 + assert "Test-StudioOwnedAdoptable $LlamaCppDir" in setup_ps1 + assert ( + "Copy-Item -Recurse -LiteralPath $ResolvedLocal -Destination $LlamaCppDir\n" + " Remove-AgentInstructionFiles -Roots @($LlamaCppDir)" + ) in setup_ps1 + assert not (PACKAGE_ROOT / "studio" / "frontend" / "src" / "i18n" / "AGENTS.md").exists() + assert (PACKAGE_ROOT / "studio" / "frontend" / "src" / "i18n" / "README.md").is_file() def test_install_prebuilt_does_not_skip_unhealthy_existing_install(