code-aide 1.13.0__tar.gz → 1.14.0__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.13.0 → code_aide-1.14.0}/PKG-INFO +1 -1
- {code_aide-1.13.0 → code_aide-1.14.0}/script-archive/amp-install.sh +2 -9
- {code_aide-1.13.0 → code_aide-1.14.0}/src/code_aide/__init__.py +1 -1
- {code_aide-1.13.0 → code_aide-1.14.0}/src/code_aide/data/tools.json +4 -1
- {code_aide-1.13.0 → code_aide-1.14.0}/src/code_aide/install.py +23 -1
- {code_aide-1.13.0 → code_aide-1.14.0}/src/code_aide/operations.py +5 -1
- {code_aide-1.13.0 → code_aide-1.14.0}/tests/test_install.py +96 -0
- {code_aide-1.13.0 → code_aide-1.14.0}/tests/test_operations.py +59 -0
- {code_aide-1.13.0 → code_aide-1.14.0}/.github/workflows/ci.yml +0 -0
- {code_aide-1.13.0 → code_aide-1.14.0}/.github/workflows/publish.yml +0 -0
- {code_aide-1.13.0 → code_aide-1.14.0}/.gitignore +0 -0
- {code_aide-1.13.0 → code_aide-1.14.0}/.gitlab-ci.yml +0 -0
- {code_aide-1.13.0 → code_aide-1.14.0}/.pre-commit-config.yaml +0 -0
- {code_aide-1.13.0 → code_aide-1.14.0}/AGENTS.md +0 -0
- {code_aide-1.13.0 → code_aide-1.14.0}/CLAUDE.md +0 -0
- {code_aide-1.13.0 → code_aide-1.14.0}/LICENSE +0 -0
- {code_aide-1.13.0 → code_aide-1.14.0}/README.md +0 -0
- {code_aide-1.13.0 → code_aide-1.14.0}/TODO.md +0 -0
- {code_aide-1.13.0 → code_aide-1.14.0}/pyproject.toml +0 -0
- {code_aide-1.13.0 → code_aide-1.14.0}/script-archive/README.md +0 -0
- {code_aide-1.13.0 → code_aide-1.14.0}/script-archive/claude-install.sh +0 -0
- {code_aide-1.13.0 → code_aide-1.14.0}/script-archive/cursor-install.sh +0 -0
- {code_aide-1.13.0 → code_aide-1.14.0}/specs/auto-migrate-deprecated-installs.md +0 -0
- {code_aide-1.13.0 → code_aide-1.14.0}/specs/claude-native-installer-migration.md +0 -0
- {code_aide-1.13.0 → code_aide-1.14.0}/specs/missing-coding-llm-cli-tools.md +0 -0
- {code_aide-1.13.0 → code_aide-1.14.0}/specs/pre-commit-uv-setup.md +0 -0
- {code_aide-1.13.0 → code_aide-1.14.0}/specs/remove-bundled-version-baseline.md +0 -0
- {code_aide-1.13.0 → code_aide-1.14.0}/specs/unify-upgrade-eligibility-with-shared-evaluator.md +0 -0
- {code_aide-1.13.0 → code_aide-1.14.0}/src/code_aide/__main__.py +0 -0
- {code_aide-1.13.0 → code_aide-1.14.0}/src/code_aide/commands_actions.py +0 -0
- {code_aide-1.13.0 → code_aide-1.14.0}/src/code_aide/commands_tools.py +0 -0
- {code_aide-1.13.0 → code_aide-1.14.0}/src/code_aide/config.py +0 -0
- {code_aide-1.13.0 → code_aide-1.14.0}/src/code_aide/console.py +0 -0
- {code_aide-1.13.0 → code_aide-1.14.0}/src/code_aide/constants.py +0 -0
- {code_aide-1.13.0 → code_aide-1.14.0}/src/code_aide/detection.py +0 -0
- {code_aide-1.13.0 → code_aide-1.14.0}/src/code_aide/entry.py +0 -0
- {code_aide-1.13.0 → code_aide-1.14.0}/src/code_aide/install_types.py +0 -0
- {code_aide-1.13.0 → code_aide-1.14.0}/src/code_aide/package_managers.py +0 -0
- {code_aide-1.13.0 → code_aide-1.14.0}/src/code_aide/prereqs.py +0 -0
- {code_aide-1.13.0 → code_aide-1.14.0}/src/code_aide/status.py +0 -0
- {code_aide-1.13.0 → code_aide-1.14.0}/src/code_aide/versions.py +0 -0
- {code_aide-1.13.0 → code_aide-1.14.0}/tests/test_commands_actions.py +0 -0
- {code_aide-1.13.0 → code_aide-1.14.0}/tests/test_commands_tools.py +0 -0
- {code_aide-1.13.0 → code_aide-1.14.0}/tests/test_config.py +0 -0
- {code_aide-1.13.0 → code_aide-1.14.0}/tests/test_console.py +0 -0
- {code_aide-1.13.0 → code_aide-1.14.0}/tests/test_constants.py +0 -0
- {code_aide-1.13.0 → code_aide-1.14.0}/tests/test_detection.py +0 -0
- {code_aide-1.13.0 → code_aide-1.14.0}/tests/test_install_types.py +0 -0
- {code_aide-1.13.0 → code_aide-1.14.0}/tests/test_package_managers.py +0 -0
- {code_aide-1.13.0 → code_aide-1.14.0}/tests/test_status.py +0 -0
- {code_aide-1.13.0 → code_aide-1.14.0}/tests/test_versions.py +0 -0
- {code_aide-1.13.0 → code_aide-1.14.0}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: code-aide
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.14.0
|
|
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
|
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
set -euo pipefail
|
|
3
3
|
|
|
4
|
-
# Amp CLI Binary Installation Script (EXPERIMENTAL - NOT DOCUMENTED)
|
|
5
|
-
# This is a secondary install script for testing binary distribution
|
|
6
|
-
# Downloads pre-compiled Amp CLI binary instead of using npm
|
|
7
|
-
|
|
8
4
|
# Configuration
|
|
9
5
|
AMP_HOME="${AMP_HOME:-$HOME/.amp}"
|
|
10
6
|
BIN_DIR="$AMP_HOME/bin"
|
|
@@ -437,11 +433,8 @@ update_shell_profile() {
|
|
|
437
433
|
return
|
|
438
434
|
fi
|
|
439
435
|
else
|
|
440
|
-
# Non-interactive:
|
|
441
|
-
log "
|
|
442
|
-
log "To use amp, add ~/.local/bin to your PATH:"
|
|
443
|
-
echo " $path_export"
|
|
444
|
-
return
|
|
436
|
+
# Non-interactive: add automatically
|
|
437
|
+
log "Adding ~/.local/bin to PATH in $tilde_profile..."
|
|
445
438
|
fi
|
|
446
439
|
|
|
447
440
|
# Create config file if it doesn't exist
|
|
@@ -64,7 +64,10 @@
|
|
|
64
64
|
"command": "amp",
|
|
65
65
|
"install_type": "script",
|
|
66
66
|
"install_url": "https://ampcode.com/install.sh",
|
|
67
|
-
"install_sha256": "
|
|
67
|
+
"install_sha256": "2442f20759a2be3beaef6691c7e5bf349f469eb0ff7ea2885746500243446e24",
|
|
68
|
+
"install_script_path_prepend": [
|
|
69
|
+
"~/.local/bin"
|
|
70
|
+
],
|
|
68
71
|
"version_url": "https://storage.googleapis.com/amp-public-assets-prod-0/cli/cli-version.txt",
|
|
69
72
|
"prerequisites": [],
|
|
70
73
|
"min_node_version": null,
|
|
@@ -43,6 +43,7 @@ def run_install_script(
|
|
|
43
43
|
tool_name: str,
|
|
44
44
|
expected_sha256: Optional[str] = None,
|
|
45
45
|
dryrun: bool = False,
|
|
46
|
+
env: Optional[Dict[str, str]] = None,
|
|
46
47
|
) -> bool:
|
|
47
48
|
"""Download and run an installation script with SHA256 verification."""
|
|
48
49
|
try:
|
|
@@ -82,6 +83,7 @@ def run_install_script(
|
|
|
82
83
|
["bash"],
|
|
83
84
|
stdin=subprocess.PIPE,
|
|
84
85
|
stderr=subprocess.PIPE,
|
|
86
|
+
env=env,
|
|
85
87
|
)
|
|
86
88
|
_, stderr = bash_process.communicate(input=script_content)
|
|
87
89
|
|
|
@@ -99,6 +101,22 @@ def run_install_script(
|
|
|
99
101
|
return False
|
|
100
102
|
|
|
101
103
|
|
|
104
|
+
def get_install_script_env(tool_config: Dict[str, Any]) -> Optional[Dict[str, str]]:
|
|
105
|
+
"""Return environment overrides for running a tool's install script."""
|
|
106
|
+
path_prepend = tool_config.get("install_script_path_prepend", [])
|
|
107
|
+
if not path_prepend:
|
|
108
|
+
return None
|
|
109
|
+
|
|
110
|
+
env = os.environ.copy()
|
|
111
|
+
expanded_paths = [os.path.expanduser(path) for path in path_prepend]
|
|
112
|
+
current_path = env.get("PATH", "")
|
|
113
|
+
path_parts = [*expanded_paths]
|
|
114
|
+
if current_path:
|
|
115
|
+
path_parts.append(current_path)
|
|
116
|
+
env["PATH"] = os.pathsep.join(path_parts)
|
|
117
|
+
return env
|
|
118
|
+
|
|
119
|
+
|
|
102
120
|
ARCH_MAP = {
|
|
103
121
|
"x86_64": "x64",
|
|
104
122
|
"amd64": "x64",
|
|
@@ -327,7 +345,11 @@ def install_tool(tool_name: str, dryrun: bool = False, force: bool = False) -> b
|
|
|
327
345
|
install_url = tool_config["install_url"]
|
|
328
346
|
expected_sha256 = tool_config.get("install_sha256")
|
|
329
347
|
if run_install_script(
|
|
330
|
-
install_url,
|
|
348
|
+
install_url,
|
|
349
|
+
tool_config["name"],
|
|
350
|
+
expected_sha256,
|
|
351
|
+
dryrun,
|
|
352
|
+
env=get_install_script_env(tool_config),
|
|
331
353
|
):
|
|
332
354
|
if dryrun:
|
|
333
355
|
success(f"{tool_config['name']} verification passed")
|
|
@@ -16,6 +16,7 @@ from code_aide.detection import (
|
|
|
16
16
|
)
|
|
17
17
|
from code_aide.package_managers import query_package_owner
|
|
18
18
|
from code_aide.install import (
|
|
19
|
+
get_install_script_env,
|
|
19
20
|
install_direct_download,
|
|
20
21
|
install_tool,
|
|
21
22
|
run_install_script,
|
|
@@ -225,7 +226,10 @@ def upgrade_tool(tool_name: str) -> UpgradeResult:
|
|
|
225
226
|
install_url = tool_config["install_url"]
|
|
226
227
|
expected_sha256 = tool_config.get("install_sha256")
|
|
227
228
|
if run_install_script(
|
|
228
|
-
install_url,
|
|
229
|
+
install_url,
|
|
230
|
+
tool_config["name"],
|
|
231
|
+
expected_sha256,
|
|
232
|
+
env=get_install_script_env(tool_config),
|
|
229
233
|
):
|
|
230
234
|
pass
|
|
231
235
|
else:
|
|
@@ -148,6 +148,67 @@ class TestExtractTarMember(unittest.TestCase):
|
|
|
148
148
|
self.assertTrue(os.path.exists(extracted))
|
|
149
149
|
|
|
150
150
|
|
|
151
|
+
class TestInstallScriptEnv(unittest.TestCase):
|
|
152
|
+
"""Tests for install script environment handling."""
|
|
153
|
+
|
|
154
|
+
def test_no_env_without_path_prepend(self):
|
|
155
|
+
self.assertIsNone(cli_install.get_install_script_env({}))
|
|
156
|
+
|
|
157
|
+
def test_prepends_expanded_paths(self):
|
|
158
|
+
with tempfile.TemporaryDirectory() as td:
|
|
159
|
+
with mock.patch.dict(
|
|
160
|
+
os.environ,
|
|
161
|
+
{"HOME": td, "PATH": "/usr/bin"},
|
|
162
|
+
clear=True,
|
|
163
|
+
):
|
|
164
|
+
env = cli_install.get_install_script_env(
|
|
165
|
+
{"install_script_path_prepend": ["~/.local/bin"]}
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
self.assertIsNotNone(env)
|
|
169
|
+
self.assertEqual(
|
|
170
|
+
env["PATH"],
|
|
171
|
+
os.path.join(td, ".local", "bin") + os.pathsep + "/usr/bin",
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
def test_path_prepend_without_existing_path(self):
|
|
175
|
+
with tempfile.TemporaryDirectory() as td:
|
|
176
|
+
with mock.patch.dict(os.environ, {"HOME": td}, clear=True):
|
|
177
|
+
env = cli_install.get_install_script_env(
|
|
178
|
+
{"install_script_path_prepend": ["~/.local/bin"]}
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
self.assertIsNotNone(env)
|
|
182
|
+
self.assertEqual(env["PATH"], os.path.join(td, ".local", "bin"))
|
|
183
|
+
|
|
184
|
+
@mock.patch.object(cli_install, "fetch_url")
|
|
185
|
+
@mock.patch.object(cli_install.subprocess, "Popen")
|
|
186
|
+
def test_run_install_script_passes_env_to_bash(self, mock_popen, mock_fetch):
|
|
187
|
+
script_content = b"#!/usr/bin/env bash\n"
|
|
188
|
+
expected_sha256 = cli_install.hashlib.sha256(script_content).hexdigest()
|
|
189
|
+
process = mock.Mock()
|
|
190
|
+
process.communicate.return_value = (b"", b"")
|
|
191
|
+
process.returncode = 0
|
|
192
|
+
mock_popen.return_value = process
|
|
193
|
+
mock_fetch.return_value = (script_content, None)
|
|
194
|
+
env = {"PATH": "/tmp/bin:/usr/bin"}
|
|
195
|
+
|
|
196
|
+
result = cli_install.run_install_script(
|
|
197
|
+
"https://example.com/install.sh",
|
|
198
|
+
"Example",
|
|
199
|
+
expected_sha256,
|
|
200
|
+
env=env,
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
self.assertTrue(result)
|
|
204
|
+
mock_popen.assert_called_once_with(
|
|
205
|
+
["bash"],
|
|
206
|
+
stdin=cli_install.subprocess.PIPE,
|
|
207
|
+
stderr=cli_install.subprocess.PIPE,
|
|
208
|
+
env=env,
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
|
|
151
212
|
class TestInstallDirectDownloadDryrun(unittest.TestCase):
|
|
152
213
|
"""Tests for install_direct_download in dryrun mode."""
|
|
153
214
|
|
|
@@ -270,3 +331,38 @@ class TestInstallTool(unittest.TestCase):
|
|
|
270
331
|
"/usr/local/bin/test-tool"
|
|
271
332
|
)
|
|
272
333
|
mock_info.assert_any_call("[DRYRUN] Would install npm package: test-tool")
|
|
334
|
+
|
|
335
|
+
def test_script_install_passes_configured_env(self):
|
|
336
|
+
tool_config = {
|
|
337
|
+
"name": "Test Tool",
|
|
338
|
+
"command": "test-tool",
|
|
339
|
+
"install_type": "script",
|
|
340
|
+
"install_url": "https://example.com/install.sh",
|
|
341
|
+
"install_sha256": "abc123",
|
|
342
|
+
"install_script_path_prepend": ["~/.local/bin"],
|
|
343
|
+
"next_steps": "Run test-tool",
|
|
344
|
+
}
|
|
345
|
+
env = {"PATH": "/tmp/bin:/usr/bin"}
|
|
346
|
+
|
|
347
|
+
with (
|
|
348
|
+
mock.patch.dict(cli_install.TOOLS, {"test": tool_config}, clear=True),
|
|
349
|
+
mock.patch.object(cli_install, "command_exists", return_value=False),
|
|
350
|
+
mock.patch.object(cli_install.platform, "system", return_value="Darwin"),
|
|
351
|
+
mock.patch.object(
|
|
352
|
+
cli_install, "get_install_script_env", return_value=env
|
|
353
|
+
) as mock_env,
|
|
354
|
+
mock.patch.object(
|
|
355
|
+
cli_install, "run_install_script", return_value=True
|
|
356
|
+
) as mock_script,
|
|
357
|
+
):
|
|
358
|
+
result = cli_install.install_tool("test")
|
|
359
|
+
|
|
360
|
+
self.assertTrue(result)
|
|
361
|
+
mock_env.assert_called_once_with(tool_config)
|
|
362
|
+
mock_script.assert_called_once_with(
|
|
363
|
+
"https://example.com/install.sh",
|
|
364
|
+
"Test Tool",
|
|
365
|
+
"abc123",
|
|
366
|
+
False,
|
|
367
|
+
env=env,
|
|
368
|
+
)
|
|
@@ -244,6 +244,65 @@ class TestMigrateInstallMethod(unittest.TestCase):
|
|
|
244
244
|
mock_dd.assert_called_once_with("cursor", tool_config)
|
|
245
245
|
mock_script.assert_not_called()
|
|
246
246
|
|
|
247
|
+
def test_upgrade_script_passes_configured_env(self):
|
|
248
|
+
"""Script upgrades pass per-tool install environment overrides."""
|
|
249
|
+
tool_config = {
|
|
250
|
+
"name": "Amp (Sourcegraph)",
|
|
251
|
+
"command": "amp",
|
|
252
|
+
"install_type": "script",
|
|
253
|
+
"install_url": "https://ampcode.com/install.sh",
|
|
254
|
+
"install_sha256": "abc123",
|
|
255
|
+
"install_script_path_prepend": ["~/.local/bin"],
|
|
256
|
+
}
|
|
257
|
+
env = {"PATH": "/tmp/bin:/usr/bin"}
|
|
258
|
+
with (
|
|
259
|
+
mock.patch.dict(cli_operations.TOOLS, {"amp": tool_config}),
|
|
260
|
+
mock.patch.object(cli_operations, "is_tool_installed", return_value=True),
|
|
261
|
+
mock.patch.object(
|
|
262
|
+
cli_operations, "is_deprecated_install", return_value=False
|
|
263
|
+
),
|
|
264
|
+
mock.patch.object(
|
|
265
|
+
cli_operations,
|
|
266
|
+
"detect_install_method",
|
|
267
|
+
side_effect=[
|
|
268
|
+
{"method": "script", "detail": "native installer"},
|
|
269
|
+
{"method": "script", "detail": "native installer"},
|
|
270
|
+
],
|
|
271
|
+
),
|
|
272
|
+
mock.patch.object(
|
|
273
|
+
cli_operations, "get_install_script_env", return_value=env
|
|
274
|
+
) as mock_env,
|
|
275
|
+
mock.patch.object(
|
|
276
|
+
cli_operations, "run_install_script", return_value=True
|
|
277
|
+
) as mock_script,
|
|
278
|
+
mock.patch.object(
|
|
279
|
+
cli_operations,
|
|
280
|
+
"_get_upgrade_snapshot",
|
|
281
|
+
side_effect=[
|
|
282
|
+
{
|
|
283
|
+
"method": "script",
|
|
284
|
+
"detail": "native installer",
|
|
285
|
+
"version": "1.0.0",
|
|
286
|
+
},
|
|
287
|
+
{
|
|
288
|
+
"method": "script",
|
|
289
|
+
"detail": "native installer",
|
|
290
|
+
"version": "2.0.0",
|
|
291
|
+
},
|
|
292
|
+
],
|
|
293
|
+
),
|
|
294
|
+
):
|
|
295
|
+
result = cli_operations.upgrade_tool("amp")
|
|
296
|
+
|
|
297
|
+
self.assertEqual(result, UpgradeResult.CHANGED)
|
|
298
|
+
mock_env.assert_called_once_with(tool_config)
|
|
299
|
+
mock_script.assert_called_once_with(
|
|
300
|
+
"https://ampcode.com/install.sh",
|
|
301
|
+
"Amp (Sourcegraph)",
|
|
302
|
+
"abc123",
|
|
303
|
+
env=env,
|
|
304
|
+
)
|
|
305
|
+
|
|
247
306
|
def test_migration_fails_on_remove(self):
|
|
248
307
|
"""Migration fails if remove_tool returns False."""
|
|
249
308
|
tool_config = {
|
|
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
|
{code_aide-1.13.0 → code_aide-1.14.0}/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
|