jeeg 2.6.0__tar.gz → 2.7.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.
- {jeeg-2.6.0 → jeeg-2.7.0}/PKG-INFO +26 -6
- {jeeg-2.6.0 → jeeg-2.7.0}/README.md +25 -5
- {jeeg-2.6.0 → jeeg-2.7.0}/jeeg.egg-info/PKG-INFO +26 -6
- {jeeg-2.6.0 → jeeg-2.7.0}/jeeg.egg-info/SOURCES.txt +12 -1
- {jeeg-2.6.0 → jeeg-2.7.0}/jeeg.py +107 -9
- jeeg-2.7.0/scripts/_dues.py +75 -0
- jeeg-2.7.0/scripts/borrow_money.py +25 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/git_log.py +2 -0
- jeeg-2.7.0/scripts/lend_money.py +25 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/life_report.py +4 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/notify_due.py +7 -0
- jeeg-2.7.0/scripts/reading_list.py +44 -0
- jeeg-2.7.0/scripts/settle_ledger.py +29 -0
- jeeg-2.7.0/scripts/show_ledger.py +31 -0
- jeeg-2.7.0/scripts/vaccine_tracker.py +11 -0
- jeeg-2.7.0/scripts/vehicle_dues.py +10 -0
- jeeg-2.7.0/scripts/warranty_tracker.py +10 -0
- jeeg-2.7.0/scripts/weight_log.py +34 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/tests/test_cmds.py +1 -1
- jeeg-2.7.0/tests/test_v27.py +154 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/LICENSE +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/jeeg.egg-info/dependency_links.txt +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/jeeg.egg-info/entry_points.txt +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/jeeg.egg-info/requires.txt +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/jeeg.egg-info/top_level.txt +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/jeeg_cmds.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/pyproject.toml +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/__init__.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/_astro.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/_backup.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/_dates.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/_files.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/_info.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/_lib.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/_money.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/_net.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/_routine.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/_store.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/_sys.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/_text.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/_time.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/_undo.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/_worker.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/_zones.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/add_bill.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/add_birthday.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/add_class.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/add_contact.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/add_countdown.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/add_days.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/add_email_address.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/add_event.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/add_expense.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/add_medicine.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/age_calculator.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/backup_folder.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/backup_info.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/battery_status.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/binary_hex.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/bmi_calculator.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/breach_check.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/budget_report.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/bulk_rename.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/calculate.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/calorie_calculator.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/cancel_shutdown.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/check_internet.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/check_speed.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/check_updates.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/check_versions.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/check_weather.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/check_website.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/clean_temp_files.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/clear_clipboard.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/clipboard_history.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/compare_files.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/compare_folders.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/compose_email.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/compound_interest.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/compress_video.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/convert_encoding.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/convert_picture.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/convert_time_zone.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/convert_units.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/cooking_convert.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/copy_file.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/copy_path.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/copy_to_clipboard.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/count_lines_of_code.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/count_words.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/cpu_temperature.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/cpu_usage.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/create_folder.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/create_gitignore.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/create_venv.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/cricket_score.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/crypto_price.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/csv_preview.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/csv_summary.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/currency_convert.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/daily_briefing.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/daily_quote.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/date_difference.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/days_until.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/decode_base64.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/dedupe_lines.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/define_word.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/delete_empty_folders.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/delete_old_downloads.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/discount_price.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/disk_space.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/dns_lookup.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/docker_ps.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/docker_stop_all.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/edit_routines.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/email_to.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/emi_loan.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/empty_trash.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/encode_base64.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/end_work_day.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/exchange_rates.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/expense_total.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/export_data.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/export_records.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/extract_audio.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/eye_break.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/file_hash.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/file_types.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/find_contact.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/find_duplicates.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/find_file.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/find_text.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/flashcards.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/flight_status.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/flip_coin.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/flush_dns.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/folder_size.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/folder_tree.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/fuel_cost.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/generate_password.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/generate_uuid.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/get_directions.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/git_branch.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/git_checkout.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/git_clone.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/git_commit.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/git_contributors.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/git_diff.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/git_pull.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/git_push.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/git_recent_changes.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/git_stash.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/git_status.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/git_undo_commit.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/gold_price.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/gpu_info.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/gst_amount.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/guess_number.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/habit_done.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/habit_streak.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/hash_text.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/http_get.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/import_data.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/installed_programs.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/journal_add.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/journal_prompt.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/journal_show.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/json_format.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/json_minify.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/json_to_csv.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/json_to_yaml.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/keep_awake.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/kill_port.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/kill_process.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/large_files.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/large_folders.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/list_links.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/list_routines.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/list_snippets.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/listening_ports.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/live_network_speed.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/lock_screen.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/log_out.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/lorem_ipsum.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/lowercase_text.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/make_invoice.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/medicine_today.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/meeting_mode.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/meeting_notes.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/memory_usage.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/merge_pdf.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/moon_phase.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/move_file.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/movie_info.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/multiplication_table.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/mute_volume.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/my_data_lookup.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/new_document.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/new_meeting.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/new_presentation.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/new_project.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/new_spreadsheet.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/news_headlines.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/next_track.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/night_mode.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/note_add.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/note_list.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/number_to_words.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/ocr_text.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_amazon.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_app.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_bluetooth_settings.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_brave.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_browser.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_calculator.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_calendar.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_camera.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_canva.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_chatgpt.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_chrome.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_classroom.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_claude.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_clock.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_codepen.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_control_panel.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_coursera.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_desktop.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_device_manager.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_discord.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_dockerhub.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_docs.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_documents.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_downloads.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_drive.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_duolingo.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_edge.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_excel.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_facebook.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_figma.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_file_explorer.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_firefox.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_flipkart.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_gemini.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_github.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_gitlab.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_gmail.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_hotstar.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_huggingface.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_ilovepdf.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_incognito.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_instagram.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_irctc.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_jiosaavn.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_kaggle.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_keep.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_khanacademy.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_last_download.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_leetcode.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_link.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_linkedin.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_localhost.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_makemytrip.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_maps.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_mdn.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_meet.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_music_folder.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_myntra.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_netflix.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_notepad.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_notion.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_npm.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_nptel.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_office.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_onedrive.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_outlook.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_paint.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_perplexity.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_photos.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_pictures.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_pinterest.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_powerpoint.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_powershell.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_primevideo.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_pypi.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_reddit.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_removebg.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_resource_monitor.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_scripts_folder.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_settings.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_sheets.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_slack.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_slides.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_snapchat.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_snipping_tool.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_spotify.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_stackoverflow.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_swiggy.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_taskmanager.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_teams.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_telegram.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_terminal.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_tinypng.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_trash.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_trello.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_twitch.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_twitter.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_uber.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_udemy.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_videos_folder.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_virustotal.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_vlc.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_vscode.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_vscode_here.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_w3schools.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_website.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_whatsapp.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_wifi_settings.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_wikipedia.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_word.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_youtube.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_youtube_music.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_zomato.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_zoom.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/organize_downloads.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/password_strength.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/paste_snippet.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/pdf_text.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/percentage_change.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/pharmacy_near_me.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/pictures_to_pdf.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/pincode_lookup.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/ping_host.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/pip_install.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/pip_list.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/pip_outdated.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/play_pause.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/play_song.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/pnr_status.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/pomodoro.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/port_check.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/port_owner.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/previous_track.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/public_holidays.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/qr_code.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/qrcode-8.2-py3-none-any.whl +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/quiz_notes.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/random_choice.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/random_fact.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/random_number.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/rd_maturity.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/reading_time.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/recent_files.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/recipe_lookup.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/regex_tester.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/remind_me.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/remove_bill.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/remove_birthday.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/remove_budget.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/remove_class.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/remove_contact.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/remove_countdown.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/remove_link.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/remove_snippet.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/rename_by_pattern.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/rename_photos_by_date.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/resize_picture.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/restart_explorer.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/restart_pc.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/restaurants_near_me.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/reverse_text.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/rock_paper_scissors.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/roll_dice.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/roman_numerals.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/run_routine.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/run_tests.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/running_processes.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/save_clipboard.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/save_link.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/save_snippet.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/schedule_shutdown.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/screen_off.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/screen_resolution.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/search_google.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/search_images.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/search_news.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/search_scholar.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/send_whatsapp.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/serve_folder.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/set_alarm.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/set_brightness.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/set_budget.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/set_timer.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/set_volume.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/shopping_add.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/shopping_clear.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/shopping_list.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/shopping_remove.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/show_calendar.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/show_clipboard.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/show_contacts.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/show_countdowns.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/show_desktop.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/show_env_variables.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/show_expenses.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/show_ip.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/show_path.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/show_reminders.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/show_time.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/show_timetable.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/show_uptime.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/shred_file.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/shrink_photos.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/shutdown_pc.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/sip_growth.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/sleep_log.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/sleep_pc.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/song_lyrics.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/sort_lines.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/speak_text.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/split_bill.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/split_pdf.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/stale_files.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/start_work_day.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/startup_programs.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/stock_price.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/stop_reminders.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/stopwatch.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/stretch_reminder.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/study_log.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/study_mode.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/sunrise_sunset.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/system_info.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/take_screenshot.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/tell_joke.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/text_diff.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/tip_calculator.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/title_case_text.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/todays_classes.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/todo_add.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/todo_clear.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/todo_done.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/todo_list.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/toggle_dark_mode.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/trace_route.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/train_status.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/translate_text.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/typing_test.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/undo_expense.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/undo_files.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/unit_price.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/unix_timestamp.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/unzip_file.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/upcoming_bills.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/upcoming_birthdays.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/uppercase_text.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/url_decode.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/url_encode.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/usb_devices.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/volume_down.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/volume_up.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/water_log.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/water_reminder.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/weather_report.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/week_number.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/weekday_of.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/weekly_summary.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/wifi_info.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/wiki_summary.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/word_frequency.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/world_time.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/yaml_to_json.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/zip_folder.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/scripts/zip_with_password.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/setup.cfg +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/tests/test_engine.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/tests/test_parsers.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/tests/test_privacy.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/tests/test_routing.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/tests/test_security.py +0 -0
- {jeeg-2.6.0 → jeeg-2.7.0}/tests/test_v26.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: jeeg
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.7.0
|
|
4
4
|
Summary: Type a task in plain words and it runs. No AI model, no account, no cloud: 400+ everyday tasks that work offline.
|
|
5
5
|
Author: asrbmy
|
|
6
6
|
License: MIT
|
|
@@ -22,7 +22,7 @@ Requires-Dist: qrcode[pil]; extra == "extras"
|
|
|
22
22
|
Requires-Dist: psutil; extra == "extras"
|
|
23
23
|
Dynamic: license-file
|
|
24
24
|
|
|
25
|
-
# JeeG 2.
|
|
25
|
+
# JeeG 2.7.0 - How It Works
|
|
26
26
|
|
|
27
27
|

|
|
28
28
|
|
|
@@ -31,13 +31,13 @@ JeeG finds the matching script in the `scripts` folder and runs it.
|
|
|
31
31
|
|
|
32
32
|
- Pure Python 3.9+, nothing to install. Works on Windows, macOS and Linux.
|
|
33
33
|
- Light: about 16 MB of memory, about 65 ms per command, nothing running in the background (except reminders and alarms you set yourself).
|
|
34
|
-
-
|
|
34
|
+
- 440 ready-made tasks. You can add your own by dropping in a file.
|
|
35
35
|
|
|
36
36
|
---
|
|
37
37
|
|
|
38
38
|
## 1. Quick start
|
|
39
39
|
|
|
40
|
-
0. Easiest: `pip install jeeg
|
|
40
|
+
0. Easiest: `pipx install jeeg` (or `pip install jeeg`), then `jeeg setup`. (Or use the folder, below.)
|
|
41
41
|
1. Install Python 3.9 or newer (python.org). Unzip this folder anywhere.
|
|
42
42
|
2. Open a terminal in the folder and run:
|
|
43
43
|
|
|
@@ -259,6 +259,16 @@ Optional extras for a few of these: `pip install pyzipper pyyaml`.
|
|
|
259
259
|
| Help yourself | `learn` (tour), `doctor fix`, short forms like `pwd` and `mail` |
|
|
260
260
|
| Privacy | `privacy` lists the few tasks that can go online; see `PRIVACY.md` and `SECURITY.md` |
|
|
261
261
|
|
|
262
|
+
### New in version 2.7
|
|
263
|
+
|
|
264
|
+
| Area | Examples |
|
|
265
|
+
|---|---|
|
|
266
|
+
| For other programs | `jeeg --json gst 2500 18`, `jeeg --output result.txt gst 2500 18` |
|
|
267
|
+
| First run | the tour is offered once; `misses export` shares what JeeG did not understand |
|
|
268
|
+
| Money | `lend 500 to ravi`, `borrow 300 from asha`, `ledger`, `settle ravi` |
|
|
269
|
+
| Dates | `add warranty laptop 12 mar 2027`, `add vehicle due insurance bike 15 dec 2026`, `add vaccine asha polio 12 nov 2026`, `what is due` |
|
|
270
|
+
| Personal | `weight 72.5 kg`, `add book Atomic Habits`, `books` |
|
|
271
|
+
|
|
262
272
|
## 6. Adding your own task
|
|
263
273
|
|
|
264
274
|
1. Create a file in `scripts/` named after the task, for example `open_notes_app.py`. The file name is what people will say ("open notes app"), so use words, joined by underscores.
|
|
@@ -294,6 +304,15 @@ Helpers you can use inside Python scripts (from `_lib.py`): `args_text()` (the w
|
|
|
294
304
|
|
|
295
305
|
---
|
|
296
306
|
|
|
307
|
+
## Share a plugin
|
|
308
|
+
|
|
309
|
+
A plugin is one `.py` file. Make a starter with `jeeg plugin new my_task`, edit it, and try it. To share it:
|
|
310
|
+
|
|
311
|
+
1. Keep it to the standard library, one file, under 200 KB, and make it say honestly what it does in `# desc:`.
|
|
312
|
+
2. Put it in `library/`, run `python tools/make_library_index.py`, and open a pull request (or use the "Share a plugin" issue template and paste the file).
|
|
313
|
+
3. Others can then add it with `jeeg plugin get my_task`. JeeG checks the fingerprint and shows what the plugin says it does before installing.
|
|
314
|
+
|
|
315
|
+
|
|
297
316
|
## 7. Safety summary
|
|
298
317
|
|
|
299
318
|
- Dangerous tasks always ask first (see step 6).
|
|
@@ -303,7 +322,7 @@ Helpers you can use inside Python scripts (from `_lib.py`): `args_text()` (the w
|
|
|
303
322
|
- Critical system processes cannot be killed with `kill process`.
|
|
304
323
|
- No task sends your files or personal data anywhere. Web tasks only open a page in your browser.
|
|
305
324
|
|
|
306
|
-
Security notes (version 2.
|
|
325
|
+
Security notes (version 2.7.0): only safe link types are opened, archives cannot write outside their folder, backups are validated before import, your data folder is private to you on Mac/Linux, over-long or control-character input is refused, and unexpected errors show one plain line (`JEEG_DEBUG=1` for details). Plugins and `--dir` folders run with your permissions, so only use ones you trust.
|
|
307
326
|
|
|
308
327
|
## 8. Troubleshooting
|
|
309
328
|
|
|
@@ -329,8 +348,9 @@ Optional extras: `pip install pillow psutil pypdf qrcode[pil]` (screenshots, pro
|
|
|
329
348
|
python tests/test_cmds.py update, plugins, setup, completion, the local web page, new tasks
|
|
330
349
|
python tests/test_v26.py plugin library, sharing, tracking, file tasks, doctor fix, tour
|
|
331
350
|
python tests/test_privacy.py which files can use the network, documented addresses
|
|
351
|
+
python tests/test_v27.py --json/--output, first-run tour, new tasks, speed guard
|
|
332
352
|
|
|
333
|
-
The folder includes `.github/workflows/test.yml`. If you put this project on GitHub, it runs all
|
|
353
|
+
The folder includes `.github/workflows/test.yml`. If you put this project on GitHub, it runs all eight test files, and builds and installs the pip package, on Windows, macOS and Linux with Python 3.9 and 3.12 on every change, which is the easiest way to find problems on systems you do not have at hand.
|
|
334
354
|
|
|
335
355
|
## 10. What was and was not tested
|
|
336
356
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# JeeG 2.
|
|
1
|
+
# JeeG 2.7.0 - How It Works
|
|
2
2
|
|
|
3
3
|

|
|
4
4
|
|
|
@@ -7,13 +7,13 @@ JeeG finds the matching script in the `scripts` folder and runs it.
|
|
|
7
7
|
|
|
8
8
|
- Pure Python 3.9+, nothing to install. Works on Windows, macOS and Linux.
|
|
9
9
|
- Light: about 16 MB of memory, about 65 ms per command, nothing running in the background (except reminders and alarms you set yourself).
|
|
10
|
-
-
|
|
10
|
+
- 440 ready-made tasks. You can add your own by dropping in a file.
|
|
11
11
|
|
|
12
12
|
---
|
|
13
13
|
|
|
14
14
|
## 1. Quick start
|
|
15
15
|
|
|
16
|
-
0. Easiest: `pip install jeeg
|
|
16
|
+
0. Easiest: `pipx install jeeg` (or `pip install jeeg`), then `jeeg setup`. (Or use the folder, below.)
|
|
17
17
|
1. Install Python 3.9 or newer (python.org). Unzip this folder anywhere.
|
|
18
18
|
2. Open a terminal in the folder and run:
|
|
19
19
|
|
|
@@ -235,6 +235,16 @@ Optional extras for a few of these: `pip install pyzipper pyyaml`.
|
|
|
235
235
|
| Help yourself | `learn` (tour), `doctor fix`, short forms like `pwd` and `mail` |
|
|
236
236
|
| Privacy | `privacy` lists the few tasks that can go online; see `PRIVACY.md` and `SECURITY.md` |
|
|
237
237
|
|
|
238
|
+
### New in version 2.7
|
|
239
|
+
|
|
240
|
+
| Area | Examples |
|
|
241
|
+
|---|---|
|
|
242
|
+
| For other programs | `jeeg --json gst 2500 18`, `jeeg --output result.txt gst 2500 18` |
|
|
243
|
+
| First run | the tour is offered once; `misses export` shares what JeeG did not understand |
|
|
244
|
+
| Money | `lend 500 to ravi`, `borrow 300 from asha`, `ledger`, `settle ravi` |
|
|
245
|
+
| Dates | `add warranty laptop 12 mar 2027`, `add vehicle due insurance bike 15 dec 2026`, `add vaccine asha polio 12 nov 2026`, `what is due` |
|
|
246
|
+
| Personal | `weight 72.5 kg`, `add book Atomic Habits`, `books` |
|
|
247
|
+
|
|
238
248
|
## 6. Adding your own task
|
|
239
249
|
|
|
240
250
|
1. Create a file in `scripts/` named after the task, for example `open_notes_app.py`. The file name is what people will say ("open notes app"), so use words, joined by underscores.
|
|
@@ -270,6 +280,15 @@ Helpers you can use inside Python scripts (from `_lib.py`): `args_text()` (the w
|
|
|
270
280
|
|
|
271
281
|
---
|
|
272
282
|
|
|
283
|
+
## Share a plugin
|
|
284
|
+
|
|
285
|
+
A plugin is one `.py` file. Make a starter with `jeeg plugin new my_task`, edit it, and try it. To share it:
|
|
286
|
+
|
|
287
|
+
1. Keep it to the standard library, one file, under 200 KB, and make it say honestly what it does in `# desc:`.
|
|
288
|
+
2. Put it in `library/`, run `python tools/make_library_index.py`, and open a pull request (or use the "Share a plugin" issue template and paste the file).
|
|
289
|
+
3. Others can then add it with `jeeg plugin get my_task`. JeeG checks the fingerprint and shows what the plugin says it does before installing.
|
|
290
|
+
|
|
291
|
+
|
|
273
292
|
## 7. Safety summary
|
|
274
293
|
|
|
275
294
|
- Dangerous tasks always ask first (see step 6).
|
|
@@ -279,7 +298,7 @@ Helpers you can use inside Python scripts (from `_lib.py`): `args_text()` (the w
|
|
|
279
298
|
- Critical system processes cannot be killed with `kill process`.
|
|
280
299
|
- No task sends your files or personal data anywhere. Web tasks only open a page in your browser.
|
|
281
300
|
|
|
282
|
-
Security notes (version 2.
|
|
301
|
+
Security notes (version 2.7.0): only safe link types are opened, archives cannot write outside their folder, backups are validated before import, your data folder is private to you on Mac/Linux, over-long or control-character input is refused, and unexpected errors show one plain line (`JEEG_DEBUG=1` for details). Plugins and `--dir` folders run with your permissions, so only use ones you trust.
|
|
283
302
|
|
|
284
303
|
## 8. Troubleshooting
|
|
285
304
|
|
|
@@ -305,8 +324,9 @@ Optional extras: `pip install pillow psutil pypdf qrcode[pil]` (screenshots, pro
|
|
|
305
324
|
python tests/test_cmds.py update, plugins, setup, completion, the local web page, new tasks
|
|
306
325
|
python tests/test_v26.py plugin library, sharing, tracking, file tasks, doctor fix, tour
|
|
307
326
|
python tests/test_privacy.py which files can use the network, documented addresses
|
|
327
|
+
python tests/test_v27.py --json/--output, first-run tour, new tasks, speed guard
|
|
308
328
|
|
|
309
|
-
The folder includes `.github/workflows/test.yml`. If you put this project on GitHub, it runs all
|
|
329
|
+
The folder includes `.github/workflows/test.yml`. If you put this project on GitHub, it runs all eight test files, and builds and installs the pip package, on Windows, macOS and Linux with Python 3.9 and 3.12 on every change, which is the easiest way to find problems on systems you do not have at hand.
|
|
310
330
|
|
|
311
331
|
## 10. What was and was not tested
|
|
312
332
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: jeeg
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.7.0
|
|
4
4
|
Summary: Type a task in plain words and it runs. No AI model, no account, no cloud: 400+ everyday tasks that work offline.
|
|
5
5
|
Author: asrbmy
|
|
6
6
|
License: MIT
|
|
@@ -22,7 +22,7 @@ Requires-Dist: qrcode[pil]; extra == "extras"
|
|
|
22
22
|
Requires-Dist: psutil; extra == "extras"
|
|
23
23
|
Dynamic: license-file
|
|
24
24
|
|
|
25
|
-
# JeeG 2.
|
|
25
|
+
# JeeG 2.7.0 - How It Works
|
|
26
26
|
|
|
27
27
|

|
|
28
28
|
|
|
@@ -31,13 +31,13 @@ JeeG finds the matching script in the `scripts` folder and runs it.
|
|
|
31
31
|
|
|
32
32
|
- Pure Python 3.9+, nothing to install. Works on Windows, macOS and Linux.
|
|
33
33
|
- Light: about 16 MB of memory, about 65 ms per command, nothing running in the background (except reminders and alarms you set yourself).
|
|
34
|
-
-
|
|
34
|
+
- 440 ready-made tasks. You can add your own by dropping in a file.
|
|
35
35
|
|
|
36
36
|
---
|
|
37
37
|
|
|
38
38
|
## 1. Quick start
|
|
39
39
|
|
|
40
|
-
0. Easiest: `pip install jeeg
|
|
40
|
+
0. Easiest: `pipx install jeeg` (or `pip install jeeg`), then `jeeg setup`. (Or use the folder, below.)
|
|
41
41
|
1. Install Python 3.9 or newer (python.org). Unzip this folder anywhere.
|
|
42
42
|
2. Open a terminal in the folder and run:
|
|
43
43
|
|
|
@@ -259,6 +259,16 @@ Optional extras for a few of these: `pip install pyzipper pyyaml`.
|
|
|
259
259
|
| Help yourself | `learn` (tour), `doctor fix`, short forms like `pwd` and `mail` |
|
|
260
260
|
| Privacy | `privacy` lists the few tasks that can go online; see `PRIVACY.md` and `SECURITY.md` |
|
|
261
261
|
|
|
262
|
+
### New in version 2.7
|
|
263
|
+
|
|
264
|
+
| Area | Examples |
|
|
265
|
+
|---|---|
|
|
266
|
+
| For other programs | `jeeg --json gst 2500 18`, `jeeg --output result.txt gst 2500 18` |
|
|
267
|
+
| First run | the tour is offered once; `misses export` shares what JeeG did not understand |
|
|
268
|
+
| Money | `lend 500 to ravi`, `borrow 300 from asha`, `ledger`, `settle ravi` |
|
|
269
|
+
| Dates | `add warranty laptop 12 mar 2027`, `add vehicle due insurance bike 15 dec 2026`, `add vaccine asha polio 12 nov 2026`, `what is due` |
|
|
270
|
+
| Personal | `weight 72.5 kg`, `add book Atomic Habits`, `books` |
|
|
271
|
+
|
|
262
272
|
## 6. Adding your own task
|
|
263
273
|
|
|
264
274
|
1. Create a file in `scripts/` named after the task, for example `open_notes_app.py`. The file name is what people will say ("open notes app"), so use words, joined by underscores.
|
|
@@ -294,6 +304,15 @@ Helpers you can use inside Python scripts (from `_lib.py`): `args_text()` (the w
|
|
|
294
304
|
|
|
295
305
|
---
|
|
296
306
|
|
|
307
|
+
## Share a plugin
|
|
308
|
+
|
|
309
|
+
A plugin is one `.py` file. Make a starter with `jeeg plugin new my_task`, edit it, and try it. To share it:
|
|
310
|
+
|
|
311
|
+
1. Keep it to the standard library, one file, under 200 KB, and make it say honestly what it does in `# desc:`.
|
|
312
|
+
2. Put it in `library/`, run `python tools/make_library_index.py`, and open a pull request (or use the "Share a plugin" issue template and paste the file).
|
|
313
|
+
3. Others can then add it with `jeeg plugin get my_task`. JeeG checks the fingerprint and shows what the plugin says it does before installing.
|
|
314
|
+
|
|
315
|
+
|
|
297
316
|
## 7. Safety summary
|
|
298
317
|
|
|
299
318
|
- Dangerous tasks always ask first (see step 6).
|
|
@@ -303,7 +322,7 @@ Helpers you can use inside Python scripts (from `_lib.py`): `args_text()` (the w
|
|
|
303
322
|
- Critical system processes cannot be killed with `kill process`.
|
|
304
323
|
- No task sends your files or personal data anywhere. Web tasks only open a page in your browser.
|
|
305
324
|
|
|
306
|
-
Security notes (version 2.
|
|
325
|
+
Security notes (version 2.7.0): only safe link types are opened, archives cannot write outside their folder, backups are validated before import, your data folder is private to you on Mac/Linux, over-long or control-character input is refused, and unexpected errors show one plain line (`JEEG_DEBUG=1` for details). Plugins and `--dir` folders run with your permissions, so only use ones you trust.
|
|
307
326
|
|
|
308
327
|
## 8. Troubleshooting
|
|
309
328
|
|
|
@@ -329,8 +348,9 @@ Optional extras: `pip install pillow psutil pypdf qrcode[pil]` (screenshots, pro
|
|
|
329
348
|
python tests/test_cmds.py update, plugins, setup, completion, the local web page, new tasks
|
|
330
349
|
python tests/test_v26.py plugin library, sharing, tracking, file tasks, doctor fix, tour
|
|
331
350
|
python tests/test_privacy.py which files can use the network, documented addresses
|
|
351
|
+
python tests/test_v27.py --json/--output, first-run tour, new tasks, speed guard
|
|
332
352
|
|
|
333
|
-
The folder includes `.github/workflows/test.yml`. If you put this project on GitHub, it runs all
|
|
353
|
+
The folder includes `.github/workflows/test.yml`. If you put this project on GitHub, it runs all eight test files, and builds and installs the pip package, on Windows, macOS and Linux with Python 3.9 and 3.12 on every change, which is the easiest way to find problems on systems you do not have at hand.
|
|
334
354
|
|
|
335
355
|
## 10. What was and was not tested
|
|
336
356
|
|
|
@@ -13,6 +13,7 @@ scripts/__init__.py
|
|
|
13
13
|
scripts/_astro.py
|
|
14
14
|
scripts/_backup.py
|
|
15
15
|
scripts/_dates.py
|
|
16
|
+
scripts/_dues.py
|
|
16
17
|
scripts/_files.py
|
|
17
18
|
scripts/_info.py
|
|
18
19
|
scripts/_lib.py
|
|
@@ -42,6 +43,7 @@ scripts/backup_info.py
|
|
|
42
43
|
scripts/battery_status.py
|
|
43
44
|
scripts/binary_hex.py
|
|
44
45
|
scripts/bmi_calculator.py
|
|
46
|
+
scripts/borrow_money.py
|
|
45
47
|
scripts/breach_check.py
|
|
46
48
|
scripts/budget_report.py
|
|
47
49
|
scripts/bulk_rename.py
|
|
@@ -159,6 +161,7 @@ scripts/kill_port.py
|
|
|
159
161
|
scripts/kill_process.py
|
|
160
162
|
scripts/large_files.py
|
|
161
163
|
scripts/large_folders.py
|
|
164
|
+
scripts/lend_money.py
|
|
162
165
|
scripts/life_report.py
|
|
163
166
|
scripts/list_links.py
|
|
164
167
|
scripts/list_routines.py
|
|
@@ -335,6 +338,7 @@ scripts/random_choice.py
|
|
|
335
338
|
scripts/random_fact.py
|
|
336
339
|
scripts/random_number.py
|
|
337
340
|
scripts/rd_maturity.py
|
|
341
|
+
scripts/reading_list.py
|
|
338
342
|
scripts/reading_time.py
|
|
339
343
|
scripts/recent_files.py
|
|
340
344
|
scripts/recipe_lookup.py
|
|
@@ -378,6 +382,7 @@ scripts/set_brightness.py
|
|
|
378
382
|
scripts/set_budget.py
|
|
379
383
|
scripts/set_timer.py
|
|
380
384
|
scripts/set_volume.py
|
|
385
|
+
scripts/settle_ledger.py
|
|
381
386
|
scripts/shopping_add.py
|
|
382
387
|
scripts/shopping_clear.py
|
|
383
388
|
scripts/shopping_list.py
|
|
@@ -390,6 +395,7 @@ scripts/show_desktop.py
|
|
|
390
395
|
scripts/show_env_variables.py
|
|
391
396
|
scripts/show_expenses.py
|
|
392
397
|
scripts/show_ip.py
|
|
398
|
+
scripts/show_ledger.py
|
|
393
399
|
scripts/show_path.py
|
|
394
400
|
scripts/show_reminders.py
|
|
395
401
|
scripts/show_time.py
|
|
@@ -443,14 +449,18 @@ scripts/uppercase_text.py
|
|
|
443
449
|
scripts/url_decode.py
|
|
444
450
|
scripts/url_encode.py
|
|
445
451
|
scripts/usb_devices.py
|
|
452
|
+
scripts/vaccine_tracker.py
|
|
453
|
+
scripts/vehicle_dues.py
|
|
446
454
|
scripts/volume_down.py
|
|
447
455
|
scripts/volume_up.py
|
|
456
|
+
scripts/warranty_tracker.py
|
|
448
457
|
scripts/water_log.py
|
|
449
458
|
scripts/water_reminder.py
|
|
450
459
|
scripts/weather_report.py
|
|
451
460
|
scripts/week_number.py
|
|
452
461
|
scripts/weekday_of.py
|
|
453
462
|
scripts/weekly_summary.py
|
|
463
|
+
scripts/weight_log.py
|
|
454
464
|
scripts/wifi_info.py
|
|
455
465
|
scripts/wiki_summary.py
|
|
456
466
|
scripts/word_frequency.py
|
|
@@ -464,4 +474,5 @@ tests/test_parsers.py
|
|
|
464
474
|
tests/test_privacy.py
|
|
465
475
|
tests/test_routing.py
|
|
466
476
|
tests/test_security.py
|
|
467
|
-
tests/test_v26.py
|
|
477
|
+
tests/test_v26.py
|
|
478
|
+
tests/test_v27.py
|
|
@@ -27,6 +27,8 @@ Options:
|
|
|
27
27
|
--dir PATH extra scripts folder (repeatable; also TASK_SCRIPTS_DIR)
|
|
28
28
|
-y, --yes do not ask before risky tasks
|
|
29
29
|
--dry-run show what would run, run nothing
|
|
30
|
+
--json print the result as JSON (task, exit code, output) for other programs
|
|
31
|
+
--output F save what the task prints to a new file F (never overwrites)
|
|
30
32
|
--timeout S stop a script after S seconds
|
|
31
33
|
|
|
32
34
|
Script header (first 15 lines of any script, comment style of that language):
|
|
@@ -55,7 +57,7 @@ import sys
|
|
|
55
57
|
import time
|
|
56
58
|
from pathlib import Path
|
|
57
59
|
|
|
58
|
-
__version__ = "2.
|
|
60
|
+
__version__ = "2.7.0"
|
|
59
61
|
|
|
60
62
|
HERE = Path(__file__).resolve().parent
|
|
61
63
|
def _state_dir() -> Path:
|
|
@@ -633,16 +635,38 @@ def log_miss(query: str, why: str) -> None:
|
|
|
633
635
|
_append_line("misses.log", f"{time.strftime('%Y-%m-%d %H:%M')}\t{why}\t{query}", keep=200, limit=100_000)
|
|
634
636
|
|
|
635
637
|
|
|
636
|
-
def cmd_misses() -> int:
|
|
638
|
+
def cmd_misses(sub: str = "") -> int:
|
|
639
|
+
f = STATE / "misses.log"
|
|
640
|
+
if sub == "clear":
|
|
641
|
+
try:
|
|
642
|
+
f.unlink()
|
|
643
|
+
except OSError:
|
|
644
|
+
pass
|
|
645
|
+
print("Cleared the list of missed phrases.")
|
|
646
|
+
return 0
|
|
637
647
|
try:
|
|
638
|
-
lines =
|
|
648
|
+
lines = f.read_text(encoding="utf-8").splitlines()
|
|
639
649
|
except OSError:
|
|
640
650
|
lines = []
|
|
651
|
+
if sub == "export":
|
|
652
|
+
if not lines:
|
|
653
|
+
print("Nothing missed so far, so there is nothing to export.")
|
|
654
|
+
return 1
|
|
655
|
+
dst = Path.cwd() / "jeeg-misses.txt"
|
|
656
|
+
if dst.exists():
|
|
657
|
+
print(f"{dst} already exists; move it first.")
|
|
658
|
+
return 1
|
|
659
|
+
rows = [l.split("\t")[-1] for l in lines[-100:]]
|
|
660
|
+
dst.write_text("Phrases JeeG did not understand (newest last).\nREAD AND EDIT BEFORE SHARING: remove anything personal such as names or numbers.\n"
|
|
661
|
+
"Share them as a GitHub issue ('A phrase JeeG did not understand') so they can be taught.\n\n" + "\n".join(rows) + "\n", encoding="utf-8")
|
|
662
|
+
print(f"Wrote {len(rows)} phrase(s) to {dst}. Nothing was sent anywhere; check the file, then share what you are comfortable with.")
|
|
663
|
+
return 0
|
|
641
664
|
if not lines:
|
|
642
665
|
print("Nothing missed so far.")
|
|
643
666
|
return 0
|
|
644
667
|
print("Phrases that matched nothing (or were ambiguous). Teach one with: alias add <short> = <task words>")
|
|
645
|
-
|
|
668
|
+
print("To help improve JeeG: 'misses export' writes them to a file you can review and share; 'misses clear' empties the list.")
|
|
669
|
+
for l in lines[-20:]:
|
|
646
670
|
print(" " + l.replace("\t", " "))
|
|
647
671
|
return 0
|
|
648
672
|
|
|
@@ -844,12 +868,27 @@ def run_entry(e: dict, args: list[str], opt: dict) -> int:
|
|
|
844
868
|
return 0
|
|
845
869
|
safe_preview = any(a.lower() in {"preview", "dry-run", "dryrun"} for a in args)
|
|
846
870
|
if e["confirm"] and not opt["yes"] and not safe_preview:
|
|
847
|
-
if not sys.stdin.isatty():
|
|
871
|
+
if not sys.stdin.isatty() or opt.get("quiet"):
|
|
848
872
|
print(f"!! '{label}' needs confirmation; re-run with --yes.")
|
|
849
873
|
return 3
|
|
850
874
|
if input(f"Run '{label}'? [y/N] ").strip().lower() not in {"y", "yes"}:
|
|
851
875
|
print("Cancelled.")
|
|
852
876
|
return 3
|
|
877
|
+
if opt.get("quiet"): # --json / --output: capture what the task prints, no headers
|
|
878
|
+
t0 = time.perf_counter()
|
|
879
|
+
try:
|
|
880
|
+
r = subprocess.run(cmd, capture_output=True, text=True, encoding="utf-8", errors="replace",
|
|
881
|
+
timeout=opt["timeout"], stdin=subprocess.DEVNULL)
|
|
882
|
+
rc, text = r.returncode, (r.stdout or "") + (r.stderr or "")
|
|
883
|
+
except subprocess.TimeoutExpired:
|
|
884
|
+
rc, text = 1, f"{label} timed out after {opt['timeout']}s.\n"
|
|
885
|
+
except OSError as ex:
|
|
886
|
+
rc, text = 1, f"could not start {label}: {ex}\n"
|
|
887
|
+
dt = time.perf_counter() - t0
|
|
888
|
+
log_run(e, rc, dt)
|
|
889
|
+
opt.setdefault("_tasks", []).append({"name": e["name"], "args": list(args), "exit": rc, "seconds": round(dt, 3)})
|
|
890
|
+
sys.stdout.write(text if text.endswith("\n") or not text else text + "\n")
|
|
891
|
+
return 0 if rc == 0 else 1
|
|
853
892
|
print(f">> {label}" + (f" ({' '.join(args)})" if args else ""))
|
|
854
893
|
t0 = time.perf_counter()
|
|
855
894
|
try:
|
|
@@ -934,7 +973,7 @@ def run_query(query: str, entries: list[dict], opt: dict) -> int:
|
|
|
934
973
|
return run_query(alt, entries, opt)
|
|
935
974
|
near = did_you_mean(query, entries)
|
|
936
975
|
log_miss(query, "no match")
|
|
937
|
-
if near and sys.stdin.isatty():
|
|
976
|
+
if near and sys.stdin.isatty() and not opt.get("quiet"):
|
|
938
977
|
ans = input(f"No exact match. Did you mean '{near[0]}'? [y/N] ").strip().lower()
|
|
939
978
|
if ans in {"y", "yes"}:
|
|
940
979
|
if len(query.split()) <= 3 and query.lower() not in RESERVED:
|
|
@@ -1084,8 +1123,8 @@ def handle(line: str, dirs, opt) -> int | None:
|
|
|
1084
1123
|
return cmd_explain(q, entries)
|
|
1085
1124
|
if low in {"privacy", "what goes online", "network tasks"}:
|
|
1086
1125
|
return cmd_privacy(entries)
|
|
1087
|
-
if low
|
|
1088
|
-
return cmd_misses()
|
|
1126
|
+
if low in {"misses", "misses export", "misses clear"}:
|
|
1127
|
+
return cmd_misses(low.split()[1] if " " in low else "")
|
|
1089
1128
|
if low == "stats":
|
|
1090
1129
|
return cmd_stats()
|
|
1091
1130
|
if low == "doctor":
|
|
@@ -1115,8 +1154,55 @@ def handle(line: str, dirs, opt) -> int | None:
|
|
|
1115
1154
|
return run_query(expand_alias(q), entries, opt)
|
|
1116
1155
|
|
|
1117
1156
|
|
|
1157
|
+
def offer_tour(dirs, opt) -> None:
|
|
1158
|
+
"""The very first time JeeG starts in a terminal, offer the guided tour once."""
|
|
1159
|
+
marker = STATE / ".toured"
|
|
1160
|
+
try:
|
|
1161
|
+
if marker.exists() or not (sys.stdin.isatty() and sys.stdout.isatty()):
|
|
1162
|
+
return
|
|
1163
|
+
_ensure_state()
|
|
1164
|
+
marker.write_text("1", encoding="utf-8") # ask only once, whatever the answer
|
|
1165
|
+
ans = input("First time here? Take the 1-minute tour? [Y/n] ").strip().lower()
|
|
1166
|
+
if ans in {"", "y", "yes"}:
|
|
1167
|
+
handle("learn", dirs, opt)
|
|
1168
|
+
except (OSError, EOFError, KeyboardInterrupt):
|
|
1169
|
+
print()
|
|
1170
|
+
|
|
1171
|
+
|
|
1172
|
+
def run_captured(line: str, dirs, opt) -> int:
|
|
1173
|
+
"""--json / --output: run one line, collect the text, and write it as JSON or to a file."""
|
|
1174
|
+
import contextlib
|
|
1175
|
+
import io
|
|
1176
|
+
buf = io.StringIO()
|
|
1177
|
+
with contextlib.redirect_stdout(buf):
|
|
1178
|
+
rc = handle(line, dirs, opt)
|
|
1179
|
+
rc = 0 if rc is None else rc
|
|
1180
|
+
text = buf.getvalue()
|
|
1181
|
+
if opt["json"]:
|
|
1182
|
+
text = json.dumps({"jeeg": __version__, "input": line, "tasks": opt.get("_tasks", []), "exit": rc, "output": text.rstrip("\n")},
|
|
1183
|
+
indent=2, ensure_ascii=False) + "\n"
|
|
1184
|
+
if opt["out"]:
|
|
1185
|
+
dest = Path(opt["out"]).expanduser()
|
|
1186
|
+
try:
|
|
1187
|
+
if "\0" in str(dest) or dest.is_dir() or not dest.parent.is_dir():
|
|
1188
|
+
raise OSError("that is not a usable file path")
|
|
1189
|
+
if dest.exists():
|
|
1190
|
+
raise OSError(f"{dest} already exists (JeeG never overwrites; choose another name)")
|
|
1191
|
+
if len(text) > 5_000_000:
|
|
1192
|
+
raise OSError("the output is too large")
|
|
1193
|
+
with open(dest, "x", encoding="utf-8", newline="\n") as fh:
|
|
1194
|
+
fh.write(text)
|
|
1195
|
+
except OSError as ex:
|
|
1196
|
+
print(f"Could not save the output: {ex}")
|
|
1197
|
+
return 1
|
|
1198
|
+
print(f"Saved to {dest}")
|
|
1199
|
+
else:
|
|
1200
|
+
sys.stdout.write(text)
|
|
1201
|
+
return rc
|
|
1202
|
+
|
|
1203
|
+
|
|
1118
1204
|
def main(argv: list[str]) -> int:
|
|
1119
|
-
opt = {"yes": False, "dry": False, "timeout": None}
|
|
1205
|
+
opt = {"yes": False, "dry": False, "timeout": None, "json": False, "out": None, "quiet": False}
|
|
1120
1206
|
cli_dirs, words, i = [], [], 0
|
|
1121
1207
|
while i < len(argv):
|
|
1122
1208
|
a = argv[i]
|
|
@@ -1124,6 +1210,12 @@ def main(argv: list[str]) -> int:
|
|
|
1124
1210
|
opt["yes"] = True
|
|
1125
1211
|
elif a == "--dry-run":
|
|
1126
1212
|
opt["dry"] = True
|
|
1213
|
+
elif a == "--json":
|
|
1214
|
+
opt["json"] = opt["quiet"] = True
|
|
1215
|
+
elif a == "--output" and i + 1 < len(argv):
|
|
1216
|
+
i += 1
|
|
1217
|
+
opt["out"] = argv[i]
|
|
1218
|
+
opt["quiet"] = True
|
|
1127
1219
|
elif a == "--dir" and i + 1 < len(argv):
|
|
1128
1220
|
i += 1
|
|
1129
1221
|
cli_dirs.append(argv[i])
|
|
@@ -1151,8 +1243,13 @@ def main(argv: list[str]) -> int:
|
|
|
1151
1243
|
return 1
|
|
1152
1244
|
|
|
1153
1245
|
if words:
|
|
1246
|
+
if opt["quiet"]:
|
|
1247
|
+
return run_captured(" ".join(words), dirs, opt)
|
|
1154
1248
|
rc = handle(" ".join(words), dirs, opt)
|
|
1155
1249
|
return 0 if rc is None else rc
|
|
1250
|
+
if opt["quiet"]:
|
|
1251
|
+
print("--json and --output need a task, for example: jeeg --json gst 2500 18")
|
|
1252
|
+
return 1
|
|
1156
1253
|
|
|
1157
1254
|
try:
|
|
1158
1255
|
import readline # noqa: F401 (arrow-key history where available)
|
|
@@ -1160,6 +1257,7 @@ def main(argv: list[str]) -> int:
|
|
|
1160
1257
|
pass
|
|
1161
1258
|
n = len(load_index(dirs))
|
|
1162
1259
|
print(f"JeeG {__version__} - {n} tasks. Say what to do ('list' for help, 'exit' to quit).")
|
|
1260
|
+
offer_tour(dirs, opt)
|
|
1163
1261
|
nudge_backup()
|
|
1164
1262
|
while True:
|
|
1165
1263
|
try:
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"""Things with a due date (warranties, vehicle papers, vaccines). Not a task."""
|
|
2
|
+
import datetime
|
|
3
|
+
import re
|
|
4
|
+
|
|
5
|
+
from _dates import parse_date
|
|
6
|
+
from _lib import fail
|
|
7
|
+
from _store import load, save
|
|
8
|
+
|
|
9
|
+
ADD_RE = re.compile(r"^\s*(?:add|new|save|set|log|renew|record)\b\s*", re.I)
|
|
10
|
+
LIST_RE = re.compile(r"\b(show|list|when|soon|expir\w*|which|what)\b", re.I)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _split(text):
|
|
14
|
+
"""'laptop 12 mar 2027' -> ('laptop', date): the longest tail that reads as a date."""
|
|
15
|
+
words = text.split()
|
|
16
|
+
for i in range(1, len(words)):
|
|
17
|
+
d = parse_date(" ".join(words[i:]))
|
|
18
|
+
if d:
|
|
19
|
+
return " ".join(words[:i]).strip(" ,-:").lower(), d
|
|
20
|
+
return None, None
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def handle(store, noun, text, drop, example):
|
|
24
|
+
"""One command for add / show / remove. Prints the result."""
|
|
25
|
+
t = text.strip()
|
|
26
|
+
items = load(store, [])
|
|
27
|
+
rm = re.match(r"^\s*(?:remove|delete|forget|clear)\s+(.+)$", t, re.I)
|
|
28
|
+
if rm:
|
|
29
|
+
key = " ".join(x for x in rm.group(1).lower().split() if x not in drop)
|
|
30
|
+
hit = [i for i in items if key and (key in i["name"] or i["name"] in key)]
|
|
31
|
+
if len(hit) != 1:
|
|
32
|
+
fail(f"Say which one to remove: {', '.join(i['name'] for i in items) or '(nothing saved yet)'}")
|
|
33
|
+
items.remove(hit[0])
|
|
34
|
+
save(store, items)
|
|
35
|
+
print(f"Removed {hit[0]['name']}.")
|
|
36
|
+
return
|
|
37
|
+
is_add = bool(ADD_RE.match(t))
|
|
38
|
+
body = " ".join(x for x in ADD_RE.sub("", t).split() if x.lower() not in drop)
|
|
39
|
+
name, d = _split(body) if body else (None, None)
|
|
40
|
+
if is_add or (name and d and not LIST_RE.search(t)):
|
|
41
|
+
if not name or not d:
|
|
42
|
+
fail(f"Usage: add {noun} <what> <date> e.g. add {noun} {example}")
|
|
43
|
+
if len(name) > 60:
|
|
44
|
+
fail("That name is too long.")
|
|
45
|
+
if len(items) >= 300:
|
|
46
|
+
fail("That is a lot of entries; remove some first.")
|
|
47
|
+
items = [i for i in items if i["name"] != name]
|
|
48
|
+
items.append({"name": name, "date": d.isoformat()})
|
|
49
|
+
save(store, items)
|
|
50
|
+
left = (d - datetime.date.today()).days
|
|
51
|
+
print(f"Saved {name}: {d:%d %b %Y} ({'in ' + str(left) + ' day(s)' if left >= 0 else 'already ' + str(-left) + ' day(s) ago'}).")
|
|
52
|
+
return
|
|
53
|
+
if not items:
|
|
54
|
+
fail(f"Nothing saved yet. Example: add {noun} {example}")
|
|
55
|
+
today = datetime.date.today()
|
|
56
|
+
print(f"{noun.capitalize()} entries, soonest first:")
|
|
57
|
+
for i in sorted(items, key=lambda x: x["date"]):
|
|
58
|
+
left = (datetime.date.fromisoformat(i["date"]) - today).days
|
|
59
|
+
tag = "OVERDUE" if left < 0 else "soon" if left <= 30 else ""
|
|
60
|
+
when = f"{-left} day(s) ago" if left < 0 else f"in {left} day(s)"
|
|
61
|
+
print(f" {i['name']:<26}{i['date']} {when:<16}{tag}")
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def due_soon(store, within):
|
|
65
|
+
"""[(name, days_left)] for notifications."""
|
|
66
|
+
out = []
|
|
67
|
+
today = datetime.date.today()
|
|
68
|
+
for i in load(store, []):
|
|
69
|
+
try:
|
|
70
|
+
k = (datetime.date.fromisoformat(i["date"]) - today).days
|
|
71
|
+
except (KeyError, ValueError):
|
|
72
|
+
continue
|
|
73
|
+
if k <= within:
|
|
74
|
+
out.append((i["name"], k))
|
|
75
|
+
return out
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# keywords: borrow, borrowed, borrow money, i borrowed, i owe, took loan from, money borrowed
|
|
2
|
+
# desc: Note money you borrowed from someone (borrow 300 from asha)
|
|
3
|
+
# category: money
|
|
4
|
+
# usage: borrow <amount> from <name>
|
|
5
|
+
# rawargs: yes
|
|
6
|
+
# private: yes
|
|
7
|
+
# patterns: ^\s*borrow\b ;; \bi\s+borrowed\b ;; \btook\s+(a\s+)?loan\s+from\b
|
|
8
|
+
# priority: 2
|
|
9
|
+
import re, time
|
|
10
|
+
from _lib import args_text, fail
|
|
11
|
+
from _store import load, save
|
|
12
|
+
t = args_text()
|
|
13
|
+
m = re.search(r"(?<![\w.])(\d[\d,]*(?:\.\d{1,2})?)(?![\w])", t)
|
|
14
|
+
if not m: fail("Usage: borrow <amount> from <name> e.g. borrow 300 from asha")
|
|
15
|
+
amt = float(m.group(1).replace(",", ""))
|
|
16
|
+
rest = (t[:m.start()] + " " + t[m.end():])
|
|
17
|
+
rest = re.sub(r"\b(?:borrow|borrowed|i|rs\.?|inr|rupees?|money|took|loan|from|for|a|the)\b|[₹$]", " ", rest, flags=re.I).split()
|
|
18
|
+
if not rest or not 0 < amt <= 1e9: fail("Usage: borrow <amount> from <name> e.g. borrow 300 from asha")
|
|
19
|
+
name, note = rest[0].lower()[:30], " ".join(rest[1:])[:60]
|
|
20
|
+
led = load("ledger.json", {})
|
|
21
|
+
p = led.setdefault(name, {"balance": 0.0, "log": []})
|
|
22
|
+
p["balance"] = round(p["balance"] - amt, 2); p["log"] = (p["log"] + [[time.strftime("%Y-%m-%d"), -amt, note]])[-50:]
|
|
23
|
+
save("ledger.json", led)
|
|
24
|
+
b = p["balance"]
|
|
25
|
+
print(f"Noted: you owe {name} {amt:,.2f}. " + (f"Total you owe {name}: {-b:,.2f}." if b < 0 else f"{name} now owes you {b:,.2f}." if b > 0 else "All settled."))
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# keywords: git log, commits, history, recent commits, commit history, last commits
|
|
2
2
|
# desc: Show the last 10 git commits
|
|
3
3
|
# category: developer
|
|
4
|
+
# patterns: ^\s*git\s+log\b ;; \b(recent|last)\s+commits\b
|
|
5
|
+
# priority: 1
|
|
4
6
|
from _lib import call
|
|
5
7
|
raise SystemExit(call(['git', 'log', '-10', '--oneline', '--graph', '--decorate']))
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# keywords: lend, lent, lend money, i lent, gave loan to, money lent, owes me
|
|
2
|
+
# desc: Note money you lent to someone (lend 500 to ravi for lunch)
|
|
3
|
+
# category: money
|
|
4
|
+
# usage: lend <amount> to <name> [note]
|
|
5
|
+
# rawargs: yes
|
|
6
|
+
# private: yes
|
|
7
|
+
# patterns: ^\s*lend\b ;; \bi\s+lent\b ;; \bgave\s+(a\s+)?loan\s+to\b
|
|
8
|
+
# priority: 2
|
|
9
|
+
import re, time
|
|
10
|
+
from _lib import args_text, fail
|
|
11
|
+
from _store import load, save
|
|
12
|
+
t = args_text()
|
|
13
|
+
m = re.search(r"(?<![\w.])(\d[\d,]*(?:\.\d{1,2})?)(?![\w])", t)
|
|
14
|
+
if not m: fail("Usage: lend <amount> to <name> [note] e.g. lend 500 to ravi for lunch")
|
|
15
|
+
amt = float(m.group(1).replace(",", ""))
|
|
16
|
+
rest = (t[:m.start()] + " " + t[m.end():])
|
|
17
|
+
rest = re.sub(r"\b(?:lend|lent|i|rs\.?|inr|rupees?|money|gave|loan|to|for|a|the)\b|[₹$]", " ", rest, flags=re.I).split()
|
|
18
|
+
if not rest or not 0 < amt <= 1e9: fail("Usage: lend <amount> to <name> [note] e.g. lend 500 to ravi for lunch")
|
|
19
|
+
name, note = rest[0].lower()[:30], " ".join(rest[1:])[:60]
|
|
20
|
+
led = load("ledger.json", {})
|
|
21
|
+
p = led.setdefault(name, {"balance": 0.0, "log": []})
|
|
22
|
+
p["balance"] = round(p["balance"] + amt, 2); p["log"] = (p["log"] + [[time.strftime("%Y-%m-%d"), amt, note]])[-50:]
|
|
23
|
+
save("ledger.json", led)
|
|
24
|
+
b = p["balance"]
|
|
25
|
+
print(f"Noted: {name} owes you {amt:,.2f}. " + (f"Total {name} owes you: {b:,.2f}." if b > 0 else f"You now owe {name} {-b:,.2f}." if b < 0 else "All settled."))
|