code-aide 1.15.6__tar.gz → 1.15.7__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {code_aide-1.15.6 → code_aide-1.15.7}/PKG-INFO +1 -1
- {code_aide-1.15.6 → code_aide-1.15.7}/src/code_aide/__init__.py +1 -1
- {code_aide-1.15.6 → code_aide-1.15.7}/src/code_aide/commands_actions.py +5 -2
- {code_aide-1.15.6 → code_aide-1.15.7}/src/code_aide/install.py +49 -13
- {code_aide-1.15.6 → code_aide-1.15.7}/src/code_aide/prereqs.py +9 -14
- {code_aide-1.15.6 → code_aide-1.15.7}/tests/test_commands_actions.py +34 -1
- {code_aide-1.15.6 → code_aide-1.15.7}/tests/test_install.py +16 -0
- {code_aide-1.15.6 → code_aide-1.15.7}/tests/test_prereqs.py +3 -9
- {code_aide-1.15.6 → code_aide-1.15.7}/.github/workflows/ci.yml +0 -0
- {code_aide-1.15.6 → code_aide-1.15.7}/.github/workflows/publish.yml +0 -0
- {code_aide-1.15.6 → code_aide-1.15.7}/.gitignore +0 -0
- {code_aide-1.15.6 → code_aide-1.15.7}/.gitlab-ci.yml +0 -0
- {code_aide-1.15.6 → code_aide-1.15.7}/.pre-commit-config.yaml +0 -0
- {code_aide-1.15.6 → code_aide-1.15.7}/AGENTS.md +0 -0
- {code_aide-1.15.6 → code_aide-1.15.7}/CLAUDE.md +0 -0
- {code_aide-1.15.6 → code_aide-1.15.7}/LICENSE +0 -0
- {code_aide-1.15.6 → code_aide-1.15.7}/README.md +0 -0
- {code_aide-1.15.6 → code_aide-1.15.7}/TODO.md +0 -0
- {code_aide-1.15.6 → code_aide-1.15.7}/pyproject.toml +0 -0
- {code_aide-1.15.6 → code_aide-1.15.7}/script-archive/README.md +0 -0
- {code_aide-1.15.6 → code_aide-1.15.7}/script-archive/amp-install.sh +0 -0
- {code_aide-1.15.6 → code_aide-1.15.7}/script-archive/claude-install.sh +0 -0
- {code_aide-1.15.6 → code_aide-1.15.7}/script-archive/cursor-install.sh +0 -0
- {code_aide-1.15.6 → code_aide-1.15.7}/specs/2026-05-03-qwen3.6-code-quality-analysis-original.md +0 -0
- {code_aide-1.15.6 → code_aide-1.15.7}/specs/2026-05-03-qwen3.6-code-quality-analysis.md +0 -0
- {code_aide-1.15.6 → code_aide-1.15.7}/specs/auto-migrate-deprecated-installs.md +0 -0
- {code_aide-1.15.6 → code_aide-1.15.7}/specs/claude-native-installer-migration.md +0 -0
- {code_aide-1.15.6 → code_aide-1.15.7}/specs/missing-coding-llm-cli-tools.md +0 -0
- {code_aide-1.15.6 → code_aide-1.15.7}/specs/pre-commit-uv-setup.md +0 -0
- {code_aide-1.15.6 → code_aide-1.15.7}/specs/remove-bundled-version-baseline.md +0 -0
- {code_aide-1.15.6 → code_aide-1.15.7}/specs/unify-upgrade-eligibility-with-shared-evaluator.md +0 -0
- {code_aide-1.15.6 → code_aide-1.15.7}/src/code_aide/__main__.py +0 -0
- {code_aide-1.15.6 → code_aide-1.15.7}/src/code_aide/commands_tools.py +0 -0
- {code_aide-1.15.6 → code_aide-1.15.7}/src/code_aide/config.py +0 -0
- {code_aide-1.15.6 → code_aide-1.15.7}/src/code_aide/console.py +0 -0
- {code_aide-1.15.6 → code_aide-1.15.7}/src/code_aide/constants.py +0 -0
- {code_aide-1.15.6 → code_aide-1.15.7}/src/code_aide/data/tools.json +0 -0
- {code_aide-1.15.6 → code_aide-1.15.7}/src/code_aide/detection.py +0 -0
- {code_aide-1.15.6 → code_aide-1.15.7}/src/code_aide/detection_types.py +0 -0
- {code_aide-1.15.6 → code_aide-1.15.7}/src/code_aide/entry.py +0 -0
- {code_aide-1.15.6 → code_aide-1.15.7}/src/code_aide/install_types.py +0 -0
- {code_aide-1.15.6 → code_aide-1.15.7}/src/code_aide/operation_handlers.py +0 -0
- {code_aide-1.15.6 → code_aide-1.15.7}/src/code_aide/operations.py +0 -0
- {code_aide-1.15.6 → code_aide-1.15.7}/src/code_aide/package_info.py +0 -0
- {code_aide-1.15.6 → code_aide-1.15.7}/src/code_aide/package_managers.py +0 -0
- {code_aide-1.15.6 → code_aide-1.15.7}/src/code_aide/status.py +0 -0
- {code_aide-1.15.6 → code_aide-1.15.7}/src/code_aide/status_print.py +0 -0
- {code_aide-1.15.6 → code_aide-1.15.7}/src/code_aide/versions.py +0 -0
- {code_aide-1.15.6 → code_aide-1.15.7}/tests/test_commands_tools.py +0 -0
- {code_aide-1.15.6 → code_aide-1.15.7}/tests/test_config.py +0 -0
- {code_aide-1.15.6 → code_aide-1.15.7}/tests/test_console.py +0 -0
- {code_aide-1.15.6 → code_aide-1.15.7}/tests/test_constants.py +0 -0
- {code_aide-1.15.6 → code_aide-1.15.7}/tests/test_detection.py +0 -0
- {code_aide-1.15.6 → code_aide-1.15.7}/tests/test_entry.py +0 -0
- {code_aide-1.15.6 → code_aide-1.15.7}/tests/test_error_paths.py +0 -0
- {code_aide-1.15.6 → code_aide-1.15.7}/tests/test_install_types.py +0 -0
- {code_aide-1.15.6 → code_aide-1.15.7}/tests/test_operations.py +0 -0
- {code_aide-1.15.6 → code_aide-1.15.7}/tests/test_package_managers.py +0 -0
- {code_aide-1.15.6 → code_aide-1.15.7}/tests/test_status.py +0 -0
- {code_aide-1.15.6 → code_aide-1.15.7}/tests/test_versions.py +0 -0
- {code_aide-1.15.6 → code_aide-1.15.7}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: code-aide
|
|
3
|
-
Version: 1.15.
|
|
3
|
+
Version: 1.15.7
|
|
4
4
|
Summary: Manage AI coding CLI tools (Claude, Copilot, Cursor, Gemini, Amp, Codex)
|
|
5
5
|
Project-URL: Homepage, https://github.com/dajobe/code-aide
|
|
6
6
|
Project-URL: Repository, https://github.com/dajobe/code-aide
|
|
@@ -73,6 +73,7 @@ def cmd_install(args: argparse.Namespace) -> None:
|
|
|
73
73
|
)
|
|
74
74
|
|
|
75
75
|
installed = []
|
|
76
|
+
installed_bin_dirs = []
|
|
76
77
|
failed = []
|
|
77
78
|
|
|
78
79
|
for tool in tools_to_install:
|
|
@@ -82,8 +83,10 @@ def cmd_install(args: argparse.Namespace) -> None:
|
|
|
82
83
|
else:
|
|
83
84
|
info(f"=== Installing {tool} ===")
|
|
84
85
|
|
|
85
|
-
|
|
86
|
+
outcome = install_tool(tool, dryrun=dryrun)
|
|
87
|
+
if outcome:
|
|
86
88
|
installed.append(tool)
|
|
89
|
+
installed_bin_dirs.extend(outcome.bin_dirs)
|
|
87
90
|
else:
|
|
88
91
|
failed.append(tool)
|
|
89
92
|
|
|
@@ -120,7 +123,7 @@ def cmd_install(args: argparse.Namespace) -> None:
|
|
|
120
123
|
print(f" {tool_config['next_steps']}")
|
|
121
124
|
|
|
122
125
|
print()
|
|
123
|
-
check_path_directories(
|
|
126
|
+
check_path_directories(installed_bin_dirs)
|
|
124
127
|
success("All installations completed successfully!")
|
|
125
128
|
|
|
126
129
|
|
|
@@ -8,6 +8,7 @@ import shutil
|
|
|
8
8
|
import subprocess
|
|
9
9
|
import tarfile
|
|
10
10
|
import tempfile
|
|
11
|
+
from dataclasses import dataclass
|
|
11
12
|
from typing import Any, Dict, Optional
|
|
12
13
|
|
|
13
14
|
from code_aide.constants import TOOLS
|
|
@@ -18,6 +19,29 @@ from code_aide.versions import check_script_tool, fetch_url
|
|
|
18
19
|
DOWNLOAD_TIMEOUT_SECONDS = 120
|
|
19
20
|
|
|
20
21
|
|
|
22
|
+
@dataclass(frozen=True)
|
|
23
|
+
class InstallOutcome:
|
|
24
|
+
"""Result of an install, including directories that may need PATH setup."""
|
|
25
|
+
|
|
26
|
+
succeeded: bool
|
|
27
|
+
bin_dirs: tuple[str, ...] = ()
|
|
28
|
+
|
|
29
|
+
def __bool__(self) -> bool:
|
|
30
|
+
return self.succeeded
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def get_npm_global_bin_dir() -> Optional[str]:
|
|
34
|
+
"""Return the directory where the active npm installs global executables."""
|
|
35
|
+
try:
|
|
36
|
+
prefix = run_command(["npm", "prefix", "--global"]).stdout.strip()
|
|
37
|
+
except (OSError, subprocess.CalledProcessError):
|
|
38
|
+
return None
|
|
39
|
+
|
|
40
|
+
if not prefix:
|
|
41
|
+
return None
|
|
42
|
+
return os.path.join(prefix, "bin")
|
|
43
|
+
|
|
44
|
+
|
|
21
45
|
def run_pkg_command(
|
|
22
46
|
base_cmd: list,
|
|
23
47
|
pkg_name: str,
|
|
@@ -265,12 +289,14 @@ def install_direct_download(
|
|
|
265
289
|
return False
|
|
266
290
|
|
|
267
291
|
|
|
268
|
-
def install_tool(
|
|
292
|
+
def install_tool(
|
|
293
|
+
tool_name: str, dryrun: bool = False, force: bool = False
|
|
294
|
+
) -> InstallOutcome:
|
|
269
295
|
"""Install a tool based on its configuration."""
|
|
270
296
|
tool_config = TOOLS.get(tool_name)
|
|
271
297
|
if not tool_config:
|
|
272
298
|
error(f"Unknown tool: {tool_name}")
|
|
273
|
-
return False
|
|
299
|
+
return InstallOutcome(False)
|
|
274
300
|
|
|
275
301
|
if dryrun:
|
|
276
302
|
info(f"[DRYRUN] Checking {tool_config['name']}...")
|
|
@@ -283,7 +309,7 @@ def install_tool(tool_name: str, dryrun: bool = False, force: bool = False) -> b
|
|
|
283
309
|
info(f"{tool_config['command']} already installed at {tool_path}")
|
|
284
310
|
else:
|
|
285
311
|
warning(f"{tool_config['command']} already installed at {tool_path}")
|
|
286
|
-
return True
|
|
312
|
+
return InstallOutcome(True)
|
|
287
313
|
if command_exists(tool_config["command"]) and force:
|
|
288
314
|
tool_path = shutil.which(tool_config["command"])
|
|
289
315
|
if dryrun:
|
|
@@ -301,7 +327,7 @@ def install_tool(tool_name: str, dryrun: bool = False, force: bool = False) -> b
|
|
|
301
327
|
freebsd_port = tool_config.get("freebsd_port")
|
|
302
328
|
if not freebsd_port:
|
|
303
329
|
error(f"{tool_config['name']} is not available on FreeBSD (no port exists)")
|
|
304
|
-
return False
|
|
330
|
+
return InstallOutcome(False)
|
|
305
331
|
pkg_repo = tool_config.get("freebsd_pkg_repo")
|
|
306
332
|
if dryrun:
|
|
307
333
|
repo_flag = f" -r {pkg_repo}" if pkg_repo else ""
|
|
@@ -309,7 +335,7 @@ def install_tool(tool_name: str, dryrun: bool = False, force: bool = False) -> b
|
|
|
309
335
|
f"[DRYRUN] Would install FreeBSD port: "
|
|
310
336
|
f"pkg install{repo_flag} {freebsd_port}"
|
|
311
337
|
)
|
|
312
|
-
return True
|
|
338
|
+
return InstallOutcome(True)
|
|
313
339
|
try:
|
|
314
340
|
run_pkg_command(
|
|
315
341
|
["sudo", "pkg", "install", "-y"],
|
|
@@ -321,27 +347,32 @@ def install_tool(tool_name: str, dryrun: bool = False, force: bool = False) -> b
|
|
|
321
347
|
info(tool_config["next_steps"])
|
|
322
348
|
if "docs_url" in tool_config:
|
|
323
349
|
info(f"Documentation: {tool_config['docs_url']}")
|
|
324
|
-
return True
|
|
350
|
+
return InstallOutcome(True)
|
|
325
351
|
except subprocess.CalledProcessError as exc:
|
|
326
352
|
error(f"Failed to install {tool_config['name']}: {exc.stderr}")
|
|
327
|
-
return False
|
|
353
|
+
return InstallOutcome(False)
|
|
328
354
|
except Exception as exc:
|
|
329
355
|
error(f"Failed to install {tool_config['name']}: {exc}")
|
|
330
|
-
return False
|
|
356
|
+
return InstallOutcome(False)
|
|
331
357
|
|
|
332
358
|
try:
|
|
333
359
|
install_type = get_tool_install_type(tool_config)
|
|
334
360
|
|
|
335
361
|
if install_type == InstallType.NPM:
|
|
336
362
|
npm_package = tool_config["npm_package"]
|
|
363
|
+
bin_dirs = ()
|
|
337
364
|
if dryrun:
|
|
338
365
|
info(f"[DRYRUN] Would install npm package: {npm_package}")
|
|
339
366
|
else:
|
|
340
367
|
run_command(["npm", "install", "-g", npm_package], check=True)
|
|
368
|
+
npm_bin_dir = get_npm_global_bin_dir()
|
|
369
|
+
if npm_bin_dir:
|
|
370
|
+
bin_dirs = (npm_bin_dir,)
|
|
341
371
|
success(f"{tool_config['name']} installed successfully")
|
|
342
372
|
info(tool_config["next_steps"])
|
|
343
373
|
if "docs_url" in tool_config:
|
|
344
374
|
info(f"Documentation: {tool_config['docs_url']}")
|
|
375
|
+
return InstallOutcome(True, bin_dirs)
|
|
345
376
|
|
|
346
377
|
elif install_type == InstallType.SCRIPT:
|
|
347
378
|
install_url = tool_config["install_url"]
|
|
@@ -361,7 +392,7 @@ def install_tool(tool_name: str, dryrun: bool = False, force: bool = False) -> b
|
|
|
361
392
|
if "docs_url" in tool_config:
|
|
362
393
|
info(f"Documentation: {tool_config['docs_url']}")
|
|
363
394
|
else:
|
|
364
|
-
return False
|
|
395
|
+
return InstallOutcome(False)
|
|
365
396
|
|
|
366
397
|
elif install_type == InstallType.DIRECT_DOWNLOAD:
|
|
367
398
|
if install_direct_download(tool_name, tool_config, dryrun):
|
|
@@ -373,13 +404,18 @@ def install_tool(tool_name: str, dryrun: bool = False, force: bool = False) -> b
|
|
|
373
404
|
if "docs_url" in tool_config:
|
|
374
405
|
info(f"Documentation: {tool_config['docs_url']}")
|
|
375
406
|
else:
|
|
376
|
-
return False
|
|
407
|
+
return InstallOutcome(False)
|
|
377
408
|
|
|
378
|
-
|
|
409
|
+
if not dryrun and tool_config.get("bin_dir"):
|
|
410
|
+
return InstallOutcome(
|
|
411
|
+
True, (os.path.expanduser(tool_config["bin_dir"]),)
|
|
412
|
+
)
|
|
413
|
+
|
|
414
|
+
return InstallOutcome(True)
|
|
379
415
|
|
|
380
416
|
except subprocess.CalledProcessError as exc:
|
|
381
417
|
error(f"Failed to install {tool_config['name']}: {exc.stderr}")
|
|
382
|
-
return False
|
|
418
|
+
return InstallOutcome(False)
|
|
383
419
|
except Exception as exc:
|
|
384
420
|
error(f"Failed to install {tool_config['name']}: {exc}")
|
|
385
|
-
return False
|
|
421
|
+
return InstallOutcome(False)
|
|
@@ -149,28 +149,23 @@ def is_tool_installed(tool_name: str) -> bool:
|
|
|
149
149
|
return command_exists(tool_config["command"])
|
|
150
150
|
|
|
151
151
|
|
|
152
|
-
def check_path_directories(
|
|
152
|
+
def check_path_directories(directories: Optional[List[str]] = None) -> None:
|
|
153
153
|
"""Check if binary installation directories are in PATH and warn if not.
|
|
154
154
|
|
|
155
|
-
When *
|
|
156
|
-
|
|
155
|
+
When *directories* is given, only those directories are checked. Otherwise
|
|
156
|
+
a small set of common directories is used.
|
|
157
157
|
"""
|
|
158
158
|
current_path = os.environ.get("PATH", "")
|
|
159
159
|
path_entries = current_path.split(os.pathsep)
|
|
160
160
|
|
|
161
|
-
if
|
|
161
|
+
if directories is not None:
|
|
162
162
|
seen: set = set()
|
|
163
163
|
dirs_to_check: List[str] = []
|
|
164
|
-
for
|
|
165
|
-
|
|
166
|
-
if not
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
if bin_dir:
|
|
170
|
-
expanded = os.path.expanduser(bin_dir)
|
|
171
|
-
if expanded not in seen:
|
|
172
|
-
seen.add(expanded)
|
|
173
|
-
dirs_to_check.append(expanded)
|
|
164
|
+
for directory in directories:
|
|
165
|
+
expanded = os.path.expanduser(directory)
|
|
166
|
+
if expanded not in seen:
|
|
167
|
+
seen.add(expanded)
|
|
168
|
+
dirs_to_check.append(expanded)
|
|
174
169
|
else:
|
|
175
170
|
dirs_to_check = [
|
|
176
171
|
os.path.expanduser("~/.local/bin"),
|
|
@@ -8,6 +8,7 @@ from unittest import mock
|
|
|
8
8
|
|
|
9
9
|
from code_aide import constants, commands_actions
|
|
10
10
|
from code_aide import entry
|
|
11
|
+
from code_aide.install import InstallOutcome
|
|
11
12
|
from code_aide.operations import UpgradeResult
|
|
12
13
|
|
|
13
14
|
|
|
@@ -39,7 +40,7 @@ class TestCmdInstall(unittest.TestCase):
|
|
|
39
40
|
mock.patch.dict(constants._TOOLS_DATA, tools, clear=True),
|
|
40
41
|
mock.patch.object(commands_actions, "validate_tools"),
|
|
41
42
|
mock.patch.object(
|
|
42
|
-
commands_actions, "install_tool", return_value=True
|
|
43
|
+
commands_actions, "install_tool", return_value=InstallOutcome(True)
|
|
43
44
|
) as mock_install_tool,
|
|
44
45
|
mock.patch.object(commands_actions, "check_prerequisites") as mock_prereqs,
|
|
45
46
|
):
|
|
@@ -50,6 +51,38 @@ class TestCmdInstall(unittest.TestCase):
|
|
|
50
51
|
mock_install_tool.assert_called_once_with("default_tool", dryrun=True)
|
|
51
52
|
mock_prereqs.assert_not_called()
|
|
52
53
|
|
|
54
|
+
def test_checks_bin_directories_reported_by_installers(self):
|
|
55
|
+
tools = {
|
|
56
|
+
"test": {
|
|
57
|
+
"name": "Test Tool",
|
|
58
|
+
"command": "test",
|
|
59
|
+
"install_type": "npm",
|
|
60
|
+
"next_steps": "run test",
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
args = type(
|
|
64
|
+
"Args",
|
|
65
|
+
(),
|
|
66
|
+
{"tools": ["test"], "dryrun": False, "install_prerequisites": False},
|
|
67
|
+
)()
|
|
68
|
+
|
|
69
|
+
with (
|
|
70
|
+
mock.patch.dict(constants._TOOLS_DATA, tools, clear=True),
|
|
71
|
+
mock.patch.object(commands_actions, "validate_tools"),
|
|
72
|
+
mock.patch.object(commands_actions, "check_prerequisites"),
|
|
73
|
+
mock.patch.object(
|
|
74
|
+
commands_actions,
|
|
75
|
+
"install_tool",
|
|
76
|
+
return_value=InstallOutcome(True, ("/home/test/.npm-packages/bin",)),
|
|
77
|
+
),
|
|
78
|
+
mock.patch.object(
|
|
79
|
+
commands_actions, "check_path_directories"
|
|
80
|
+
) as mock_check_path,
|
|
81
|
+
):
|
|
82
|
+
commands_actions.cmd_install(args)
|
|
83
|
+
|
|
84
|
+
mock_check_path.assert_called_once_with(["/home/test/.npm-packages/bin"])
|
|
85
|
+
|
|
53
86
|
|
|
54
87
|
class TestCmdUpdateVersions(unittest.TestCase):
|
|
55
88
|
"""Tests for cmd_update_versions."""
|
|
@@ -286,6 +286,16 @@ class TestInstallDirectDownload(unittest.TestCase):
|
|
|
286
286
|
class TestInstallTool(unittest.TestCase):
|
|
287
287
|
"""Tests for install_tool behavior."""
|
|
288
288
|
|
|
289
|
+
def test_get_npm_global_bin_dir_uses_active_prefix(self):
|
|
290
|
+
npm_prefix = mock.Mock(stdout="/home/test/.npm-packages\n")
|
|
291
|
+
with mock.patch.object(
|
|
292
|
+
cli_install, "run_command", return_value=npm_prefix
|
|
293
|
+
) as mock_run:
|
|
294
|
+
result = cli_install.get_npm_global_bin_dir()
|
|
295
|
+
|
|
296
|
+
self.assertEqual(result, "/home/test/.npm-packages/bin")
|
|
297
|
+
mock_run.assert_called_once_with(["npm", "prefix", "--global"])
|
|
298
|
+
|
|
289
299
|
def test_force_reinstalls_even_when_binary_exists(self):
|
|
290
300
|
tool_config = {
|
|
291
301
|
"name": "Test Tool",
|
|
@@ -301,11 +311,17 @@ class TestInstallTool(unittest.TestCase):
|
|
|
301
311
|
mock.patch.object(
|
|
302
312
|
cli_install.shutil, "which", return_value="/usr/local/bin/test-tool"
|
|
303
313
|
),
|
|
314
|
+
mock.patch.object(
|
|
315
|
+
cli_install,
|
|
316
|
+
"get_npm_global_bin_dir",
|
|
317
|
+
return_value="/home/test/.npm-packages/bin",
|
|
318
|
+
),
|
|
304
319
|
mock.patch.object(cli_install, "run_command") as mock_run,
|
|
305
320
|
):
|
|
306
321
|
result = cli_install.install_tool("test", force=True)
|
|
307
322
|
|
|
308
323
|
self.assertTrue(result)
|
|
324
|
+
self.assertEqual(result.bin_dirs, ("/home/test/.npm-packages/bin",))
|
|
309
325
|
mock_run.assert_called_once_with(
|
|
310
326
|
["npm", "install", "-g", "test-tool"], check=True
|
|
311
327
|
)
|
|
@@ -210,14 +210,12 @@ class TestCheckPathDirectories(unittest.TestCase):
|
|
|
210
210
|
"""Tests for check_path_directories."""
|
|
211
211
|
|
|
212
212
|
def test_warns_when_existing_bin_dir_not_in_path(self):
|
|
213
|
-
tool_config = {"name": "Test", "command": "test", "bin_dir": "/custom/bin"}
|
|
214
213
|
with (
|
|
215
|
-
mock.patch.dict(constants._TOOLS_DATA, {"test": tool_config}, clear=True),
|
|
216
214
|
mock.patch.dict("os.environ", {"PATH": "/usr/bin"}, clear=False),
|
|
217
215
|
mock.patch.object(cli_prereqs.os.path, "isdir", return_value=True),
|
|
218
216
|
mock.patch("builtins.print") as mock_print,
|
|
219
217
|
):
|
|
220
|
-
cli_prereqs.check_path_directories(["
|
|
218
|
+
cli_prereqs.check_path_directories(["/custom/bin"])
|
|
221
219
|
# Should have printed at least the bin dir as a warning detail
|
|
222
220
|
printed = " ".join(
|
|
223
221
|
str(call.args[0]) for call in mock_print.call_args_list if call.args
|
|
@@ -225,27 +223,23 @@ class TestCheckPathDirectories(unittest.TestCase):
|
|
|
225
223
|
self.assertIn("/custom/bin", printed)
|
|
226
224
|
|
|
227
225
|
def test_silent_when_dir_already_in_path(self):
|
|
228
|
-
tool_config = {"name": "Test", "command": "test", "bin_dir": "/custom/bin"}
|
|
229
226
|
with (
|
|
230
|
-
mock.patch.dict(constants._TOOLS_DATA, {"test": tool_config}, clear=True),
|
|
231
227
|
mock.patch.dict(
|
|
232
228
|
"os.environ", {"PATH": "/custom/bin:/usr/bin"}, clear=False
|
|
233
229
|
),
|
|
234
230
|
mock.patch.object(cli_prereqs.os.path, "isdir", return_value=True),
|
|
235
231
|
mock.patch("builtins.print") as mock_print,
|
|
236
232
|
):
|
|
237
|
-
cli_prereqs.check_path_directories(["
|
|
233
|
+
cli_prereqs.check_path_directories(["/custom/bin"])
|
|
238
234
|
self.assertFalse(mock_print.called)
|
|
239
235
|
|
|
240
236
|
def test_silent_when_bin_dir_does_not_exist(self):
|
|
241
|
-
tool_config = {"name": "Test", "command": "test", "bin_dir": "/missing/bin"}
|
|
242
237
|
with (
|
|
243
|
-
mock.patch.dict(constants._TOOLS_DATA, {"test": tool_config}, clear=True),
|
|
244
238
|
mock.patch.dict("os.environ", {"PATH": "/usr/bin"}, clear=False),
|
|
245
239
|
mock.patch.object(cli_prereqs.os.path, "isdir", return_value=False),
|
|
246
240
|
mock.patch("builtins.print") as mock_print,
|
|
247
241
|
):
|
|
248
|
-
cli_prereqs.check_path_directories(["
|
|
242
|
+
cli_prereqs.check_path_directories(["/missing/bin"])
|
|
249
243
|
self.assertFalse(mock_print.called)
|
|
250
244
|
|
|
251
245
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{code_aide-1.15.6 → code_aide-1.15.7}/specs/2026-05-03-qwen3.6-code-quality-analysis-original.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{code_aide-1.15.6 → code_aide-1.15.7}/specs/unify-upgrade-eligibility-with-shared-evaluator.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|