evolver-tools 30.0.0__tar.gz → 31.0.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.
- {evolver_tools-30.0.0/src/evolver_tools.egg-info → evolver_tools-31.0.0}/PKG-INFO +2 -2
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/pyproject.toml +2 -2
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/cli.py +1 -1
- evolver_tools-31.0.0/src/evolver_tools/vendor/_test_find_empty.py +1 -0
- evolver_tools-31.0.0/src/evolver_tools/vendor/csv_filter.py +120 -0
- evolver_tools-31.0.0/src/evolver_tools/vendor/env_sorter.py +321 -0
- evolver_tools-31.0.0/src/evolver_tools/vendor/file_patch.py +189 -0
- evolver_tools-31.0.0/src/evolver_tools/vendor/find_empty.py +161 -0
- evolver_tools-31.0.0/src/evolver_tools/vendor/mac_address.py +85 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0/src/evolver_tools.egg-info}/PKG-INFO +2 -2
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools.egg-info/SOURCES.txt +6 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/LICENSE +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/README.md +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/setup.cfg +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/__init__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/__main__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/autoreg.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/__init__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/agent_b_tool.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/ansi_strip.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/api_tester.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/ascii_banner.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/ascii_gen.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/audit_log.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/b64/__init__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/b64/b64.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/backup.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/banner/__init__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/banner/banner.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/base32.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/base58.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/bookmark.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/cal_tool/__init__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/cal_tool/cli.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/calendar_cli.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/case_convert.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/cert_check.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/cert_info.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/changelog_gen/__init__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/changelog_gen/changelog_gen.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/changelog_gen.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/chart_cli/__init__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/chart_cli/__main__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/checksum_dir.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/clipboard/__init__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/clipboard/clipboard.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/code_auditor.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/code_review.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/code_stats.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/colorize.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/colors/__init__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/colors/__main__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/config_validator.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/config_vault.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/cowsay.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/crc_check.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/cron/__init__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/cron/__main__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/cron_pretty.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/crontab_helper.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/crypto_box.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/crypto_price.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/csv2json.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/csv_dedup.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/csv_merge.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/csv_slice.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/csv_stats/__init__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/csv_stats/__main__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/csv_stats/analyzer.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/csv_stats/cli.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/csv_to_table.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/csv_validate.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/csv_view.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/date_diff.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/db_mate.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/db_schema.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/dedup_files.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/dep_graph.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/dev_dashboard.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/dice_roll.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/diff_csv.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/diff_files.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/diff_tool/__init__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/diff_tool/__main__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/dirsize/__init__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/disk_cleanup.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/disk_usage/__init__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/disk_usage/disk_usage.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/dns_lookup.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/docker_helper.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/dt_convert.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/env_diff.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/env_manager.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/env_template.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/envcheck/__init__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/epoch.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/excel2csv.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/factor.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/ff/__init__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/ff/__main__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/figlet_cli.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/figlet_tool.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/file_encrypt.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/file_find.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/file_joiner.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/file_splitter.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/file_watch.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/find_dups/__init__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/find_dups/cli.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/firewall_rule.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/fmt/__init__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/fmt/fmt.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/fold.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/geo_ip.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/git_branch_cleaner.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/git_log_pretty.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/git_stats.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/gzip_cli.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/hash_check.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/hash_file.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/hashsum/__init__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/hashsum/__main__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/hex_tool.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/hexdump.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/html2markdown.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/html2md.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/http_headers.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/http_live/__init__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/http_live/__main__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/http_server.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/http_status.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/image_meta.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/ini2json.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/ini_parser/__init__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/ini_parser/ini_parser.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/ip_info.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/ip_location.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/ipcalc/__init__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/ipcalc/__main__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/ipinfo/__init__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/ipinfo/__main__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/join.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/joke.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/jq_lite/__init__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/jq_lite/__main__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/json2csv/__init__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/json2csv/__main__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/json2ini.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/json_diff.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/json_flatten.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/json_merge.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/json_path.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/json_pretty/__init__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/json_pretty/json_pretty.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/json_schema_validate.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/json_sort.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/json_to_table.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/json_to_yaml.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/jsonql/__init__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/jsonql/__main__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/jwt_decode.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/key_value_store.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/license.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/license_cli/__init__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/license_cli/__main__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/license_cli/cli.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/log_analyzer.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/log_hawk.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/log_tail.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/macrogen.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/markdown_check/__init__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/markdown_lint.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/markdown_preview.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/markdown_to_html.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/markdown_toc.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/math_eval.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/media_studio.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/morse.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/nb/__init__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/nb/__main__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/net_analyzer.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/net_speed.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/network_scan.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/nl.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/note_taker.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/otp_gen.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/passgen/__init__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/password_strength.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/pdf_info.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/pdf_text.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/pipe_viewer.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/pomodoro.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/port_scan.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/portcheck/__init__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/portcheck/__main__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/pr_tool/__init__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/pr_tool/pr_tool.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/process_kill.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/progress_bar.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/project_doctor/__init__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/project_doctor/__main__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/qc_calc.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/qc_report.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/qc_sample.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/qr_cli.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/qrcode.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/quote.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/quote_tool/__init__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/quote_tool/quote.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/rainbow.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/random.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/random_cli.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/random_string.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/reminder.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/ren/__init__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/ren/__main__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/replace_text.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/restore.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/rot13.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/route_trace.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/scan_open_ports.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/scan_ports.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/screen_recorder.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/screenshot_cli.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/search_files.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/search_history.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/secret_scanner.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/seq.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/service_check.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/shuffle.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/siege_lite/__init__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/siege_lite/__main__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/slugify.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/smellfinder/__init__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/smellfinder/__main__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/sort/__init__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/sort/sort.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/spinner.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/split.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/split_tool/__init__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/split_tool/split.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/sql2csv.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/sqlite_cli/__init__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/sqlite_cli/__main__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/ssh_key_gen.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/ssl_check.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/stopwatch.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/subnet.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/sysmon/__init__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/sysmon/__main__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/sysmon_pro.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/system_info.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/temp_cleaner.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/template.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/text_dedent.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/text_stats.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/text_wrap.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/time_duration.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/timeout.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/timer/__init__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/timer_pro/__init__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/timer_pro/timer_pro.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/timer_pro.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/todo_cli.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/toml2json.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/tr.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/tree.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/treedir/__init__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/treedir/__main__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/tsv2csv.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/uniq_tool/__init__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/uniq_tool/uniq.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/unit_convert.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/uri_encode.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/url_parser.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/urlparse_tool/__init__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/urlparse_tool/cli.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/uuid_gen.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/uuid_tool/__init__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/uuid_tool/__main__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/watch.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/weather_cli.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/web_download.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/web_summary/__init__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/web_summary/__main__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/whois_lookup.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/wordcount/__init__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/wordcount/__main__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/world_clock.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/xml2json.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/xml_format.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/yaml2json/__init__.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/yaml2json/yaml2json.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/yaml2toml.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/yaml_validate.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools/vendor/yes.py +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools.egg-info/dependency_links.txt +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools.egg-info/entry_points.txt +0 -0
- {evolver_tools-30.0.0 → evolver_tools-31.0.0}/src/evolver_tools.egg-info/top_level.txt +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: evolver-tools
|
|
3
|
-
Version:
|
|
4
|
-
Summary:
|
|
3
|
+
Version: 31.0.0
|
|
4
|
+
Summary: 238 CLI tools + 9 flagship projects — one pip install
|
|
5
5
|
Author: EVOLVER
|
|
6
6
|
License-Expression: MIT
|
|
7
7
|
Project-URL: Homepage, https://evolver-dev.github.io/evolver-tools
|
|
@@ -4,8 +4,8 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "evolver-tools"
|
|
7
|
-
version = "
|
|
8
|
-
description = "
|
|
7
|
+
version = "31.0.0"
|
|
8
|
+
description = "238 CLI tools + 9 flagship projects — one pip install"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
|
11
11
|
requires-python = ">=3.8"
|
|
@@ -14,7 +14,7 @@ from evolver_tools.autoreg import auto_discover
|
|
|
14
14
|
def list_tools():
|
|
15
15
|
"""Display all available tools."""
|
|
16
16
|
tools = auto_discover()
|
|
17
|
-
print(f'\x1b[1;36m===== EVOLVER Tools
|
|
17
|
+
print(f'\x1b[1;36m===== EVOLVER Tools v31.0.0 =====\x1b[0m')
|
|
18
18
|
print()
|
|
19
19
|
for name, info in sorted(tools.items()):
|
|
20
20
|
print(f' \033[1;33m{name:<18}\033[0m {info["desc"]}')
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""csv_filter.py — Filter CSV rows by column value matching.
|
|
3
|
+
|
|
4
|
+
Usage:
|
|
5
|
+
cat data.csv | python csv_filter.py --column name --value Alice
|
|
6
|
+
python csv_filter.py data.csv --column age --value 30
|
|
7
|
+
python csv_filter.py data.csv --column city --value "New York" --delimiter ';'
|
|
8
|
+
python csv_filter.py data.csv --column status --value active --invert
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import csv
|
|
14
|
+
import sys
|
|
15
|
+
import argparse
|
|
16
|
+
from typing import Dict, List, Sequence, TextIO
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
TOOL_META = {
|
|
20
|
+
"name": "csv-filter",
|
|
21
|
+
"func": "main",
|
|
22
|
+
"desc": "Filter CSV rows by column value",
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
27
|
+
parser = argparse.ArgumentParser(
|
|
28
|
+
description="Filter CSV rows by column value matching.",
|
|
29
|
+
)
|
|
30
|
+
parser.add_argument(
|
|
31
|
+
"file",
|
|
32
|
+
nargs="?",
|
|
33
|
+
default=None,
|
|
34
|
+
help="CSV file path (omit or use '-' to read from stdin)",
|
|
35
|
+
)
|
|
36
|
+
parser.add_argument(
|
|
37
|
+
"--column",
|
|
38
|
+
required=True,
|
|
39
|
+
help="Column name to filter on",
|
|
40
|
+
)
|
|
41
|
+
parser.add_argument(
|
|
42
|
+
"--value",
|
|
43
|
+
required=True,
|
|
44
|
+
help="Value to match (exact string match)",
|
|
45
|
+
)
|
|
46
|
+
parser.add_argument(
|
|
47
|
+
"--delimiter",
|
|
48
|
+
default=",",
|
|
49
|
+
help="CSV delimiter character (default: ',')",
|
|
50
|
+
)
|
|
51
|
+
parser.add_argument(
|
|
52
|
+
"--invert",
|
|
53
|
+
action="store_true",
|
|
54
|
+
default=False,
|
|
55
|
+
help="Invert match — print rows where column does NOT equal value",
|
|
56
|
+
)
|
|
57
|
+
return parser
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def open_input(file_arg: str | None) -> TextIO:
|
|
61
|
+
"""Return a file handle for the given argument or stdin."""
|
|
62
|
+
if file_arg is None or file_arg == "-":
|
|
63
|
+
return sys.stdin
|
|
64
|
+
return open(file_arg, newline="")
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def filter_rows(
|
|
68
|
+
reader: csv.DictReader,
|
|
69
|
+
column: str,
|
|
70
|
+
value: str,
|
|
71
|
+
invert: bool,
|
|
72
|
+
) -> List[Dict[str, str]]:
|
|
73
|
+
"""Filter rows from a DictReader where column matches (or doesn't match) value."""
|
|
74
|
+
results: List[Dict[str, str]] = []
|
|
75
|
+
for row in reader:
|
|
76
|
+
cell = row.get(column, "")
|
|
77
|
+
if invert:
|
|
78
|
+
if cell != value:
|
|
79
|
+
results.append(row)
|
|
80
|
+
else:
|
|
81
|
+
if cell == value:
|
|
82
|
+
results.append(row)
|
|
83
|
+
return results
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def write_rows(rows: List[Dict[str, str]], fieldnames: Sequence[str], delimiter: str) -> None:
|
|
87
|
+
"""Write filtered rows to stdout as CSV."""
|
|
88
|
+
writer = csv.DictWriter(sys.stdout, fieldnames=fieldnames, delimiter=delimiter)
|
|
89
|
+
writer.writeheader()
|
|
90
|
+
writer.writerows(rows)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def main(argv: list[str] | None = None) -> None:
|
|
94
|
+
parser = build_parser()
|
|
95
|
+
args = parser.parse_args(argv)
|
|
96
|
+
|
|
97
|
+
fh = open_input(args.file)
|
|
98
|
+
try:
|
|
99
|
+
reader = csv.DictReader(fh, delimiter=args.delimiter)
|
|
100
|
+
if not reader.fieldnames:
|
|
101
|
+
sys.exit("error: CSV has no header row (column names required)")
|
|
102
|
+
|
|
103
|
+
fieldnames = reader.fieldnames
|
|
104
|
+
|
|
105
|
+
if args.column not in fieldnames:
|
|
106
|
+
sys.exit(
|
|
107
|
+
f"error: column '{args.column}' not found in CSV header. "
|
|
108
|
+
f"Available columns: {', '.join(fieldnames)}"
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
rows = filter_rows(reader, args.column, args.value, args.invert)
|
|
112
|
+
finally:
|
|
113
|
+
if fh is not sys.stdin:
|
|
114
|
+
fh.close()
|
|
115
|
+
|
|
116
|
+
write_rows(rows, fieldnames, args.delimiter)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
if __name__ == "__main__":
|
|
120
|
+
main()
|
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""env-sorter — Sort environment variables in .env files alphabetically.
|
|
3
|
+
|
|
4
|
+
Usage:
|
|
5
|
+
env-sorter --file .env
|
|
6
|
+
env-sorter --file .env --in-place
|
|
7
|
+
env-sorter --file .env --sort-by value
|
|
8
|
+
env-sorter --file .env --group
|
|
9
|
+
env-sorter --file .env --remove-dups
|
|
10
|
+
env-sorter --file .env --in-place --group --sort-by value --remove-dups
|
|
11
|
+
|
|
12
|
+
Sorts the content of dotenv-style files while preserving comments and
|
|
13
|
+
blank lines as best possible. Supports grouping by common prefix,
|
|
14
|
+
sorting by name or value, and deduplication.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
import sys
|
|
18
|
+
import os
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
TOOL_META = {
|
|
22
|
+
"name": "env-sorter",
|
|
23
|
+
"func": "main",
|
|
24
|
+
"desc": "Sort environment variables in .env files",
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
# ---------------------------------------------------------------------------
|
|
29
|
+
# .env parsing & writing
|
|
30
|
+
# ---------------------------------------------------------------------------
|
|
31
|
+
|
|
32
|
+
def parse_env(text):
|
|
33
|
+
"""Parse .env text into a list of (kind, key, value) tuples.
|
|
34
|
+
|
|
35
|
+
Each tuple is one of:
|
|
36
|
+
('blank', None, None) — empty line
|
|
37
|
+
('comment', None, text) — comment line (includes leading ``#``)
|
|
38
|
+
('var', key, value) — KEY=VALUE assignment
|
|
39
|
+
Order is preserved as-is.
|
|
40
|
+
"""
|
|
41
|
+
entries = []
|
|
42
|
+
for line in text.splitlines(keepends=True):
|
|
43
|
+
stripped = line.strip()
|
|
44
|
+
if stripped == "":
|
|
45
|
+
entries.append(("blank", None, None))
|
|
46
|
+
elif stripped.startswith("#"):
|
|
47
|
+
entries.append(("comment", None, line))
|
|
48
|
+
elif "=" in stripped:
|
|
49
|
+
# Split on first '=' only
|
|
50
|
+
key, _, val = line.partition("=")
|
|
51
|
+
entries.append(("var", key.rstrip(), val))
|
|
52
|
+
else:
|
|
53
|
+
# Lines that don't match any pattern — treat as literal
|
|
54
|
+
entries.append(("comment", None, line))
|
|
55
|
+
return entries
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _get_prefix(key, sep="_"):
|
|
59
|
+
"""Return the prefix of *key* (everything before the first separator)."""
|
|
60
|
+
idx = key.find(sep)
|
|
61
|
+
if idx == -1:
|
|
62
|
+
return ""
|
|
63
|
+
return key[:idx]
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def group_sort(entries, sort_key="name", group=True, remove_dups=False):
|
|
67
|
+
"""Sort *entries* by variable key/value with optional grouping & dedup.
|
|
68
|
+
|
|
69
|
+
Parameters
|
|
70
|
+
----------
|
|
71
|
+
entries : list[tuple]
|
|
72
|
+
Parsed entries from :func:`parse_env`.
|
|
73
|
+
sort_key : str
|
|
74
|
+
``"name"`` (default) sorts by variable name; ``"value"`` sorts by
|
|
75
|
+
value (ties broken by name).
|
|
76
|
+
group : bool
|
|
77
|
+
When *True*, entries are grouped by prefix (everything before the
|
|
78
|
+
first ``_``). Blank lines and comments stay in their relative
|
|
79
|
+
position between groups.
|
|
80
|
+
remove_dups : bool
|
|
81
|
+
When *True*, only the *last* occurrence of a variable name is kept.
|
|
82
|
+
"""
|
|
83
|
+
# Separate non-variable entries and variable entries
|
|
84
|
+
non_vars = [(i, e) for i, e in enumerate(entries) if e[0] != "var"]
|
|
85
|
+
var_entries = [(i, e) for i, e in enumerate(entries) if e[0] == "var"]
|
|
86
|
+
|
|
87
|
+
if remove_dups:
|
|
88
|
+
seen = {}
|
|
89
|
+
# Iterate in original order so the *last* occurrence wins
|
|
90
|
+
for idx, e in var_entries:
|
|
91
|
+
seen[e[1]] = (idx, e)
|
|
92
|
+
var_entries = sorted(seen.values(), key=lambda x: x[0])
|
|
93
|
+
|
|
94
|
+
# Build sort key for each variable entry
|
|
95
|
+
def _sort_key(item):
|
|
96
|
+
idx, e = item
|
|
97
|
+
key = e[1]
|
|
98
|
+
val = e[2].lstrip()
|
|
99
|
+
if sort_key == "value":
|
|
100
|
+
primary = val.lower() if val else ""
|
|
101
|
+
secondary = key.lower()
|
|
102
|
+
else:
|
|
103
|
+
primary = key.lower()
|
|
104
|
+
secondary = ""
|
|
105
|
+
if group:
|
|
106
|
+
prefix = _get_prefix(key)
|
|
107
|
+
return (prefix, primary, secondary, idx)
|
|
108
|
+
return (primary, secondary, idx)
|
|
109
|
+
|
|
110
|
+
var_entries.sort(key=_sort_key)
|
|
111
|
+
|
|
112
|
+
if group:
|
|
113
|
+
return _interleave_groups(var_entries, non_vars, entries)
|
|
114
|
+
else:
|
|
115
|
+
return _rebuild(var_entries, non_vars)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def _interleave_groups(var_entries, non_vars, original_entries):
|
|
119
|
+
"""Interleave non-variable lines between groups of variable entries.
|
|
120
|
+
|
|
121
|
+
Non-variable lines (blanks, comments) that appeared *between* groups
|
|
122
|
+
in the original file are placed before each group. Trailing
|
|
123
|
+
non-variable lines are appended at the end.
|
|
124
|
+
"""
|
|
125
|
+
# Determine groups
|
|
126
|
+
groups = []
|
|
127
|
+
current_group_key = None
|
|
128
|
+
current_group = []
|
|
129
|
+
for idx, e in var_entries:
|
|
130
|
+
prefix = _get_prefix(e[1])
|
|
131
|
+
if prefix != current_group_key:
|
|
132
|
+
if current_group:
|
|
133
|
+
groups.append((current_group_key, current_group))
|
|
134
|
+
current_group_key = prefix
|
|
135
|
+
current_group = []
|
|
136
|
+
current_group.append((idx, e))
|
|
137
|
+
if current_group:
|
|
138
|
+
groups.append((current_group_key, current_group))
|
|
139
|
+
|
|
140
|
+
# Map original index to its entry
|
|
141
|
+
original_by_idx = {i: e for i, e in enumerate(original_entries)}
|
|
142
|
+
|
|
143
|
+
# Assign non-var lines to groups based on proximity in original file
|
|
144
|
+
# Simple approach: split non_vars at the boundaries between groups
|
|
145
|
+
if not groups:
|
|
146
|
+
return [e for _, e in sorted(non_vars, key=lambda x: x[0])]
|
|
147
|
+
|
|
148
|
+
# Collect boundaries: the indices where each group started in the original
|
|
149
|
+
group_boundaries = [(g[0][0], g) for g in groups] # (start_idx, group_data)
|
|
150
|
+
|
|
151
|
+
result = []
|
|
152
|
+
non_var_iter = iter(sorted(non_vars, key=lambda x: x[0]))
|
|
153
|
+
consumed = set()
|
|
154
|
+
|
|
155
|
+
for g_idx, (g_start, (g_prefix, g_entries)) in enumerate(group_boundaries):
|
|
156
|
+
# Add non-var lines that appeared before this group in the original
|
|
157
|
+
while True:
|
|
158
|
+
try:
|
|
159
|
+
nv_idx, nv_entry = next(non_var_iter)
|
|
160
|
+
except StopIteration:
|
|
161
|
+
break
|
|
162
|
+
if nv_idx > g_start and g_idx > 0:
|
|
163
|
+
# This non-var belongs to the previous group, put it back
|
|
164
|
+
# Actually let's just insert any non-var that's before the group start
|
|
165
|
+
if nv_idx < g_start:
|
|
166
|
+
result.append(nv_entry)
|
|
167
|
+
consumed.add(nv_idx)
|
|
168
|
+
else:
|
|
169
|
+
# Put it back by recreating the iterator
|
|
170
|
+
# Simple: just add all remaining non-vars at the end
|
|
171
|
+
non_var_iter = iter([(nv_idx, nv_entry)] + list(non_var_iter))
|
|
172
|
+
break
|
|
173
|
+
else:
|
|
174
|
+
if nv_idx < g_start:
|
|
175
|
+
result.append(nv_entry)
|
|
176
|
+
consumed.add(nv_idx)
|
|
177
|
+
else:
|
|
178
|
+
non_var_iter = iter([(nv_idx, nv_entry)] + list(non_var_iter))
|
|
179
|
+
break
|
|
180
|
+
|
|
181
|
+
# Add the group entries
|
|
182
|
+
for _, e in g_entries:
|
|
183
|
+
result.append(e)
|
|
184
|
+
|
|
185
|
+
# Add any remaining non-var lines
|
|
186
|
+
for nv_idx, nv_entry in sorted(non_vars, key=lambda x: x[0]):
|
|
187
|
+
if nv_idx not in consumed and nv_idx not in {id(e) for e in result}:
|
|
188
|
+
result.append(nv_entry)
|
|
189
|
+
|
|
190
|
+
return result
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def _rebuild(var_entries, non_vars):
|
|
194
|
+
"""Merge sorted variable entries with non-variable lines.
|
|
195
|
+
|
|
196
|
+
Non-variable lines are inserted in their original relative positions
|
|
197
|
+
where possible; otherwise appended at the end.
|
|
198
|
+
"""
|
|
199
|
+
result = []
|
|
200
|
+
non_var_sorted = sorted(non_vars, key=lambda x: x[0])
|
|
201
|
+
nv_idx = 0
|
|
202
|
+
|
|
203
|
+
# Place non-var lines that appeared before the first variable at top
|
|
204
|
+
while nv_idx < len(non_var_sorted) and non_var_sorted[nv_idx][0] < var_entries[0][0]:
|
|
205
|
+
result.append(non_var_sorted[nv_idx][1])
|
|
206
|
+
nv_idx += 1
|
|
207
|
+
|
|
208
|
+
for idx, e in var_entries:
|
|
209
|
+
result.append(e)
|
|
210
|
+
# Add any non-var lines that sit between this var and the next
|
|
211
|
+
while nv_idx < len(non_var_sorted) and (
|
|
212
|
+
nv_idx + 1 >= len(non_var_sorted)
|
|
213
|
+
or (
|
|
214
|
+
idx < non_var_sorted[nv_idx][0]
|
|
215
|
+
and (
|
|
216
|
+
nv_idx + 1 >= len(var_entries)
|
|
217
|
+
or non_var_sorted[nv_idx][0] < var_entries[nv_idx + 1][0]
|
|
218
|
+
if nv_idx + 1 < len(var_entries)
|
|
219
|
+
else True
|
|
220
|
+
)
|
|
221
|
+
)
|
|
222
|
+
):
|
|
223
|
+
result.append(non_var_sorted[nv_idx][1])
|
|
224
|
+
nv_idx += 1
|
|
225
|
+
|
|
226
|
+
# Any leftover non-var lines
|
|
227
|
+
while nv_idx < len(non_var_sorted):
|
|
228
|
+
result.append(non_var_sorted[nv_idx][1])
|
|
229
|
+
nv_idx += 1
|
|
230
|
+
|
|
231
|
+
return result
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def serialize_entries(entries):
|
|
235
|
+
"""Convert a list of entry tuples back into a single string."""
|
|
236
|
+
lines = []
|
|
237
|
+
for e in entries:
|
|
238
|
+
kind = e[0]
|
|
239
|
+
if kind == "blank":
|
|
240
|
+
lines.append("\n")
|
|
241
|
+
elif kind == "comment":
|
|
242
|
+
lines.append(e[2]) # e[2] is the full line
|
|
243
|
+
elif kind == "var":
|
|
244
|
+
lines.append(f"{e[1]}={e[2]}")
|
|
245
|
+
return "".join(lines)
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
# ---------------------------------------------------------------------------
|
|
249
|
+
# CLI
|
|
250
|
+
# ---------------------------------------------------------------------------
|
|
251
|
+
|
|
252
|
+
def main():
|
|
253
|
+
args = sys.argv[1:]
|
|
254
|
+
|
|
255
|
+
if "-h" in args or "--help" in args:
|
|
256
|
+
print(__doc__)
|
|
257
|
+
return
|
|
258
|
+
|
|
259
|
+
filepath = None
|
|
260
|
+
in_place = False
|
|
261
|
+
sort_by = "name"
|
|
262
|
+
group = False
|
|
263
|
+
remove_dups = False
|
|
264
|
+
|
|
265
|
+
i = 0
|
|
266
|
+
while i < len(args):
|
|
267
|
+
a = args[i]
|
|
268
|
+
if a == "--file" and i + 1 < len(args):
|
|
269
|
+
filepath = args[i + 1]
|
|
270
|
+
i += 2
|
|
271
|
+
elif a == "--in-place":
|
|
272
|
+
in_place = True
|
|
273
|
+
i += 1
|
|
274
|
+
elif a == "--sort-by" and i + 1 < len(args):
|
|
275
|
+
val = args[i + 1].lower()
|
|
276
|
+
if val not in ("name", "value"):
|
|
277
|
+
print(f"Error: --sort-by must be 'name' or 'value', got '{val}'", file=sys.stderr)
|
|
278
|
+
sys.exit(1)
|
|
279
|
+
sort_by = val
|
|
280
|
+
i += 2
|
|
281
|
+
elif a == "--group":
|
|
282
|
+
group = True
|
|
283
|
+
i += 1
|
|
284
|
+
elif a == "--remove-dups":
|
|
285
|
+
remove_dups = True
|
|
286
|
+
i += 1
|
|
287
|
+
else:
|
|
288
|
+
print(f"Error: unknown argument '{a}'", file=sys.stderr)
|
|
289
|
+
sys.exit(1)
|
|
290
|
+
|
|
291
|
+
# Read input
|
|
292
|
+
if filepath:
|
|
293
|
+
try:
|
|
294
|
+
with open(filepath, "r") as f:
|
|
295
|
+
text = f.read()
|
|
296
|
+
except FileNotFoundError:
|
|
297
|
+
print(f"Error: file not found '{filepath}'", file=sys.stderr)
|
|
298
|
+
sys.exit(1)
|
|
299
|
+
except PermissionError:
|
|
300
|
+
print(f"Error: permission denied '{filepath}'", file=sys.stderr)
|
|
301
|
+
sys.exit(1)
|
|
302
|
+
else:
|
|
303
|
+
# Read from stdin
|
|
304
|
+
text = sys.stdin.read()
|
|
305
|
+
|
|
306
|
+
entries = parse_env(text)
|
|
307
|
+
sorted_entries = group_sort(entries, sort_by, group, remove_dups)
|
|
308
|
+
output = serialize_entries(sorted_entries)
|
|
309
|
+
|
|
310
|
+
if in_place:
|
|
311
|
+
if not filepath:
|
|
312
|
+
print("Error: --in-place requires --file", file=sys.stderr)
|
|
313
|
+
sys.exit(1)
|
|
314
|
+
with open(filepath, "w") as f:
|
|
315
|
+
f.write(output)
|
|
316
|
+
else:
|
|
317
|
+
sys.stdout.write(output)
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
if __name__ == "__main__":
|
|
321
|
+
main()
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""file-patch — Apply simple text patches (find/replace) to files.
|
|
3
|
+
|
|
4
|
+
Performs a single find-and-replace operation on a text file.
|
|
5
|
+
Supports dry-run mode and automatic backup creation.
|
|
6
|
+
|
|
7
|
+
Usage:
|
|
8
|
+
file-patch --file README.md --old "foo" --new "bar"
|
|
9
|
+
file-patch --file config.py --old "DEBUG = True" --new "DEBUG = False" --dry-run
|
|
10
|
+
file-patch --file settings.ini --old "port=8080" --new "port=9090" --backup
|
|
11
|
+
file-patch --file script.sh --old "some old text" --new ""
|
|
12
|
+
|
|
13
|
+
Options:
|
|
14
|
+
--file PATH Path to the file to patch (required)
|
|
15
|
+
--old TEXT Text to find (required)
|
|
16
|
+
--new TEXT Replacement text (required, may be empty string)
|
|
17
|
+
--dry-run Show what would be changed without modifying the file
|
|
18
|
+
--backup Create a .bak backup of the original file
|
|
19
|
+
-h, --help Show this help message
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
import sys
|
|
23
|
+
import os
|
|
24
|
+
import argparse
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
TOOL_META = {
|
|
28
|
+
"name": "file-patch",
|
|
29
|
+
"func": "main",
|
|
30
|
+
"desc": "Apply text patches to files",
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def apply_patch(
|
|
35
|
+
filepath: str,
|
|
36
|
+
old_text: str,
|
|
37
|
+
new_text: str,
|
|
38
|
+
dry_run: bool = False,
|
|
39
|
+
backup: bool = False,
|
|
40
|
+
) -> int:
|
|
41
|
+
"""Apply a find/replace patch to a file.
|
|
42
|
+
|
|
43
|
+
Reads the file, performs exactly one replacement of *old_text* with
|
|
44
|
+
*new_text*, and writes the result back. If *old_text* is not found,
|
|
45
|
+
or is found more than once, the function exits with an error.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
filepath: Path to the file to patch.
|
|
49
|
+
old_text: The exact text to search for.
|
|
50
|
+
new_text: The text to replace it with.
|
|
51
|
+
dry_run: If True, only display what would change.
|
|
52
|
+
backup: If True, create a .bak copy before modifying.
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
0 on success, 1 on failure.
|
|
56
|
+
"""
|
|
57
|
+
if not os.path.isfile(filepath):
|
|
58
|
+
print(f"Error: file not found: {filepath}", file=sys.stderr)
|
|
59
|
+
return 1
|
|
60
|
+
|
|
61
|
+
# Read the file
|
|
62
|
+
try:
|
|
63
|
+
with open(filepath, "r", encoding="utf-8") as f:
|
|
64
|
+
content = f.read()
|
|
65
|
+
except (OSError, UnicodeDecodeError) as e:
|
|
66
|
+
print(f"Error: cannot read '{filepath}': {e}", file=sys.stderr)
|
|
67
|
+
return 1
|
|
68
|
+
|
|
69
|
+
# Count occurrences
|
|
70
|
+
count = content.count(old_text)
|
|
71
|
+
|
|
72
|
+
if count == 0:
|
|
73
|
+
print(f"Error: string not found in '{filepath}'", file=sys.stderr)
|
|
74
|
+
return 1
|
|
75
|
+
|
|
76
|
+
if count > 1:
|
|
77
|
+
print(
|
|
78
|
+
f"Error: found {count} occurrences of the search string in "
|
|
79
|
+
f"'{filepath}'. Expected exactly 1. Use a more specific "
|
|
80
|
+
f"--old string.",
|
|
81
|
+
file=sys.stderr,
|
|
82
|
+
)
|
|
83
|
+
return 1
|
|
84
|
+
|
|
85
|
+
# Perform the replacement
|
|
86
|
+
new_content = content.replace(old_text, new_text, 1)
|
|
87
|
+
|
|
88
|
+
# Show diff-style output
|
|
89
|
+
_show_patch(filepath, old_text, new_text, count)
|
|
90
|
+
|
|
91
|
+
if dry_run:
|
|
92
|
+
print(f"[DRY-RUN] No changes written to '{filepath}'.")
|
|
93
|
+
return 0
|
|
94
|
+
|
|
95
|
+
# Create backup if requested
|
|
96
|
+
if backup:
|
|
97
|
+
backup_path = filepath + ".bak"
|
|
98
|
+
try:
|
|
99
|
+
# Avoid overwriting an existing backup
|
|
100
|
+
if os.path.exists(backup_path):
|
|
101
|
+
base, ext = backup_path, ""
|
|
102
|
+
idx = 1
|
|
103
|
+
while os.path.exists(f"{base}.{idx}"):
|
|
104
|
+
idx += 1
|
|
105
|
+
backup_path = f"{filepath}.bak.{idx}"
|
|
106
|
+
with open(backup_path, "w", encoding="utf-8") as bf:
|
|
107
|
+
bf.write(content)
|
|
108
|
+
print(f"Backup created: {backup_path}")
|
|
109
|
+
except OSError as e:
|
|
110
|
+
print(f"Error: cannot create backup '{backup_path}': {e}", file=sys.stderr)
|
|
111
|
+
return 1
|
|
112
|
+
|
|
113
|
+
# Write the patched file
|
|
114
|
+
try:
|
|
115
|
+
with open(filepath, "w", encoding="utf-8") as f:
|
|
116
|
+
f.write(new_content)
|
|
117
|
+
print(f"Patched: {filepath}")
|
|
118
|
+
except OSError as e:
|
|
119
|
+
print(f"Error: cannot write '{filepath}': {e}", file=sys.stderr)
|
|
120
|
+
return 1
|
|
121
|
+
|
|
122
|
+
return 0
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def _show_patch(filepath: str, old_text: str, new_text: str, count: int) -> None:
|
|
126
|
+
"""Print a human-readable summary of the patch."""
|
|
127
|
+
print(f"File: {filepath} ({count} occurrence{'s' if count != 1 else ''})")
|
|
128
|
+
# Show context: first line of old text
|
|
129
|
+
old_first = old_text.split("\n")[0]
|
|
130
|
+
new_first = new_text.split("\n")[0]
|
|
131
|
+
if old_text == new_text:
|
|
132
|
+
print(" (no change — old and new text are identical)")
|
|
133
|
+
else:
|
|
134
|
+
print(f" -{old_first}")
|
|
135
|
+
print(f" +{new_first}")
|
|
136
|
+
print()
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def main():
|
|
140
|
+
parser = argparse.ArgumentParser(
|
|
141
|
+
description="Apply a simple text find/replace patch to a file.",
|
|
142
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
143
|
+
epilog=(
|
|
144
|
+
"Examples:\n"
|
|
145
|
+
" file-patch --file README.md --old \"foo\" --new \"bar\"\n"
|
|
146
|
+
" file-patch --file config.py --old \"DEBUG = True\" "
|
|
147
|
+
"--new \"DEBUG = False\" --dry-run\n"
|
|
148
|
+
" file-patch --file settings.ini --old \"port=8080\" "
|
|
149
|
+
"--new \"port=9090\" --backup\n"
|
|
150
|
+
" file-patch --file script.sh --old \"some old text\" --new \"\"\n"
|
|
151
|
+
),
|
|
152
|
+
)
|
|
153
|
+
parser.add_argument(
|
|
154
|
+
"--file", required=True,
|
|
155
|
+
help="Path to the file to patch",
|
|
156
|
+
)
|
|
157
|
+
parser.add_argument(
|
|
158
|
+
"--old", required=True,
|
|
159
|
+
help="Text to find (exact match)",
|
|
160
|
+
)
|
|
161
|
+
parser.add_argument(
|
|
162
|
+
"--new", required=True,
|
|
163
|
+
default="",
|
|
164
|
+
help="Replacement text (may be empty string)",
|
|
165
|
+
)
|
|
166
|
+
parser.add_argument(
|
|
167
|
+
"--dry-run", action="store_true",
|
|
168
|
+
help="Show what would be changed without modifying the file",
|
|
169
|
+
)
|
|
170
|
+
parser.add_argument(
|
|
171
|
+
"--backup", action="store_true",
|
|
172
|
+
help="Create a .bak backup of the original file before patching",
|
|
173
|
+
)
|
|
174
|
+
args = parser.parse_args()
|
|
175
|
+
|
|
176
|
+
filepath = os.path.abspath(args.file)
|
|
177
|
+
|
|
178
|
+
exit_code = apply_patch(
|
|
179
|
+
filepath=filepath,
|
|
180
|
+
old_text=args.old,
|
|
181
|
+
new_text=args.new,
|
|
182
|
+
dry_run=args.dry_run,
|
|
183
|
+
backup=args.backup,
|
|
184
|
+
)
|
|
185
|
+
sys.exit(exit_code)
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
if __name__ == "__main__":
|
|
189
|
+
main()
|