From 4227465bfba9475f2724a1fa55fffab558abce2c Mon Sep 17 00:00:00 2001 From: Marty Oehme Date: Mon, 7 Apr 2025 12:45:19 +0200 Subject: [PATCH 01/11] fix: Use tw default dirs --- topen.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/topen.py b/topen.py index 3e5fad1..945fa60 100755 --- a/topen.py +++ b/topen.py @@ -28,8 +28,8 @@ from tasklib import Task, TaskWarrior DEFAULTS_DICT = { "task.rc": "~/.config/task/taskrc", - "task.data": "~/.local/share/task", - "notes.dir": "~/.local/share/task/notes", + "task.data": "~/.task", + "notes.dir": "~/.task/notes", "notes.ext": "md", "notes.annot": "Note", "notes.editor": os.getenv("EDITOR") or os.getenv("VISUAL") or "nano", From f3930ae4c72c40df10de240990961b9c39659c5e Mon Sep 17 00:00:00 2001 From: Marty Oehme Date: Mon, 7 Apr 2025 11:31:40 +0200 Subject: [PATCH 02/11] doc: Add configuration description --- README.md | 68 +++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 49 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index e8203f9..67a5e17 100644 --- a/README.md +++ b/README.md @@ -14,13 +14,6 @@ to let you see that there exists a note file next time you view the task. Should just work as-is without additional configuration in most modern taskwarrior setups.[^moderntw] -[^moderntw]: The script assumes your taskwarrior setup follows the XDG base directory suggestions. That means, -taskrc in `$XDG_CONFIG_HOME/task/taskrc`, usually `~/.config/task/taskrc`. Furthermore, at the moment it -assumes the taskwarrior _data_ residing in the `$XDG_DATA_HOME/task` directory. This will diverge from -many taskwarrior setups still and can be set through the cli option `--task-data`. The idea is for future -`topen` versions to recognize the task data directory from the taskrc file itself but this has not been -implemented. - Can be configured through environment variables or cli options, see below. Can be used as-is with the `topen` command or directly from taskwarrior by being aliased in your `taskrc`: @@ -59,22 +52,59 @@ Only has [tasklib](https://github.com/GothenburgBitFactory/tasklib) as a depende ## Configuration -```python -TASK_RC = os.getenv("TASKRC", "~/.config/task/taskrc") # not implemented yet -TASK_DATA_DIR = os.getenv("TASKDATA", "~/.local/share/task") -TOPEN_DIR = os.getenv("TOPEN_DIR", "~/.local/share/task/notes") -TOPEN_EXT = os.getenv("TOPEN_EXT", "md") -TOPEN_ANNOT = os.getenv("TOPEN_ANNOT", "Note") -TOPEN_EDITOR = os.getenv("EDITOR") or os.getenv("VISUAL", "nano") -TOPEN_QUIET = os.getenv("TOPEN_QUIET", False) +By default the script generally assumes your taskwarrior setup follows the XDG +base directory suggestions. + +That means, taskrc in `$XDG_CONFIG_HOME/task/taskrc`, usually +`~/.config/task/taskrc`. Furthermore, at the moment it assumes the taskwarrior +_data_ residing in the `$XDG_DATA_HOME/task` directory. This may diverge from +taskwarrior setups still. + +This program can be configured in 3 different ways: options set in your regular taskwarrior `taskrc` file, +environment variables or options given on the command line. + +### Taskrc configuration + +All options can be changed directly in your taskrc file. +This may be most useful for settings which do not change often for you, +such as the note extension or notes directory. + +The following settings are supported: + +```ini +data.location # used for the taskwarrior data directory +notes.dir # set the notes directory itself +notes.ext # set the note file extension +notes.annot # set the annotation added to tasks with notes +notes.editor # set the editor used to open notes +notes.quiet # set topen to hide all verbose information during use ``` -These are all environment variables offered, needs improved documentation. - Ultimately the goal would probably be to support reading from a taskwarrior 'taskrc' file, which can then be optionally overwritten with env variables, which can then be optionally overwritten with cli options. -This is not fully implemented -- we support the above environment variables -and cli options, that's it. +### Environment variables + +Each option can be changed through setting the corresponding environment variable. + +These are the same as the `taskrc` file options with a prepended `TOPEN_` and dots turned to underscores. + +The following settings are supported: + +```bash +TASKRC= # taskwarrior config file location +TASKDATA= # taskwarrior data directory location +TOPEN_NOTES_DIR= # set the notes directory itself +TOPEN_NOTES_EXT= # set the note file extension +TOPEN_NOTES_ANNOT= # set the annotation added to tasks with notes +TOPEN_NOTES_EDITOR= notes.editor # set the editor used to open notes +TOPEN_NOTES_QUIET= # set topen to hide all verbose information during use +``` + +### CLI options + +Finally, each option can be set through the cli itself. + +To find out all the available options use `topen --help`. From 4ad9f4c98188e358881081e1a8c1f19a4fd2b52d Mon Sep 17 00:00:00 2001 From: Marty Oehme Date: Tue, 8 Apr 2025 16:19:34 +0200 Subject: [PATCH 03/11] ref: Move default opt handling into TConf object --- topen.py | 107 ++++++++++++++++++++++++------------------------------- 1 file changed, 47 insertions(+), 60 deletions(-) diff --git a/topen.py b/topen.py index 945fa60..68af842 100755 --- a/topen.py +++ b/topen.py @@ -26,16 +26,6 @@ from pathlib import Path from tasklib import Task, TaskWarrior -DEFAULTS_DICT = { - "task.rc": "~/.config/task/taskrc", - "task.data": "~/.task", - "notes.dir": "~/.task/notes", - "notes.ext": "md", - "notes.annot": "Note", - "notes.editor": os.getenv("EDITOR") or os.getenv("VISUAL") or "nano", - "notes.quiet": "False", -} - def main(): """Runs the cli interface. @@ -50,8 +40,8 @@ def main(): If the task does not yet have a note annotation it also adds it automatically. """ - opts_override = {"task.rc": DEFAULTS_DICT["task.rc"]} | parse_env() | parse_cli() - conf_file = _real_path(opts_override["task.rc"]) + opts_override = {"task_rc": TConf(0).task_rc} | parse_env() | parse_cli() + conf_file = _real_path(opts_override["task_rc"]) opts: dict = parse_conf(conf_file) | opts_override cfg = conf_from_dict(opts) @@ -120,24 +110,29 @@ class TConf: Contains all the configuration options that can affect Topen note creation. """ - task_rc: Path - """The path to the taskwarrior taskrc file.""" - task_data: Path - """The path to the taskwarrior data directory.""" task_id: int """The id (or uuid) of the task to edit a note for.""" + task_rc: Path = Path("~/.config/task/taskrc") + """The path to the taskwarrior taskrc file.""" + task_data: Path = Path("~/.task") + """The path to the taskwarrior data directory.""" - notes_dir: Path + notes_dir: Path = Path("~/.task/notes") """The path to the notes directory.""" - notes_ext: str + notes_ext: str = "md" """The extension of note files.""" - notes_annot: str + notes_annot: str = "Note" """The annotation to add to taskwarrior tasks with notes.""" - notes_editor: str + notes_editor: str = os.getenv("EDITOR") or os.getenv("VISUAL") or "nano" """The editor to open note files with.""" - notes_quiet: bool + notes_quiet: bool = False """If set topen will give no feedback on note editing.""" + def __post_init__(self): + self.task_rc = _real_path(self.task_rc) + self.task_data = _real_path(self.task_data) + self.notes_dir = _real_path(self.notes_dir) + def conf_from_dict(d: dict) -> TConf: """Generate a TConf class from a dictionary. @@ -145,16 +140,7 @@ def conf_from_dict(d: dict) -> TConf: Turns a dictionary containing all the necessary entries into a TConf configuration file. Will error if one any of the entries are missing. """ - return TConf( - task_rc=_real_path(d["task.rc"]), - task_data=_real_path(d["task.data"]), - task_id=d["task.id"], - notes_dir=_real_path(d["notes.dir"]), - notes_ext=d["notes.ext"], - notes_annot=d["notes.annot"], - notes_editor=d["notes.editor"], - notes_quiet=d["notes.quiet"], - ) + return TConf(**d) def parse_cli() -> dict: @@ -199,14 +185,14 @@ you view the task. p = parser.parse_args() return _filtered_dict( { - "task.id": p.id, - "task.rc": p.task_rc, - "task.data": p.task_data, - "notes.dir": p.notes_dir, - "notes.ext": p.extension, - "notes.annot": p.annotation, - "notes.editor": p.editor, - "notes.quiet": p.quiet, + "task_id": p.id, + "task_rc": p.task_rc, + "task_data": p.task_data, + "notes_dir": p.notes_dir, + "notes_ext": p.extension, + "notes_annot": p.annotation, + "notes_editor": p.editor, + "notes_quiet": p.quiet, } ) @@ -218,13 +204,13 @@ def parse_env() -> dict: """ return _filtered_dict( { - "task.rc": os.getenv("TASKRC"), - "task.data": os.getenv("TASKDATA"), - "notes.dir": os.getenv("TOPEN_NOTES_DIR"), - "notes.ext": os.getenv("TOPEN_NOTES_EXT"), - "notes.annot": os.getenv("TOPEN_NOTES_ANNOT"), - "notes.editor": os.getenv("TOPEN_NOTES_EDITOR"), - "notes.quiet": os.getenv("TOPEN_NOTES_QUIET"), + "task_rc": os.getenv("TASKRC"), + "task_data": os.getenv("TASKDATA"), + "notes_dir": os.getenv("TOPEN_NOTES_DIR"), + "notes_ext": os.getenv("TOPEN_NOTES_EXT"), + "notes_annot": os.getenv("TOPEN_NOTES_ANNOT"), + "notes_editor": os.getenv("TOPEN_NOTES_EDITOR"), + "notes_quiet": os.getenv("TOPEN_NOTES_QUIET"), } ) @@ -235,22 +221,23 @@ def parse_conf(conf_file: Path) -> dict: Returns them as a simple dict object. Uses dot.annotation for options just like taskwarrior settings. """ - c = configparser.ConfigParser( - defaults=DEFAULTS_DICT, allow_unnamed_section=True, allow_no_value=True - ) + c = configparser.ConfigParser(allow_unnamed_section=True, allow_no_value=True) with open(conf_file.expanduser()) as f: - c.read_string("[DEFAULT]\n" + f.read()) + c.read_string("[GENERAL]\n" + f.read()) - return _filtered_dict( - { - "task.data": c.get("DEFAULT", "data.location"), - "notes.dir": c.get("DEFAULT", "notes.dir"), - "notes.ext": c.get("DEFAULT", "notes.ext"), - "notes.annot": c.get("DEFAULT", "notes.annot"), - "notes.editor": c.get("DEFAULT", "notes.editor"), - "notes.quiet": c.get("DEFAULT", "notes.quiet"), - } - ) + res = {} + for option in [ + # tuples with: (conf option name, TConf member name) + ("data.location", "task_data"), + ("notes.dir", "notes_dir"), + ("notes.ext", "notes_ext"), + ("notes.annot", "notes_annot"), + ("notes.editor", "notes_editor"), + ("notes.quiet", "notes_quiet"), + ]: + if c.has_option("GENERAL", option[0]): + res[option[1]] = c.get("GENERAL", option[0]) + return _filtered_dict(res) IS_QUIET = False From 46d57042cd0ee2844e1e173e6e3e85c978fc924d Mon Sep 17 00:00:00 2001 From: Marty Oehme Date: Tue, 8 Apr 2025 16:53:51 +0200 Subject: [PATCH 04/11] ref: Turn TConf from dict into class factory method Code from here: https://stackoverflow.com/questions/56849331/what-is-the-proper-way-in-python-to-define-a-dataclass-that-has-both-an-auto-gen --- topen.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/topen.py b/topen.py index 68af842..0516616 100755 --- a/topen.py +++ b/topen.py @@ -21,8 +21,9 @@ import configparser import os import subprocess import sys -from dataclasses import dataclass +from dataclasses import asdict, dataclass from pathlib import Path +from typing import Any, Self from tasklib import Task, TaskWarrior @@ -43,7 +44,7 @@ def main(): opts_override = {"task_rc": TConf(0).task_rc} | parse_env() | parse_cli() conf_file = _real_path(opts_override["task_rc"]) opts: dict = parse_conf(conf_file) | opts_override - cfg = conf_from_dict(opts) + cfg = TConf.from_dict(opts) if not cfg.task_id: _ = sys.stderr.write("Please provide task ID as argument.\n") @@ -133,14 +134,16 @@ class TConf: self.task_data = _real_path(self.task_data) self.notes_dir = _real_path(self.notes_dir) + def __or__(self, other: Any, /) -> Self: + return self.__class__(**asdict(self) | asdict(other)) -def conf_from_dict(d: dict) -> TConf: - """Generate a TConf class from a dictionary. + @classmethod + def from_dict(cls, d: dict) -> Self: + """Generate a TConf class from a dictionary. - Turns a dictionary containing all the necessary entries into a TConf configuration file. - Will error if one any of the entries are missing. - """ - return TConf(**d) + Turns a dictionary containing all the necessary entries into a TConf configuration file. + """ + return cls(**d) def parse_cli() -> dict: From caec33120cacf1415c4e8d01f725e80c49ce9e48 Mon Sep 17 00:00:00 2001 From: Marty Oehme Date: Tue, 8 Apr 2025 18:19:30 +0200 Subject: [PATCH 05/11] feat: Locate notes dir in task data dir by default --- topen.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/topen.py b/topen.py index 0516616..a388b57 100755 --- a/topen.py +++ b/topen.py @@ -21,9 +21,9 @@ import configparser import os import subprocess import sys -from dataclasses import asdict, dataclass +from dataclasses import asdict, dataclass, field from pathlib import Path -from typing import Any, Self +from typing import Any, Self, cast from tasklib import Task, TaskWarrior @@ -118,8 +118,20 @@ class TConf: task_data: Path = Path("~/.task") """The path to the taskwarrior data directory.""" - notes_dir: Path = Path("~/.task/notes") + notes_dir: Path """The path to the notes directory.""" + _notes_dir: Path | None = field(init=False, repr=False, default=None) + + @property + def notes_dir(self) -> Path: + return self._notes_dir if self._notes_dir else self.task_data.joinpath("notes") + + @notes_dir.setter + def notes_dir(self, value: Path | property | None): + if type(value) is property: + value = TConf._notes_dir + self._notes_dir = cast(Path, value) + notes_ext: str = "md" """The extension of note files.""" notes_annot: str = "Note" From 9d5fa3e2445f7e37e1f5aa45a642ed8ebf18ee98 Mon Sep 17 00:00:00 2001 From: Marty Oehme Date: Tue, 8 Apr 2025 20:06:25 +0200 Subject: [PATCH 06/11] feat: Only annotate tasks if note file was created In the case of opening the notes but then backing out again without actually writing a notes file we should also not annotate the task with anything, since it technically still does not have a note. --- topen.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/topen.py b/topen.py index a388b57..b2206ef 100755 --- a/topen.py +++ b/topen.py @@ -57,11 +57,12 @@ def main(): if not uuid: _ = sys.stderr.write(f"Could not find task for ID: {cfg.task_id}.") sys.exit(1) - fname = get_notes_file(uuid, notes_dir=cfg.notes_dir, notes_ext=cfg.notes_ext) + fpath = get_notes_file(uuid, notes_dir=cfg.notes_dir, notes_ext=cfg.notes_ext) - open_editor(fname, editor=cfg.notes_editor) + open_editor(fpath, editor=cfg.notes_editor) - add_annotation_if_missing(task, annotation_content=cfg.notes_annot) + if fpath.exists(): + add_annotation_if_missing(task, annotation_content=cfg.notes_annot) def get_task(id: str | int, data_location: Path) -> Task: From 4642b24c6beb0d6824872b3f9932f7495b78d775 Mon Sep 17 00:00:00 2001 From: Marty Oehme Date: Tue, 8 Apr 2025 20:06:25 +0200 Subject: [PATCH 07/11] ref: Parse conf file with dict comprehension and named tuple --- topen.py | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/topen.py b/topen.py index b2206ef..342f435 100755 --- a/topen.py +++ b/topen.py @@ -21,6 +21,7 @@ import configparser import os import subprocess import sys +from collections import namedtuple from dataclasses import asdict, dataclass, field from pathlib import Path from typing import Any, Self, cast @@ -241,19 +242,21 @@ def parse_conf(conf_file: Path) -> dict: with open(conf_file.expanduser()) as f: c.read_string("[GENERAL]\n" + f.read()) - res = {} - for option in [ - # tuples with: (conf option name, TConf member name) - ("data.location", "task_data"), - ("notes.dir", "notes_dir"), - ("notes.ext", "notes_ext"), - ("notes.annot", "notes_annot"), - ("notes.editor", "notes_editor"), - ("notes.quiet", "notes_quiet"), - ]: - if c.has_option("GENERAL", option[0]): - res[option[1]] = c.get("GENERAL", option[0]) - return _filtered_dict(res) + ConfTrans = namedtuple("ParsedToTConf", ["name", "tconf_name"]) + return _filtered_dict( + { + opt.tconf_name: c.get("GENERAL", opt.name) + for opt in [ + ConfTrans("data.location", "task_data"), + ConfTrans("notes.dir", "notes_dir"), + ConfTrans("notes.ext", "notes_ext"), + ConfTrans("notes.annot", "notes_annot"), + ConfTrans("notes.editor", "notes_editor"), + ConfTrans("notes.quiet", "notes_quiet"), + ] + if c.has_option("GENERAL", opt.name) + } + ) IS_QUIET = False From 5976651a2661c827c2d26947fd1ab64341ffbef5 Mon Sep 17 00:00:00 2001 From: Marty Oehme Date: Tue, 8 Apr 2025 20:06:25 +0200 Subject: [PATCH 08/11] feat: Create note parent directories if necessary --- topen.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/topen.py b/topen.py index 342f435..9de9da5 100755 --- a/topen.py +++ b/topen.py @@ -58,8 +58,11 @@ def main(): if not uuid: _ = sys.stderr.write(f"Could not find task for ID: {cfg.task_id}.") sys.exit(1) + fpath = get_notes_file(uuid, notes_dir=cfg.notes_dir, notes_ext=cfg.notes_ext) + if not fpath.parent.exists(): + fpath.parent.mkdir(parents=True, exist_ok=True) open_editor(fpath, editor=cfg.notes_editor) if fpath.exists(): From ee4f3781f88799d72247980e42b846cbc7ef5bdd Mon Sep 17 00:00:00 2001 From: Marty Oehme Date: Tue, 8 Apr 2025 20:35:33 +0200 Subject: [PATCH 09/11] feat: Use xdg location or home dir taskrc if it exists The preference structure goes highest to lowest: 1. ~/.taskrc 2. $XDG_CONFIG_HOME/task/taskrc 3. ~/.config/task/taskrc Uses first file found. --- topen.py | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/topen.py b/topen.py index 9de9da5..274b84f 100755 --- a/topen.py +++ b/topen.py @@ -118,10 +118,29 @@ class TConf: task_id: int """The id (or uuid) of the task to edit a note for.""" - task_rc: Path = Path("~/.config/task/taskrc") - """The path to the taskwarrior taskrc file.""" + task_rc: Path + _task_rc: Path | None = field(init=False, repr=False, default=None) + """The path to the taskwarrior taskrc file. Can be absolute or relative to cwd.""" + + @property + def task_rc(self) -> Path: + if self._task_rc: + return self._task_rc + elif _real_path("~/.taskrc").exists(): + return _real_path("~/.taskrc") + elif _real_path("$XDG_CONFIG_HOME/task/taskrc").exists(): + return _real_path("$XDG_CONFIG_HOME/task/taskrc") + else: + return _real_path("~/.config/task/taskrc") + + @task_rc.setter + def task_rc(self, value: Path | property | None): + if type(value) is property: + value = TConf._notes_dir + self._task_rc = cast(Path, value) + task_data: Path = Path("~/.task") - """The path to the taskwarrior data directory.""" + """The path to the taskwarrior data directory. Can be absolute or relative to cwd.""" notes_dir: Path """The path to the notes directory.""" From e3db83df46bd678afc60257efddf19790b46758e Mon Sep 17 00:00:00 2001 From: Marty Oehme Date: Tue, 8 Apr 2025 20:54:38 +0200 Subject: [PATCH 10/11] doc: Update README --- README.md | 41 ++++++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 67a5e17..4956bff 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,7 @@ -# Topen - simple taskwarrior note editing +# Simple taskwarrior note management + +[Docs](https://marty-oehme.github.io/topen) +[Pypi](https://pypi.org/project/topen) A script without bells and whistles. Focuses on letting you quickly: @@ -8,7 +11,7 @@ Focuses on letting you quickly: It does both by simply being invoked with `topen `. -Provide a taskwarrior task id or uuid and topen creates a new note file or lets +Provide a taskwarrior task id or uuid and `topen` creates a new note file or lets you edit an existing one. Additionally it adds a small annotation to the task to let you see that there exists a note file next time you view the task. @@ -32,37 +35,46 @@ That's all there is to it. You can install the script with your favorite python environment manager: ```bash -uv tool install git+https://git.martyoeh.me/Marty/topen.git +uv tool install topen ``` ```bash -pipx install git+https://git.martyoeh.me/Marty/topen.git +pipx install topen ``` ```bash -pip install git+https://git.martyoeh.me/Marty/topen.git +pip install topen ``` Or just manually copy the `topen` file to a directory in your PATH. +[tasklib](https://github.com/GothenburgBitFactory/tasklib) is the only dependency aside from the python standard library. If you just want to try the script out, feel free to do so by invoking it e.g. with `uvx git+https://git.martyoeh.me/Marty/topen.git`. -Only has [tasklib](https://github.com/GothenburgBitFactory/tasklib) as a dependency. +If you want to install the trunk version instead of a versioned release simply substitute for the git path: + +```bash +uv tool install git+https://git.martyoeh.me/Marty/topen.git +``` ## Configuration -By default the script generally assumes your taskwarrior setup follows the XDG -base directory suggestions. +Most taskwarrior setups should not need much further configuration and just work out of the box. +However, if you want to diverge from the defaults explained here, +use the below settings to configure everything to your preferences. -That means, taskrc in `$XDG_CONFIG_HOME/task/taskrc`, usually -`~/.config/task/taskrc`. Furthermore, at the moment it assumes the taskwarrior -_data_ residing in the `$XDG_DATA_HOME/task` directory. This may diverge from -taskwarrior setups still. +It looks for a taskrc file in the user's home directory (`~/.taskrc`) or the XDG base config directory (usually `~/.config/task/taskrc`). +The data directory also follows the taskwarrior defaults (`~/.task`) or is read from the taskrc `data.location` option. + +The notes directory defaults to be in the `notes` subdirectory of where-ever your taskwarrior data location is, +but can be set to anywhere else independently as well. This program can be configured in 3 different ways: options set in your regular taskwarrior `taskrc` file, environment variables or options given on the command line. +CLI options override environment variables, which in turn override configuration set in the `taskrc` file. + ### Taskrc configuration All options can be changed directly in your taskrc file. @@ -80,11 +92,6 @@ notes.editor # set the editor used to open notes notes.quiet # set topen to hide all verbose information during use ``` - -Ultimately the goal would probably be to support reading from a taskwarrior 'taskrc' file, -which can then be optionally overwritten with env variables, -which can then be optionally overwritten with cli options. - ### Environment variables Each option can be changed through setting the corresponding environment variable. From 5e7d997e69cf97cfb9ad764a469021abd79135a8 Mon Sep 17 00:00:00 2001 From: Marty Oehme Date: Tue, 8 Apr 2025 20:54:38 +0200 Subject: [PATCH 11/11] doc: Start CHANGELOG --- CHANGELOG.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..08090e9 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,22 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +### Added + +- Place notes into `notes` subdirectory of taskwarrior data directory by default +- Only annotate tasks for which a note has actually been created + +### Changed + +- Default to same paths as taskwarrior defaults (e.g. `~/.taskrc` and `~/.task/`) +- Look for taskrc file both in xdg location and in home directory + +### Fixed + +- Create any necessary parent directories for notes directory.