memtask 0.0.1__py3-none-any.whl

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.
memtask/storage.py ADDED
@@ -0,0 +1,150 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import os
5
+ import sqlite3
6
+ from pathlib import Path
7
+
8
+
9
+ PROJECT_ROOT = Path(__file__).resolve().parents[2]
10
+ MEMTASK_HOME_ENV = "MEMTASK_HOME"
11
+ MEMTASK_DB_PATH_ENV = "MEMTASK_DB_PATH"
12
+
13
+
14
+ def default_home() -> Path:
15
+ return Path(os.environ.get(MEMTASK_HOME_ENV, Path.home() / ".memtask")).expanduser()
16
+
17
+
18
+ def resolve_default_db_path() -> Path:
19
+ if os.environ.get(MEMTASK_DB_PATH_ENV):
20
+ return Path(os.environ[MEMTASK_DB_PATH_ENV]).expanduser()
21
+
22
+ repo_db_path = PROJECT_ROOT / "data" / "tasks.sqlite"
23
+ if repo_db_path.exists():
24
+ return repo_db_path
25
+
26
+ return default_home() / "tasks.sqlite"
27
+
28
+
29
+ DEFAULT_DB_PATH = resolve_default_db_path()
30
+ DB_PATH = DEFAULT_DB_PATH
31
+
32
+
33
+ def set_db_path(path: str | Path) -> None:
34
+ """Set the SQLite path used by subsequent manager calls."""
35
+ global DB_PATH
36
+ DB_PATH = Path(path)
37
+
38
+
39
+ def get_db_path() -> Path:
40
+ return DB_PATH
41
+
42
+
43
+ def get_connection() -> sqlite3.Connection:
44
+ DB_PATH.parent.mkdir(parents=True, exist_ok=True)
45
+ conn = sqlite3.connect(DB_PATH, detect_types=sqlite3.PARSE_DECLTYPES)
46
+ conn.row_factory = sqlite3.Row
47
+ conn.execute("PRAGMA foreign_keys = ON")
48
+ return conn
49
+
50
+
51
+ def init_db() -> None:
52
+ with get_connection() as conn:
53
+ conn.executescript(
54
+ """
55
+ CREATE TABLE IF NOT EXISTS tasks (
56
+ task_ref TEXT PRIMARY KEY,
57
+ description TEXT NOT NULL,
58
+ project TEXT,
59
+ tags_json TEXT NOT NULL DEFAULT '[]',
60
+ memory_refs_json TEXT NOT NULL DEFAULT '[]',
61
+ status TEXT NOT NULL DEFAULT 'pending',
62
+ is_current INTEGER NOT NULL DEFAULT 0,
63
+ created_at REAL NOT NULL,
64
+ updated_at REAL NOT NULL,
65
+ completed_at REAL
66
+ );
67
+
68
+ CREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks (status);
69
+ CREATE INDEX IF NOT EXISTS idx_tasks_is_current ON tasks (is_current);
70
+
71
+ CREATE TABLE IF NOT EXISTS memories (
72
+ memory_id TEXT PRIMARY KEY,
73
+ content TEXT NOT NULL,
74
+ memory_scope TEXT NOT NULL DEFAULT 'global',
75
+ kind TEXT NOT NULL DEFAULT 'fact',
76
+ confidence INTEGER NOT NULL DEFAULT 100,
77
+ parent_memory_id TEXT,
78
+ tags_json TEXT NOT NULL DEFAULT '[]',
79
+ created_at REAL NOT NULL,
80
+ updated_at REAL NOT NULL,
81
+ last_accessed_at REAL,
82
+ FOREIGN KEY (parent_memory_id) REFERENCES memories (memory_id) ON DELETE SET NULL
83
+ );
84
+
85
+ CREATE INDEX IF NOT EXISTS idx_memories_scope ON memories (memory_scope);
86
+ CREATE INDEX IF NOT EXISTS idx_memories_kind ON memories (kind);
87
+ CREATE INDEX IF NOT EXISTS idx_memories_confidence ON memories (confidence);
88
+ CREATE INDEX IF NOT EXISTS idx_memories_parent ON memories (parent_memory_id);
89
+
90
+ CREATE TABLE IF NOT EXISTS task_dependencies (
91
+ task_ref TEXT NOT NULL,
92
+ depends_on_task_ref TEXT NOT NULL,
93
+ PRIMARY KEY (task_ref, depends_on_task_ref),
94
+ FOREIGN KEY (task_ref) REFERENCES tasks (task_ref) ON DELETE CASCADE,
95
+ FOREIGN KEY (depends_on_task_ref) REFERENCES tasks (task_ref) ON DELETE CASCADE
96
+ );
97
+
98
+ CREATE INDEX IF NOT EXISTS idx_task_dependencies_task_ref ON task_dependencies (task_ref);
99
+ CREATE INDEX IF NOT EXISTS idx_task_dependencies_depends_on ON task_dependencies (depends_on_task_ref);
100
+ """
101
+ )
102
+
103
+
104
+ def normalize_non_empty_string(value: str, label: str) -> str:
105
+ text = str(value).strip()
106
+ if not text:
107
+ raise ValueError(f"{label} is required")
108
+ return text
109
+
110
+
111
+ def normalize_confidence(confidence: int) -> int:
112
+ if isinstance(confidence, bool) or not isinstance(confidence, int):
113
+ raise ValueError("confidence must be an integer between 0 and 100")
114
+ if not 0 <= confidence <= 100:
115
+ raise ValueError("confidence must be between 0 and 100")
116
+ return confidence
117
+
118
+
119
+ def encode_string_list(values: list[str] | None) -> str:
120
+ if values is None:
121
+ return "[]"
122
+ normalized = []
123
+ seen = set()
124
+ for value in values:
125
+ item = str(value).strip()
126
+ if not item or item in seen:
127
+ continue
128
+ seen.add(item)
129
+ normalized.append(item)
130
+ return json.dumps(normalized)
131
+
132
+
133
+ def decode_string_list(value: str | None) -> list[str]:
134
+ if not value:
135
+ return []
136
+ try:
137
+ parsed = json.loads(value)
138
+ except (TypeError, json.JSONDecodeError):
139
+ return []
140
+ if isinstance(parsed, list):
141
+ return [str(item) for item in parsed]
142
+ return []
143
+
144
+
145
+ def encode_tags(tags: list[str] | None) -> str:
146
+ return encode_string_list(tags)
147
+
148
+
149
+ def decode_tags(value: str | None) -> list[str]:
150
+ return decode_string_list(value)
@@ -0,0 +1,104 @@
1
+ Metadata-Version: 2.4
2
+ Name: memtask
3
+ Version: 0.0.1
4
+ Summary: A local MCP server for durable agent task state and lightweight memory.
5
+ Requires-Python: >=3.10
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: mcp
8
+ Provides-Extra: test
9
+ Requires-Dist: pytest>=8; extra == "test"
10
+
11
+ <p align="center">
12
+ <img src="docs/assets/memtask-logo.png" alt="MemTask logo" width="220">
13
+ </p>
14
+
15
+ <h1 align="center">MemTask</h1>
16
+
17
+ MemTask is a local MCP server for developer-built agents that need durable task state and lightweight memory. It combines task planning, dependency tracking, active work selection, completion history, and scoped memories in a small SQLite-backed service. The goal is to give agents a structured place to manage agency: what to do next, what depends on what, and what context should persist across sessions.
18
+
19
+ ## What It Provides
20
+
21
+ MemTask exposes two local primitives over MCP:
22
+
23
+ - Tasks: pending work, stable task references, parent/child dependency edges, active task selection, and completed task state.
24
+ - Memories: scoped pieces of context with confidence scores, optional parent memory relationships, tags, and task-memory references.
25
+
26
+ The server is intentionally local-first. State lives in SQLite, the tool surface is small, and the manager layer can be tested directly without running an MCP transport.
27
+
28
+ ## Quickstart
29
+
30
+ Install MemTask:
31
+
32
+ ```bash
33
+ pip install MemTask
34
+ ```
35
+
36
+ Then ask MemTask for the MCP config to add to your agent:
37
+
38
+ ```bash
39
+ memtask install-help
40
+ ```
41
+
42
+ Most MCP clients should launch MemTask over stdio:
43
+
44
+ ```bash
45
+ memtask start --transport stdio
46
+ ```
47
+
48
+ If your client connects to a running HTTP server instead, start it in the background:
49
+
50
+ ```bash
51
+ memtask start --transport http --host 127.0.0.1 --port 8000
52
+ ```
53
+
54
+ For local development from this repo:
55
+
56
+ ```bash
57
+ PYTHONPATH=src python -m memtask start --transport stdio
58
+ ```
59
+
60
+ Useful HTTP commands: `memtask status` and `memtask stop`.
61
+
62
+ ## Storage
63
+
64
+ Runtime state in this repo is stored in `data/tasks.sqlite`.
65
+
66
+ When installed outside this repo, MemTask uses `~/.memtask/tasks.sqlite` by default. Set `MEMTASK_DB_PATH` to choose a specific SQLite path.
67
+
68
+ The server creates the required SQLite tables on startup using `CREATE TABLE IF NOT EXISTS`. There is no migration framework.
69
+
70
+ ## Tools
71
+
72
+ Task tools:
73
+
74
+ - `list_tasks`
75
+ - `get_task`
76
+ - `add_task`
77
+ - `add_batch_tasks`
78
+ - `complete_task`
79
+ - `remove_task`
80
+ - `remove_all_tasks`
81
+ - `current_tasks`
82
+ - `set_current_task`
83
+
84
+ Memory tools:
85
+
86
+ - `remember`
87
+ - `recall`
88
+ - `get_memory`
89
+ - `update_memory`
90
+ - `delete_memory`
91
+
92
+ ## Development
93
+
94
+ Run tests:
95
+
96
+ ```bash
97
+ python -m pytest
98
+ ```
99
+
100
+ Compile-check the package:
101
+
102
+ ```bash
103
+ python -m py_compile src/memtask/*.py tests/*.py
104
+ ```
@@ -0,0 +1,12 @@
1
+ memtask/__init__.py,sha256=divNnoUi6IWmEF-sDvLArNGhV7TGKo1NAQizsRdrj08,95
2
+ memtask/__main__.py,sha256=wu5N2wk8mvBgyvr2ghmQf4prezAe0_i-p123VVreyYc,62
3
+ memtask/api.py,sha256=an6h6oZndPz7YR_W2X3kDbThQWPuBvPL1HTFz5mxx6U,11389
4
+ memtask/app.py,sha256=rn0dm4GbdbZ2av4sBT3aRV8hXDdKfxkDaTHvGfFNC5Q,1813
5
+ memtask/cli.py,sha256=vIo3LqBL0HxhlY0uNjqLmjvQd4tCCT87dSa5jDNJ0aA,8313
6
+ memtask/manager.py,sha256=92S57jlFUI2hlO4PsrhN-nOyisZlAfmz-Vy5qWob3ak,24316
7
+ memtask/storage.py,sha256=J4x9X0AlB5yLJ9F1szi_aH-5zvhP46dRk6AZoz3MGSw,4928
8
+ memtask-0.0.1.dist-info/METADATA,sha256=jHsV5GZfV543jQXdqniY7tK1N4U71riMOHxGPGrrSa4,2665
9
+ memtask-0.0.1.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
10
+ memtask-0.0.1.dist-info/entry_points.txt,sha256=tL-GQyfa41gXgzWQc3vG6chNQ90S6VgfDH2yMzI45o0,45
11
+ memtask-0.0.1.dist-info/top_level.txt,sha256=h6tvWvx4NeUvwA6UG0IXdjQCc_OL6KIM5TwMWl2Poqw,8
12
+ memtask-0.0.1.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ memtask = memtask.cli:main
@@ -0,0 +1 @@
1
+ memtask