{"id":16139525,"url":"https://github.com/bergercookie/tw-hooks","last_synced_at":"2025-09-08T18:38:05.663Z","repository":{"id":62592007,"uuid":"499237063","full_name":"bergercookie/tw-hooks","owner":"bergercookie","description":"Collection of Taskwarrior hooks + detection and registration mechanism","archived":false,"fork":false,"pushed_at":"2023-01-22T18:34:28.000Z","size":224,"stargazers_count":14,"open_issues_count":2,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-08-19T01:48:29.334Z","etag":null,"topics":["automation","python","python3","task","taskmanager","taskwarrior","taskwarrior-hooks"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bergercookie.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE.md","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null},"funding":{"github":["bergercookie"]}},"created_at":"2022-06-02T17:56:21.000Z","updated_at":"2025-06-09T19:42:49.000Z","dependencies_parsed_at":"2023-02-12T17:30:27.770Z","dependency_job_id":null,"html_url":"https://github.com/bergercookie/tw-hooks","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/bergercookie/tw-hooks","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bergercookie%2Ftw-hooks","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bergercookie%2Ftw-hooks/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bergercookie%2Ftw-hooks/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bergercookie%2Ftw-hooks/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bergercookie","download_url":"https://codeload.github.com/bergercookie/tw-hooks/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bergercookie%2Ftw-hooks/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":274229384,"owners_count":25245189,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-08T02:00:09.813Z","response_time":121,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["automation","python","python3","task","taskmanager","taskwarrior","taskwarrior-hooks"],"created_at":"2024-10-09T23:49:15.584Z","updated_at":"2025-09-08T18:38:05.634Z","avatar_url":"https://github.com/bergercookie.png","language":"Python","readme":"# Taskwarrior Hooks\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://raw.githubusercontent.com/bergercookie/tw-hooks/master/misc/logo.png\"/\u003e\n\u003c/p\u003e\n\n\u003ca href=\"https://github.com/bergercookie/tw-hooks/actions\" alt=\"CI\"\u003e\n\u003cimg src=\"https://github.com/bergercookie/tw-hooks/actions/workflows/ci.yml/badge.svg\" /\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/pre-commit/pre-commit\"\u003e\n\u003cimg src=\"https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit\u0026logoColor=white\" alt=\"pre-commit\"\u003e\u003c/a\u003e\n\n\u003ca href='https://coveralls.io/github/bergercookie/tw-hooks?branch=master'\u003e\n\u003cimg src='https://coveralls.io/repos/github/bergercookie/tw-hooks/badge.svg?branch=master' alt='Coverage Status' /\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/bergercookie/tw-hooks/blob/master/LICENSE.md\" alt=\"LICENSE\"\u003e\n\u003cimg src=\"https://img.shields.io/github/license/bergercookie/tw-hooks.svg\" /\u003e\u003c/a\u003e\n\u003ca href=\"https://pypi.org/project/tw_hooks/\" alt=\"pypi\"\u003e\n\u003cimg src=\"https://img.shields.io/pypi/pyversions/tw-hooks.svg\" /\u003e\u003c/a\u003e\n\u003ca href=\"https://badge.fury.io/py/tw-hooks\"\u003e\n\u003cimg src=\"https://badge.fury.io/py/tw-hooks.svg\" alt=\"PyPI version\" height=\"18\"\u003e\u003c/a\u003e\n\u003ca href=\"https://pepy.tech/project/tw-hooks\"\u003e\n\u003cimg alt=\"Downloads\" src=\"https://pepy.tech/badge/tw_hooks\"\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/psf/black\"\u003e\n\u003cimg alt=\"Code style: black\" src=\"https://img.shields.io/badge/code%20style-black-000000.svg\"\u003e\u003c/a\u003e\n\n## Description\n\nThis is a collection of [Taskwarrior\nhooks](https://taskwarrior.org/docs/hooks_guide.html) that I use in my\nday-to-day workflows. It comes along a detection and easy-registration mechanism\nthat should make it easy to develop and then distribute your own hooks. The\nhooks are structured as classes under the `tw_hooks/hooks` directory.\n\n## Installation\n\nInstall it from `PyPI`:\n\n```sh\npip3 install --user --upgrade tw_hooks\n```\n\nTo get the latest version install directly from source:\n\n```sh\npip3 install --user --upgrade git+https://github.com/bergercookie/tw-hooks\n```\n\nAfter the installation, you have to run the `install-hooks-shims` executable\n(which by this point should be in your `$PATH`). Running it will create shims\n(thin wrapper scripts) under `~/.task/hooks` in order to register all the hooks\nwith Taskwarrior.\n\n## Available hooks\n\nCurrently the following hooks are available out-of-the-box:\n\n\u003ctable style=\"undefined;table-layout: fixed; width: 823px\"\u003e\n\u003cthead\u003e\n  \u003ctr\u003e\n    \u003cth\u003eHook\u003c/th\u003e\n    \u003cth\u003eDescription\u003c/th\u003e\n    \u003cth\u003eEvents\u003c/th\u003e\n  \u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\u003ctt\u003eAutoTagBasedOnTags\u003c/tt\u003e\u003c/td\u003e\n    \u003ctd\u003eInspect the list of tags in the added/modified tasks provided and add additional tags if required\u003c/td\u003e\n    \u003ctd\u003e\u003ctt\u003eon-modify\u003c/tt\u003e, \u003ctt\u003eon-add\u003c/tt\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\u003ctt\u003eCorrectTagNames\u003c/tt\u003e\u003c/td\u003e\n    \u003ctd\u003eChange tag names based on a predefined lookup table\u003c/td\u003e\n    \u003ctd\u003e\u003ctt\u003eon-modify\u003c/tt\u003e, \u003ctt\u003eon-add\u003c/tt\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\u003ctt\u003eDetectMutuallyExclusiveTags\u003c/tt\u003e\u003c/td\u003e\n    \u003ctd\u003eSee whether the user has specified an incompatible combination of tags\u003c/td\u003e\n    \u003ctd\u003e\u003ctt\u003eon-modify\u003c/tt\u003e, \u003ctt\u003eon-add\u003c/tt\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\u003ctt\u003ePostLatestStartToI3Status\u003c/tt\u003e\u003c/td\u003e\n    \u003ctd\u003eWhen a task is started, send the title of the task to i3status-rs via DBus\u003c/td\u003e\n    \u003ctd\u003e\u003ctt\u003eon-modify\u003c/tt\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\u003ctt\u003eWarnOnTaskCongestion\u003c/tt\u003e\u003c/td\u003e\n    \u003ctd\u003eWarn the user if there are too many tasks (due:today)\u003c/td\u003e\n    \u003ctd\u003e\u003ctt\u003eon-exit\u003c/tt\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\n\n## Structure of a Hook\n\nThe purpose of this package is to facilitate the development and distribution of\nTaskwarrior hooks. To this purpose `install-hooks-shims` allows you to easily\nregister your own hooks, without having to manually copy items over to the\ntaskwarrior hooks location. `install-hooks-shims` will install a shim which will\ncall your hook automatically when required.\n\nThis is an example of a Taskwarrior hook that will be executed on Taskwarrior\nexit:\n\n```python\nfrom tw_hooks import OnExitHook\nclass WarnOnTaskCongestion(OnExitHook):\n    \"\"\"Warn the user if there are too many tasks.\"\"\"\n    def _on_exit(self, _):  # \u003c--- Mandatory to implement this signature\n      # ...\n      return 0\n```\n\nAssuming that this hook is in a module called `warn_on_task_congestion.py` and\nthat the directory of this module is in your python path (e.g., by adding it\nexplicitly to `$PYTHONPATH`), then you can run the following to register your\nhook with taskwarrior:\n\n```sh\ninstall-hooks-shims -r warn_on_task_congestion\n```\n\nDuring your next Taskwarrior operation, if there are too many due:today tasks,\nyou should see something like this:\n\n```sh\nt add +test kalimera\nCreated task 719.\n[WarnOnTaskCongestion] Too many due:today tasks [threshold=9]\n```\n\n## Hooks API\n\nSubclass one of the following base hooks, and your method is going to be called\nduring that event:\n\n- [`OnAddHook`](https://github.com/bergercookie/tw-hooks/blob/master/tw_hooks/base_hooks/on_add_hook.py)\n  - Implement the `_on_add(self, added_task: TaskT)` method.\n- [`OnExitHook`](https://github.com/bergercookie/tw-hooks/blob/master/tw_hooks/base_hooks/on_exit_hook.py)\n  - Implement the `_on_exit(self, added_modified_tasks: List[TaskT])` method.\n- [`OnLaunchHook`](https://github.com/bergercookie/tw-hooks/blob/master/tw_hooks/base_hooks/on_launch_hook.py)\n  - Implement the `_on_launch(self)` method.\n- [`OnModifyHook`](https://github.com/bergercookie/tw-hooks/blob/master/tw_hooks/base_hooks/on_modify_hook.py)\n  - Implement the `_on_modify(self, original_task: TaskT, modified_task: TaskT)`\n    method.\n\n## Usage instructions for `install-hooks-shims`\n\n\u003c!-- START sniff-and-replace install-hook-shims --help START --\u003e\n\n```python\nusage: Detect Taskwarrior hooks and register an executable shim for each one of them.\n       [-h] [-t TASK_DIR] [-a] [-l]\n       [-r REGISTER_ADDITIONAL [REGISTER_ADDITIONAL ...]]\n\noptional arguments:\n  -h, --help            show this help message and exit\n  -t TASK_DIR, --task-dir TASK_DIR\n                        Path to the taskwarrior main directory\n  -a, --all-hooks       Install shims for all the hooks\n  -l, --list-hooks      List the available hooks and exit\n  -r REGISTER_ADDITIONAL [REGISTER_ADDITIONAL ...], --register-additional REGISTER_ADDITIONAL [REGISTER_ADDITIONAL ...]\n\nUsage examples:\n===============\n\n- Install only the WarnOnTaskCongestion hook (assuming you've installed tw_hooks with e.g., pip3)\n  install-hook-shims -r tw_hooks.hooks.warn_on_task_congestion\n\n- Install all the available hooks from this repo (assuming you've installed tw_hooks with e.g., pip3)\n  install-hook-shims --all-hooks\n\n- Install a custom hook defined in .../dir/mod/hook_name.py. \"dir\" should be in your PYTHONPATH\n  install-hook-shims -r mod.hook_name\n\n- List all the available hooks and exit\n  install-hook-shims --list-hooks\n\n```\n\n\u003c!-- END sniff-and-replace --\u003e\n\n## Miscellaneous\n\n- [Contributing Guide](CONTRIBUTING.md)\n\n## FAQ\n\n- Why should I use this over raw taskwarrior hooks?\n  - Because this package does the heavy lifting pre-processing the input tasks\n    from the command line. It does so in a robust manner making sure it does\n    the right thing regardless of weather one or two commands are provided and\n    being robust to errors (e.g., `utf-8` decoding errors).\n  - It takes care to make the hooks fail safely even if it can't find required\n    modules (e.g., if you try invoking `task` from inside a `virtualenv` where\n    `tw-hooks` is not importable.\n  - It gives you a class-oriented approach and lets you install multiple hooks\n    from the same class, thus allowing these hooks to share common\n    configuration.\n  - It allows you to keep all your hooks together and keep\n    them as a package in some other place in your filesystem, e.g., in your\n    dotfiles and automatically adds the right glue-code so that Taskwarrior your\n    scripts without having to explicitly place it in `~/.task/hooks` or\n    symlinking it.\n\n## Self Promotion\n\nIf you find this tool useful, please [star it on\nGithub](https://github.com/bergercookie/tw-hooks)\nand consider donating.\n\n## Support\n\nIf something doesn't work, feel free to open an issue. You can also find me in\nthe [#taskwarrior Libera Chat](https://matrix.to/#/#taskwarrior:libera.chat).\n\n## TODO List\n\nSee [ISSUES\nlist](https://github.com/bergercookie/tw-hooks/issues)\nfor the things that I'm currently either working on or interested in\nimplementing in the near future. In case there's something you are interesting\nin working on, don't hesitate to either ask for clarifications or just do it and\ndirectly make a PR.\n","funding_links":["https://github.com/sponsors/bergercookie"],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbergercookie%2Ftw-hooks","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbergercookie%2Ftw-hooks","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbergercookie%2Ftw-hooks/lists"}