code-aide 1.16.2__tar.gz → 1.18.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.
Files changed (68) hide show
  1. {code_aide-1.16.2 → code_aide-1.18.0}/.github/workflows/ci.yml +2 -0
  2. {code_aide-1.16.2 → code_aide-1.18.0}/.gitlab-ci.yml +1 -0
  3. {code_aide-1.16.2 → code_aide-1.18.0}/.pre-commit-config.yaml +8 -0
  4. code_aide-1.18.0/Makefile +29 -0
  5. {code_aide-1.16.2 → code_aide-1.18.0}/PKG-INFO +56 -30
  6. {code_aide-1.16.2 → code_aide-1.18.0}/README.md +55 -29
  7. code_aide-1.18.0/TODO.md +30 -0
  8. {code_aide-1.16.2 → code_aide-1.18.0}/pyproject.toml +0 -3
  9. {code_aide-1.16.2 → code_aide-1.18.0}/src/code_aide/__init__.py +1 -1
  10. {code_aide-1.16.2 → code_aide-1.18.0}/src/code_aide/commands_actions.py +127 -5
  11. {code_aide-1.16.2 → code_aide-1.18.0}/src/code_aide/commands_tools.py +49 -74
  12. {code_aide-1.16.2 → code_aide-1.18.0}/src/code_aide/config.py +38 -12
  13. {code_aide-1.16.2 → code_aide-1.18.0}/src/code_aide/console.py +10 -0
  14. {code_aide-1.16.2 → code_aide-1.18.0}/src/code_aide/constants.py +33 -16
  15. {code_aide-1.16.2 → code_aide-1.18.0}/src/code_aide/data/tools.json +2 -0
  16. {code_aide-1.16.2 → code_aide-1.18.0}/src/code_aide/entry.py +37 -0
  17. {code_aide-1.16.2 → code_aide-1.18.0}/src/code_aide/install.py +66 -7
  18. {code_aide-1.16.2 → code_aide-1.18.0}/src/code_aide/operation_handlers.py +10 -2
  19. {code_aide-1.16.2 → code_aide-1.18.0}/src/code_aide/operations.py +80 -4
  20. {code_aide-1.16.2 → code_aide-1.18.0}/src/code_aide/package_managers.py +1 -1
  21. {code_aide-1.16.2 → code_aide-1.18.0}/src/code_aide/prereqs.py +4 -5
  22. {code_aide-1.16.2 → code_aide-1.18.0}/src/code_aide/status.py +5 -2
  23. {code_aide-1.16.2 → code_aide-1.18.0}/src/code_aide/versions.py +18 -9
  24. code_aide-1.18.0/tests/test_commands_actions.py +732 -0
  25. {code_aide-1.16.2 → code_aide-1.18.0}/tests/test_config.py +54 -2
  26. {code_aide-1.16.2 → code_aide-1.18.0}/tests/test_console.py +16 -0
  27. code_aide-1.18.0/tests/test_constants.py +45 -0
  28. {code_aide-1.16.2 → code_aide-1.18.0}/tests/test_install.py +96 -0
  29. {code_aide-1.16.2 → code_aide-1.18.0}/tests/test_operations.py +73 -0
  30. code_aide-1.18.0/tests/test_package_info.py +57 -0
  31. {code_aide-1.16.2 → code_aide-1.18.0}/tests/test_prereqs.py +29 -0
  32. {code_aide-1.16.2 → code_aide-1.18.0}/tests/test_status.py +46 -0
  33. {code_aide-1.16.2 → code_aide-1.18.0}/tests/test_versions.py +82 -3
  34. code_aide-1.16.2/TODO.md +0 -78
  35. code_aide-1.16.2/tests/test_commands_actions.py +0 -345
  36. code_aide-1.16.2/tests/test_constants.py +0 -21
  37. {code_aide-1.16.2 → code_aide-1.18.0}/.github/workflows/publish.yml +0 -0
  38. {code_aide-1.16.2 → code_aide-1.18.0}/.gitignore +0 -0
  39. {code_aide-1.16.2 → code_aide-1.18.0}/AGENTS.md +0 -0
  40. {code_aide-1.16.2 → code_aide-1.18.0}/CLAUDE.md +0 -0
  41. {code_aide-1.16.2 → code_aide-1.18.0}/LICENSE +0 -0
  42. {code_aide-1.16.2 → code_aide-1.18.0}/script-archive/README.md +0 -0
  43. {code_aide-1.16.2 → code_aide-1.18.0}/script-archive/amp-install.sh +0 -0
  44. {code_aide-1.16.2 → code_aide-1.18.0}/script-archive/antigravity-install.sh +0 -0
  45. {code_aide-1.16.2 → code_aide-1.18.0}/script-archive/claude-install.sh +0 -0
  46. {code_aide-1.16.2 → code_aide-1.18.0}/script-archive/cursor-install.sh +0 -0
  47. {code_aide-1.16.2 → code_aide-1.18.0}/specs/2026-05-03-qwen3.6-code-quality-analysis-original.md +0 -0
  48. {code_aide-1.16.2 → code_aide-1.18.0}/specs/2026-05-03-qwen3.6-code-quality-analysis.md +0 -0
  49. {code_aide-1.16.2 → code_aide-1.18.0}/specs/antigravity-cli-integration.md +0 -0
  50. {code_aide-1.16.2 → code_aide-1.18.0}/specs/auto-migrate-deprecated-installs.md +0 -0
  51. {code_aide-1.16.2 → code_aide-1.18.0}/specs/claude-native-installer-migration.md +0 -0
  52. {code_aide-1.16.2 → code_aide-1.18.0}/specs/missing-coding-llm-cli-tools.md +0 -0
  53. {code_aide-1.16.2 → code_aide-1.18.0}/specs/pre-commit-uv-setup.md +0 -0
  54. {code_aide-1.16.2 → code_aide-1.18.0}/specs/remove-bundled-version-baseline.md +0 -0
  55. {code_aide-1.16.2 → code_aide-1.18.0}/specs/unify-upgrade-eligibility-with-shared-evaluator.md +0 -0
  56. {code_aide-1.16.2 → code_aide-1.18.0}/src/code_aide/__main__.py +0 -0
  57. {code_aide-1.16.2 → code_aide-1.18.0}/src/code_aide/detection.py +0 -0
  58. {code_aide-1.16.2 → code_aide-1.18.0}/src/code_aide/detection_types.py +0 -0
  59. {code_aide-1.16.2 → code_aide-1.18.0}/src/code_aide/install_types.py +0 -0
  60. {code_aide-1.16.2 → code_aide-1.18.0}/src/code_aide/package_info.py +0 -0
  61. {code_aide-1.16.2 → code_aide-1.18.0}/src/code_aide/status_print.py +0 -0
  62. {code_aide-1.16.2 → code_aide-1.18.0}/tests/test_commands_tools.py +0 -0
  63. {code_aide-1.16.2 → code_aide-1.18.0}/tests/test_detection.py +0 -0
  64. {code_aide-1.16.2 → code_aide-1.18.0}/tests/test_entry.py +0 -0
  65. {code_aide-1.16.2 → code_aide-1.18.0}/tests/test_error_paths.py +0 -0
  66. {code_aide-1.16.2 → code_aide-1.18.0}/tests/test_install_types.py +0 -0
  67. {code_aide-1.16.2 → code_aide-1.18.0}/tests/test_package_managers.py +0 -0
  68. {code_aide-1.16.2 → code_aide-1.18.0}/uv.lock +0 -0
@@ -20,5 +20,7 @@ jobs:
20
20
  run: uv run ruff check .
21
21
  - name: Format check
22
22
  run: uv run black --check .
23
+ - name: Type check
24
+ run: uv run mypy
23
25
  - name: Run tests
24
26
  run: uv run pytest tests/ -v
@@ -12,6 +12,7 @@ variables:
12
12
  script:
13
13
  - uv run ruff check .
14
14
  - uv run black --check .
15
+ - uv run mypy
15
16
  - uv run pytest tests/ -v
16
17
 
17
18
  check:python3.11:
@@ -36,3 +36,11 @@ repos:
36
36
  rev: 0.10.6
37
37
  hooks:
38
38
  - id: uv-lock
39
+ - repo: local
40
+ hooks:
41
+ - id: mypy
42
+ name: mypy
43
+ entry: uv run mypy
44
+ language: system
45
+ pass_filenames: false
46
+ files: ^src/code_aide/.*\.py$
@@ -0,0 +1,29 @@
1
+ # code-aide quality checks (uv-based)
2
+
3
+ .PHONY: build install test lint format format-check typecheck hooks check
4
+
5
+ build:
6
+ uv build
7
+
8
+ install:
9
+ uv sync
10
+
11
+ test:
12
+ uv run pytest -n4 -q
13
+
14
+ lint:
15
+ uv run ruff check .
16
+
17
+ format:
18
+ uv run black .
19
+
20
+ format-check:
21
+ uv run black --check .
22
+
23
+ typecheck:
24
+ uv run mypy
25
+
26
+ hooks:
27
+ pre-commit install
28
+
29
+ check: lint format-check typecheck
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: code-aide
3
- Version: 1.16.2
3
+ Version: 1.18.0
4
4
  Summary: Manage AI coding CLI tools (Claude, Antigravity, 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
@@ -49,6 +49,9 @@ pipx install code-aide
49
49
  ## Usage
50
50
 
51
51
  ```bash
52
+ # Show compact one-line-per-tool status (default when no command is given)
53
+ code-aide
54
+
52
55
  # List available tools and their status
53
56
  code-aide list
54
57
 
@@ -67,12 +70,24 @@ code-aide install
67
70
  # Install with automatic prerequisite installation (Node.js, npm)
68
71
  code-aide install -p
69
72
 
73
+ # Verify checksums and show what would be installed, without installing
74
+ code-aide install -n
75
+
70
76
  # Upgrade installed tools (no args = only out-of-date tools)
71
77
  code-aide upgrade [NAMES]
72
78
 
73
- # Remove tools
79
+ # Remove tools (prompts before removing all managed tools)
74
80
  code-aide remove [NAMES]
75
81
 
82
+ # Preview a removal without changing anything
83
+ code-aide remove -n
84
+
85
+ # Remove all managed tools without prompting
86
+ code-aide remove -y
87
+
88
+ # Remove stale version directories of direct-download tools
89
+ code-aide clean [NAMES]
90
+
76
91
  # Check upstream for latest versions (dry-run)
77
92
  code-aide update-versions -n
78
93
 
@@ -106,8 +121,8 @@ code-aide uses a two-layer version data model:
106
121
  versions of code-aide.
107
122
 
108
123
  2. **User's local version cache** (`~/.config/code-aide/versions.json`): Written
109
- by `code-aide update-versions`. Provides latest versions, dates, and updated
110
- SHA256 checksums.
124
+ by `code-aide update-versions`. Provides latest versions, dates, updated
125
+ SHA256 checksums, and recorded tarball checksums for direct_download tools.
111
126
 
112
127
  Run `code-aide update-versions` to get the latest version data without waiting
113
128
  for a new code-aide release.
@@ -118,32 +133,34 @@ Each entry in `tools.json` describes one managed tool. Required fields are
118
133
  `name`, `command`, and `install_type`; the others are optional and only
119
134
  consulted when the relevant install method or platform applies.
120
135
 
121
- | Field | Required | Notes |
122
- |:--------------------------------|:---------|:---------------------------------------------------------------------------------------------|
123
- | `name` | Yes | Display name for status output |
124
- | `command` | Yes | Binary name (looked up in `PATH`) |
125
- | `aliases` | No | Alternate names accepted by commands and resolved to the canonical tool key |
126
- | `install_type` | Yes | One of `npm`, `script`, `direct_download`, `pkg` |
127
- | `version_args` | No | List of arguments passed to the binary to print its version (default: `["--version"]`) |
128
- | `version_url` | No | URL queried by `update-versions` to discover the latest upstream version |
129
- | `version_manifest_url_template` | No | JSON manifest URL template with `{platform}`; reads its `version` field |
130
- | `latest_version` | Cache | Last known upstream version (filled by `update-versions`) |
131
- | `latest_date` | Cache | Release date for `latest_version` |
132
- | `prerequisites` | No | List of required external commands (e.g. `["npm"]`) |
133
- | `min_node_version` | No | Minimum major Node.js version required |
134
- | `npm_package` | npm | npm package name (e.g. `@anthropic-ai/claude-code`) |
135
- | `install_url` | script | URL of the install script that will be downloaded and piped to `bash` |
136
- | `install_sha256` | script | SHA-256 of the install script body — required for verified installs |
137
- | `install_script_path_prepend` | script | Extra directories prepended to `PATH` when the install script runs (e.g. `["~/.local/bin"]`) |
138
- | `download_url_template` | direct | URL template with `{version}`, `{os}`, `{arch}` placeholders |
139
- | `install_dir` | direct | Directory to extract into; supports `{version}` |
140
- | `bin_dir` | direct | Directory where symlinks for `command` are created (e.g. `~/.local/bin`) |
141
- | `symlinks` | direct | Map of `link_name` → relative path inside `install_dir` to symlink into `bin_dir` |
142
- | `freebsd_port` | FreeBSD | FreeBSD ports/pkg name (used when running on FreeBSD) |
143
- | `freebsd_pkg_repo` | FreeBSD | Optional non-default pkg repository name (passed via `pkg install -r <repo>`) |
144
- | `next_steps` | No | Free-form text printed after a successful install |
145
- | `docs_url` | No | Documentation URL printed after install |
146
- | `self_updates` | No | Tool updates itself when run; excludes it from managed upgrade actions |
136
+ | Field | Required | Notes |
137
+ |:--------------------------------|:---------|:----------------------------------------------------------------------------------------------------------------|
138
+ | `name` | Yes | Display name for status output |
139
+ | `command` | Yes | Binary name (looked up in `PATH`) |
140
+ | `aliases` | No | Alternate names accepted by commands and resolved to the canonical tool key |
141
+ | `install_type` | Yes | One of `npm`, `script`, `direct_download`, `pkg` |
142
+ | `version_args` | No | List of arguments passed to the binary to print its version (default: `["--version"]`) |
143
+ | `version_url` | No | URL queried by `update-versions` to discover the latest upstream version |
144
+ | `version_manifest_url_template` | No | JSON manifest URL template with `{platform}`; reads its `version` field |
145
+ | `version_extract_pattern` | No | Regex applied to the install script to extract the version before generic heuristics |
146
+ | `latest_version` | Cache | Last known upstream version (filled by `update-versions`) |
147
+ | `latest_date` | Cache | Release date for `latest_version` |
148
+ | `prerequisites` | No | List of required external commands (e.g. `["npm"]`) |
149
+ | `min_node_version` | No | Minimum major Node.js version required |
150
+ | `npm_package` | npm | npm package name (e.g. `@anthropic-ai/claude-code`) |
151
+ | `install_url` | script | URL of the install script that will be downloaded and piped to `bash` |
152
+ | `install_sha256` | script | SHA-256 of the install script body — required for verified installs |
153
+ | `download_sha256` | Cache | SHA-256 of the downloaded tarball for direct_download tools; recorded by `update-versions`, verified on install |
154
+ | `install_script_path_prepend` | script | Extra directories prepended to `PATH` when the install script runs (e.g. `["~/.local/bin"]`) |
155
+ | `download_url_template` | direct | URL template with `{version}`, `{os}`, `{arch}` placeholders |
156
+ | `install_dir` | direct | Directory to extract into; supports `{version}` |
157
+ | `bin_dir` | direct | Directory where symlinks for `command` are created (e.g. `~/.local/bin`) |
158
+ | `symlinks` | direct | Map of `link_name` → relative path inside `install_dir` to symlink into `bin_dir` |
159
+ | `freebsd_port` | FreeBSD | FreeBSD ports/pkg name (used when running on FreeBSD) |
160
+ | `freebsd_pkg_repo` | FreeBSD | Optional non-default pkg repository name (passed via `pkg install -r <repo>`) |
161
+ | `next_steps` | No | Free-form text printed after a successful install |
162
+ | `docs_url` | No | Documentation URL printed after install |
163
+ | `self_updates` | No | Tool updates itself when run; excludes it from managed upgrade actions |
147
164
 
148
165
  ## Features
149
166
 
@@ -155,6 +172,15 @@ consulted when the relevant install method or platform applies.
155
172
  - PATH configuration validation and warnings
156
173
  - Supports Linux and macOS
157
174
 
175
+ ## Configuration
176
+
177
+ - Tool definitions are bundled at `src/code_aide/data/tools.json`.
178
+ - The version cache lives at `~/.config/code-aide/versions.json` by default; set
179
+ `XDG_CONFIG_HOME` to relocate the `code-aide` config directory.
180
+ - Color output honors `NO_COLOR`, `FORCE_COLOR`, `CLICOLOR_FORCE`, `CLICOLOR=0`,
181
+ and `TERM=dumb`, in that priority order; color is disabled when stdout is not
182
+ a terminal.
183
+
158
184
  ## Requirements
159
185
 
160
186
  - Python 3.11+
@@ -22,6 +22,9 @@ pipx install code-aide
22
22
  ## Usage
23
23
 
24
24
  ```bash
25
+ # Show compact one-line-per-tool status (default when no command is given)
26
+ code-aide
27
+
25
28
  # List available tools and their status
26
29
  code-aide list
27
30
 
@@ -40,12 +43,24 @@ code-aide install
40
43
  # Install with automatic prerequisite installation (Node.js, npm)
41
44
  code-aide install -p
42
45
 
46
+ # Verify checksums and show what would be installed, without installing
47
+ code-aide install -n
48
+
43
49
  # Upgrade installed tools (no args = only out-of-date tools)
44
50
  code-aide upgrade [NAMES]
45
51
 
46
- # Remove tools
52
+ # Remove tools (prompts before removing all managed tools)
47
53
  code-aide remove [NAMES]
48
54
 
55
+ # Preview a removal without changing anything
56
+ code-aide remove -n
57
+
58
+ # Remove all managed tools without prompting
59
+ code-aide remove -y
60
+
61
+ # Remove stale version directories of direct-download tools
62
+ code-aide clean [NAMES]
63
+
49
64
  # Check upstream for latest versions (dry-run)
50
65
  code-aide update-versions -n
51
66
 
@@ -79,8 +94,8 @@ code-aide uses a two-layer version data model:
79
94
  versions of code-aide.
80
95
 
81
96
  2. **User's local version cache** (`~/.config/code-aide/versions.json`): Written
82
- by `code-aide update-versions`. Provides latest versions, dates, and updated
83
- SHA256 checksums.
97
+ by `code-aide update-versions`. Provides latest versions, dates, updated
98
+ SHA256 checksums, and recorded tarball checksums for direct_download tools.
84
99
 
85
100
  Run `code-aide update-versions` to get the latest version data without waiting
86
101
  for a new code-aide release.
@@ -91,32 +106,34 @@ Each entry in `tools.json` describes one managed tool. Required fields are
91
106
  `name`, `command`, and `install_type`; the others are optional and only
92
107
  consulted when the relevant install method or platform applies.
93
108
 
94
- | Field | Required | Notes |
95
- |:--------------------------------|:---------|:---------------------------------------------------------------------------------------------|
96
- | `name` | Yes | Display name for status output |
97
- | `command` | Yes | Binary name (looked up in `PATH`) |
98
- | `aliases` | No | Alternate names accepted by commands and resolved to the canonical tool key |
99
- | `install_type` | Yes | One of `npm`, `script`, `direct_download`, `pkg` |
100
- | `version_args` | No | List of arguments passed to the binary to print its version (default: `["--version"]`) |
101
- | `version_url` | No | URL queried by `update-versions` to discover the latest upstream version |
102
- | `version_manifest_url_template` | No | JSON manifest URL template with `{platform}`; reads its `version` field |
103
- | `latest_version` | Cache | Last known upstream version (filled by `update-versions`) |
104
- | `latest_date` | Cache | Release date for `latest_version` |
105
- | `prerequisites` | No | List of required external commands (e.g. `["npm"]`) |
106
- | `min_node_version` | No | Minimum major Node.js version required |
107
- | `npm_package` | npm | npm package name (e.g. `@anthropic-ai/claude-code`) |
108
- | `install_url` | script | URL of the install script that will be downloaded and piped to `bash` |
109
- | `install_sha256` | script | SHA-256 of the install script body — required for verified installs |
110
- | `install_script_path_prepend` | script | Extra directories prepended to `PATH` when the install script runs (e.g. `["~/.local/bin"]`) |
111
- | `download_url_template` | direct | URL template with `{version}`, `{os}`, `{arch}` placeholders |
112
- | `install_dir` | direct | Directory to extract into; supports `{version}` |
113
- | `bin_dir` | direct | Directory where symlinks for `command` are created (e.g. `~/.local/bin`) |
114
- | `symlinks` | direct | Map of `link_name` → relative path inside `install_dir` to symlink into `bin_dir` |
115
- | `freebsd_port` | FreeBSD | FreeBSD ports/pkg name (used when running on FreeBSD) |
116
- | `freebsd_pkg_repo` | FreeBSD | Optional non-default pkg repository name (passed via `pkg install -r <repo>`) |
117
- | `next_steps` | No | Free-form text printed after a successful install |
118
- | `docs_url` | No | Documentation URL printed after install |
119
- | `self_updates` | No | Tool updates itself when run; excludes it from managed upgrade actions |
109
+ | Field | Required | Notes |
110
+ |:--------------------------------|:---------|:----------------------------------------------------------------------------------------------------------------|
111
+ | `name` | Yes | Display name for status output |
112
+ | `command` | Yes | Binary name (looked up in `PATH`) |
113
+ | `aliases` | No | Alternate names accepted by commands and resolved to the canonical tool key |
114
+ | `install_type` | Yes | One of `npm`, `script`, `direct_download`, `pkg` |
115
+ | `version_args` | No | List of arguments passed to the binary to print its version (default: `["--version"]`) |
116
+ | `version_url` | No | URL queried by `update-versions` to discover the latest upstream version |
117
+ | `version_manifest_url_template` | No | JSON manifest URL template with `{platform}`; reads its `version` field |
118
+ | `version_extract_pattern` | No | Regex applied to the install script to extract the version before generic heuristics |
119
+ | `latest_version` | Cache | Last known upstream version (filled by `update-versions`) |
120
+ | `latest_date` | Cache | Release date for `latest_version` |
121
+ | `prerequisites` | No | List of required external commands (e.g. `["npm"]`) |
122
+ | `min_node_version` | No | Minimum major Node.js version required |
123
+ | `npm_package` | npm | npm package name (e.g. `@anthropic-ai/claude-code`) |
124
+ | `install_url` | script | URL of the install script that will be downloaded and piped to `bash` |
125
+ | `install_sha256` | script | SHA-256 of the install script body — required for verified installs |
126
+ | `download_sha256` | Cache | SHA-256 of the downloaded tarball for direct_download tools; recorded by `update-versions`, verified on install |
127
+ | `install_script_path_prepend` | script | Extra directories prepended to `PATH` when the install script runs (e.g. `["~/.local/bin"]`) |
128
+ | `download_url_template` | direct | URL template with `{version}`, `{os}`, `{arch}` placeholders |
129
+ | `install_dir` | direct | Directory to extract into; supports `{version}` |
130
+ | `bin_dir` | direct | Directory where symlinks for `command` are created (e.g. `~/.local/bin`) |
131
+ | `symlinks` | direct | Map of `link_name` → relative path inside `install_dir` to symlink into `bin_dir` |
132
+ | `freebsd_port` | FreeBSD | FreeBSD ports/pkg name (used when running on FreeBSD) |
133
+ | `freebsd_pkg_repo` | FreeBSD | Optional non-default pkg repository name (passed via `pkg install -r <repo>`) |
134
+ | `next_steps` | No | Free-form text printed after a successful install |
135
+ | `docs_url` | No | Documentation URL printed after install |
136
+ | `self_updates` | No | Tool updates itself when run; excludes it from managed upgrade actions |
120
137
 
121
138
  ## Features
122
139
 
@@ -128,6 +145,15 @@ consulted when the relevant install method or platform applies.
128
145
  - PATH configuration validation and warnings
129
146
  - Supports Linux and macOS
130
147
 
148
+ ## Configuration
149
+
150
+ - Tool definitions are bundled at `src/code_aide/data/tools.json`.
151
+ - The version cache lives at `~/.config/code-aide/versions.json` by default; set
152
+ `XDG_CONFIG_HOME` to relocate the `code-aide` config directory.
153
+ - Color output honors `NO_COLOR`, `FORCE_COLOR`, `CLICOLOR_FORCE`, `CLICOLOR=0`,
154
+ and `TERM=dumb`, in that priority order; color is disabled when stdout is not
155
+ a terminal.
156
+
131
157
  ## Requirements
132
158
 
133
159
  - Python 3.11+
@@ -0,0 +1,30 @@
1
+ # TODO
2
+
3
+ Keep the project focused on managing AI coding CLI tools (install, upgrade,
4
+ remove, status, and version metadata).
5
+
6
+ All items below are deferred; none are scheduled.
7
+
8
+ | # | Item | Risk | Effort | Notes |
9
+ |:---|:-------------------------------|:-----|:-------|:-------------------------------------------------------|
10
+ | 1 | `doctor` command | Low | Med | Health checks exist but are scattered |
11
+ | 2 | Cross-distro package detection | Med | High | Only Gentoo supported; other Linux distros get nothing |
12
+ | 3 | Split large modules | Low | High | Refactoring only; no behavior change |
13
+ | 4 | Tests for edge cases | Med | Med | PATH oddities, more version parsing corners |
14
+
15
+ ## CLI and Automation UX
16
+
17
+ - [ ] Add a focused `doctor` command for environment checks (PATH,
18
+ prerequisites, command health).
19
+
20
+ ## Platform and Package Detection
21
+
22
+ - [ ] Broaden system package metadata detection beyond Gentoo-specific tooling
23
+ where practical; note that non-Gentoo system installs currently report "up to
24
+ date" because no available version is detectable.
25
+
26
+ ## Maintainability and Tests
27
+
28
+ - [ ] Split larger modules into smaller focused files where practical
29
+ (`install.py`, `commands_actions.py`, `versions.py`).
30
+ - [ ] Add tests for more prerequisite edge cases and PATH oddities.
@@ -47,9 +47,6 @@ filterwarnings = [
47
47
  "error",
48
48
  "ignore::DeprecationWarning:_pytest.*",
49
49
  ]
50
- markers = [
51
- "network: tests that make real network calls (deselect with '-m \"not network\"')",
52
- ]
53
50
 
54
51
  [tool.mypy]
55
52
  python_version = "3.11"
@@ -1,3 +1,3 @@
1
1
  """code-aide - Manage AI coding CLI tools."""
2
2
 
3
- __version__ = "1.16.2"
3
+ __version__ = "1.18.0"
@@ -6,7 +6,7 @@ from typing import Any, Dict, List
6
6
 
7
7
  from code_aide.constants import TOOLS
8
8
  from code_aide.detection import detect_install_method, get_brew_package_info
9
- from code_aide.install import install_tool
9
+ from code_aide.install import fetch_download_checksum, install_tool
10
10
  from code_aide.install_types import (
11
11
  InstallMethod,
12
12
  InstallType,
@@ -16,6 +16,7 @@ from code_aide.install_types import (
16
16
  from code_aide.console import error, info, success, warning
17
17
  from code_aide.operations import (
18
18
  UpgradeResult,
19
+ clean_tool_versions,
19
20
  remove_tool,
20
21
  upgrade_tool,
21
22
  validate_tools,
@@ -34,6 +35,7 @@ from code_aide.versions import (
34
35
  print_check_results_table,
35
36
  )
36
37
  from code_aide.config import (
38
+ ensure_versions_cache,
37
39
  load_bundled_tools,
38
40
  load_versions_cache,
39
41
  merge_cached_versions,
@@ -45,6 +47,7 @@ from code_aide.config import (
45
47
  def cmd_install(args: argparse.Namespace) -> None:
46
48
  """Handle install command."""
47
49
  dryrun = getattr(args, "dryrun", False)
50
+ force = getattr(args, "force", False)
48
51
 
49
52
  if args.tools:
50
53
  tools_to_install = args.tools
@@ -75,7 +78,7 @@ def cmd_install(args: argparse.Namespace) -> None:
75
78
  )
76
79
 
77
80
  installed = []
78
- installed_bin_dirs = []
81
+ installed_bin_dirs: List[str] = []
79
82
  failed = []
80
83
 
81
84
  for tool in tools_to_install:
@@ -85,7 +88,7 @@ def cmd_install(args: argparse.Namespace) -> None:
85
88
  else:
86
89
  info(f"=== Installing {tool} ===")
87
90
 
88
- outcome = install_tool(tool, dryrun=dryrun)
91
+ outcome = install_tool(tool, dryrun=dryrun, force=force)
89
92
  if outcome:
90
93
  installed.append(tool)
91
94
  installed_bin_dirs.extend(outcome.bin_dirs)
@@ -136,6 +139,9 @@ def cmd_upgrade(args: argparse.Namespace) -> None:
136
139
  info(f"Upgrading specified tools: {', '.join(tools_to_upgrade)}")
137
140
  else:
138
141
  tools_to_upgrade = []
142
+ # Default selection is driven by latest_version, so refresh stale
143
+ # cache data first; named upgrades below skip this.
144
+ ensure_versions_cache(TOOLS)
139
145
  for name, config in TOOLS.items():
140
146
  assessment = ToolUpgradeEvaluator(name, config).evaluate()
141
147
  if assessment.actionable_by_upgrade and name not in tools_to_upgrade:
@@ -198,14 +204,37 @@ def cmd_upgrade(args: argparse.Namespace) -> None:
198
204
  )
199
205
 
200
206
 
207
+ def _confirm_remove_all(tools: List[str]) -> bool:
208
+ """Prompt before removing every managed tool."""
209
+ print(f"This will remove {len(tools)} managed tools: {', '.join(tools)}")
210
+ try:
211
+ answer = input("Remove all of them? [y/N] ").strip().lower()
212
+ except (EOFError, KeyboardInterrupt):
213
+ print()
214
+ return False
215
+ return answer in ("y", "yes")
216
+
217
+
201
218
  def cmd_remove(args: argparse.Namespace) -> None:
202
219
  """Handle remove command."""
220
+ dryrun = getattr(args, "dryrun", False)
221
+ assume_yes = getattr(args, "yes", False)
222
+
203
223
  if args.tools:
204
224
  tools_to_remove = args.tools
205
225
  info(f"Removing specified tools: {', '.join(tools_to_remove)}")
206
226
  else:
207
227
  tools_to_remove = list(TOOLS.keys())
208
- info(f"No tools specified, removing all: {', '.join(tools_to_remove)}")
228
+ if dryrun:
229
+ info(
230
+ "[DRYRUN] Would remove all managed tools: "
231
+ f"{', '.join(tools_to_remove)}"
232
+ )
233
+ else:
234
+ info(f"No tools specified, removing all: {', '.join(tools_to_remove)}")
235
+ if not assume_yes and not _confirm_remove_all(tools_to_remove):
236
+ print("Aborted.")
237
+ return
209
238
 
210
239
  tools_to_remove = resolve_tool_names(tools_to_remove, TOOLS)
211
240
  validate_tools(tools_to_remove)
@@ -216,6 +245,14 @@ def cmd_remove(args: argparse.Namespace) -> None:
216
245
 
217
246
  for tool in tools_to_remove:
218
247
  print()
248
+ if dryrun:
249
+ info(f"=== Checking {tool} ===")
250
+ if is_tool_installed(tool):
251
+ removed.append(tool)
252
+ else:
253
+ skipped.append(tool)
254
+ continue
255
+
219
256
  info(f"=== Removing {tool} ===")
220
257
 
221
258
  if not is_tool_installed(tool):
@@ -229,8 +266,19 @@ def cmd_remove(args: argparse.Namespace) -> None:
229
266
 
230
267
  print()
231
268
  print("=" * 42)
232
- info("Removal Summary")
269
+ if dryrun:
270
+ info("Removal Summary (dry run)")
271
+ else:
272
+ info("Removal Summary")
233
273
  print("=" * 42)
274
+ if dryrun:
275
+ if removed:
276
+ success(f"Would remove: {', '.join(removed)}")
277
+ if skipped:
278
+ warning(f"Not installed (skipped): {', '.join(skipped)}")
279
+ print()
280
+ success("Dry run completed without removing anything!")
281
+ return
234
282
 
235
283
  if removed:
236
284
  success(f"Successfully removed: {', '.join(removed)}")
@@ -248,6 +296,56 @@ def cmd_remove(args: argparse.Namespace) -> None:
248
296
  info("No tools were removed (all were not installed)")
249
297
 
250
298
 
299
+ def cmd_clean(args: argparse.Namespace) -> None:
300
+ """Handle clean command: remove stale direct-download version dirs."""
301
+ dryrun = getattr(args, "dryrun", False)
302
+
303
+ if args.tools:
304
+ tools_to_clean = resolve_tool_names(args.tools, TOOLS)
305
+ else:
306
+ tools_to_clean = [
307
+ name
308
+ for name, config in TOOLS.items()
309
+ if parse_install_type(config.get("install_type"))
310
+ == InstallType.DIRECT_DOWNLOAD
311
+ ]
312
+ if not tools_to_clean:
313
+ info("No direct-download tools with cleanable versions")
314
+ return
315
+ info(
316
+ "No tools specified, cleaning direct-download tools: "
317
+ f"{', '.join(tools_to_clean)}"
318
+ )
319
+
320
+ validate_tools(tools_to_clean)
321
+
322
+ cleaned: Dict[str, List[str]] = {}
323
+ skipped = []
324
+ for tool in tools_to_clean:
325
+ print()
326
+ info(f"=== {'Checking' if dryrun else 'Cleaning'} {tool} ===")
327
+ removed = clean_tool_versions(tool, dryrun=dryrun)
328
+ if removed is None:
329
+ skipped.append(tool)
330
+ elif removed:
331
+ cleaned[tool] = removed
332
+
333
+ print()
334
+ print("=" * 42)
335
+ info("Cleanup Summary" + (" (dry run)" if dryrun else ""))
336
+ print("=" * 42)
337
+
338
+ if cleaned:
339
+ total = sum(len(paths) for paths in cleaned.values())
340
+ details = ", ".join(f"{tool} ({len(paths)})" for tool, paths in cleaned.items())
341
+ noun = "Would remove" if dryrun else "Removed"
342
+ success(f"{noun} {total} stale version directories: {details}")
343
+ if skipped:
344
+ info(f"Not cleanable (no versioned install layout): {', '.join(skipped)}")
345
+ if not cleaned:
346
+ success("Nothing to clean!")
347
+
348
+
251
349
  def cmd_update_versions(args: argparse.Namespace) -> None:
252
350
  """Handle update-versions command: check upstream for latest tool versions."""
253
351
  bundled = load_bundled_tools()
@@ -326,6 +424,30 @@ def cmd_update_versions(args: argparse.Namespace) -> None:
326
424
  tool_entry["latest_version"] = normalized
327
425
  version_info_changed = True
328
426
 
427
+ # Record the tarball checksum for direct-download tools so later
428
+ # installs can verify integrity against it.
429
+ if not args.dryrun:
430
+ for tool_name in tool_names:
431
+ tool_entry = config["tools"][tool_name]
432
+ if (
433
+ parse_install_type(tool_entry.get("install_type"))
434
+ != InstallType.DIRECT_DOWNLOAD
435
+ ):
436
+ continue
437
+ if "download_url_template" not in tool_entry:
438
+ continue
439
+ if not tool_entry.get("latest_version"):
440
+ continue
441
+ checksum = fetch_download_checksum(tool_entry)
442
+ if not checksum:
443
+ continue
444
+ if tool_entry.get("download_sha256") != checksum:
445
+ tool_entry["download_sha256"] = checksum
446
+ version_info_changed = True
447
+ print(f"Recorded download SHA256 for {tool_name}.")
448
+ else:
449
+ print(f"Download SHA256 for {tool_name} is unchanged.")
450
+
329
451
  def _save(tools: dict) -> str:
330
452
  """Save versions to user cache. Returns description."""
331
453
  save_updated_versions(tools)