Add package manager & Update build process & Update npm lib process installation#18
Add package manager & Update build process & Update npm lib process installation#18Maciek-roboblog merged 35 commits intomainfrom
Conversation
Add modern Python packaging with uv tool installation support: - Add pyproject.toml with console script entry points (ccusage-monitor, claude-monitor) - Update README.md to prioritize uv installation over legacy methods - Add CLAUDE.md development documentation - Add comprehensive .gitignore for Python project Users can now install with: uv tool install claude-usage-monitor Maintains full backwards compatibility with existing installation methods
Update installation docs to use local directory instead of PyPI: - Change from 'uv tool install claude-usage-monitor' to 'uv tool install .' - Add git clone step since package not yet published to PyPI - Clarify platform-specific uv installation commands Fixes installation errors for users trying to install from PyPI
feat: add uv package manager support
- Add comprehensive .ruff.toml configuration with essential linting rules - Set up pre-commit hooks for automated code quality checks - Create GitHub Actions CI workflow for ruff validation - Configure VS Code settings for real-time ruff integration - Format existing codebase to comply with ruff standards - Update documentation with ruff usage instructions - Update Python requirement from 3.6+ to 3.7+ (ruff minimum) - Replace outdated tools (black, flake8) with modern ruff tooling Resolves #15
feat: integrate ruff for code quality and formatting
|
@taylorwilsdon We should merge it changes with #11 What do you think? |
I say we merge this one in and then cherry pick what we want from #11 |
|
All future releases will be much smaller, but this one was a bit hard for me to split up. |
|
looks great, consider adding @coderabbitai to help review these complicated/big ones |
adawalli
left a comment
There was a problem hiding this comment.
LGTM, but definitely hard to spot problems in something so big :)
|
Caution Review failedThe pull request is closed. WalkthroughThis update introduces a modernized project structure for the Claude Code Usage Monitor, adding comprehensive documentation, automated code quality tools, robust dependency checks, and improved packaging. The main script is refactored for better terminal and dependency handling. New GitHub Actions workflows automate linting, versioning, and release processes, while development and troubleshooting guides are expanded. Changes
Sequence Diagram(s)Main Dependency and Monitoring FlowsequenceDiagram
participant User
participant claude_monitor.py
participant check_dependency.py
participant Terminal
participant npx/ccusage
User->>claude_monitor.py: Run monitor
claude_monitor.py->>check_dependency.py: test_node()
check_dependency.py-->>claude_monitor.py: Node.js check result
claude_monitor.py->>check_dependency.py: test_npx()
check_dependency.py-->>claude_monitor.py: npx check result
claude_monitor.py->>Terminal: Setup raw mode
claude_monitor.py->>npx/ccusage: Run "npx ccusage blocks --json"
npx/ccusage-->>claude_monitor.py: Usage data (JSON)
claude_monitor.py->>Terminal: Display usage, progress bars, status
claude_monitor.py->>Terminal: Restore settings on exit/error
Poem
📜 Recent review detailsConfiguration used: .coderabbit.yaml 📒 Files selected for processing (2)
✨ Finishing Touches
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
Documentation and Community
|
There was a problem hiding this comment.
Actionable comments posted: 16
♻️ Duplicate comments (1)
.vscode/settings.json (1)
1-17: Well-configured VS Code settings for Python development.The configuration properly sets up Ruff as the primary linter and formatter with appropriate save actions.
Note: This conflicts with the
.gitignoreexclusion of.vscode/directory as mentioned in the.gitignorereview.
🧹 Nitpick comments (10)
CLAUDE.md (1)
173-178: Add language identifier to code blockAdd
tomllanguage identifier for proper syntax highlighting.### Console Script Entry Points The `pyproject.toml` defines two console commands: -``` +```toml [project.scripts] claude-monitor = "claude_monitor:main"</blockquote></details> <details> <summary>claude_monitor.py (2)</summary><blockquote> `394-397`: **Simplify token limit update logic** Use `max()` for more concise code as suggested by static analysis. ```diff new_limit = get_token_limit("custom_max", data["blocks"]) -if new_limit > token_limit: - token_limit = new_limit +token_limit = max(token_limit, new_limit)
317-541: Consider refactoring main() to reduce complexityThe
main()function has grown quite complex with 39 local variables, 22 branches, and 118 statements. Consider extracting logical sections into separate functions for better maintainability:
- Session data processing
- Display formatting and output
- Token limit detection and switching
- Time calculations
This would improve readability and make the code easier to test and maintain.
TROUBLESHOOTING.md (7)
11-13: Quick fixes table formatting
The new rows render correctly, but consider replacing the tilde in~/.local/binwith$HOME/.local/binfor clarity and consistency. Also verify that the table alignment displays properly in all Markdown renderers.
15-15: Clarify “Permission denied” quick fix
Wrap thechmod +x claude_monitor.pysnippet in backticks for consistency, e.g.:chmod +x claude_monitor.pyAnd consider adding a note that this only applies when running from source.
22-28: Specify language for the error code block
The fenced block showing the externally-managed-environment error should include a language tag (e.g.,bashortext) to satisfy lint rules and improve readability:error: externally-managed-environment × This environment is externally managed
30-31: Use a heading instead of bold emphasis
The line**This is common on Ubuntu 23.04+, Debian 12+, Fedora 38+**should be a proper Markdown heading (e.g.,
###or####) for semantic consistency.
118-118: Add language to fenced code block
The fence before theFailed to get usage datasnippet is missing a language tag. Addbash(ortext) after the opening backticks:Failed to get usage data: [Errno 2] No such file or directory: 'ccusage'
173-173: Mark error snippet with language
Fenced code showing the permission error should specify a language:Permission denied: ./claude_monitor.py
177-177: Convert emphasis to heading
The line**This only applies when running from source**functions as a subheading—switch to a Markdown header (e.g.,
####) for clarity.
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (15)
.coderabbit.yaml(1 hunks).github/workflows/lint.yml(1 hunks).gitignore(1 hunks).pre-commit-config.yaml(1 hunks).ruff.toml(1 hunks).vscode/settings.json(1 hunks)CHANGELOG.md(1 hunks)CLAUDE.md(1 hunks)CONTRIBUTING.md(9 hunks)DEVELOPMENT.md(5 hunks)README.md(21 hunks)TROUBLESHOOTING.md(14 hunks)claude_monitor.py(5 hunks)init_dependency.py(1 hunks)pyproject.toml(1 hunks)
🧰 Additional context used
🪛 Pylint (3.3.7)
claude_monitor.py
[refactor] 273-274: Consider using 'max_tokens = max(max_tokens, tokens)' instead of unnecessary if block
(R1731)
[refactor] 317-317: Too many local variables (39/15)
(R0914)
[refactor] 395-396: Consider using 'token_limit = max(token_limit, new_limit)' instead of unnecessary if block
(R1731)
[refactor] 317-317: Too many branches (22/12)
(R0912)
[refactor] 317-317: Too many statements (118/50)
(R0915)
🪛 LanguageTool
README.md
[uncategorized] ~133-~133: Possible missing preposition found.
Context: ...e above) - it's safer and easier > 2. Use a virtual environment - `python3 -m v...
(AI_HYDRA_LEO_MISSING_TO)
[uncategorized] ~232-~232: Use a comma before ‘and’ if it connects two independent clauses (unless they are closely connected and short).
Context: ... need your help!** This is a new release and we want to ensure it works perfectly on...
(COMMA_COMPOUND_SENTENCE)
[style] ~249-~249: The wording of this phrase can be improved.
Context: ...issues/new) > > Thank you for helping make this tool better! 🚀 ## ✨ Features & How It Works #...
(MAKE_STYLE_BETTER)
🪛 markdownlint-cli2 (0.17.2)
README.md
454-454: Emphasis used instead of a heading
null
(MD036, no-emphasis-as-heading)
562-562: Emphasis used instead of a heading
null
(MD036, no-emphasis-as-heading)
701-701: Fenced code blocks should have a language specified
null
(MD040, fenced-code-language)
TROUBLESHOOTING.md
25-25: Fenced code blocks should have a language specified
null
(MD040, fenced-code-language)
30-30: Emphasis used instead of a heading
null
(MD036, no-emphasis-as-heading)
118-118: Fenced code blocks should have a language specified
null
(MD040, fenced-code-language)
173-173: Fenced code blocks should have a language specified
null
(MD040, fenced-code-language)
177-177: Emphasis used instead of a heading
null
(MD036, no-emphasis-as-heading)
🔇 Additional comments (23)
.ruff.toml (1)
1-12: LGTM! Well-configured Ruff setup.The configuration follows best practices with appropriate rule selection, reasonable line length, and proper separation of concerns between linting and formatting.
.coderabbit.yaml (1)
1-16: LGTM! Proper CodeRabbit configuration.The configuration is valid according to the schema and uses sensible settings for automated code reviews with a balanced "chill" profile.
CONTRIBUTING.md (5)
96-96: Approve PEP 8 example update
The “Bad: Unclear abbreviations” example is clear and aligns with the new style guide.
108-108: Docstring formatting is consistent
The adjusted indentation and spacing in the example docstring maintain readability.
335-335: Coverage targets updated
Lowering ML components coverage from 95% to 90% is reasonable given the complexity.
346-346: Python versions matrix updated
Adding support for 3.12 and removing 3.6 reflects current compatibility requirements.
389-389: Session duration preference entry
The new bullet for session duration preferences enhances the research section.DEVELOPMENT.md (5)
9-9: ML Status label added
Marking the ML-Powered Auto Mode as “In Active Development” correctly sets expectations for contributors.
136-136: Docker section introduction
The new “🐳 Docker Image” heading outlines upcoming containerization work.
317-323: Code Quality Tools section
The Ruff and pre-commit installation instructions are clear; ensure theuv tool install pre-commitcommand installs the correct plugin.
355-355: Development workflow update
Noting that code is auto-formatted and linted via hooks replaces manual PEP 8 steps accurately.
395-400: Developer contact details
The updated email and GitHub contact points are precise for contributor questions..pre-commit-config.yaml (2)
5-14: Ruff hooks pinned to v0.8.4
Pinning ensures reproducible CI but verify that v0.8.4 supports all current Python versions (3.7–3.12).
15-25: Standard pre-commit-hooks configuration
The selected hooks cover whitespace, EOL, YAML/TOML validation, and merge conflict detection. Looks good..github/workflows/lint.yml (2)
27-32: Ruff checks and formatting
Running bothruff checkandruff format --checkensures code style consistency. Good integration.
47-57: Pre-commit job enforcement
The pre-commit run with diff-on-failure instructs contributors to fix locally—this will keep the codebase clean.CHANGELOG.md (5)
1-7: Version 1.0.8 entry added
Documenting the automatic Node.js installation support is accurate and clear.
8-15: Version 1.0.7 changes
The enhancements toinit_dependency.pyare thoroughly summarized.
16-30: Version 1.0.6 additions
The extensive packaging and tooling improvements are well documented.
31-37: “Changed” section for v1.0.6
Accurate reflection of renaming and command updates.
38-42: “Fixed” section for v1.0.6
The terminal corruption fixes are clearly listed.claude_monitor.py (2)
282-315: Well-implemented terminal handlingThe terminal management functions properly handle raw mode setup and restoration, with appropriate fallbacks for systems without termios support. The flush_input() function effectively prevents display corruption from stray keyboard input.
425-427: Fix incorrect command name in commentThe comment references
ccusage-monitorbut the actual command defined inpyproject.tomlisclaude-monitor.# Press Ctrl+C after checking status -# ccusage-monitor +# claude-monitorLikely an incorrect or invalid review comment.
✅ Actions performedReviews resumed. |
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (1)
init_dependency.py (1)
58-67: Security concern: Downloaded files lack integrity verification.The script downloads Node.js archives without verifying checksums or signatures, which poses a security risk. Malicious actors could potentially serve compromised downloads.
Consider adding checksum verification:
+import hashlib filename = f"node-v{NODE_VERSION}-{system}-{arch}.tar.xz" url = f"{NODE_DIST_URL}/v{NODE_VERSION}/{filename}" +checksum_url = f"{url}.sha256" print(f"Downloading Node.js from {url}") urllib.request.urlretrieve(url, filename) +urllib.request.urlretrieve(checksum_url, f"{filename}.sha256") + +# Verify checksum +with open(f"{filename}.sha256", 'r') as f: + expected = f.read().split()[0] +with open(filename, 'rb') as f: + actual = hashlib.sha256(f.read()).hexdigest() +if actual != expected: + print("❌ Checksum verification failed!") + sys.exit(1) +print("✓ Checksum verified")
🧹 Nitpick comments (5)
init_dependency.py (3)
78-187: Refactor to reduce function complexity.This function has 84 statements, exceeding the recommended 50. The extensive error handling and user interaction logic could be broken down into smaller, more focused functions.
Consider extracting helper functions:
+def get_user_consent_for_installation(): + """Get user consent for Node.js installation with detailed prompts.""" + # Move lines 86-116 here + +def download_node_installer(url, filename): + """Download Node.js MSI installer with error handling.""" + # Move lines 121-139 here + +def run_msi_installer(filename): + """Run MSI installer with comprehensive error handling.""" + # Move lines 144-180 here def install_node_windows(): """Install Node.js on Windows using MSI installer with user consent.""" + if not get_user_consent_for_installation(): + sys.exit(0) + + filename = download_node_installer(url, filename) + run_msi_installer(filename) # Rest of function
190-268: Improve error handling granularity.The function has 59 statements and handles multiple types of errors. Consider breaking down error handling to be more specific and actionable.
Extract specific error handling functions:
+def handle_ccusage_installation_errors(global_error, local_error=None): + """Provide specific troubleshooting for ccusage installation failures.""" + print("\n❌ ccusage installation failed.") + # Move error analysis logic here + +def attempt_ccusage_installation(): + """Attempt ccusage installation with fallback strategies.""" + # Move installation logic here + def ensure_ccusage_available(): """Ensure ccusage is available via npx.""" + if is_ccusage_available(): + return + attempt_ccusage_installation()
10-11: Consider making Node.js version configurable.The hardcoded Node.js version might become outdated. Consider making it configurable or adding a mechanism to use the latest LTS version.
-NODE_VERSION = "18.17.1" -NODE_DIST_URL = "https://nodejs.org/dist" +NODE_VERSION = os.environ.get("NODE_VERSION", "18.17.1") +NODE_DIST_URL = os.environ.get("NODE_DIST_URL", "https://nodejs.org/dist")Or add a function to fetch the latest LTS version from Node.js API.
claude_monitor.py (1)
271-274: Minor optimization opportunity.Static analysis suggests using
max()instead of the if block for finding maximum tokens.- if tokens > max_tokens: - max_tokens = tokens + max_tokens = max(max_tokens, tokens)CLAUDE.md (1)
12-12: Update line count to match current file size.The documentation states 545 lines, but
claude_monitor.pycurrently has 541 lines based on the provided code.-This is a single-file Python application (545 lines) with modern packaging: +This is a single-file Python application (541 lines) with modern packaging:
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
.github/workflows/lint.yml(1 hunks).gitignore(1 hunks)CLAUDE.md(1 hunks)CONTRIBUTING.md(9 hunks)README.md(21 hunks)claude_monitor.py(5 hunks)init_dependency.py(1 hunks)pyproject.toml(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
- CONTRIBUTING.md
- .gitignore
- .github/workflows/lint.yml
- pyproject.toml
🧰 Additional context used
🪛 Pylint (3.3.7)
init_dependency.py
[refactor] 95-115: Unnecessary "elif" after "break", remove the leading "el" from "elif"
(R1723)
[refactor] 78-78: Too many statements (84/50)
(R0915)
[refactor] 190-190: Too many statements (59/50)
(R0915)
claude_monitor.py
[refactor] 273-274: Consider using 'max_tokens = max(max_tokens, tokens)' instead of unnecessary if block
(R1731)
[refactor] 317-317: Too many local variables (36/15)
(R0914)
[refactor] 395-396: Consider using 'token_limit = max(token_limit, new_limit)' instead of unnecessary if block
(R1731)
[refactor] 317-317: Too many branches (22/12)
(R0912)
[refactor] 317-317: Too many statements (114/50)
(R0915)
🪛 LanguageTool
README.md
[uncategorized] ~133-~133: Possible missing preposition found.
Context: ...e above) - it's safer and easier > 2. Use a virtual environment - `python3 -m v...
(AI_HYDRA_LEO_MISSING_TO)
[uncategorized] ~232-~232: Use a comma before ‘and’ if it connects two independent clauses (unless they are closely connected and short).
Context: ... need your help!** This is a new release and we want to ensure it works perfectly on...
(COMMA_COMPOUND_SENTENCE)
[style] ~249-~249: The wording of this phrase can be improved.
Context: ...issues/new) > > Thank you for helping make this tool better! 🚀 ## ✨ Features & How It Works #...
(MAKE_STYLE_BETTER)
🪛 markdownlint-cli2 (0.17.2)
README.md
454-454: Emphasis used instead of a heading
null
(MD036, no-emphasis-as-heading)
562-562: Emphasis used instead of a heading
null
(MD036, no-emphasis-as-heading)
701-701: Fenced code blocks should have a language specified
null
(MD040, fenced-code-language)
🔇 Additional comments (9)
claude_monitor.py (3)
13-13: Good integration with dependency management.The import of
ensure_node_installedproperly integrates with the new dependency management system, ensuring Node.js and ccusage are available before the monitor starts.
27-32: Excellent update to use npx for ccusage execution.The change from direct
ccusagetonpx ccusageis a smart improvement that leverages npm's package execution capabilities and aligns with the new dependency management approach.
282-315: Well-implemented terminal handling with proper error handling.The terminal setup and restoration functions properly handle the case where termios is not available (e.g., on Windows) and include appropriate exception handling for edge cases.
CLAUDE.md (2)
1-191: Comprehensive documentation with accurate technical details.The documentation provides excellent coverage of the project architecture, installation methods, development commands, and technical implementation details. The structure is well-organized and user-friendly.
25-27: Verify function line numbers for accuracy.The line numbers for key functions should be verified against the actual file to ensure developers can easily locate them.
#!/bin/bash # Check actual line numbers for key functions in claude_monitor.py echo "Checking function locations:" grep -n "def run_ccusage" claude_monitor.py grep -n "def calculate_hourly_burn_rate" claude_monitor.py grep -n "def main" claude_monitor.pyREADME.md (4)
60-876: Comprehensive and well-structured documentation update.The README provides excellent coverage of installation methods, usage examples, troubleshooting, and development setup. The addition of automatic dependency installation explanation and modern installation methods (uv, pipx) significantly improves the user experience.
587-589: Update Python version requirement.The documentation mentions Python 3.7+, but earlier in the README (line 4) it shows Python 3.7+ in the badge. Ensure this matches the actual requirement in
pyproject.toml.#!/bin/bash # Check Python version requirement in pyproject.toml if [ -f pyproject.toml ]; then echo "Python version requirement in pyproject.toml:" grep -A 5 "requires-python" pyproject.toml || echo "Python version requirement not found" fi
3-4: Verify PyPI package availability.The README references a PyPI package
claude-monitor, but this should be verified to ensure the package actually exists and the installation commands work.#!/bin/bash # Check if PyPI package exists and installation commands are valid echo "Checking PyPI package availability:" curl -s "https://pypi.org/pypi/claude-monitor/json" | jq -r '.info.name, .info.version' 2>/dev/null || echo "Package not found on PyPI" # Check if the package name matches what's in pyproject.toml if [ -f pyproject.toml ]; then echo "Package name in pyproject.toml:" grep -A 2 "^\[project\]" pyproject.toml | grep "name" || echo "Name not found in pyproject.toml" fi
122-127: Package name inconsistency in pip installation.The pip installation command uses
claude-usage-monitorwhile other commands useclaude-monitor. This inconsistency could confuse users.-pip install claude-usage-monitor +pip install claude-monitorEnsure all package references use the same name consistently throughout the documentation.
Likely an incorrect or invalid review comment.
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (8)
init_dependency.py (2)
34-47: Simplify control flow by removing unnecessary else block.The else block is unnecessary after the return statement. This improves code readability.
- if computed_checksum == expected_checksum: - print("✓ Checksum verification passed") - return True - else: - print("✗ Checksum verification failed!") - print(f"Expected: {expected_checksum}") - print(f"Computed: {computed_checksum}") - return False + if computed_checksum == expected_checksum: + print("✓ Checksum verification passed") + return True + + print("✗ Checksum verification failed!") + print(f"Expected: {expected_checksum}") + print(f"Computed: {computed_checksum}") + return False
145-166: Simplify control flow by replacing elif with if after break.Since the first condition breaks out of the loop, the subsequent elif statements can be simplified to if statements.
if choice in ["y", "yes"]: break - elif choice in ["n", "no"]: + if choice in ["n", "no"]: print("\nInstallation cancelled. Alternative installation options:") print("1. Download and install Node.js manually from: https://nodejs.org/") print("2. Use a package manager like Chocolatey: choco install nodejs") print("3. Use Scoop: scoop install nodejs") print("4. Use Windows Package Manager: winget install OpenJS.NodeJS") print("\nAfter installing Node.js, please run this script again.") sys.exit(0) - elif choice == "help": + if choice == "help": print("\nAutomatic installation details:") print("- Downloads Node.js MSI installer from official source") print("- Runs: msiexec /i node-installer.msi /quiet /norestart") print("- Requires administrator privileges (UAC prompt may appear)") print("- Installs to: C:\\Program Files\\nodejs") continue - else: - print( - "Please enter 'y' for yes, 'n' for no, or 'help' for more information." - ) + + print( + "Please enter 'y' for yes, 'n' for no, or 'help' for more information." + )RELEASE.md (1)
19-22: Format bare URLs as proper Markdown links.Bare URLs should be formatted as Markdown links for better accessibility and consistency.
For line 20:
- - Generate at: https://pypi.org/manage/account/token/ + - Generate at: [https://pypi.org/manage/account/token/](https://pypi.org/manage/account/token/)For line 104:
-1. Go to: https://github.com/Maciek-roboblog/Claude-Code-Usage-Monitor/releases/new +1. Go to: [https://github.com/Maciek-roboblog/Claude-Code-Usage-Monitor/releases/new](https://github.com/Maciek-roboblog/Claude-Code-Usage-Monitor/releases/new)For line 126:
-1. Check PyPI: https://pypi.org/project/claude-monitor/ +1. Check PyPI: [https://pypi.org/project/claude-monitor/](https://pypi.org/project/claude-monitor/)Also applies to: 104-104, 126-126
.github/workflows/release.yml (1)
19-24: Consider using a TOML parser for more robust version extraction.Using grep and sed to parse TOML files is fragile and may break if the file format changes (e.g., spaces around the equals sign, different quotes).
Consider using a proper TOML parser:
- name: Extract version from pyproject.toml id: extract run: | - VERSION=$(grep '^version = ' pyproject.toml | sed 's/version = "\(.*\)"/\1/') + VERSION=$(python3 -c "import tomllib; print(tomllib.load(open('pyproject.toml', 'rb'))['project']['version'])") echo "version=$VERSION" >> $GITHUB_OUTPUT echo "Version: $VERSION"Note:
tomllibis available in Python 3.11+. For older versions, you might need to usepip install tomland use thetomlpackage instead..github/workflows/version-bump.yml (1)
40-45: Use TOML parser for robust version extraction and updating.Similar to the release workflow, using grep/sed for TOML parsing is fragile. Additionally, using sed to update the version might not handle all edge cases correctly.
Consider using Python with a TOML library for both reading and writing:
- name: Extract current version id: current run: | - CURRENT_VERSION=$(grep '^version = ' pyproject.toml | sed 's/version = "\(.*\)"/\1/') + CURRENT_VERSION=$(python3 -c "import tomllib; print(tomllib.load(open('pyproject.toml', 'rb'))['project']['version'])") echo "version=$CURRENT_VERSION" >> $GITHUB_OUTPUT echo "Current version: $CURRENT_VERSION"For updating the version:
- name: Update pyproject.toml run: | NEW_VERSION="${{ steps.new.outputs.version }}" - sed -i "s/^version = .*/version = \"$NEW_VERSION\"/" pyproject.toml + python3 -c " +import toml +with open('pyproject.toml', 'r') as f: + data = toml.load(f) +data['project']['version'] = '$NEW_VERSION' +with open('pyproject.toml', 'w') as f: + toml.dump(data, f) +" echo "Updated pyproject.toml to version $NEW_VERSION"Also applies to: 76-80
README.md (3)
454-454: Consider using proper headings instead of bold emphasis.Using emphasis for section headers reduces document structure clarity and accessibility.
For line 454:
-**Start with Default (Recommended for New Users)** +##### Start with Default (Recommended for New Users)For line 562:
-**Sprint Development** +##### Sprint DevelopmentAlso applies to: 562-562
701-704: Add language specification to fenced code block.Fenced code blocks should specify the language for proper syntax highlighting.
-``` +```text error: externally-managed-environment × This environment is externally managed
232-232: Add comma before 'and' for better readability.In formal writing, a comma before 'and' helps separate independent clauses.
-> **We need your help!** This is a new release and we want to ensure it works perfectly on all systems. +> **We need your help!** This is a new release, and we want to ensure it works perfectly on all systems.
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
.github/workflows/release.yml(1 hunks).github/workflows/version-bump.yml(1 hunks)README.md(21 hunks)RELEASE.md(1 hunks)init_dependency.py(1 hunks)
🧰 Additional context used
🪛 Pylint (3.3.7)
init_dependency.py
[refactor] 34-41: Unnecessary "else" after "return", remove the "else" and de-indent the code inside it
(R1705)
[refactor] 145-165: Unnecessary "elif" after "break", remove the leading "el" from "elif"
(R1723)
[refactor] 128-128: Too many statements (84/50)
(R0915)
[refactor] 240-240: Too many statements (59/50)
(R0915)
🪛 LanguageTool
README.md
[uncategorized] ~133-~133: Possible missing preposition found.
Context: ...e above) - it's safer and easier > 2. Use a virtual environment - `python3 -m v...
(AI_HYDRA_LEO_MISSING_TO)
[uncategorized] ~232-~232: Use a comma before ‘and’ if it connects two independent clauses (unless they are closely connected and short).
Context: ... need your help!** This is a new release and we want to ensure it works perfectly on...
(COMMA_COMPOUND_SENTENCE)
[style] ~249-~249: The wording of this phrase can be improved.
Context: ...issues/new) > > Thank you for helping make this tool better! 🚀 ## ✨ Features & How It Works #...
(MAKE_STYLE_BETTER)
🪛 markdownlint-cli2 (0.17.2)
README.md
454-454: Emphasis used instead of a heading
null
(MD036, no-emphasis-as-heading)
562-562: Emphasis used instead of a heading
null
(MD036, no-emphasis-as-heading)
701-701: Fenced code blocks should have a language specified
null
(MD040, fenced-code-language)
RELEASE.md
20-20: Bare URL used
null
(MD034, no-bare-urls)
104-104: Bare URL used
null
(MD034, no-bare-urls)
126-126: Bare URL used
null
(MD034, no-bare-urls)
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (3)
init_dependency.py (2)
36-43: Improve code style by removing unnecessary "else" after "return"The Pylint suggestion is valid - the
elseclause after areturnstatement is unnecessary and can be simplified.if computed_checksum == expected_checksum: print("✓ Checksum verification passed") return True -else: - print("✗ Checksum verification failed!") - print(f"Expected: {expected_checksum}") - print(f"Computed: {computed_checksum}") - return False + +print("✗ Checksum verification failed!") +print(f"Expected: {expected_checksum}") +print(f"Computed: {computed_checksum}") +return False
147-167: Minor code style improvement for user input handlingThe user input validation logic can be simplified per Pylint's suggestion.
if choice in ["y", "yes"]: break -elif choice in ["n", "no"]: +if choice in ["n", "no"]: print("\nInstallation cancelled. Alternative installation options:") print("1. Download and install Node.js manually from: https://nodejs.org/") print("2. Use a package manager like Chocolatey: choco install nodejs") print("3. Use Scoop: scoop install nodejs") print("4. Use Windows Package Manager: winget install OpenJS.NodeJS") print("\nAfter installing Node.js, please run this script again.") sys.exit(0) -elif choice == "help": +if choice == "help": print("\nAutomatic installation details:") print("- Downloads Node.js MSI installer from official source") print("- Runs: msiexec /i node-installer.msi /quiet /norestart") print("- Requires administrator privileges (UAC prompt may appear)") print("- Installs to: C:\\Program Files\\nodejs") continue -else: - print( - "Please enter 'y' for yes, 'n' for no, or 'help' for more information." - ) + +print( + "Please enter 'y' for yes, 'n' for no, or 'help' for more information." +)README.md (1)
701-704: Minor formatting improvement neededThe fenced code block should specify a language for better rendering and accessibility.
-``` +```text error: externally-managed-environment × This environment is externally managed</blockquote></details> </blockquote></details> <details> <summary>📜 Review details</summary> **Configuration used: .coderabbit.yaml** **Review profile: CHILL** **Plan: Pro** <details> <summary>📥 Commits</summary> Reviewing files that changed from the base of the PR and between d1b443cb46dcf48308fec7d3f1694d30bc20d6a7 and 4a1109825095909bfbad3ecf58421e087c1d9431. </details> <details> <summary>📒 Files selected for processing (2)</summary> * `README.md` (21 hunks) * `init_dependency.py` (1 hunks) </details> <details> <summary>🧰 Additional context used</summary> <details> <summary>🪛 LanguageTool</summary> <details> <summary>README.md</summary> [uncategorized] ~133-~133: Possible missing preposition found. Context: ...e above) - it's safer and easier > 2. **Use a virtual environment** - `python3 -m v... (AI_HYDRA_LEO_MISSING_TO) --- [uncategorized] ~232-~232: Use a comma before ‘and’ if it connects two independent clauses (unless they are closely connected and short). Context: ... need your help!** This is a new release and we want to ensure it works perfectly on... (COMMA_COMPOUND_SENTENCE) --- [style] ~249-~249: The wording of this phrase can be improved. Context: ...issues/new) > > **Thank you for helping make this tool better! 🚀** ## ✨ Features & How It Works #... (MAKE_STYLE_BETTER) </details> </details> <details> <summary>🪛 markdownlint-cli2 (0.17.2)</summary> <details> <summary>README.md</summary> 454-454: Emphasis used instead of a heading null (MD036, no-emphasis-as-heading) --- 562-562: Emphasis used instead of a heading null (MD036, no-emphasis-as-heading) --- 701-701: Fenced code blocks should have a language specified null (MD040, fenced-code-language) </details> </details> <details> <summary>🪛 Pylint (3.3.7)</summary> <details> <summary>init_dependency.py</summary> [refactor] 36-43: Unnecessary "else" after "return", remove the "else" and de-indent the code inside it (R1705) --- [refactor] 147-167: Unnecessary "elif" after "break", remove the leading "el" from "elif" (R1723) --- [refactor] 130-130: Too many statements (84/50) (R0915) --- [refactor] 242-242: Too many statements (59/50) (R0915) </details> </details> </details> <details> <summary>🔇 Additional comments (6)</summary><blockquote> <details> <summary>init_dependency.py (3)</summary> `14-20`: **✓ macOS checksums successfully added** Great improvement! The NODE_CHECKSUMS dictionary now includes all required checksums for both Linux and macOS platforms, addressing the previous review concern about missing macOS support. --- `242-321`: **Comprehensive npm installation handling** Excellent implementation with proper fallback strategy (global → local installation) and detailed error handling. The extensive troubleshooting guidance will help users resolve common npm permission and installation issues. --- `323-356`: **Well-orchestrated dependency management** The main `ensure_node_installed()` function provides excellent orchestration of the entire dependency installation process. The logical flow from Node.js → npm → npx → ccusage is clear and handles all major platforms appropriately. </details> <details> <summary>README.md (3)</summary> `71-157`: **Excellent modernization of installation instructions** The installation section has been significantly improved with: - Clear promotion of `uv` as the recommended method with compelling benefits - Proper handling of "externally-managed-environment" warnings - Comprehensive alternative installation methods - Good progression from simple to complex installation scenarios This addresses modern Python packaging challenges effectively. --- `158-567`: **Comprehensive and consistent usage documentation** The usage examples are well-structured with: - Consistent use of `claude-monitor` command throughout - Logical progression from basic to advanced scenarios - Real-world workflow examples - Good integration of timezone and scheduling options The command consistency issues from previous reviews have been properly addressed. --- `230-250`: **Excellent community engagement approach** The "Please Help Test This Release" section shows great community engagement practices: - Clear instructions for testing and feedback - Proper issue reporting format guidance - Fallback to develop branch for immediate fixes - Appreciative tone that encourages participation This will help ensure release quality and build community involvement. </details> </blockquote></details> </details> <!-- This is an auto-generated comment by CodeRabbit for review status -->
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
init_dependency.py (1)
36-43: Simplify conditional logic.The
elseclause afterreturnis unnecessary and can be simplified.if computed_checksum == expected_checksum: print("✓ Checksum verification passed") return True - else: - print("✗ Checksum verification failed!") - print(f"Expected: {expected_checksum}") - print(f"Computed: {computed_checksum}") - return False + + print("✗ Checksum verification failed!") + print(f"Expected: {expected_checksum}") + print(f"Computed: {computed_checksum}") + return False
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
README.md(21 hunks)init_dependency.py(1 hunks)
🧰 Additional context used
🪛 LanguageTool
README.md
[uncategorized] ~139-~139: Possible missing preposition found.
Context: ...e above) - it's safer and easier > 2. Use a virtual environment - `python3 -m v...
(AI_HYDRA_LEO_MISSING_TO)
[uncategorized] ~238-~238: Use a comma before ‘and’ if it connects two independent clauses (unless they are closely connected and short).
Context: ... need your help!** This is a new release and we want to ensure it works perfectly on...
(COMMA_COMPOUND_SENTENCE)
[style] ~255-~255: The wording of this phrase can be improved.
Context: ...issues/new) > > Thank you for helping make this tool better! 🚀 ## ✨ Features & How It Works #...
(MAKE_STYLE_BETTER)
🪛 markdownlint-cli2 (0.17.2)
README.md
460-460: Emphasis used instead of a heading
null
(MD036, no-emphasis-as-heading)
568-568: Emphasis used instead of a heading
null
(MD036, no-emphasis-as-heading)
707-707: Fenced code blocks should have a language specified
null
(MD040, fenced-code-language)
🪛 Pylint (3.3.7)
init_dependency.py
[refactor] 36-43: Unnecessary "else" after "return", remove the "else" and de-indent the code inside it
(R1705)
[refactor] 147-167: Unnecessary "elif" after "break", remove the leading "el" from "elif"
(R1723)
[refactor] 130-130: Too many statements (84/50)
(R0915)
[refactor] 244-380: Too many nested blocks (6/5)
(R1702)
[refactor] 244-380: Too many nested blocks (6/5)
(R1702)
[refactor] 242-242: Too many branches (25/12)
(R0912)
[refactor] 242-242: Too many statements (88/50)
(R0915)
🔇 Additional comments (6)
README.md (6)
3-4: LGTM! Modern badge and Python version update.Good update to reflect the actual PyPI package name and bumping the minimum Python version to 3.7+, which aligns with modern Python practices.
56-69: Excellent addition of automatic dependency installation documentation.The clear explanation of automatic dependency installation addresses a major user pain point and sets proper expectations. This aligns well with the new
init_dependency.pyfunctionality.
71-118: Outstanding modernization with uv installation method.The addition of uv as the recommended installation method is excellent. It addresses the "externally-managed-environment" issues that plague modern Linux distributions and provides a much better user experience.
236-256: Great addition of testing request section.This proactive approach to getting user feedback and testing on the develop branch shows good project management. The clear instructions for issue reporting will help improve the tool's reliability.
704-746: Comprehensive troubleshooting for modern Linux distributions.Excellent coverage of the "externally-managed-environment" error with multiple solution approaches prioritized correctly (uv first, then pipx, then virtual env). This will save users significant frustration.
873-879: Nice addition of contributors section.Good recognition of contributors and encouragement for future contributions. This helps build community around the project.
init_dependency.py
Outdated
| def install_node_windows(): | ||
| """Install Node.js on Windows using MSI installer with user consent and return the installation path.""" | ||
| print("\nNode.js is required but not found on your system.") | ||
| print( | ||
| "This script can automatically install Node.js, but it requires administrator privileges." | ||
| ) | ||
| print(f"Node.js version {NODE_VERSION} will be downloaded and installed.") | ||
|
|
||
| while True: | ||
| choice = ( | ||
| input( | ||
| "\nDo you want to proceed with the automatic installation? (y/n/help): " | ||
| ) | ||
| .lower() | ||
| .strip() | ||
| ) | ||
|
|
||
| if choice in ["y", "yes"]: | ||
| break | ||
| elif choice in ["n", "no"]: | ||
| print("\nInstallation cancelled. Alternative installation options:") | ||
| print("1. Download and install Node.js manually from: https://nodejs.org/") | ||
| print("2. Use a package manager like Chocolatey: choco install nodejs") | ||
| print("3. Use Scoop: scoop install nodejs") | ||
| print("4. Use Windows Package Manager: winget install OpenJS.NodeJS") | ||
| print("\nAfter installing Node.js, please run this script again.") | ||
| sys.exit(0) | ||
| elif choice == "help": | ||
| print("\nAutomatic installation details:") | ||
| print("- Downloads Node.js MSI installer from official source") | ||
| print("- Runs: msiexec /i node-installer.msi /quiet /norestart") | ||
| print("- Requires administrator privileges (UAC prompt may appear)") | ||
| print("- Installs to: C:\\Program Files\\nodejs") | ||
| continue | ||
| else: | ||
| print( | ||
| "Please enter 'y' for yes, 'n' for no, or 'help' for more information." | ||
| ) | ||
|
|
||
| print("Downloading Node.js MSI installer for Windows...") | ||
| filename = os.path.join(tempfile.gettempdir(), "node-installer.msi") | ||
| url = f"{NODE_DIST_URL}/v{NODE_VERSION}/node-v{NODE_VERSION}-x64.msi" | ||
|
|
||
| try: | ||
| urllib.request.urlretrieve(url, filename) | ||
| print(f"✓ Downloaded installer ({os.path.getsize(filename)} bytes)") | ||
| except urllib.error.URLError as e: | ||
| print(f"\n❌ Failed to download Node.js installer from {url}") | ||
| print(f"Network error: {e}") | ||
| print("\n🔧 Next steps:") | ||
| print("1. Check your internet connection") | ||
| print("2. Download Node.js manually from: https://nodejs.org/") | ||
| print("3. Try again later if the server is temporarily unavailable") | ||
| sys.exit(1) | ||
| except OSError as e: | ||
| print(f"\n❌ Failed to save installer file to {filename}") | ||
| print(f"File system error: {e}") | ||
| print("\n🔧 Next steps:") | ||
| print("1. Check disk space and permissions") | ||
| print("2. Try running as administrator") | ||
| print("3. Download Node.js manually from: https://nodejs.org/") | ||
| sys.exit(1) | ||
|
|
||
| print( | ||
| "Running installer (administrator privileges required - UAC prompt may appear)..." | ||
| ) | ||
| try: | ||
| subprocess.run( | ||
| ["msiexec", "/i", filename, "/quiet", "/norestart"], | ||
| check=True, | ||
| capture_output=True, | ||
| text=True, | ||
| ) | ||
| except subprocess.CalledProcessError as e: | ||
| print("\n❌ Node.js installation failed.") | ||
| print("\nCommon causes and solutions:") | ||
| print("• Insufficient administrator privileges - Run as administrator") | ||
| print("• Windows Installer service issues - Restart Windows Installer service") | ||
| print("• MSI file corruption - Try downloading again") | ||
| print("• Conflicting existing installation - Uninstall old Node.js first") | ||
|
|
||
| if e.stderr: | ||
| print(f"\nInstaller error output: {e.stderr.strip()}") | ||
| if e.stdout: | ||
| print(f"Installer output: {e.stdout.strip()}") | ||
| print(f"Return code: {e.returncode}") | ||
|
|
||
| print("\n🔧 Next steps:") | ||
| print("1. Download and install Node.js manually from: https://nodejs.org/") | ||
| print("2. Or use Windows Package Manager: winget install OpenJS.NodeJS") | ||
| print("3. Or use Chocolatey: choco install nodejs") | ||
| print("4. Restart your terminal after installation") | ||
| sys.exit(1) | ||
| except FileNotFoundError: | ||
| print("\n❌ Windows Installer (msiexec) not found.") | ||
| print("This indicates a serious Windows system issue.") | ||
| print("\nPlease install Node.js manually from: https://nodejs.org/") | ||
| sys.exit(1) | ||
| except PermissionError: | ||
| print("\n❌ Permission denied when running installer.") | ||
| print("Please run this script as administrator or install Node.js manually.") | ||
| print("\nManual installation: https://nodejs.org/") | ||
| sys.exit(1) | ||
|
|
||
| print("Node.js installed successfully.") | ||
| node_path = "C:\\Program Files\\nodejs" | ||
| os.environ["PATH"] = node_path + os.pathsep + os.environ["PATH"] | ||
|
|
||
| print("Node.js installed successfully and added to PATH.") | ||
| return node_path | ||
|
|
There was a problem hiding this comment.
🛠️ Refactor suggestion
Refactor the Windows installation function to reduce complexity.
The install_node_windows function is quite large (84 statements) and handles multiple concerns. Consider breaking it down into smaller, focused functions for better maintainability.
+def _get_user_consent_for_installation():
+ """Get user consent for Windows Node.js installation."""
+ print("\nNode.js is required but not found on your system.")
+ print("This script can automatically install Node.js, but it requires administrator privileges.")
+ print(f"Node.js version {NODE_VERSION} will be downloaded and installed.")
+
+ while True:
+ choice = input("\nDo you want to proceed with the automatic installation? (y/n/help): ").lower().strip()
+ if choice in ["y", "yes"]:
+ return True
+ elif choice in ["n", "no"]:
+ print("\nInstallation cancelled. Alternative installation options:")
+ print("1. Download and install Node.js manually from: https://nodejs.org/")
+ print("2. Use a package manager like Chocolatey: choco install nodejs")
+ print("3. Use Scoop: scoop install nodejs")
+ print("4. Use Windows Package Manager: winget install OpenJS.NodeJS")
+ print("\nAfter installing Node.js, please run this script again.")
+ return False
+ elif choice == "help":
+ print("\nAutomatic installation details:")
+ print("- Downloads Node.js MSI installer from official source")
+ print("- Runs: msiexec /i node-installer.msi /quiet /norestart")
+ print("- Requires administrator privileges (UAC prompt may appear)")
+ print("- Installs to: C:\\Program Files\\nodejs")
+ else:
+ print("Please enter 'y' for yes, 'n' for no, or 'help' for more information.")
+def _download_windows_installer():
+ """Download the Windows Node.js installer."""
+ filename = os.path.join(tempfile.gettempdir(), "node-installer.msi")
+ url = f"{NODE_DIST_URL}/v{NODE_VERSION}/node-v{NODE_VERSION}-x64.msi"
+
+ try:
+ urllib.request.urlretrieve(url, filename)
+ print(f"✓ Downloaded installer ({os.path.getsize(filename)} bytes)")
+ return filename
+ except urllib.error.URLError as e:
+ print(f"\n❌ Failed to download Node.js installer from {url}")
+ print(f"Network error: {e}")
+ print("\n🔧 Next steps:")
+ print("1. Check your internet connection")
+ print("2. Download Node.js manually from: https://nodejs.org/")
+ print("3. Try again later if the server is temporarily unavailable")
+ sys.exit(1)
+ except OSError as e:
+ print(f"\n❌ Failed to save installer file to {filename}")
+ print(f"File system error: {e}")
+ print("\n🔧 Next steps:")
+ print("1. Check disk space and permissions")
+ print("2. Try running as administrator")
+ print("3. Download Node.js manually from: https://nodejs.org/")
+ sys.exit(1)
+def _run_windows_installer(filename):
+ """Run the Windows Node.js installer."""
+ print("Running installer (administrator privileges required - UAC prompt may appear)...")
+ try:
+ subprocess.run(
+ ["msiexec", "/i", filename, "/quiet", "/norestart"],
+ check=True,
+ capture_output=True,
+ text=True,
+ )
+ print("Node.js installed successfully.")
+ except subprocess.CalledProcessError as e:
+ print("\n❌ Node.js installation failed.")
+ print("\nCommon causes and solutions:")
+ print("• Insufficient administrator privileges - Run as administrator")
+ print("• Windows Installer service issues - Restart Windows Installer service")
+ print("• MSI file corruption - Try downloading again")
+ print("• Conflicting existing installation - Uninstall old Node.js first")
+
+ if e.stderr:
+ print(f"\nInstaller error output: {e.stderr.strip()}")
+ if e.stdout:
+ print(f"Installer output: {e.stdout.strip()}")
+ print(f"Return code: {e.returncode}")
+
+ print("\n🔧 Next steps:")
+ print("1. Download and install Node.js manually from: https://nodejs.org/")
+ print("2. Or use Windows Package Manager: winget install OpenJS.NodeJS")
+ print("3. Or use Chocolatey: choco install nodejs")
+ print("4. Restart your terminal after installation")
+ sys.exit(1)
+ except (FileNotFoundError, PermissionError) as e:
+ # Handle specific exceptions with targeted messages
+ if isinstance(e, FileNotFoundError):
+ print("\n❌ Windows Installer (msiexec) not found.")
+ print("This indicates a serious Windows system issue.")
+ else:
+ print("\n❌ Permission denied when running installer.")
+ print("Please run this script as administrator or install Node.js manually.")
+ print("\nManual installation: https://nodejs.org/")
+ sys.exit(1)
def install_node_windows():
"""Install Node.js on Windows using MSI installer with user consent and return the installation path."""
- print("\nNode.js is required but not found on your system.")
- print("This script can automatically install Node.js, but it requires administrator privileges.")
- print(f"Node.js version {NODE_VERSION} will be downloaded and installed.")
-
- while True:
- choice = input("\nDo you want to proceed with the automatic installation? (y/n/help): ").lower().strip()
-
- if choice in ["y", "yes"]:
- break
- elif choice in ["n", "no"]:
- print("\nInstallation cancelled. Alternative installation options:")
- print("1. Download and install Node.js manually from: https://nodejs.org/")
- print("2. Use a package manager like Chocolatey: choco install nodejs")
- print("3. Use Scoop: scoop install nodejs")
- print("4. Use Windows Package Manager: winget install OpenJS.NodeJS")
- print("\nAfter installing Node.js, please run this script again.")
- sys.exit(0)
- elif choice == "help":
- print("\nAutomatic installation details:")
- print("- Downloads Node.js MSI installer from official source")
- print("- Runs: msiexec /i node-installer.msi /quiet /norestart")
- print("- Requires administrator privileges (UAC prompt may appear)")
- print("- Installs to: C:\\Program Files\\nodejs")
- continue
- else:
- print("Please enter 'y' for yes, 'n' for no, or 'help' for more information.")
-
- print("Downloading Node.js MSI installer for Windows...")
- filename = os.path.join(tempfile.gettempdir(), "node-installer.msi")
- url = f"{NODE_DIST_URL}/v{NODE_VERSION}/node-v{NODE_VERSION}-x64.msi"
-
- try:
- urllib.request.urlretrieve(url, filename)
- print(f"✓ Downloaded installer ({os.path.getsize(filename)} bytes)")
- except urllib.error.URLError as e:
- print(f"\n❌ Failed to download Node.js installer from {url}")
- print(f"Network error: {e}")
- print("\n🔧 Next steps:")
- print("1. Check your internet connection")
- print("2. Download Node.js manually from: https://nodejs.org/")
- print("3. Try again later if the server is temporarily unavailable")
- sys.exit(1)
- except OSError as e:
- print(f"\n❌ Failed to save installer file to {filename}")
- print(f"File system error: {e}")
- print("\n🔧 Next steps:")
- print("1. Check disk space and permissions")
- print("2. Try running as administrator")
- print("3. Download Node.js manually from: https://nodejs.org/")
- sys.exit(1)
-
- print("Running installer (administrator privileges required - UAC prompt may appear)...")
- try:
- subprocess.run(
- ["msiexec", "/i", filename, "/quiet", "/norestart"],
- check=True,
- capture_output=True,
- text=True,
- )
- except subprocess.CalledProcessError as e:
- print("\n❌ Node.js installation failed.")
- print("\nCommon causes and solutions:")
- print("• Insufficient administrator privileges - Run as administrator")
- print("• Windows Installer service issues - Restart Windows Installer service")
- print("• MSI file corruption - Try downloading again")
- print("• Conflicting existing installation - Uninstall old Node.js first")
-
- if e.stderr:
- print(f"\nInstaller error output: {e.stderr.strip()}")
- if e.stdout:
- print(f"Installer output: {e.stdout.strip()}")
- print(f"Return code: {e.returncode}")
-
- print("\n🔧 Next steps:")
- print("1. Download and install Node.js manually from: https://nodejs.org/")
- print("2. Or use Windows Package Manager: winget install OpenJS.NodeJS")
- print("3. Or use Chocolatey: choco install nodejs")
- print("4. Restart your terminal after installation")
- sys.exit(1)
- except FileNotFoundError:
- print("\n❌ Windows Installer (msiexec) not found.")
- print("This indicates a serious Windows system issue.")
- print("\nPlease install Node.js manually from: https://nodejs.org/")
- sys.exit(1)
- except PermissionError:
- print("\n❌ Permission denied when running installer.")
- print("Please run this script as administrator or install Node.js manually.")
- print("\nManual installation: https://nodejs.org/")
- sys.exit(1)
-
- print("Node.js installed successfully.")
+ if not _get_user_consent_for_installation():
+ sys.exit(0)
+
+ print("Downloading Node.js MSI installer for Windows...")
+ filename = _download_windows_installer()
+ _run_windows_installer(filename)
+
node_path = "C:\\Program Files\\nodejs"
os.environ["PATH"] = node_path + os.pathsep + os.environ["PATH"]
-
print("Node.js installed successfully and added to PATH.")
return node_path📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| def install_node_windows(): | |
| """Install Node.js on Windows using MSI installer with user consent and return the installation path.""" | |
| print("\nNode.js is required but not found on your system.") | |
| print( | |
| "This script can automatically install Node.js, but it requires administrator privileges." | |
| ) | |
| print(f"Node.js version {NODE_VERSION} will be downloaded and installed.") | |
| while True: | |
| choice = ( | |
| input( | |
| "\nDo you want to proceed with the automatic installation? (y/n/help): " | |
| ) | |
| .lower() | |
| .strip() | |
| ) | |
| if choice in ["y", "yes"]: | |
| break | |
| elif choice in ["n", "no"]: | |
| print("\nInstallation cancelled. Alternative installation options:") | |
| print("1. Download and install Node.js manually from: https://nodejs.org/") | |
| print("2. Use a package manager like Chocolatey: choco install nodejs") | |
| print("3. Use Scoop: scoop install nodejs") | |
| print("4. Use Windows Package Manager: winget install OpenJS.NodeJS") | |
| print("\nAfter installing Node.js, please run this script again.") | |
| sys.exit(0) | |
| elif choice == "help": | |
| print("\nAutomatic installation details:") | |
| print("- Downloads Node.js MSI installer from official source") | |
| print("- Runs: msiexec /i node-installer.msi /quiet /norestart") | |
| print("- Requires administrator privileges (UAC prompt may appear)") | |
| print("- Installs to: C:\\Program Files\\nodejs") | |
| continue | |
| else: | |
| print( | |
| "Please enter 'y' for yes, 'n' for no, or 'help' for more information." | |
| ) | |
| print("Downloading Node.js MSI installer for Windows...") | |
| filename = os.path.join(tempfile.gettempdir(), "node-installer.msi") | |
| url = f"{NODE_DIST_URL}/v{NODE_VERSION}/node-v{NODE_VERSION}-x64.msi" | |
| try: | |
| urllib.request.urlretrieve(url, filename) | |
| print(f"✓ Downloaded installer ({os.path.getsize(filename)} bytes)") | |
| except urllib.error.URLError as e: | |
| print(f"\n❌ Failed to download Node.js installer from {url}") | |
| print(f"Network error: {e}") | |
| print("\n🔧 Next steps:") | |
| print("1. Check your internet connection") | |
| print("2. Download Node.js manually from: https://nodejs.org/") | |
| print("3. Try again later if the server is temporarily unavailable") | |
| sys.exit(1) | |
| except OSError as e: | |
| print(f"\n❌ Failed to save installer file to {filename}") | |
| print(f"File system error: {e}") | |
| print("\n🔧 Next steps:") | |
| print("1. Check disk space and permissions") | |
| print("2. Try running as administrator") | |
| print("3. Download Node.js manually from: https://nodejs.org/") | |
| sys.exit(1) | |
| print( | |
| "Running installer (administrator privileges required - UAC prompt may appear)..." | |
| ) | |
| try: | |
| subprocess.run( | |
| ["msiexec", "/i", filename, "/quiet", "/norestart"], | |
| check=True, | |
| capture_output=True, | |
| text=True, | |
| ) | |
| except subprocess.CalledProcessError as e: | |
| print("\n❌ Node.js installation failed.") | |
| print("\nCommon causes and solutions:") | |
| print("• Insufficient administrator privileges - Run as administrator") | |
| print("• Windows Installer service issues - Restart Windows Installer service") | |
| print("• MSI file corruption - Try downloading again") | |
| print("• Conflicting existing installation - Uninstall old Node.js first") | |
| if e.stderr: | |
| print(f"\nInstaller error output: {e.stderr.strip()}") | |
| if e.stdout: | |
| print(f"Installer output: {e.stdout.strip()}") | |
| print(f"Return code: {e.returncode}") | |
| print("\n🔧 Next steps:") | |
| print("1. Download and install Node.js manually from: https://nodejs.org/") | |
| print("2. Or use Windows Package Manager: winget install OpenJS.NodeJS") | |
| print("3. Or use Chocolatey: choco install nodejs") | |
| print("4. Restart your terminal after installation") | |
| sys.exit(1) | |
| except FileNotFoundError: | |
| print("\n❌ Windows Installer (msiexec) not found.") | |
| print("This indicates a serious Windows system issue.") | |
| print("\nPlease install Node.js manually from: https://nodejs.org/") | |
| sys.exit(1) | |
| except PermissionError: | |
| print("\n❌ Permission denied when running installer.") | |
| print("Please run this script as administrator or install Node.js manually.") | |
| print("\nManual installation: https://nodejs.org/") | |
| sys.exit(1) | |
| print("Node.js installed successfully.") | |
| node_path = "C:\\Program Files\\nodejs" | |
| os.environ["PATH"] = node_path + os.pathsep + os.environ["PATH"] | |
| print("Node.js installed successfully and added to PATH.") | |
| return node_path | |
| # Add these helper functions above install_node_windows() | |
| def _get_user_consent_for_installation(): | |
| """Get user consent for Windows Node.js installation.""" | |
| print("\nNode.js is required but not found on your system.") | |
| print("This script can automatically install Node.js, but it requires administrator privileges.") | |
| print(f"Node.js version {NODE_VERSION} will be downloaded and installed.") | |
| while True: | |
| choice = input("\nDo you want to proceed with the automatic installation? (y/n/help): ").lower().strip() | |
| if choice in ["y", "yes"]: | |
| return True | |
| elif choice in ["n", "no"]: | |
| print("\nInstallation cancelled. Alternative installation options:") | |
| print("1. Download and install Node.js manually from: https://nodejs.org/") | |
| print("2. Use a package manager like Chocolatey: choco install nodejs") | |
| print("3. Use Scoop: scoop install nodejs") | |
| print("4. Use Windows Package Manager: winget install OpenJS.NodeJS") | |
| print("\nAfter installing Node.js, please run this script again.") | |
| return False | |
| elif choice == "help": | |
| print("\nAutomatic installation details:") | |
| print("- Downloads Node.js MSI installer from official source") | |
| print("- Runs: msiexec /i node-installer.msi /quiet /norestart") | |
| print("- Requires administrator privileges (UAC prompt may appear)") | |
| print("- Installs to: C:\\Program Files\\nodejs") | |
| else: | |
| print("Please enter 'y' for yes, 'n' for no, or 'help' for more information.") | |
| def _download_windows_installer(): | |
| """Download the Windows Node.js installer.""" | |
| filename = os.path.join(tempfile.gettempdir(), "node-installer.msi") | |
| url = f"{NODE_DIST_URL}/v{NODE_VERSION}/node-v{NODE_VERSION}-x64.msi" | |
| try: | |
| urllib.request.urlretrieve(url, filename) | |
| print(f"✓ Downloaded installer ({os.path.getsize(filename)} bytes)") | |
| return filename | |
| except urllib.error.URLError as e: | |
| print(f"\n❌ Failed to download Node.js installer from {url}") | |
| print(f"Network error: {e}") | |
| print("\n🔧 Next steps:") | |
| print("1. Check your internet connection") | |
| print("2. Download Node.js manually from: https://nodejs.org/") | |
| print("3. Try again later if the server is temporarily unavailable") | |
| sys.exit(1) | |
| except OSError as e: | |
| print(f"\n❌ Failed to save installer file to {filename}") | |
| print(f"File system error: {e}") | |
| print("\n🔧 Next steps:") | |
| print("1. Check disk space and permissions") | |
| print("2. Try running as administrator") | |
| print("3. Download Node.js manually from: https://nodejs.org/") | |
| sys.exit(1) | |
| def _run_windows_installer(filename): | |
| """Run the Windows Node.js installer.""" | |
| print("Running installer (administrator privileges required - UAC prompt may appear)...") | |
| try: | |
| subprocess.run( | |
| ["msiexec", "/i", filename, "/quiet", "/norestart"], | |
| check=True, | |
| capture_output=True, | |
| text=True, | |
| ) | |
| print("Node.js installed successfully.") | |
| except subprocess.CalledProcessError as e: | |
| print("\n❌ Node.js installation failed.") | |
| print("\nCommon causes and solutions:") | |
| print("• Insufficient administrator privileges - Run as administrator") | |
| print("• Windows Installer service issues - Restart Windows Installer service") | |
| print("• MSI file corruption - Try downloading again") | |
| print("• Conflicting existing installation - Uninstall old Node.js first") | |
| if e.stderr: | |
| print(f"\nInstaller error output: {e.stderr.strip()}") | |
| if e.stdout: | |
| print(f"Installer output: {e.stdout.strip()}") | |
| print(f"Return code: {e.returncode}") | |
| print("\n🔧 Next steps:") | |
| print("1. Download and install Node.js manually from: https://nodejs.org/") | |
| print("2. Or use Windows Package Manager: winget install OpenJS.NodeJS") | |
| print("3. Or use Chocolatey: choco install nodejs") | |
| print("4. Restart your terminal after installation") | |
| sys.exit(1) | |
| except (FileNotFoundError, PermissionError) as e: | |
| if isinstance(e, FileNotFoundError): | |
| print("\n❌ Windows Installer (msiexec) not found.") | |
| print("This indicates a serious Windows system issue.") | |
| else: | |
| print("\n❌ Permission denied when running installer.") | |
| print("Please run this script as administrator or install Node.js manually.") | |
| print("\nManual installation: https://nodejs.org/") | |
| sys.exit(1) | |
| # Refactored install_node_windows | |
| def install_node_windows(): | |
| """Install Node.js on Windows using MSI installer with user consent and return the installation path.""" | |
| if not _get_user_consent_for_installation(): | |
| sys.exit(0) | |
| print("Downloading Node.js MSI installer for Windows...") | |
| filename = _download_windows_installer() | |
| _run_windows_installer(filename) | |
| node_path = "C:\\Program Files\\nodejs" | |
| os.environ["PATH"] = node_path + os.pathsep + os.environ["PATH"] | |
| print("Node.js installed successfully and added to PATH.") | |
| return node_path |
🧰 Tools
🪛 Pylint (3.3.7)
[refactor] 147-167: Unnecessary "elif" after "break", remove the leading "el" from "elif"
(R1723)
[refactor] 130-130: Too many statements (84/50)
(R0915)
🤖 Prompt for AI Agents
In init_dependency.py between lines 130 and 240, the install_node_windows
function is too large and handles multiple concerns, making it complex. Refactor
by splitting it into smaller functions such as one for user consent and input
handling, one for downloading the installer, one for running the installer, and
one for error handling and messaging. This modular approach will improve
readability and maintainability.
init_dependency.py
Outdated
| def ensure_ccusage_available(): | ||
| """Ensure ccusage is available via npx.""" | ||
| try: | ||
| # Find npm and npx commands - try multiple locations | ||
| npm_cmd = shutil.which("npm") | ||
| npx_cmd = shutil.which("npx") | ||
|
|
||
| # If not found in PATH, check common installation locations | ||
| if not npm_cmd: | ||
| common_paths = [ | ||
| os.path.join(os.environ.get("HOME", ""), ".local", "bin", "npm"), | ||
| "/usr/local/bin/npm", | ||
| "/usr/bin/npm", | ||
| "C:\\Program Files\\nodejs\\npm.cmd" | ||
| if platform.system() == "Windows" | ||
| else None, | ||
| ] | ||
| # Also check in the directory where we might have just installed Node.js | ||
| if os.path.exists("nodejs"): | ||
| for subdir in os.listdir("nodejs"): | ||
| if subdir.startswith("node-v"): | ||
| npm_path = os.path.join("nodejs", subdir, "bin", "npm") | ||
| if os.path.exists(npm_path): | ||
| common_paths.insert(0, os.path.abspath(npm_path)) | ||
|
|
||
| for path in common_paths: | ||
| if path and os.path.exists(path): | ||
| npm_cmd = path | ||
| break | ||
|
|
||
| if not npx_cmd: | ||
| common_paths = [ | ||
| os.path.join(os.environ.get("HOME", ""), ".local", "bin", "npx"), | ||
| "/usr/local/bin/npx", | ||
| "/usr/bin/npx", | ||
| "C:\\Program Files\\nodejs\\npx.cmd" | ||
| if platform.system() == "Windows" | ||
| else None, | ||
| ] | ||
| # Also check in the directory where we might have just installed Node.js | ||
| if os.path.exists("nodejs"): | ||
| for subdir in os.listdir("nodejs"): | ||
| if subdir.startswith("node-v"): | ||
| npx_path = os.path.join("nodejs", subdir, "bin", "npx") | ||
| if os.path.exists(npx_path): | ||
| common_paths.insert(0, os.path.abspath(npx_path)) | ||
|
|
||
| for path in common_paths: | ||
| if path and os.path.exists(path): | ||
| npx_cmd = path | ||
| break | ||
|
|
||
| if not npm_cmd or not npx_cmd: | ||
| print("\n❌ npm or npx command not found even after installation.") | ||
| print("PATH environment variable may not be updated in current process.") | ||
| print("\n🔧 Please try:") | ||
| print("1. Restart your terminal and run the command again") | ||
| print("2. Or manually install ccusage: npm install -g ccusage") | ||
| sys.exit(1) | ||
|
|
||
| # Check if ccusage is available | ||
| result = subprocess.run( | ||
| [npx_cmd, "--no-install", "ccusage", "--version"], | ||
| capture_output=True, | ||
| text=True, | ||
| env=os.environ.copy(), # Use current environment | ||
| ) | ||
| if result.returncode == 0: | ||
| print("✓ ccusage is available") | ||
| return # ccusage is available | ||
|
|
||
| print("Installing ccusage...") | ||
| # Try global installation first | ||
| try: | ||
| result = subprocess.run( | ||
| [npm_cmd, "install", "-g", "ccusage"], | ||
| check=True, | ||
| capture_output=True, | ||
| text=True, | ||
| env=os.environ.copy(), # Use current environment | ||
| ) | ||
| print("✓ ccusage installed globally") | ||
| except subprocess.CalledProcessError as e: | ||
| print("⚠️ Global installation failed, trying local installation...") | ||
| if e.stderr: | ||
| print(f"Global install error: {e.stderr.strip()}") | ||
|
|
||
| # If global fails, install locally | ||
| try: | ||
| result = subprocess.run( | ||
| [npm_cmd, "install", "ccusage"], | ||
| check=True, | ||
| capture_output=True, | ||
| text=True, | ||
| env=os.environ.copy(), # Use current environment | ||
| ) | ||
| print("✓ ccusage installed locally") | ||
| except subprocess.CalledProcessError as local_e: | ||
| print("\n❌ Both global and local ccusage installation failed.") | ||
| print("\nGlobal installation error:") | ||
| if e.stderr: | ||
| print(f" {e.stderr.strip()}") | ||
| print(f" Return code: {e.returncode}") | ||
|
|
||
| print("\nLocal installation error:") | ||
| if local_e.stderr: | ||
| print(f" {local_e.stderr.strip()}") | ||
| print(f" Return code: {local_e.returncode}") | ||
|
|
||
| print("\n🔧 Troubleshooting steps:") | ||
| print("1. Check npm permissions: npm config get prefix") | ||
| print("2. Try with sudo (Linux/Mac): sudo npm install -g ccusage") | ||
| print("3. Check npm registry: npm config get registry") | ||
| print("4. Clear npm cache: npm cache clean --force") | ||
| print("5. Manual install: npm install -g ccusage") | ||
| sys.exit(1) | ||
| except FileNotFoundError: | ||
| print("\n❌ npm command not found.") | ||
| print("Node.js and npm must be installed first.") | ||
| print("This should not happen if Node.js installation succeeded.") | ||
| sys.exit(1) | ||
| except PermissionError: | ||
| print("\n❌ Permission denied when running npm.") | ||
| print("Try running with elevated privileges or check npm permissions.") | ||
| print("\nOn Linux/Mac: sudo npm install -g ccusage") | ||
| print("On Windows: Run as administrator") | ||
| sys.exit(1) | ||
| except FileNotFoundError: | ||
| print("\n❌ npx command not found.") | ||
| print("Node.js and npm must be installed and available in PATH.") | ||
| print("Please restart your terminal or check your Node.js installation.") | ||
| sys.exit(1) | ||
| except Exception as e: | ||
| print(f"\n❌ Unexpected error during ccusage installation: {e}") | ||
| print("\n🔧 Manual installation steps:") | ||
| print("1. npm install -g ccusage") | ||
| print("2. Or use npx: npx ccusage --version") | ||
| print("3. Check Node.js installation: node --version") | ||
| sys.exit(1) | ||
|
|
There was a problem hiding this comment.
🛠️ Refactor suggestion
Refactor the ccusage installation function to reduce complexity.
The ensure_ccusage_available function is extremely complex (88 statements, 25 branches, 6 nested levels) and handles too many responsibilities. Consider breaking it into smaller, focused functions.
+def _find_npm_npx_commands():
+ """Find npm and npx commands in common locations."""
+ npm_cmd = shutil.which("npm")
+ npx_cmd = shutil.which("npx")
+
+ common_npm_paths = [
+ os.path.join(os.environ.get("HOME", ""), ".local", "bin", "npm"),
+ "/usr/local/bin/npm",
+ "/usr/bin/npm",
+ "C:\\Program Files\\nodejs\\npm.cmd" if platform.system() == "Windows" else None,
+ ]
+
+ common_npx_paths = [
+ os.path.join(os.environ.get("HOME", ""), ".local", "bin", "npx"),
+ "/usr/local/bin/npx",
+ "/usr/bin/npx",
+ "C:\\Program Files\\nodejs\\npx.cmd" if platform.system() == "Windows" else None,
+ ]
+
+ # Check local nodejs installation directory
+ if os.path.exists("nodejs"):
+ for subdir in os.listdir("nodejs"):
+ if subdir.startswith("node-v"):
+ npm_path = os.path.join("nodejs", subdir, "bin", "npm")
+ npx_path = os.path.join("nodejs", subdir, "bin", "npx")
+ if os.path.exists(npm_path):
+ common_npm_paths.insert(0, os.path.abspath(npm_path))
+ if os.path.exists(npx_path):
+ common_npx_paths.insert(0, os.path.abspath(npx_path))
+
+ # Find npm command
+ if not npm_cmd:
+ for path in common_npm_paths:
+ if path and os.path.exists(path):
+ npm_cmd = path
+ break
+
+ # Find npx command
+ if not npx_cmd:
+ for path in common_npx_paths:
+ if path and os.path.exists(path):
+ npx_cmd = path
+ break
+
+ return npm_cmd, npx_cmd
+def _check_ccusage_available(npx_cmd):
+ """Check if ccusage is already available."""
+ result = subprocess.run(
+ [npx_cmd, "--no-install", "ccusage", "--version"],
+ capture_output=True,
+ text=True,
+ env=os.environ.copy(),
+ )
+ return result.returncode == 0
+def _install_ccusage(npm_cmd):
+ """Install ccusage globally or locally."""
+ print("Installing ccusage...")
+
+ # Try global installation first
+ try:
+ subprocess.run(
+ [npm_cmd, "install", "-g", "ccusage"],
+ check=True,
+ capture_output=True,
+ text=True,
+ env=os.environ.copy(),
+ )
+ print("✓ ccusage installed globally")
+ return
+ except subprocess.CalledProcessError as e:
+ print("⚠️ Global installation failed, trying local installation...")
+ if e.stderr:
+ print(f"Global install error: {e.stderr.strip()}")
+
+ # Try local installation
+ try:
+ subprocess.run(
+ [npm_cmd, "install", "ccusage"],
+ check=True,
+ capture_output=True,
+ text=True,
+ env=os.environ.copy(),
+ )
+ print("✓ ccusage installed locally")
+ except subprocess.CalledProcessError as local_e:
+ print("\n❌ Both global and local ccusage installation failed.")
+ print(f"\nGlobal installation error: {e.stderr.strip() if e.stderr else 'Unknown'}")
+ print(f"Local installation error: {local_e.stderr.strip() if local_e.stderr else 'Unknown'}")
+ print("\n🔧 Troubleshooting steps:")
+ print("1. Check npm permissions: npm config get prefix")
+ print("2. Try with sudo (Linux/Mac): sudo npm install -g ccusage")
+ print("3. Check npm registry: npm config get registry")
+ print("4. Clear npm cache: npm cache clean --force")
+ print("5. Manual install: npm install -g ccusage")
+ sys.exit(1)
def ensure_ccusage_available():
"""Ensure ccusage is available via npx."""
try:
- # Find npm and npx commands - try multiple locations
- npm_cmd = shutil.which("npm")
- npx_cmd = shutil.which("npx")
-
- # If not found in PATH, check common installation locations
- if not npm_cmd:
- common_paths = [
- os.path.join(os.environ.get("HOME", ""), ".local", "bin", "npm"),
- "/usr/local/bin/npm",
- "/usr/bin/npm",
- "C:\\Program Files\\nodejs\\npm.cmd" if platform.system() == "Windows" else None,
- ]
- # Also check in the directory where we might have just installed Node.js
- if os.path.exists("nodejs"):
- for subdir in os.listdir("nodejs"):
- if subdir.startswith("node-v"):
- npm_path = os.path.join("nodejs", subdir, "bin", "npm")
- if os.path.exists(npm_path):
- common_paths.insert(0, os.path.abspath(npm_path))
-
- for path in common_paths:
- if path and os.path.exists(path):
- npm_cmd = path
- break
-
- if not npx_cmd:
- common_paths = [
- os.path.join(os.environ.get("HOME", ""), ".local", "bin", "npx"),
- "/usr/local/bin/npx",
- "/usr/bin/npx",
- "C:\\Program Files\\nodejs\\npx.cmd" if platform.system() == "Windows" else None,
- ]
- # Also check in the directory where we might have just installed Node.js
- if os.path.exists("nodejs"):
- for subdir in os.listdir("nodejs"):
- if subdir.startswith("node-v"):
- npx_path = os.path.join("nodejs", subdir, "bin", "npx")
- if os.path.exists(npx_path):
- common_paths.insert(0, os.path.abspath(npx_path))
-
- for path in common_paths:
- if path and os.path.exists(path):
- npx_cmd = path
- break
+ npm_cmd, npx_cmd = _find_npm_npx_commands()
if not npm_cmd or not npx_cmd:
print("\n❌ npm or npx command not found even after installation.")
@@ -299,66 +350,13 @@ def ensure_ccusage_available():
print("2. Or manually install ccusage: npm install -g ccusage")
sys.exit(1)
- # Check if ccusage is available
- result = subprocess.run(
- [npx_cmd, "--no-install", "ccusage", "--version"],
- capture_output=True,
- text=True,
- env=os.environ.copy(), # Use current environment
- )
- if result.returncode == 0:
+ if _check_ccusage_available(npx_cmd):
print("✓ ccusage is available")
- return # ccusage is available
+ return
- print("Installing ccusage...")
- # Try global installation first
- try:
- result = subprocess.run(
- [npm_cmd, "install", "-g", "ccusage"],
- check=True,
- capture_output=True,
- text=True,
- env=os.environ.copy(), # Use current environment
- )
- print("✓ ccusage installed globally")
- except subprocess.CalledProcessError as e:
- print("⚠️ Global installation failed, trying local installation...")
- if e.stderr:
- print(f"Global install error: {e.stderr.strip()}")
-
- # If global fails, install locally
- try:
- result = subprocess.run(
- [npm_cmd, "install", "ccusage"],
- check=True,
- capture_output=True,
- text=True,
- env=os.environ.copy(), # Use current environment
- )
- print("✓ ccusage installed locally")
- except subprocess.CalledProcessError as local_e:
- print("\n❌ Both global and local ccusage installation failed.")
- print("\nGlobal installation error:")
- if e.stderr:
- print(f" {e.stderr.strip()}")
- print(f" Return code: {e.returncode}")
-
- print("\nLocal installation error:")
- if local_e.stderr:
- print(f" {local_e.stderr.strip()}")
- print(f" Return code: {local_e.returncode}")
-
- print("\n🔧 Troubleshooting steps:")
- print("1. Check npm permissions: npm config get prefix")
- print("2. Try with sudo (Linux/Mac): sudo npm install -g ccusage")
- print("3. Check npm registry: npm config get registry")
- print("4. Clear npm cache: npm cache clean --force")
- print("5. Manual install: npm install -g ccusage")
- sys.exit(1)
- except FileNotFoundError:
- print("\n❌ npm command not found.")
- print("Node.js and npm must be installed first.")
- print("This should not happen if Node.js installation succeeded.")
- sys.exit(1)
- except PermissionError:
- print("\n❌ Permission denied when running npm.")
- print("Try running with elevated privileges or check npm permissions.")
- print("\nOn Linux/Mac: sudo npm install -g ccusage")
- print("On Windows: Run as administrator")
- sys.exit(1)
+ _install_ccusage(npm_cmd)
+
+ except FileNotFoundError:
+ print("\n❌ npm command not found.")
+ print("Node.js and npm must be installed first.")
+ print("This should not happen if Node.js installation succeeded.")
+ sys.exit(1)
+ except PermissionError:
+ print("\n❌ Permission denied when running npm.")
+ print("Try running with elevated privileges or check npm permissions.")
+ print("\nOn Linux/Mac: sudo npm install -g ccusage")
+ print("On Windows: Run as administrator")
+ sys.exit(1)
except FileNotFoundError:
print("\n❌ npx command not found.")
print("Node.js and npm must be installed and available in PATH.")🧰 Tools
🪛 Pylint (3.3.7)
[refactor] 244-380: Too many nested blocks (6/5)
(R1702)
[refactor] 244-380: Too many nested blocks (6/5)
(R1702)
[refactor] 242-242: Too many branches (25/12)
(R0912)
[refactor] 242-242: Too many statements (88/50)
(R0915)
🤖 Prompt for AI Agents
In init_dependency.py from lines 242 to 381, the ensure_ccusage_available
function is overly complex with many nested branches and responsibilities.
Refactor by splitting it into smaller functions such as locating npm and npx
commands, checking ccusage availability, performing global installation, and
handling local installation fallback. Each function should have a single
responsibility to improve readability and maintainability.
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (1)
claude_monitor.py (1)
317-541: Note: Main function complexity remains high.As mentioned in previous reviews, the main function still has high complexity (36 local variables, 22 branches, 115 statements). While the functionality works well, this should be addressed in a future refactoring as previously discussed.
The refactoring to extract logical sections into separate functions should be prioritized in future development to improve maintainability and testability.
🧹 Nitpick comments (2)
check_dependency.py (2)
16-19: Consider improving error message consistency.The error message uses a checkmark format (✗) which is good for user feedback, but consider adding more context about what the user should do next.
- print("✗ node not found on PATH") + print("✗ node not found on PATH") + print(" Please install Node.js from https://nodejs.org/")
50-53: Consistent error message pattern.Similar to the node check, consider adding installation guidance for npx as well.
- print("✗ npx not found on PATH") + print("✗ npx not found on PATH") + print(" npx should be included with Node.js 8.2+")
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
CHANGELOG.md(1 hunks)check_dependency.py(1 hunks)claude_monitor.py(5 hunks)pyproject.toml(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- pyproject.toml
🧰 Additional context used
🪛 Pylint (3.3.7)
claude_monitor.py
[refactor] 273-274: Consider using 'max_tokens = max(max_tokens, tokens)' instead of unnecessary if block
(R1731)
[refactor] 317-317: Too many local variables (36/15)
(R0914)
[refactor] 396-397: Consider using 'token_limit = max(token_limit, new_limit)' instead of unnecessary if block
(R1731)
[refactor] 317-317: Too many branches (22/12)
(R0912)
[refactor] 317-317: Too many statements (115/50)
(R0915)
🔇 Additional comments (20)
check_dependency.py (4)
1-9: LGTM: Clean module structure with appropriate dependencies.The imports and constants are well-defined. The minimum Node.js version 8.2 is reasonable as it includes npx by default.
20-34: Robust version parsing with good error handling.The version parsing logic correctly handles the 'v' prefix and compares major/minor versions appropriately. The fallback for missing minor version (line 27) is a good defensive practice.
35-42: Comprehensive error handling covers edge cases.The exception handling covers both subprocess errors and unexpected exceptions, providing clear feedback to users.
54-68: Good npx validation approach.The npx test correctly suppresses stdout while capturing stderr for meaningful error reporting. The error handling pattern is consistent with the test_node() function.
claude_monitor.py (15)
3-10: Good import organization with new dependencies.The imports are well-organized and the new dependencies (subprocess, sys, threading, datetime) support the enhanced functionality.
13-13: Proper dependency checking integration.The import of dependency checking functions from the new module is clean and follows good separation of concerns.
15-22: Robust cross-platform terminal handling.The conditional import of termios with fallback for systems without it is a good approach for cross-platform compatibility.
27-32: Updated subprocess call for npx compatibility.The change from direct
ccusagetonpx ccusageimproves compatibility as mentioned in the PR objectives.
56-66: Enhanced progress bar styling with proper color handling.The progress bar improvements with consistent color codes and proper reset handling are well implemented.
282-297: Well-implemented terminal raw mode setup.The terminal setup function properly handles the case where termios is not available and includes good error handling.
299-306: Proper terminal cleanup on exit.The restore function ensures terminal settings are properly reset even if errors occur.
308-315: Input flushing prevents display corruption.The flush_input function addresses the terminal corruption issue mentioned in past reviews and changelog.
319-320: Dependency validation at startup.Calling the dependency check functions at the start of main() ensures the environment is properly validated before proceeding.
323-327: Threading event improves timing control.Using a threading event instead of time.sleep provides better control over the refresh timing and allows for clean shutdown.
341-342: Proper cursor management for better UX.Hiding the cursor during monitoring and the clear screen approach improves the user experience.
345-346: Input flushing prevents interference.The flush_input call before each refresh prevents typed characters from interfering with the display.
363-387: Improved no-active-session display.The enhanced display for when no session is active provides better user feedback than before.
393-398: Smart token limit auto-switching.The automatic switch from pro to custom_max when limits are exceeded is a user-friendly feature.
521-537: Comprehensive cleanup on exit and error.The exception handling properly restores cursor and terminal settings in both keyboard interrupt and error cases.
CHANGELOG.md (1)
1-59: Well-structured changelog following best practices.The changelog follows the "Keep a Changelog" format with clear sections for Added, Changed, and Fixed items. The version links at the bottom are properly formatted and provide easy access to releases.
The documented changes accurately reflect the code changes observed in this PR:
- Replacement of
init_dependency.pywithcheck_dependency.py✓- Terminal handling improvements ✓
- Script renaming from
ccusage_monitor.pytoclaude_monitor.py✓- npx usage instead of direct ccusage calls ✓
- Modern packaging with pyproject.toml ✓
|
💪 |
Add package manager & Update build process & Update npm lib process installation
Many Thanks for @adawalli @taylorwilsdon
Summary by CodeRabbit
New Features
Documentation
Chores