code-aide 1.4.2__tar.gz → 1.5.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.4.2 → code_aide-1.5.0}/PKG-INFO +17 -16
- {code_aide-1.4.2 → code_aide-1.5.0}/README.md +16 -15
- code_aide-1.5.0/specs/claude-native-installer-migration.md +112 -0
- {code_aide-1.4.2 → code_aide-1.5.0}/src/code_aide/__init__.py +1 -1
- {code_aide-1.4.2 → code_aide-1.5.0}/src/code_aide/commands_actions.py +1 -1
- {code_aide-1.4.2 → code_aide-1.5.0}/src/code_aide/data/tools.json +5 -10
- {code_aide-1.4.2 → code_aide-1.5.0}/src/code_aide/detection.py +3 -2
- {code_aide-1.4.2 → code_aide-1.5.0}/src/code_aide/install.py +0 -14
- {code_aide-1.4.2 → code_aide-1.5.0}/src/code_aide/operations.py +6 -37
- {code_aide-1.4.2 → code_aide-1.5.0}/src/code_aide/versions.py +0 -2
- {code_aide-1.4.2 → code_aide-1.5.0}/tests/test_config.py +1 -1
- code_aide-1.5.0/tests/test_constants.py +21 -0
- {code_aide-1.4.2 → code_aide-1.5.0}/tests/test_detection.py +16 -18
- {code_aide-1.4.2 → code_aide-1.5.0}/tests/test_operations.py +10 -9
- {code_aide-1.4.2 → code_aide-1.5.0}/tests/test_versions.py +0 -10
- code_aide-1.4.2/tests/test_constants.py +0 -13
- {code_aide-1.4.2 → code_aide-1.5.0}/.github/workflows/ci.yml +0 -0
- {code_aide-1.4.2 → code_aide-1.5.0}/.github/workflows/publish.yml +0 -0
- {code_aide-1.4.2 → code_aide-1.5.0}/.gitignore +0 -0
- {code_aide-1.4.2 → code_aide-1.5.0}/.gitlab-ci.yml +0 -0
- {code_aide-1.4.2 → code_aide-1.5.0}/.pre-commit-config.yaml +0 -0
- {code_aide-1.4.2 → code_aide-1.5.0}/AGENTS.md +0 -0
- {code_aide-1.4.2 → code_aide-1.5.0}/CLAUDE.md +0 -0
- {code_aide-1.4.2 → code_aide-1.5.0}/LICENSE +0 -0
- {code_aide-1.4.2 → code_aide-1.5.0}/TODO.md +0 -0
- {code_aide-1.4.2 → code_aide-1.5.0}/pyproject.toml +0 -0
- {code_aide-1.4.2 → code_aide-1.5.0}/specs/missing-coding-llm-cli-tools.md +0 -0
- {code_aide-1.4.2 → code_aide-1.5.0}/specs/pre-commit-uv-setup.md +0 -0
- {code_aide-1.4.2 → code_aide-1.5.0}/src/code_aide/__main__.py +0 -0
- {code_aide-1.4.2 → code_aide-1.5.0}/src/code_aide/commands_tools.py +0 -0
- {code_aide-1.4.2 → code_aide-1.5.0}/src/code_aide/config.py +0 -0
- {code_aide-1.4.2 → code_aide-1.5.0}/src/code_aide/console.py +0 -0
- {code_aide-1.4.2 → code_aide-1.5.0}/src/code_aide/constants.py +0 -0
- {code_aide-1.4.2 → code_aide-1.5.0}/src/code_aide/entry.py +0 -0
- {code_aide-1.4.2 → code_aide-1.5.0}/src/code_aide/prereqs.py +0 -0
- {code_aide-1.4.2 → code_aide-1.5.0}/src/code_aide/status.py +0 -0
- {code_aide-1.4.2 → code_aide-1.5.0}/tests/test_commands_actions.py +0 -0
- {code_aide-1.4.2 → code_aide-1.5.0}/tests/test_commands_tools.py +0 -0
- {code_aide-1.4.2 → code_aide-1.5.0}/tests/test_console.py +0 -0
- {code_aide-1.4.2 → code_aide-1.5.0}/tests/test_install.py +0 -0
- {code_aide-1.4.2 → code_aide-1.5.0}/tests/test_status.py +0 -0
- {code_aide-1.4.2 → code_aide-1.5.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.5.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
|
|
@@ -84,7 +84,7 @@ code-aide update-versions -b -y
|
|
|
84
84
|
| Tool | Command | Install Type | Default |
|
|
85
85
|
|--------------------------|-----------|--------------------|---------|
|
|
86
86
|
| Cursor CLI | `agent` | Direct download | Yes |
|
|
87
|
-
| Claude CLI (Claude Code) | `claude` |
|
|
87
|
+
| Claude CLI (Claude Code) | `claude` | Script | Yes |
|
|
88
88
|
| Gemini CLI | `gemini` | npm | Yes |
|
|
89
89
|
| OpenCode | `opencode`| npm | No |
|
|
90
90
|
| Kilo CLI | `kilo` | npm | No |
|
|
@@ -96,20 +96,20 @@ code-aide update-versions -b -y
|
|
|
96
96
|
|
|
97
97
|
code-aide uses a three-layer version data model:
|
|
98
98
|
|
|
99
|
-
1. **Tool definitions** (bundled with the package): Install methods,
|
|
100
|
-
|
|
101
|
-
|
|
99
|
+
1. **Tool definitions** (bundled with the package): Install methods, URLs,
|
|
100
|
+
npm packages, version args. Updated by releasing new versions of
|
|
101
|
+
code-aide.
|
|
102
102
|
|
|
103
|
-
2. **Bundled version baseline** (in `data/tools.json`): Latest
|
|
104
|
-
|
|
105
|
-
|
|
103
|
+
2. **Bundled version baseline** (in `data/tools.json`): Latest versions and
|
|
104
|
+
SHA256 hashes as known at release time. Acts as a fallback for fresh
|
|
105
|
+
installs.
|
|
106
106
|
|
|
107
|
-
3. **User's local version cache**
|
|
108
|
-
|
|
109
|
-
|
|
107
|
+
3. **User's local version cache** (`~/.config/code-aide/versions.json`):
|
|
108
|
+
Written by `code-aide update-versions`. Takes precedence over bundled
|
|
109
|
+
data when present.
|
|
110
110
|
|
|
111
|
-
Run `code-aide update-versions` to get fresher version data without
|
|
112
|
-
|
|
111
|
+
Run `code-aide update-versions` to get fresher version data without waiting
|
|
112
|
+
for a new code-aide release.
|
|
113
113
|
|
|
114
114
|
## Features
|
|
115
115
|
|
|
@@ -130,7 +130,8 @@ waiting for a new code-aide release.
|
|
|
130
130
|
|
|
131
131
|
1. Install uv: `curl -LsSf https://astral.sh/uv/install.sh | sh`
|
|
132
132
|
2. Install dependencies: `uv sync`
|
|
133
|
-
3. Install pre-commit hooks: `uv tool install pre-commit && pre-commit
|
|
133
|
+
3. Install pre-commit hooks: `uv tool install pre-commit && pre-commit
|
|
134
|
+
install`
|
|
134
135
|
4. Run tests: `uv run pytest tests/ -v`
|
|
135
136
|
|
|
136
137
|
```bash
|
|
@@ -159,8 +160,8 @@ uv run pytest tests/test_install.py::TestDetectOsArch -v
|
|
|
159
160
|
- `git add src/code_aide/__init__.py`
|
|
160
161
|
- `git commit -m "Bumped version to X.Y.Z"`
|
|
161
162
|
5. Write useful commit messages before tagging:
|
|
162
|
-
- Start subject lines with an action verb in past tense (`Added`,
|
|
163
|
-
`Fixed`, `Removed`).
|
|
163
|
+
- Start subject lines with an action verb in past tense (`Added`,
|
|
164
|
+
`Changed`, `Fixed`, `Removed`).
|
|
164
165
|
- Keep subjects user-facing so auto-generated release notes are
|
|
165
166
|
meaningful.
|
|
166
167
|
- Group related changes into focused commits instead of one broad commit.
|
|
@@ -58,7 +58,7 @@ code-aide update-versions -b -y
|
|
|
58
58
|
| Tool | Command | Install Type | Default |
|
|
59
59
|
|--------------------------|-----------|--------------------|---------|
|
|
60
60
|
| Cursor CLI | `agent` | Direct download | Yes |
|
|
61
|
-
| Claude CLI (Claude Code) | `claude` |
|
|
61
|
+
| Claude CLI (Claude Code) | `claude` | Script | Yes |
|
|
62
62
|
| Gemini CLI | `gemini` | npm | Yes |
|
|
63
63
|
| OpenCode | `opencode`| npm | No |
|
|
64
64
|
| Kilo CLI | `kilo` | npm | No |
|
|
@@ -70,20 +70,20 @@ code-aide update-versions -b -y
|
|
|
70
70
|
|
|
71
71
|
code-aide uses a three-layer version data model:
|
|
72
72
|
|
|
73
|
-
1. **Tool definitions** (bundled with the package): Install methods,
|
|
74
|
-
|
|
75
|
-
|
|
73
|
+
1. **Tool definitions** (bundled with the package): Install methods, URLs,
|
|
74
|
+
npm packages, version args. Updated by releasing new versions of
|
|
75
|
+
code-aide.
|
|
76
76
|
|
|
77
|
-
2. **Bundled version baseline** (in `data/tools.json`): Latest
|
|
78
|
-
|
|
79
|
-
|
|
77
|
+
2. **Bundled version baseline** (in `data/tools.json`): Latest versions and
|
|
78
|
+
SHA256 hashes as known at release time. Acts as a fallback for fresh
|
|
79
|
+
installs.
|
|
80
80
|
|
|
81
|
-
3. **User's local version cache**
|
|
82
|
-
|
|
83
|
-
|
|
81
|
+
3. **User's local version cache** (`~/.config/code-aide/versions.json`):
|
|
82
|
+
Written by `code-aide update-versions`. Takes precedence over bundled
|
|
83
|
+
data when present.
|
|
84
84
|
|
|
85
|
-
Run `code-aide update-versions` to get fresher version data without
|
|
86
|
-
|
|
85
|
+
Run `code-aide update-versions` to get fresher version data without waiting
|
|
86
|
+
for a new code-aide release.
|
|
87
87
|
|
|
88
88
|
## Features
|
|
89
89
|
|
|
@@ -104,7 +104,8 @@ waiting for a new code-aide release.
|
|
|
104
104
|
|
|
105
105
|
1. Install uv: `curl -LsSf https://astral.sh/uv/install.sh | sh`
|
|
106
106
|
2. Install dependencies: `uv sync`
|
|
107
|
-
3. Install pre-commit hooks: `uv tool install pre-commit && pre-commit
|
|
107
|
+
3. Install pre-commit hooks: `uv tool install pre-commit && pre-commit
|
|
108
|
+
install`
|
|
108
109
|
4. Run tests: `uv run pytest tests/ -v`
|
|
109
110
|
|
|
110
111
|
```bash
|
|
@@ -133,8 +134,8 @@ uv run pytest tests/test_install.py::TestDetectOsArch -v
|
|
|
133
134
|
- `git add src/code_aide/__init__.py`
|
|
134
135
|
- `git commit -m "Bumped version to X.Y.Z"`
|
|
135
136
|
5. Write useful commit messages before tagging:
|
|
136
|
-
- Start subject lines with an action verb in past tense (`Added`,
|
|
137
|
-
`Fixed`, `Removed`).
|
|
137
|
+
- Start subject lines with an action verb in past tense (`Added`,
|
|
138
|
+
`Changed`, `Fixed`, `Removed`).
|
|
138
139
|
- Keep subjects user-facing so auto-generated release notes are
|
|
139
140
|
meaningful.
|
|
140
141
|
- Group related changes into focused commits instead of one broad commit.
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# Migrate Claude Code from npm to native installer in code-aide
|
|
2
|
+
|
|
3
|
+
## Context
|
|
4
|
+
|
|
5
|
+
Anthropic has deprecated npm installation of Claude Code in favor of a
|
|
6
|
+
native installer (`curl -fsSL https://claude.ai/install.sh | bash`). The
|
|
7
|
+
native binary lives at `~/.local/bin/claude` (symlink to
|
|
8
|
+
`~/.local/share/claude/versions/<version>`), auto-updates, and has no
|
|
9
|
+
Node.js dependency. code-aide currently treats Claude as `self_managed`
|
|
10
|
+
install type backed by npm, which is now outdated.
|
|
11
|
+
|
|
12
|
+
## Approach
|
|
13
|
+
|
|
14
|
+
Change Claude's install type from `self_managed` (npm-backed) to `script`
|
|
15
|
+
(install-script-backed). This reuses the existing `script` install type
|
|
16
|
+
already used by Amp, which downloads and verifies a shell script by SHA256
|
|
17
|
+
before executing it. Claude Code was the only tool using `self_managed`, so
|
|
18
|
+
that type becomes dead code and can be removed.
|
|
19
|
+
|
|
20
|
+
## Changes
|
|
21
|
+
|
|
22
|
+
### 1. `src/code_aide/data/tools.json` -- update claude config
|
|
23
|
+
|
|
24
|
+
- Change `install_type` from `self_managed` to `script`
|
|
25
|
+
- Add `install_url`: `https://claude.ai/install.sh`
|
|
26
|
+
- Add `install_sha256`:
|
|
27
|
+
`431889ac7d056f636aaf5b71524666d04c89c45560f80329940846479d484778`
|
|
28
|
+
- Remove `npm_package` field
|
|
29
|
+
- Remove `upgrade_command` field (native binary auto-updates; `script`
|
|
30
|
+
upgrade re-runs the install script which is idempotent)
|
|
31
|
+
- Change `prerequisites` from `["npm"]` to `[]`
|
|
32
|
+
- Update `docs_url` to `https://code.claude.com/docs/en/setup`
|
|
33
|
+
|
|
34
|
+
### 2. `src/code_aide/detection.py` -- detect native installer
|
|
35
|
+
|
|
36
|
+
Add a new detection case before the `node_modules` check:
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
if "/.local/share/claude/versions/" in real_path:
|
|
40
|
+
return {"method": "script", "detail": "native installer"}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
This detects the native binary's real path pattern.
|
|
44
|
+
|
|
45
|
+
### 3. `src/code_aide/detection.py` -- format label
|
|
46
|
+
|
|
47
|
+
No change needed -- the existing `format_install_method("script")` returns
|
|
48
|
+
`"script"` which is accurate.
|
|
49
|
+
|
|
50
|
+
### 4. `src/code_aide/install.py` -- remove `self_managed` branch
|
|
51
|
+
|
|
52
|
+
Remove the `elif install_type == "self_managed"` block (lines 289-301). The
|
|
53
|
+
`script` handler (lines 261-275) will now handle Claude's installation.
|
|
54
|
+
|
|
55
|
+
### 5. `src/code_aide/operations.py` -- remove `self_managed` branches
|
|
56
|
+
|
|
57
|
+
- **upgrade_tool**: Remove `elif method == "self_managed"` block (lines
|
|
58
|
+
65-71). The `script` branch (lines 53-59) handles re-running the install
|
|
59
|
+
script, which for Claude is idempotent and installs the latest version.
|
|
60
|
+
- **remove_tool**: Remove `elif method == "self_managed"` block (lines
|
|
61
|
+
203-230). The `script` branch (lines 133-158) removes the binary at its
|
|
62
|
+
`which` path. For full cleanup, add removal of `~/.local/share/claude`
|
|
63
|
+
directory in the script removal path when the tool is `claude`.
|
|
64
|
+
|
|
65
|
+
### 6. `src/code_aide/versions.py` -- remove `self_managed` branch
|
|
66
|
+
|
|
67
|
+
- `format_check_backend`: Remove the `self_managed` -> `npm-registry`
|
|
68
|
+
mapping (line 276-277). Claude will now use `script-url` backend (SHA256
|
|
69
|
+
check of install script + version extraction).
|
|
70
|
+
- `check_npm_tool` is no longer called for Claude; `check_script_tool`
|
|
71
|
+
handles it instead via the `script` type path.
|
|
72
|
+
|
|
73
|
+
### 7. `src/code_aide/commands_actions.py` -- remove `self_managed` from update check
|
|
74
|
+
|
|
75
|
+
- Line 259: Change `if install_type in ("npm", "self_managed")` to `if
|
|
76
|
+
install_type == "npm"`.
|
|
77
|
+
|
|
78
|
+
### 8. Tests -- update all `self_managed` references
|
|
79
|
+
|
|
80
|
+
- **`tests/test_constants.py`**: Change assertion from `self_managed` to
|
|
81
|
+
`script`; remove npm prerequisite assertion
|
|
82
|
+
- **`tests/test_config.py`**: Change assertion from `self_managed` to
|
|
83
|
+
`script`
|
|
84
|
+
- **`tests/test_detection.py`**:
|
|
85
|
+
- Update `test_detects_brew_npm_wrapper` -- this test was for npm Claude
|
|
86
|
+
installs; replace with test for native installer detection (realpath
|
|
87
|
+
containing `/.local/share/claude/versions/`)
|
|
88
|
+
- Update `test_detects_plain_npm_global` -- same, replace with a variant
|
|
89
|
+
- Remove `TestFormatInstallMethodSelfManaged` class
|
|
90
|
+
- Update `test_brew_npm_label` test -- change to use a different tool's
|
|
91
|
+
npm package (or remove if no tools use brew_npm)
|
|
92
|
+
- **`tests/test_operations.py`**: Change `TestRemoveToolSelfManaged` to test
|
|
93
|
+
script-based removal instead
|
|
94
|
+
- **`tests/test_versions.py`**: Remove `TestFormatCheckBackendSelfManaged`;
|
|
95
|
+
optionally add a test that `script` maps to `script-url`
|
|
96
|
+
|
|
97
|
+
### 9. `README.md` -- update table
|
|
98
|
+
|
|
99
|
+
Change Claude's install type from `Self-managed (npm)` to `Script`.
|
|
100
|
+
|
|
101
|
+
## Verification
|
|
102
|
+
|
|
103
|
+
1. `uv run pytest tests/ -v` -- all tests pass
|
|
104
|
+
2. `uv run python -m code_aide status` -- Claude shows as installed, method
|
|
105
|
+
shows `script`
|
|
106
|
+
3. `uv run python -m code_aide list` -- Claude listed with script install
|
|
107
|
+
type
|
|
108
|
+
4. `uv run python -m code_aide update-versions -n` -- Claude checked via
|
|
109
|
+
script-url backend, SHA256 verified
|
|
110
|
+
5. Manually verify detection: `claude` resolves to `~/.local/bin/claude`
|
|
111
|
+
with realpath `~/.local/share/claude/versions/...`, detected as `script`
|
|
112
|
+
method
|
|
@@ -256,7 +256,7 @@ def cmd_update_versions(args: argparse.Namespace) -> None:
|
|
|
256
256
|
tool_config = tools[name]
|
|
257
257
|
install_type = tool_config["install_type"]
|
|
258
258
|
|
|
259
|
-
if install_type
|
|
259
|
+
if install_type == "npm":
|
|
260
260
|
results.append(check_npm_tool(name, tool_config, args.verbose))
|
|
261
261
|
elif install_type in ("script", "direct_download"):
|
|
262
262
|
results.append(check_script_tool(name, tool_config, args.verbose))
|
|
@@ -28,21 +28,16 @@
|
|
|
28
28
|
"claude": {
|
|
29
29
|
"name": "Claude CLI (Claude Code)",
|
|
30
30
|
"command": "claude",
|
|
31
|
-
"install_type": "
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
|
|
35
|
-
"upgrade"
|
|
36
|
-
],
|
|
37
|
-
"prerequisites": [
|
|
38
|
-
"npm"
|
|
39
|
-
],
|
|
31
|
+
"install_type": "script",
|
|
32
|
+
"install_url": "https://claude.ai/install.sh",
|
|
33
|
+
"install_sha256": "431889ac7d056f636aaf5b71524666d04c89c45560f80329940846479d484778",
|
|
34
|
+
"prerequisites": [],
|
|
40
35
|
"min_node_version": null,
|
|
41
36
|
"next_steps": "Run 'claude' and then use '/login' to authenticate",
|
|
42
37
|
"version_args": [
|
|
43
38
|
"--version"
|
|
44
39
|
],
|
|
45
|
-
"docs_url": "https://
|
|
40
|
+
"docs_url": "https://code.claude.com/docs/en/setup",
|
|
46
41
|
"default_install": true,
|
|
47
42
|
"latest_version": "2.1.63",
|
|
48
43
|
"latest_date": "2026-02-28"
|
|
@@ -32,6 +32,9 @@ def detect_install_method(tool_name: str) -> Dict[str, Optional[str]]:
|
|
|
32
32
|
if caskroom_match:
|
|
33
33
|
return {"method": "brew_cask", "detail": caskroom_match.group(1)}
|
|
34
34
|
|
|
35
|
+
if "/.local/share/claude/versions/" in real_path:
|
|
36
|
+
return {"method": "script", "detail": "native installer"}
|
|
37
|
+
|
|
35
38
|
if "/node_modules/" in real_path:
|
|
36
39
|
npm_package = tool_config.get("npm_package")
|
|
37
40
|
if not npm_package:
|
|
@@ -180,8 +183,6 @@ def format_install_method(method: Optional[str], detail: Optional[str]) -> str:
|
|
|
180
183
|
return "script"
|
|
181
184
|
if method == "direct_download":
|
|
182
185
|
return "direct download"
|
|
183
|
-
if method == "self_managed":
|
|
184
|
-
return "self-managed"
|
|
185
186
|
if method:
|
|
186
187
|
return method
|
|
187
188
|
return "unknown"
|
|
@@ -286,20 +286,6 @@ def install_tool(tool_name: str, dryrun: bool = False) -> bool:
|
|
|
286
286
|
else:
|
|
287
287
|
return False
|
|
288
288
|
|
|
289
|
-
elif install_type == "self_managed":
|
|
290
|
-
npm_package = tool_config.get("npm_package")
|
|
291
|
-
if not npm_package:
|
|
292
|
-
error(f"No npm package configured for {tool_config['name']}")
|
|
293
|
-
return False
|
|
294
|
-
if dryrun:
|
|
295
|
-
info(f"[DRYRUN] Would install npm package: {npm_package}")
|
|
296
|
-
else:
|
|
297
|
-
run_command(["npm", "install", "-g", npm_package], check=True)
|
|
298
|
-
success(f"{tool_config['name']} installed successfully")
|
|
299
|
-
info(tool_config["next_steps"])
|
|
300
|
-
if "docs_url" in tool_config:
|
|
301
|
-
info(f"Documentation: {tool_config['docs_url']}")
|
|
302
|
-
|
|
303
289
|
return True
|
|
304
290
|
|
|
305
291
|
except subprocess.CalledProcessError as exc:
|
|
@@ -62,14 +62,6 @@ def upgrade_tool(tool_name: str) -> bool:
|
|
|
62
62
|
if not install_direct_download(tool_name, tool_config):
|
|
63
63
|
return False
|
|
64
64
|
|
|
65
|
-
elif method == "self_managed":
|
|
66
|
-
upgrade_cmd = tool_config.get("upgrade_command")
|
|
67
|
-
if not upgrade_cmd:
|
|
68
|
-
error(f"No upgrade_command configured for {tool_config['name']}")
|
|
69
|
-
return False
|
|
70
|
-
run_command(upgrade_cmd, check=True, capture=False)
|
|
71
|
-
success(f"{tool_config['name']} upgraded successfully")
|
|
72
|
-
|
|
73
65
|
elif method == "system":
|
|
74
66
|
error(
|
|
75
67
|
f"{tool_config['name']} is managed by the system package manager. "
|
|
@@ -157,6 +149,12 @@ def remove_tool(tool_name: str) -> bool:
|
|
|
157
149
|
warning(f"Could not find {command} binary to remove")
|
|
158
150
|
return True
|
|
159
151
|
|
|
152
|
+
if tool_name == "claude":
|
|
153
|
+
claude_data = os.path.expanduser("~/.local/share/claude")
|
|
154
|
+
if os.path.isdir(claude_data):
|
|
155
|
+
shutil.rmtree(claude_data)
|
|
156
|
+
info(f"Removed data directory: {claude_data}")
|
|
157
|
+
|
|
160
158
|
elif method == "direct_download":
|
|
161
159
|
bin_dir = os.path.expanduser(tool_config.get("bin_dir", "~/.local/bin"))
|
|
162
160
|
removed_links = set()
|
|
@@ -200,35 +198,6 @@ def remove_tool(tool_name: str) -> bool:
|
|
|
200
198
|
|
|
201
199
|
success(f"{tool_config['name']} removed successfully")
|
|
202
200
|
|
|
203
|
-
elif method == "self_managed":
|
|
204
|
-
command = tool_config["command"]
|
|
205
|
-
command_path = shutil.which(command)
|
|
206
|
-
removed_any = False
|
|
207
|
-
if command_path:
|
|
208
|
-
real_path = os.path.realpath(command_path)
|
|
209
|
-
remove_paths = [command_path]
|
|
210
|
-
if real_path != command_path:
|
|
211
|
-
remove_paths.append(real_path)
|
|
212
|
-
|
|
213
|
-
for path in remove_paths:
|
|
214
|
-
if not os.path.lexists(path):
|
|
215
|
-
continue
|
|
216
|
-
is_link = os.path.islink(path)
|
|
217
|
-
if os.path.isdir(path) and not is_link:
|
|
218
|
-
shutil.rmtree(path)
|
|
219
|
-
else:
|
|
220
|
-
os.remove(path)
|
|
221
|
-
if is_link:
|
|
222
|
-
info(f"Removed symlink: {path}")
|
|
223
|
-
else:
|
|
224
|
-
info(f"Removed: {path}")
|
|
225
|
-
removed_any = True
|
|
226
|
-
|
|
227
|
-
if removed_any:
|
|
228
|
-
success(f"{tool_config['name']} removed successfully")
|
|
229
|
-
else:
|
|
230
|
-
warning(f"Could not find {command} binary to remove")
|
|
231
|
-
|
|
232
201
|
elif method == "system":
|
|
233
202
|
error(
|
|
234
203
|
f"{tool_config['name']} is managed by the system package manager. "
|
|
@@ -102,7 +102,7 @@ class TestMergeCachedOverBundled(unittest.TestCase):
|
|
|
102
102
|
tools = code_aide_config.load_tools_config()
|
|
103
103
|
self.assertEqual(tools["claude"]["latest_version"], "99.0.0")
|
|
104
104
|
self.assertEqual(tools["claude"]["latest_date"], "2099-01-01")
|
|
105
|
-
self.assertEqual(tools["claude"]["install_type"], "
|
|
105
|
+
self.assertEqual(tools["claude"]["install_type"], "script")
|
|
106
106
|
|
|
107
107
|
|
|
108
108
|
if __name__ == "__main__":
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""Unit tests for shared CLI constants."""
|
|
2
|
+
|
|
3
|
+
import unittest
|
|
4
|
+
|
|
5
|
+
from code_aide import constants as cli_constants
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class TestClaudeScriptConfig(unittest.TestCase):
|
|
9
|
+
"""Tests for claude tool config expectations."""
|
|
10
|
+
|
|
11
|
+
def test_claude_install_type_is_script(self):
|
|
12
|
+
self.assertEqual(cli_constants.TOOLS["claude"]["install_type"], "script")
|
|
13
|
+
|
|
14
|
+
def test_claude_has_no_prerequisites(self):
|
|
15
|
+
self.assertEqual(cli_constants.TOOLS["claude"].get("prerequisites", []), [])
|
|
16
|
+
|
|
17
|
+
def test_claude_has_install_url(self):
|
|
18
|
+
self.assertIn("install_url", cli_constants.TOOLS["claude"])
|
|
19
|
+
|
|
20
|
+
def test_claude_has_install_sha256(self):
|
|
21
|
+
self.assertIn("install_sha256", cli_constants.TOOLS["claude"])
|
|
@@ -11,10 +11,8 @@ class TestFormatInstallMethod(unittest.TestCase):
|
|
|
11
11
|
|
|
12
12
|
def test_brew_npm_label(self):
|
|
13
13
|
self.assertEqual(
|
|
14
|
-
cli_detection.format_install_method(
|
|
15
|
-
|
|
16
|
-
),
|
|
17
|
-
"Homebrew prefix npm-global (@anthropic-ai/claude-code)",
|
|
14
|
+
cli_detection.format_install_method("brew_npm", "@google/gemini-cli"),
|
|
15
|
+
"Homebrew prefix npm-global (@google/gemini-cli)",
|
|
18
16
|
)
|
|
19
17
|
|
|
20
18
|
def test_brew_formula_label(self):
|
|
@@ -35,28 +33,28 @@ class TestDetectInstallMethod(unittest.TestCase):
|
|
|
35
33
|
|
|
36
34
|
@mock.patch.object(cli_detection.os.path, "realpath")
|
|
37
35
|
@mock.patch.object(cli_detection.shutil, "which")
|
|
38
|
-
def
|
|
39
|
-
mock_which.return_value = "/
|
|
36
|
+
def test_detects_native_installer(self, mock_which, mock_realpath):
|
|
37
|
+
mock_which.return_value = "/Users/test/.local/bin/claude"
|
|
40
38
|
mock_realpath.return_value = (
|
|
41
|
-
"/
|
|
39
|
+
"/Users/test/.local/share/claude/versions/2.1.63/claude"
|
|
42
40
|
)
|
|
43
41
|
|
|
44
42
|
self.assertEqual(
|
|
45
43
|
cli_detection.detect_install_method("claude"),
|
|
46
|
-
{"method": "
|
|
44
|
+
{"method": "script", "detail": "native installer"},
|
|
47
45
|
)
|
|
48
46
|
|
|
49
47
|
@mock.patch.object(cli_detection.os.path, "realpath")
|
|
50
48
|
@mock.patch.object(cli_detection.shutil, "which")
|
|
51
|
-
def
|
|
52
|
-
mock_which.return_value = "/Users/test/.local/bin/
|
|
49
|
+
def test_detects_npm_global_gemini(self, mock_which, mock_realpath):
|
|
50
|
+
mock_which.return_value = "/Users/test/.local/bin/gemini"
|
|
53
51
|
mock_realpath.return_value = (
|
|
54
|
-
"/Users/test/.local/lib/node_modules/
|
|
52
|
+
"/Users/test/.local/lib/node_modules/@google/gemini-cli/cli.js"
|
|
55
53
|
)
|
|
56
54
|
|
|
57
55
|
self.assertEqual(
|
|
58
|
-
cli_detection.detect_install_method("
|
|
59
|
-
{"method": "npm", "detail": "@
|
|
56
|
+
cli_detection.detect_install_method("gemini"),
|
|
57
|
+
{"method": "npm", "detail": "@google/gemini-cli"},
|
|
60
58
|
)
|
|
61
59
|
|
|
62
60
|
@mock.patch.object(cli_detection.os.path, "realpath")
|
|
@@ -108,11 +106,11 @@ class TestFormatInstallMethodDirectDownload(unittest.TestCase):
|
|
|
108
106
|
)
|
|
109
107
|
|
|
110
108
|
|
|
111
|
-
class
|
|
112
|
-
"""Tests for format_install_method with
|
|
109
|
+
class TestFormatInstallMethodScript(unittest.TestCase):
|
|
110
|
+
"""Tests for format_install_method with script method."""
|
|
113
111
|
|
|
114
|
-
def
|
|
112
|
+
def test_script_label(self):
|
|
115
113
|
self.assertEqual(
|
|
116
|
-
cli_detection.format_install_method("
|
|
117
|
-
"
|
|
114
|
+
cli_detection.format_install_method("script", None),
|
|
115
|
+
"script",
|
|
118
116
|
)
|
|
@@ -70,21 +70,22 @@ class TestRemoveToolDirectDownload(unittest.TestCase):
|
|
|
70
70
|
self.assertFalse(os.path.exists(os.path.join(versions_dir, "2.0.0")))
|
|
71
71
|
|
|
72
72
|
|
|
73
|
-
class
|
|
74
|
-
"""Tests for remove_tool with
|
|
73
|
+
class TestRemoveToolScript(unittest.TestCase):
|
|
74
|
+
"""Tests for remove_tool with script install method."""
|
|
75
75
|
|
|
76
|
-
def
|
|
76
|
+
def test_removes_binary(self):
|
|
77
77
|
with tempfile.TemporaryDirectory() as td:
|
|
78
|
-
binary_path = os.path.join(td, "
|
|
78
|
+
binary_path = os.path.join(td, "amp")
|
|
79
79
|
with open(binary_path, "w", encoding="utf-8") as f:
|
|
80
80
|
f.write("#!/bin/sh\n")
|
|
81
81
|
os.chmod(binary_path, 0o755)
|
|
82
82
|
|
|
83
|
-
tool_name = "
|
|
83
|
+
tool_name = "script-test"
|
|
84
84
|
tool_config = {
|
|
85
|
-
"name": "
|
|
86
|
-
"command": "
|
|
87
|
-
"install_type": "
|
|
85
|
+
"name": "Script Test",
|
|
86
|
+
"command": "amp",
|
|
87
|
+
"install_type": "script",
|
|
88
|
+
"install_url": "https://example.com/install.sh",
|
|
88
89
|
}
|
|
89
90
|
|
|
90
91
|
with (
|
|
@@ -96,7 +97,7 @@ class TestRemoveToolSelfManaged(unittest.TestCase):
|
|
|
96
97
|
cli_operations,
|
|
97
98
|
"detect_install_method",
|
|
98
99
|
return_value={
|
|
99
|
-
"method": "
|
|
100
|
+
"method": "script",
|
|
100
101
|
"detail": None,
|
|
101
102
|
},
|
|
102
103
|
),
|
|
@@ -238,16 +238,6 @@ class TestFormatCheckBackend(unittest.TestCase):
|
|
|
238
238
|
self.assertEqual(cli_versions.format_check_backend("custom"), "custom")
|
|
239
239
|
|
|
240
240
|
|
|
241
|
-
class TestFormatCheckBackendSelfManaged(unittest.TestCase):
|
|
242
|
-
"""Tests for format_check_backend with self_managed type."""
|
|
243
|
-
|
|
244
|
-
def test_self_managed_uses_npm_registry(self):
|
|
245
|
-
self.assertEqual(
|
|
246
|
-
cli_versions.format_check_backend("self_managed"),
|
|
247
|
-
"npm-registry",
|
|
248
|
-
)
|
|
249
|
-
|
|
250
|
-
|
|
251
241
|
class TestExtractScriptDate(unittest.TestCase):
|
|
252
242
|
"""Tests for extract_script_date."""
|
|
253
243
|
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
"""Unit tests for shared CLI constants."""
|
|
2
|
-
|
|
3
|
-
import unittest
|
|
4
|
-
|
|
5
|
-
from code_aide import constants as cli_constants
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class TestSelfManagedConfig(unittest.TestCase):
|
|
9
|
-
"""Tests for self-managed tool config expectations."""
|
|
10
|
-
|
|
11
|
-
def test_claude_install_still_requires_npm(self):
|
|
12
|
-
self.assertEqual(cli_constants.TOOLS["claude"]["install_type"], "self_managed")
|
|
13
|
-
self.assertIn("npm", cli_constants.TOOLS["claude"].get("prerequisites", []))
|
|
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
|