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.
Files changed (480) hide show
  1. {jeeg-2.6.0 → jeeg-2.7.0}/PKG-INFO +26 -6
  2. {jeeg-2.6.0 → jeeg-2.7.0}/README.md +25 -5
  3. {jeeg-2.6.0 → jeeg-2.7.0}/jeeg.egg-info/PKG-INFO +26 -6
  4. {jeeg-2.6.0 → jeeg-2.7.0}/jeeg.egg-info/SOURCES.txt +12 -1
  5. {jeeg-2.6.0 → jeeg-2.7.0}/jeeg.py +107 -9
  6. jeeg-2.7.0/scripts/_dues.py +75 -0
  7. jeeg-2.7.0/scripts/borrow_money.py +25 -0
  8. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/git_log.py +2 -0
  9. jeeg-2.7.0/scripts/lend_money.py +25 -0
  10. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/life_report.py +4 -0
  11. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/notify_due.py +7 -0
  12. jeeg-2.7.0/scripts/reading_list.py +44 -0
  13. jeeg-2.7.0/scripts/settle_ledger.py +29 -0
  14. jeeg-2.7.0/scripts/show_ledger.py +31 -0
  15. jeeg-2.7.0/scripts/vaccine_tracker.py +11 -0
  16. jeeg-2.7.0/scripts/vehicle_dues.py +10 -0
  17. jeeg-2.7.0/scripts/warranty_tracker.py +10 -0
  18. jeeg-2.7.0/scripts/weight_log.py +34 -0
  19. {jeeg-2.6.0 → jeeg-2.7.0}/tests/test_cmds.py +1 -1
  20. jeeg-2.7.0/tests/test_v27.py +154 -0
  21. {jeeg-2.6.0 → jeeg-2.7.0}/LICENSE +0 -0
  22. {jeeg-2.6.0 → jeeg-2.7.0}/jeeg.egg-info/dependency_links.txt +0 -0
  23. {jeeg-2.6.0 → jeeg-2.7.0}/jeeg.egg-info/entry_points.txt +0 -0
  24. {jeeg-2.6.0 → jeeg-2.7.0}/jeeg.egg-info/requires.txt +0 -0
  25. {jeeg-2.6.0 → jeeg-2.7.0}/jeeg.egg-info/top_level.txt +0 -0
  26. {jeeg-2.6.0 → jeeg-2.7.0}/jeeg_cmds.py +0 -0
  27. {jeeg-2.6.0 → jeeg-2.7.0}/pyproject.toml +0 -0
  28. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/__init__.py +0 -0
  29. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/_astro.py +0 -0
  30. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/_backup.py +0 -0
  31. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/_dates.py +0 -0
  32. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/_files.py +0 -0
  33. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/_info.py +0 -0
  34. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/_lib.py +0 -0
  35. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/_money.py +0 -0
  36. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/_net.py +0 -0
  37. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/_routine.py +0 -0
  38. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/_store.py +0 -0
  39. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/_sys.py +0 -0
  40. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/_text.py +0 -0
  41. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/_time.py +0 -0
  42. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/_undo.py +0 -0
  43. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/_worker.py +0 -0
  44. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/_zones.py +0 -0
  45. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/add_bill.py +0 -0
  46. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/add_birthday.py +0 -0
  47. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/add_class.py +0 -0
  48. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/add_contact.py +0 -0
  49. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/add_countdown.py +0 -0
  50. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/add_days.py +0 -0
  51. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/add_email_address.py +0 -0
  52. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/add_event.py +0 -0
  53. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/add_expense.py +0 -0
  54. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/add_medicine.py +0 -0
  55. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/age_calculator.py +0 -0
  56. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/backup_folder.py +0 -0
  57. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/backup_info.py +0 -0
  58. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/battery_status.py +0 -0
  59. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/binary_hex.py +0 -0
  60. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/bmi_calculator.py +0 -0
  61. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/breach_check.py +0 -0
  62. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/budget_report.py +0 -0
  63. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/bulk_rename.py +0 -0
  64. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/calculate.py +0 -0
  65. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/calorie_calculator.py +0 -0
  66. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/cancel_shutdown.py +0 -0
  67. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/check_internet.py +0 -0
  68. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/check_speed.py +0 -0
  69. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/check_updates.py +0 -0
  70. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/check_versions.py +0 -0
  71. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/check_weather.py +0 -0
  72. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/check_website.py +0 -0
  73. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/clean_temp_files.py +0 -0
  74. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/clear_clipboard.py +0 -0
  75. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/clipboard_history.py +0 -0
  76. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/compare_files.py +0 -0
  77. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/compare_folders.py +0 -0
  78. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/compose_email.py +0 -0
  79. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/compound_interest.py +0 -0
  80. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/compress_video.py +0 -0
  81. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/convert_encoding.py +0 -0
  82. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/convert_picture.py +0 -0
  83. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/convert_time_zone.py +0 -0
  84. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/convert_units.py +0 -0
  85. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/cooking_convert.py +0 -0
  86. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/copy_file.py +0 -0
  87. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/copy_path.py +0 -0
  88. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/copy_to_clipboard.py +0 -0
  89. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/count_lines_of_code.py +0 -0
  90. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/count_words.py +0 -0
  91. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/cpu_temperature.py +0 -0
  92. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/cpu_usage.py +0 -0
  93. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/create_folder.py +0 -0
  94. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/create_gitignore.py +0 -0
  95. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/create_venv.py +0 -0
  96. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/cricket_score.py +0 -0
  97. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/crypto_price.py +0 -0
  98. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/csv_preview.py +0 -0
  99. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/csv_summary.py +0 -0
  100. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/currency_convert.py +0 -0
  101. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/daily_briefing.py +0 -0
  102. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/daily_quote.py +0 -0
  103. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/date_difference.py +0 -0
  104. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/days_until.py +0 -0
  105. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/decode_base64.py +0 -0
  106. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/dedupe_lines.py +0 -0
  107. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/define_word.py +0 -0
  108. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/delete_empty_folders.py +0 -0
  109. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/delete_old_downloads.py +0 -0
  110. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/discount_price.py +0 -0
  111. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/disk_space.py +0 -0
  112. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/dns_lookup.py +0 -0
  113. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/docker_ps.py +0 -0
  114. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/docker_stop_all.py +0 -0
  115. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/edit_routines.py +0 -0
  116. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/email_to.py +0 -0
  117. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/emi_loan.py +0 -0
  118. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/empty_trash.py +0 -0
  119. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/encode_base64.py +0 -0
  120. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/end_work_day.py +0 -0
  121. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/exchange_rates.py +0 -0
  122. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/expense_total.py +0 -0
  123. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/export_data.py +0 -0
  124. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/export_records.py +0 -0
  125. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/extract_audio.py +0 -0
  126. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/eye_break.py +0 -0
  127. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/file_hash.py +0 -0
  128. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/file_types.py +0 -0
  129. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/find_contact.py +0 -0
  130. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/find_duplicates.py +0 -0
  131. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/find_file.py +0 -0
  132. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/find_text.py +0 -0
  133. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/flashcards.py +0 -0
  134. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/flight_status.py +0 -0
  135. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/flip_coin.py +0 -0
  136. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/flush_dns.py +0 -0
  137. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/folder_size.py +0 -0
  138. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/folder_tree.py +0 -0
  139. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/fuel_cost.py +0 -0
  140. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/generate_password.py +0 -0
  141. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/generate_uuid.py +0 -0
  142. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/get_directions.py +0 -0
  143. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/git_branch.py +0 -0
  144. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/git_checkout.py +0 -0
  145. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/git_clone.py +0 -0
  146. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/git_commit.py +0 -0
  147. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/git_contributors.py +0 -0
  148. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/git_diff.py +0 -0
  149. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/git_pull.py +0 -0
  150. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/git_push.py +0 -0
  151. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/git_recent_changes.py +0 -0
  152. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/git_stash.py +0 -0
  153. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/git_status.py +0 -0
  154. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/git_undo_commit.py +0 -0
  155. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/gold_price.py +0 -0
  156. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/gpu_info.py +0 -0
  157. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/gst_amount.py +0 -0
  158. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/guess_number.py +0 -0
  159. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/habit_done.py +0 -0
  160. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/habit_streak.py +0 -0
  161. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/hash_text.py +0 -0
  162. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/http_get.py +0 -0
  163. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/import_data.py +0 -0
  164. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/installed_programs.py +0 -0
  165. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/journal_add.py +0 -0
  166. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/journal_prompt.py +0 -0
  167. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/journal_show.py +0 -0
  168. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/json_format.py +0 -0
  169. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/json_minify.py +0 -0
  170. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/json_to_csv.py +0 -0
  171. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/json_to_yaml.py +0 -0
  172. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/keep_awake.py +0 -0
  173. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/kill_port.py +0 -0
  174. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/kill_process.py +0 -0
  175. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/large_files.py +0 -0
  176. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/large_folders.py +0 -0
  177. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/list_links.py +0 -0
  178. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/list_routines.py +0 -0
  179. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/list_snippets.py +0 -0
  180. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/listening_ports.py +0 -0
  181. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/live_network_speed.py +0 -0
  182. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/lock_screen.py +0 -0
  183. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/log_out.py +0 -0
  184. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/lorem_ipsum.py +0 -0
  185. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/lowercase_text.py +0 -0
  186. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/make_invoice.py +0 -0
  187. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/medicine_today.py +0 -0
  188. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/meeting_mode.py +0 -0
  189. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/meeting_notes.py +0 -0
  190. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/memory_usage.py +0 -0
  191. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/merge_pdf.py +0 -0
  192. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/moon_phase.py +0 -0
  193. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/move_file.py +0 -0
  194. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/movie_info.py +0 -0
  195. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/multiplication_table.py +0 -0
  196. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/mute_volume.py +0 -0
  197. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/my_data_lookup.py +0 -0
  198. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/new_document.py +0 -0
  199. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/new_meeting.py +0 -0
  200. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/new_presentation.py +0 -0
  201. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/new_project.py +0 -0
  202. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/new_spreadsheet.py +0 -0
  203. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/news_headlines.py +0 -0
  204. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/next_track.py +0 -0
  205. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/night_mode.py +0 -0
  206. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/note_add.py +0 -0
  207. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/note_list.py +0 -0
  208. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/number_to_words.py +0 -0
  209. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/ocr_text.py +0 -0
  210. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_amazon.py +0 -0
  211. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_app.py +0 -0
  212. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_bluetooth_settings.py +0 -0
  213. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_brave.py +0 -0
  214. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_browser.py +0 -0
  215. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_calculator.py +0 -0
  216. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_calendar.py +0 -0
  217. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_camera.py +0 -0
  218. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_canva.py +0 -0
  219. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_chatgpt.py +0 -0
  220. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_chrome.py +0 -0
  221. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_classroom.py +0 -0
  222. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_claude.py +0 -0
  223. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_clock.py +0 -0
  224. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_codepen.py +0 -0
  225. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_control_panel.py +0 -0
  226. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_coursera.py +0 -0
  227. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_desktop.py +0 -0
  228. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_device_manager.py +0 -0
  229. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_discord.py +0 -0
  230. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_dockerhub.py +0 -0
  231. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_docs.py +0 -0
  232. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_documents.py +0 -0
  233. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_downloads.py +0 -0
  234. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_drive.py +0 -0
  235. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_duolingo.py +0 -0
  236. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_edge.py +0 -0
  237. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_excel.py +0 -0
  238. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_facebook.py +0 -0
  239. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_figma.py +0 -0
  240. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_file_explorer.py +0 -0
  241. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_firefox.py +0 -0
  242. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_flipkart.py +0 -0
  243. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_gemini.py +0 -0
  244. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_github.py +0 -0
  245. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_gitlab.py +0 -0
  246. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_gmail.py +0 -0
  247. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_hotstar.py +0 -0
  248. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_huggingface.py +0 -0
  249. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_ilovepdf.py +0 -0
  250. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_incognito.py +0 -0
  251. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_instagram.py +0 -0
  252. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_irctc.py +0 -0
  253. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_jiosaavn.py +0 -0
  254. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_kaggle.py +0 -0
  255. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_keep.py +0 -0
  256. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_khanacademy.py +0 -0
  257. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_last_download.py +0 -0
  258. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_leetcode.py +0 -0
  259. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_link.py +0 -0
  260. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_linkedin.py +0 -0
  261. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_localhost.py +0 -0
  262. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_makemytrip.py +0 -0
  263. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_maps.py +0 -0
  264. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_mdn.py +0 -0
  265. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_meet.py +0 -0
  266. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_music_folder.py +0 -0
  267. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_myntra.py +0 -0
  268. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_netflix.py +0 -0
  269. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_notepad.py +0 -0
  270. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_notion.py +0 -0
  271. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_npm.py +0 -0
  272. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_nptel.py +0 -0
  273. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_office.py +0 -0
  274. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_onedrive.py +0 -0
  275. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_outlook.py +0 -0
  276. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_paint.py +0 -0
  277. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_perplexity.py +0 -0
  278. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_photos.py +0 -0
  279. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_pictures.py +0 -0
  280. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_pinterest.py +0 -0
  281. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_powerpoint.py +0 -0
  282. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_powershell.py +0 -0
  283. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_primevideo.py +0 -0
  284. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_pypi.py +0 -0
  285. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_reddit.py +0 -0
  286. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_removebg.py +0 -0
  287. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_resource_monitor.py +0 -0
  288. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_scripts_folder.py +0 -0
  289. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_settings.py +0 -0
  290. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_sheets.py +0 -0
  291. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_slack.py +0 -0
  292. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_slides.py +0 -0
  293. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_snapchat.py +0 -0
  294. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_snipping_tool.py +0 -0
  295. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_spotify.py +0 -0
  296. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_stackoverflow.py +0 -0
  297. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_swiggy.py +0 -0
  298. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_taskmanager.py +0 -0
  299. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_teams.py +0 -0
  300. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_telegram.py +0 -0
  301. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_terminal.py +0 -0
  302. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_tinypng.py +0 -0
  303. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_trash.py +0 -0
  304. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_trello.py +0 -0
  305. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_twitch.py +0 -0
  306. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_twitter.py +0 -0
  307. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_uber.py +0 -0
  308. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_udemy.py +0 -0
  309. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_videos_folder.py +0 -0
  310. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_virustotal.py +0 -0
  311. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_vlc.py +0 -0
  312. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_vscode.py +0 -0
  313. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_vscode_here.py +0 -0
  314. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_w3schools.py +0 -0
  315. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_website.py +0 -0
  316. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_whatsapp.py +0 -0
  317. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_wifi_settings.py +0 -0
  318. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_wikipedia.py +0 -0
  319. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_word.py +0 -0
  320. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_youtube.py +0 -0
  321. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_youtube_music.py +0 -0
  322. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_zomato.py +0 -0
  323. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/open_zoom.py +0 -0
  324. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/organize_downloads.py +0 -0
  325. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/password_strength.py +0 -0
  326. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/paste_snippet.py +0 -0
  327. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/pdf_text.py +0 -0
  328. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/percentage_change.py +0 -0
  329. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/pharmacy_near_me.py +0 -0
  330. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/pictures_to_pdf.py +0 -0
  331. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/pincode_lookup.py +0 -0
  332. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/ping_host.py +0 -0
  333. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/pip_install.py +0 -0
  334. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/pip_list.py +0 -0
  335. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/pip_outdated.py +0 -0
  336. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/play_pause.py +0 -0
  337. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/play_song.py +0 -0
  338. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/pnr_status.py +0 -0
  339. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/pomodoro.py +0 -0
  340. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/port_check.py +0 -0
  341. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/port_owner.py +0 -0
  342. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/previous_track.py +0 -0
  343. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/public_holidays.py +0 -0
  344. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/qr_code.py +0 -0
  345. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/qrcode-8.2-py3-none-any.whl +0 -0
  346. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/quiz_notes.py +0 -0
  347. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/random_choice.py +0 -0
  348. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/random_fact.py +0 -0
  349. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/random_number.py +0 -0
  350. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/rd_maturity.py +0 -0
  351. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/reading_time.py +0 -0
  352. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/recent_files.py +0 -0
  353. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/recipe_lookup.py +0 -0
  354. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/regex_tester.py +0 -0
  355. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/remind_me.py +0 -0
  356. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/remove_bill.py +0 -0
  357. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/remove_birthday.py +0 -0
  358. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/remove_budget.py +0 -0
  359. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/remove_class.py +0 -0
  360. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/remove_contact.py +0 -0
  361. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/remove_countdown.py +0 -0
  362. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/remove_link.py +0 -0
  363. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/remove_snippet.py +0 -0
  364. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/rename_by_pattern.py +0 -0
  365. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/rename_photos_by_date.py +0 -0
  366. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/resize_picture.py +0 -0
  367. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/restart_explorer.py +0 -0
  368. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/restart_pc.py +0 -0
  369. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/restaurants_near_me.py +0 -0
  370. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/reverse_text.py +0 -0
  371. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/rock_paper_scissors.py +0 -0
  372. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/roll_dice.py +0 -0
  373. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/roman_numerals.py +0 -0
  374. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/run_routine.py +0 -0
  375. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/run_tests.py +0 -0
  376. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/running_processes.py +0 -0
  377. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/save_clipboard.py +0 -0
  378. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/save_link.py +0 -0
  379. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/save_snippet.py +0 -0
  380. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/schedule_shutdown.py +0 -0
  381. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/screen_off.py +0 -0
  382. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/screen_resolution.py +0 -0
  383. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/search_google.py +0 -0
  384. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/search_images.py +0 -0
  385. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/search_news.py +0 -0
  386. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/search_scholar.py +0 -0
  387. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/send_whatsapp.py +0 -0
  388. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/serve_folder.py +0 -0
  389. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/set_alarm.py +0 -0
  390. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/set_brightness.py +0 -0
  391. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/set_budget.py +0 -0
  392. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/set_timer.py +0 -0
  393. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/set_volume.py +0 -0
  394. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/shopping_add.py +0 -0
  395. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/shopping_clear.py +0 -0
  396. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/shopping_list.py +0 -0
  397. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/shopping_remove.py +0 -0
  398. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/show_calendar.py +0 -0
  399. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/show_clipboard.py +0 -0
  400. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/show_contacts.py +0 -0
  401. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/show_countdowns.py +0 -0
  402. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/show_desktop.py +0 -0
  403. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/show_env_variables.py +0 -0
  404. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/show_expenses.py +0 -0
  405. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/show_ip.py +0 -0
  406. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/show_path.py +0 -0
  407. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/show_reminders.py +0 -0
  408. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/show_time.py +0 -0
  409. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/show_timetable.py +0 -0
  410. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/show_uptime.py +0 -0
  411. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/shred_file.py +0 -0
  412. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/shrink_photos.py +0 -0
  413. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/shutdown_pc.py +0 -0
  414. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/sip_growth.py +0 -0
  415. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/sleep_log.py +0 -0
  416. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/sleep_pc.py +0 -0
  417. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/song_lyrics.py +0 -0
  418. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/sort_lines.py +0 -0
  419. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/speak_text.py +0 -0
  420. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/split_bill.py +0 -0
  421. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/split_pdf.py +0 -0
  422. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/stale_files.py +0 -0
  423. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/start_work_day.py +0 -0
  424. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/startup_programs.py +0 -0
  425. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/stock_price.py +0 -0
  426. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/stop_reminders.py +0 -0
  427. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/stopwatch.py +0 -0
  428. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/stretch_reminder.py +0 -0
  429. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/study_log.py +0 -0
  430. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/study_mode.py +0 -0
  431. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/sunrise_sunset.py +0 -0
  432. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/system_info.py +0 -0
  433. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/take_screenshot.py +0 -0
  434. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/tell_joke.py +0 -0
  435. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/text_diff.py +0 -0
  436. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/tip_calculator.py +0 -0
  437. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/title_case_text.py +0 -0
  438. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/todays_classes.py +0 -0
  439. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/todo_add.py +0 -0
  440. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/todo_clear.py +0 -0
  441. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/todo_done.py +0 -0
  442. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/todo_list.py +0 -0
  443. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/toggle_dark_mode.py +0 -0
  444. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/trace_route.py +0 -0
  445. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/train_status.py +0 -0
  446. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/translate_text.py +0 -0
  447. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/typing_test.py +0 -0
  448. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/undo_expense.py +0 -0
  449. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/undo_files.py +0 -0
  450. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/unit_price.py +0 -0
  451. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/unix_timestamp.py +0 -0
  452. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/unzip_file.py +0 -0
  453. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/upcoming_bills.py +0 -0
  454. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/upcoming_birthdays.py +0 -0
  455. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/uppercase_text.py +0 -0
  456. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/url_decode.py +0 -0
  457. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/url_encode.py +0 -0
  458. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/usb_devices.py +0 -0
  459. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/volume_down.py +0 -0
  460. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/volume_up.py +0 -0
  461. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/water_log.py +0 -0
  462. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/water_reminder.py +0 -0
  463. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/weather_report.py +0 -0
  464. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/week_number.py +0 -0
  465. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/weekday_of.py +0 -0
  466. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/weekly_summary.py +0 -0
  467. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/wifi_info.py +0 -0
  468. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/wiki_summary.py +0 -0
  469. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/word_frequency.py +0 -0
  470. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/world_time.py +0 -0
  471. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/yaml_to_json.py +0 -0
  472. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/zip_folder.py +0 -0
  473. {jeeg-2.6.0 → jeeg-2.7.0}/scripts/zip_with_password.py +0 -0
  474. {jeeg-2.6.0 → jeeg-2.7.0}/setup.cfg +0 -0
  475. {jeeg-2.6.0 → jeeg-2.7.0}/tests/test_engine.py +0 -0
  476. {jeeg-2.6.0 → jeeg-2.7.0}/tests/test_parsers.py +0 -0
  477. {jeeg-2.6.0 → jeeg-2.7.0}/tests/test_privacy.py +0 -0
  478. {jeeg-2.6.0 → jeeg-2.7.0}/tests/test_routing.py +0 -0
  479. {jeeg-2.6.0 → jeeg-2.7.0}/tests/test_security.py +0 -0
  480. {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.6.0
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.6.0 - How It Works
25
+ # JeeG 2.7.0 - How It Works
26
26
 
27
27
  ![JeeG in a terminal](docs/demo.gif)
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
- - 431 ready-made tasks. You can add your own by dropping in a file.
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`, then `jeeg setup`. (Or use the folder, below.)
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.6.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.
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 seven 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.
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.6.0 - How It Works
1
+ # JeeG 2.7.0 - How It Works
2
2
 
3
3
  ![JeeG in a terminal](docs/demo.gif)
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
- - 431 ready-made tasks. You can add your own by dropping in a file.
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`, then `jeeg setup`. (Or use the folder, below.)
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.6.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.
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 seven 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.
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.6.0
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.6.0 - How It Works
25
+ # JeeG 2.7.0 - How It Works
26
26
 
27
27
  ![JeeG in a terminal](docs/demo.gif)
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
- - 431 ready-made tasks. You can add your own by dropping in a file.
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`, then `jeeg setup`. (Or use the folder, below.)
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.6.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.
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 seven 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.
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.6.0"
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 = (STATE / "misses.log").read_text(encoding="utf-8").splitlines()[-20:]
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
- for l in lines:
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 == "misses":
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."))