dot-tasks 0.1.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 (30) hide show
  1. dot_tasks-0.1.0/LICENSE +21 -0
  2. dot_tasks-0.1.0/PKG-INFO +173 -0
  3. dot_tasks-0.1.0/README.md +142 -0
  4. dot_tasks-0.1.0/pyproject.toml +57 -0
  5. dot_tasks-0.1.0/setup.cfg +4 -0
  6. dot_tasks-0.1.0/src/dot_tasks/__init__.py +5 -0
  7. dot_tasks-0.1.0/src/dot_tasks/__main__.py +4 -0
  8. dot_tasks-0.1.0/src/dot_tasks/_version.py +3 -0
  9. dot_tasks-0.1.0/src/dot_tasks/agents_snippet.py +80 -0
  10. dot_tasks-0.1.0/src/dot_tasks/cli.py +900 -0
  11. dot_tasks-0.1.0/src/dot_tasks/models.py +74 -0
  12. dot_tasks-0.1.0/src/dot_tasks/prompt_ui.py +441 -0
  13. dot_tasks-0.1.0/src/dot_tasks/render.py +288 -0
  14. dot_tasks-0.1.0/src/dot_tasks/resources/agents/task-management-dot-tasks.md +21 -0
  15. dot_tasks-0.1.0/src/dot_tasks/selector_ui.py +195 -0
  16. dot_tasks-0.1.0/src/dot_tasks/service.py +400 -0
  17. dot_tasks-0.1.0/src/dot_tasks/storage.py +513 -0
  18. dot_tasks-0.1.0/src/dot_tasks.egg-info/PKG-INFO +173 -0
  19. dot_tasks-0.1.0/src/dot_tasks.egg-info/SOURCES.txt +28 -0
  20. dot_tasks-0.1.0/src/dot_tasks.egg-info/dependency_links.txt +1 -0
  21. dot_tasks-0.1.0/src/dot_tasks.egg-info/entry_points.txt +2 -0
  22. dot_tasks-0.1.0/src/dot_tasks.egg-info/requires.txt +7 -0
  23. dot_tasks-0.1.0/src/dot_tasks.egg-info/top_level.txt +1 -0
  24. dot_tasks-0.1.0/tests/test_agents_snippet.py +63 -0
  25. dot_tasks-0.1.0/tests/test_cli.py +1034 -0
  26. dot_tasks-0.1.0/tests/test_examples_demo.py +41 -0
  27. dot_tasks-0.1.0/tests/test_logo_assets.py +84 -0
  28. dot_tasks-0.1.0/tests/test_prompt_ui.py +559 -0
  29. dot_tasks-0.1.0/tests/test_render.py +184 -0
  30. dot_tasks-0.1.0/tests/test_storage.py +240 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Awni
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,173 @@
1
+ Metadata-Version: 2.4
2
+ Name: dot-tasks
3
+ Version: 0.1.0
4
+ Summary: Human-readable and agent-readable task manager with a minimal CLI
5
+ Author: dot-tasks contributors
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/Awni00/dot-tasks
8
+ Project-URL: Repository, https://github.com/Awni00/dot-tasks
9
+ Project-URL: Issues, https://github.com/Awni00/dot-tasks/issues
10
+ Keywords: tasks,cli,workflow,productivity,automation
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Topic :: Software Development :: Build Tools
20
+ Classifier: Topic :: Utilities
21
+ Requires-Python: >=3.10
22
+ Description-Content-Type: text/markdown
23
+ License-File: LICENSE
24
+ Requires-Dist: typer>=0.12
25
+ Requires-Dist: PyYAML>=6.0
26
+ Requires-Dist: InquirerPy>=0.3.4
27
+ Requires-Dist: rich>=13.7.0
28
+ Provides-Extra: dev
29
+ Requires-Dist: pytest>=8.0; extra == "dev"
30
+ Dynamic: license-file
31
+
32
+ <p align="center">
33
+ <picture>
34
+ <source srcset="https://raw.githubusercontent.com/Awni00/dot-tasks/main/assets/logo/svg/banner-dark.svg" media="(prefers-color-scheme: dark)">
35
+ <source srcset="https://raw.githubusercontent.com/Awni00/dot-tasks/main/assets/logo/svg/banner-light.svg" media="(prefers-color-scheme: light)">
36
+ <img src="https://raw.githubusercontent.com/Awni00/dot-tasks/main/assets/logo/svg/banner-light.svg" alt="dot-tasks logo">
37
+ </picture>
38
+ </p>
39
+
40
+ `dot-tasks` is a small CLI for managing project-level tasks in a local `.tasks/` directory, using human- and agent-readable files.
41
+
42
+ It started as a personal workflow tool: keep task specs in files, let agents work from those specs, and keep progress updates in the repo instead of chat history. It is shared here in case the same approach is useful to others.
43
+
44
+ ![dot-tasks CLI demo](assets/demo/cli-demo.gif)
45
+
46
+ <p align="center">
47
+ <a href="https://github.com/Awni00/dot-tasks/actions/workflows/tests.yml"><img src="https://github.com/Awni00/dot-tasks/actions/workflows/tests.yml/badge.svg" alt="Unit Tests"></a>
48
+ <a href="https://github.com/Awni00/dot-tasks/actions/workflows/publish.yml"><img src="https://github.com/Awni00/dot-tasks/actions/workflows/publish.yml/badge.svg" alt="Publish"></a>
49
+ <a href="https://pypi.org/project/dot-tasks/"><img src="https://img.shields.io/pypi/v/dot-tasks" alt="PyPI version"></a>
50
+ <img src="https://img.shields.io/badge/license-MIT-green" alt="MIT License">
51
+ </p>
52
+
53
+ ## High-level Workflow
54
+
55
+ This is the workflow I usually follow.
56
+
57
+ | Step | What happens | Command(s) | Files touched |
58
+ | --- | --- | --- | --- |
59
+ | Make note of new to-do | Write down the task spec. Often I have an agent draft a spec from a rough note. | `dot-tasks create` | Creates task dir `.tasks/todo/<created-date>-<task_name>/` with `task.md`, `activity.md`|
60
+ | Choose task to work on | Scan what is in `todo`/`doing` and pick the next task to start. | `dot-tasks list` | read-only inspection of `.tasks/` |
61
+ | Start active work | Move the task to active status when implementation begins. | `dot-tasks start` | Move task from `.tasks/todo/` to `.tasks/doing/`; `plan.md` created |
62
+ | Work loop | Log notable progress updates so humans and agents share context. | `dot-tasks update` | `task.md`, `activity.md` |
63
+ | Finish and archive state | Mark done when acceptance criteria are met, preserving full history in files. | `dot-tasks complete` | task directory moves to `.tasks/done/`|
64
+
65
+ ## Contents
66
+
67
+ - [High-level Workflow](#high-level-workflow)
68
+ - [Installation](#installation)
69
+ - [Quick Start](#quick-start)
70
+ - [Task Layout](#task-layout)
71
+ - [Commands](#commands)
72
+ - [AI Agent Integration](#ai-agent-integration)
73
+ - [Example Project](#example-project)
74
+
75
+ ## Installation
76
+
77
+ ### Install via pip (PyPI)
78
+
79
+ ```bash
80
+ pip install dot-tasks
81
+ ```
82
+
83
+ Quick check:
84
+
85
+ ```bash
86
+ dot-tasks --help
87
+ ```
88
+
89
+ ### Install Latest from GitHub
90
+
91
+ Install the latest from GitHub:
92
+
93
+ ```bash
94
+ pip install "git+https://github.com/Awni00/dot-tasks.git"
95
+ ```
96
+
97
+ ### Development Install (with uv)
98
+
99
+ ```bash
100
+ git clone https://github.com/Awni00/dot-tasks.git
101
+ cd dot-tasks
102
+ uv sync --dev
103
+ uv run dot-tasks --help
104
+ ```
105
+
106
+ Editable install:
107
+
108
+ ```bash
109
+ uv pip install -e ".[dev]"
110
+ ```
111
+
112
+ ## Quick Start
113
+
114
+ ```bash
115
+ dot-tasks init
116
+ dot-tasks create add-task-manager --summary "Build initial package"
117
+ dot-tasks start add-task-manager
118
+ dot-tasks update add-task-manager --note "Implemented storage layer"
119
+ dot-tasks complete add-task-manager
120
+ ```
121
+
122
+ ## Task Layout
123
+
124
+ ```text
125
+ .tasks/
126
+ todo/
127
+ doing/
128
+ done/
129
+ trash/
130
+ ```
131
+
132
+ Each task lives in `.tasks/<status-bucket>/<created-date>-<task_name>/` and contains:
133
+
134
+ - `task.md` (canonical metadata frontmatter + task body)
135
+ - `activity.md` (append-only audit log)
136
+ - `plan.md` (created when the task is started)
137
+
138
+ ## Commands
139
+
140
+ | Command | Purpose | Typical usage |
141
+ | --- | --- | --- |
142
+ | `init` | Create `.tasks/` and write/update managed config settings; can also append workflow guidance section AGENTS.md and install the skill via `npx skills`. | `dot-tasks init` |
143
+ | `create` | Add a new task to `todo/`. | `dot-tasks create <task_name>` |
144
+ | `start` | Move a task to `doing/` and create `plan.md`. | `dot-tasks start <task_name>` |
145
+ | `complete` | Move a task to `done/`. | `dot-tasks complete <task_name>` |
146
+ | `list` | List tasks by status (rich/plain/JSON depending on context). | `dot-tasks list [todo|doing|done] [--json]` |
147
+ | `view` | Show full details for one task. | `dot-tasks view <task_name> [--json]` |
148
+ | `update` | Update metadata, dependencies, tags, or add notes. | `dot-tasks update <task_name> ...` |
149
+ | `rename` | Rename a task. | `dot-tasks rename <task_name> <new_task_name>` |
150
+ | `delete` | Move a task to `trash/`, or delete permanently with `--hard`. | `dot-tasks delete <task_name> [--hard]` |
151
+
152
+ ## AI Agent Integration
153
+
154
+ `dot-tasks` is designed so humans and agents can work from the same file-based task state in `.tasks/` instead of relying on chat context.
155
+
156
+ Typical agent workflow:
157
+
158
+ 1. Capture or refine a task spec with `dot-tasks create`.
159
+ 2. If asked what to work on, check `dot-tasks list` (`todo`/`doing`) and propose the top few options with a short rationale.
160
+ 3. Move selected work into active state with `dot-tasks start`.
161
+ 4. Log meaningful progress with `dot-tasks update --note ...` as work evolves.
162
+ 5. Close the loop with `dot-tasks complete` when acceptance criteria are met.
163
+
164
+ `dot-tasks init` can also set up integration pieces:
165
+
166
+ - It can append a canonical **“Task management with `dot-tasks`”** section to your project `AGENTS.md`.
167
+ - It can run:
168
+ `npx skills add Awni00/dot-tasks --skill dot-tasks`
169
+ to install the `dot-tasks` skill.
170
+
171
+ ## Example Project
172
+
173
+ For a full demo of the workflow, see [`examples/basic-demo/`](examples/basic-demo/) and the walkthrough in [`examples/basic-demo/README.md`](examples/basic-demo/README.md).
@@ -0,0 +1,142 @@
1
+ <p align="center">
2
+ <picture>
3
+ <source srcset="https://raw.githubusercontent.com/Awni00/dot-tasks/main/assets/logo/svg/banner-dark.svg" media="(prefers-color-scheme: dark)">
4
+ <source srcset="https://raw.githubusercontent.com/Awni00/dot-tasks/main/assets/logo/svg/banner-light.svg" media="(prefers-color-scheme: light)">
5
+ <img src="https://raw.githubusercontent.com/Awni00/dot-tasks/main/assets/logo/svg/banner-light.svg" alt="dot-tasks logo">
6
+ </picture>
7
+ </p>
8
+
9
+ `dot-tasks` is a small CLI for managing project-level tasks in a local `.tasks/` directory, using human- and agent-readable files.
10
+
11
+ It started as a personal workflow tool: keep task specs in files, let agents work from those specs, and keep progress updates in the repo instead of chat history. It is shared here in case the same approach is useful to others.
12
+
13
+ ![dot-tasks CLI demo](assets/demo/cli-demo.gif)
14
+
15
+ <p align="center">
16
+ <a href="https://github.com/Awni00/dot-tasks/actions/workflows/tests.yml"><img src="https://github.com/Awni00/dot-tasks/actions/workflows/tests.yml/badge.svg" alt="Unit Tests"></a>
17
+ <a href="https://github.com/Awni00/dot-tasks/actions/workflows/publish.yml"><img src="https://github.com/Awni00/dot-tasks/actions/workflows/publish.yml/badge.svg" alt="Publish"></a>
18
+ <a href="https://pypi.org/project/dot-tasks/"><img src="https://img.shields.io/pypi/v/dot-tasks" alt="PyPI version"></a>
19
+ <img src="https://img.shields.io/badge/license-MIT-green" alt="MIT License">
20
+ </p>
21
+
22
+ ## High-level Workflow
23
+
24
+ This is the workflow I usually follow.
25
+
26
+ | Step | What happens | Command(s) | Files touched |
27
+ | --- | --- | --- | --- |
28
+ | Make note of new to-do | Write down the task spec. Often I have an agent draft a spec from a rough note. | `dot-tasks create` | Creates task dir `.tasks/todo/<created-date>-<task_name>/` with `task.md`, `activity.md`|
29
+ | Choose task to work on | Scan what is in `todo`/`doing` and pick the next task to start. | `dot-tasks list` | read-only inspection of `.tasks/` |
30
+ | Start active work | Move the task to active status when implementation begins. | `dot-tasks start` | Move task from `.tasks/todo/` to `.tasks/doing/`; `plan.md` created |
31
+ | Work loop | Log notable progress updates so humans and agents share context. | `dot-tasks update` | `task.md`, `activity.md` |
32
+ | Finish and archive state | Mark done when acceptance criteria are met, preserving full history in files. | `dot-tasks complete` | task directory moves to `.tasks/done/`|
33
+
34
+ ## Contents
35
+
36
+ - [High-level Workflow](#high-level-workflow)
37
+ - [Installation](#installation)
38
+ - [Quick Start](#quick-start)
39
+ - [Task Layout](#task-layout)
40
+ - [Commands](#commands)
41
+ - [AI Agent Integration](#ai-agent-integration)
42
+ - [Example Project](#example-project)
43
+
44
+ ## Installation
45
+
46
+ ### Install via pip (PyPI)
47
+
48
+ ```bash
49
+ pip install dot-tasks
50
+ ```
51
+
52
+ Quick check:
53
+
54
+ ```bash
55
+ dot-tasks --help
56
+ ```
57
+
58
+ ### Install Latest from GitHub
59
+
60
+ Install the latest from GitHub:
61
+
62
+ ```bash
63
+ pip install "git+https://github.com/Awni00/dot-tasks.git"
64
+ ```
65
+
66
+ ### Development Install (with uv)
67
+
68
+ ```bash
69
+ git clone https://github.com/Awni00/dot-tasks.git
70
+ cd dot-tasks
71
+ uv sync --dev
72
+ uv run dot-tasks --help
73
+ ```
74
+
75
+ Editable install:
76
+
77
+ ```bash
78
+ uv pip install -e ".[dev]"
79
+ ```
80
+
81
+ ## Quick Start
82
+
83
+ ```bash
84
+ dot-tasks init
85
+ dot-tasks create add-task-manager --summary "Build initial package"
86
+ dot-tasks start add-task-manager
87
+ dot-tasks update add-task-manager --note "Implemented storage layer"
88
+ dot-tasks complete add-task-manager
89
+ ```
90
+
91
+ ## Task Layout
92
+
93
+ ```text
94
+ .tasks/
95
+ todo/
96
+ doing/
97
+ done/
98
+ trash/
99
+ ```
100
+
101
+ Each task lives in `.tasks/<status-bucket>/<created-date>-<task_name>/` and contains:
102
+
103
+ - `task.md` (canonical metadata frontmatter + task body)
104
+ - `activity.md` (append-only audit log)
105
+ - `plan.md` (created when the task is started)
106
+
107
+ ## Commands
108
+
109
+ | Command | Purpose | Typical usage |
110
+ | --- | --- | --- |
111
+ | `init` | Create `.tasks/` and write/update managed config settings; can also append workflow guidance section AGENTS.md and install the skill via `npx skills`. | `dot-tasks init` |
112
+ | `create` | Add a new task to `todo/`. | `dot-tasks create <task_name>` |
113
+ | `start` | Move a task to `doing/` and create `plan.md`. | `dot-tasks start <task_name>` |
114
+ | `complete` | Move a task to `done/`. | `dot-tasks complete <task_name>` |
115
+ | `list` | List tasks by status (rich/plain/JSON depending on context). | `dot-tasks list [todo|doing|done] [--json]` |
116
+ | `view` | Show full details for one task. | `dot-tasks view <task_name> [--json]` |
117
+ | `update` | Update metadata, dependencies, tags, or add notes. | `dot-tasks update <task_name> ...` |
118
+ | `rename` | Rename a task. | `dot-tasks rename <task_name> <new_task_name>` |
119
+ | `delete` | Move a task to `trash/`, or delete permanently with `--hard`. | `dot-tasks delete <task_name> [--hard]` |
120
+
121
+ ## AI Agent Integration
122
+
123
+ `dot-tasks` is designed so humans and agents can work from the same file-based task state in `.tasks/` instead of relying on chat context.
124
+
125
+ Typical agent workflow:
126
+
127
+ 1. Capture or refine a task spec with `dot-tasks create`.
128
+ 2. If asked what to work on, check `dot-tasks list` (`todo`/`doing`) and propose the top few options with a short rationale.
129
+ 3. Move selected work into active state with `dot-tasks start`.
130
+ 4. Log meaningful progress with `dot-tasks update --note ...` as work evolves.
131
+ 5. Close the loop with `dot-tasks complete` when acceptance criteria are met.
132
+
133
+ `dot-tasks init` can also set up integration pieces:
134
+
135
+ - It can append a canonical **“Task management with `dot-tasks`”** section to your project `AGENTS.md`.
136
+ - It can run:
137
+ `npx skills add Awni00/dot-tasks --skill dot-tasks`
138
+ to install the `dot-tasks` skill.
139
+
140
+ ## Example Project
141
+
142
+ For a full demo of the workflow, see [`examples/basic-demo/`](examples/basic-demo/) and the walkthrough in [`examples/basic-demo/README.md`](examples/basic-demo/README.md).
@@ -0,0 +1,57 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "dot-tasks"
7
+ dynamic = ["version"]
8
+ description = "Human-readable and agent-readable task manager with a minimal CLI"
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = {text = "MIT"}
12
+ authors = [{name = "dot-tasks contributors"}]
13
+ classifiers = [
14
+ "Development Status :: 3 - Alpha",
15
+ "Environment :: Console",
16
+ "Intended Audience :: Developers",
17
+ "License :: OSI Approved :: MIT License",
18
+ "Programming Language :: Python :: 3",
19
+ "Programming Language :: Python :: 3.10",
20
+ "Programming Language :: Python :: 3.11",
21
+ "Programming Language :: Python :: 3.12",
22
+ "Topic :: Software Development :: Build Tools",
23
+ "Topic :: Utilities",
24
+ ]
25
+ keywords = ["tasks", "cli", "workflow", "productivity", "automation"]
26
+ dependencies = [
27
+ "typer>=0.12",
28
+ "PyYAML>=6.0",
29
+ "InquirerPy>=0.3.4",
30
+ "rich>=13.7.0",
31
+ ]
32
+
33
+ [project.urls]
34
+ Homepage = "https://github.com/Awni00/dot-tasks"
35
+ Repository = "https://github.com/Awni00/dot-tasks"
36
+ Issues = "https://github.com/Awni00/dot-tasks/issues"
37
+
38
+ [project.optional-dependencies]
39
+ dev = ["pytest>=8.0"]
40
+
41
+ [project.scripts]
42
+ dot-tasks = "dot_tasks.cli:main"
43
+
44
+ [tool.setuptools]
45
+ package-dir = {"" = "src"}
46
+
47
+ [tool.setuptools.packages.find]
48
+ where = ["src"]
49
+
50
+ [tool.setuptools.package-data]
51
+ dot_tasks = ["resources/agents/*.md"]
52
+
53
+ [tool.setuptools.dynamic]
54
+ version = {attr = "dot_tasks._version.__version__"}
55
+
56
+ [tool.pytest.ini_options]
57
+ testpaths = ["tests"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,5 @@
1
+ """dot-tasks package."""
2
+
3
+ from ._version import __version__
4
+
5
+ __all__ = ["__version__"]
@@ -0,0 +1,4 @@
1
+ from .cli import main
2
+
3
+ if __name__ == "__main__":
4
+ main()
@@ -0,0 +1,3 @@
1
+ """Version definition for dot-tasks."""
2
+
3
+ __version__ = "0.1.0"
@@ -0,0 +1,80 @@
1
+ """Helpers for loading and upserting AGENTS.md snippets."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from importlib import resources
6
+ from pathlib import Path
7
+ from typing import Literal
8
+
9
+
10
+ BEGIN_MARKER = "<!-- dot-tasks:begin task-management -->"
11
+ END_MARKER = "<!-- dot-tasks:end task-management -->"
12
+ RESOURCE_PATH = "resources/agents/task-management-dot-tasks.md"
13
+
14
+
15
+ def _normalize_newlines(text: str) -> str:
16
+ return text.replace("\r\n", "\n")
17
+
18
+
19
+ def _validate_markers(text: str) -> None:
20
+ begin = text.find(BEGIN_MARKER)
21
+ end = text.find(END_MARKER)
22
+ if begin == -1 or end == -1 or begin > end:
23
+ raise ValueError("Invalid task-management snippet: missing or misordered markers.")
24
+
25
+
26
+ def load_task_management_snippet() -> str:
27
+ """Load canonical markdown section for AGENTS integration."""
28
+ content = resources.files("dot_tasks").joinpath(RESOURCE_PATH).read_text(encoding="utf-8")
29
+ normalized = _normalize_newlines(content).strip("\n")
30
+ _validate_markers(normalized)
31
+ return normalized + "\n"
32
+
33
+
34
+ def resolve_agents_file(project_root: Path, agents_file: Path | None) -> Path:
35
+ target = Path("AGENTS.md") if agents_file is None else agents_file
36
+ if target.is_absolute():
37
+ return target
38
+ return (project_root / target).resolve()
39
+
40
+
41
+ def upsert_task_management_snippet(
42
+ target_file: Path,
43
+ snippet_text: str,
44
+ ) -> Literal["created", "updated", "appended", "unchanged"]:
45
+ """Insert or replace canonical task-management snippet in target markdown file."""
46
+ block = _normalize_newlines(snippet_text).strip("\n")
47
+ _validate_markers(block)
48
+ block = block + "\n"
49
+
50
+ target_file.parent.mkdir(parents=True, exist_ok=True)
51
+ if not target_file.exists():
52
+ target_file.write_text(block, encoding="utf-8")
53
+ return "created"
54
+
55
+ existing = _normalize_newlines(target_file.read_text(encoding="utf-8"))
56
+ begin = existing.find(BEGIN_MARKER)
57
+ end = existing.find(END_MARKER)
58
+
59
+ if begin != -1 and end != -1 and begin < end:
60
+ end_with_marker = end + len(END_MARKER)
61
+ replacement = existing[:begin] + block.rstrip("\n") + existing[end_with_marker:]
62
+ if not replacement.endswith("\n"):
63
+ replacement = replacement + "\n"
64
+ if replacement == existing:
65
+ return "unchanged"
66
+ target_file.write_text(replacement, encoding="utf-8")
67
+ return "updated"
68
+
69
+ if block.rstrip("\n") in existing:
70
+ return "unchanged"
71
+
72
+ separator = ""
73
+ if existing and not existing.endswith("\n"):
74
+ separator = "\n"
75
+ if existing.rstrip("\n"):
76
+ separator = separator + "\n"
77
+
78
+ appended = existing + separator + block
79
+ target_file.write_text(appended, encoding="utf-8")
80
+ return "appended"