lost-sessions 0.0.0.dev0__tar.gz → 0.2.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.
- lost_sessions-0.2.0/PKG-INFO +92 -0
- lost_sessions-0.2.0/README.md +66 -0
- lost_sessions-0.2.0/pyproject.toml +236 -0
- lost_sessions-0.2.0/pyproject.toml.orig +300 -0
- lost_sessions-0.2.0/src/lost_sessions/__init__.py +51 -0
- lost_sessions-0.2.0/src/lost_sessions/__main__.py +5 -0
- lost_sessions-0.2.0/src/lost_sessions/agents.py +147 -0
- lost_sessions-0.2.0/src/lost_sessions/cli.py +595 -0
- lost_sessions-0.2.0/src/lost_sessions/core.py +526 -0
- lost_sessions-0.2.0/src/lost_sessions/default_plugins/__init__.py +1 -0
- lost_sessions-0.2.0/src/lost_sessions/default_plugins/claude.py +137 -0
- lost_sessions-0.2.0/src/lost_sessions/default_plugins/codex.py +157 -0
- lost_sessions-0.2.0/src/lost_sessions/default_plugins/herdr.py +362 -0
- lost_sessions-0.2.0/src/lost_sessions/default_plugins/muse.py +146 -0
- lost_sessions-0.2.0/src/lost_sessions/default_plugins/opencode.py +190 -0
- lost_sessions-0.2.0/src/lost_sessions/default_plugins/rex.py +278 -0
- lost_sessions-0.2.0/src/lost_sessions/history.py +268 -0
- lost_sessions-0.2.0/src/lost_sessions/hookspecs.py +27 -0
- lost_sessions-0.2.0/src/lost_sessions/installer.py +179 -0
- lost_sessions-0.2.0/src/lost_sessions/inventory.py +117 -0
- lost_sessions-0.2.0/src/lost_sessions/managers.py +232 -0
- lost_sessions-0.2.0/src/lost_sessions/menu.py +375 -0
- lost_sessions-0.2.0/src/lost_sessions/models.py +81 -0
- lost_sessions-0.2.0/src/lost_sessions/plugins.py +150 -0
- lost_sessions-0.0.0.dev0/PKG-INFO +0 -10
- lost_sessions-0.0.0.dev0/README.md +0 -3
- lost_sessions-0.0.0.dev0/pyproject.toml +0 -10
- /lost_sessions-0.0.0.dev0/src/lost_sessions/__init__.py → /lost_sessions-0.2.0/src/lost_sessions/py.typed +0 -0
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: lost_sessions
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Recover agent sessions that a reboot dropped, with pluggable session managers
|
|
5
|
+
Author: Steve Morin
|
|
6
|
+
Author-email: Steve Morin <steve.morin@gmail.com>
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
Classifier: Development Status :: 4 - Beta
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
13
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
14
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
15
|
+
Requires-Dist: click>=8.1.0
|
|
16
|
+
Requires-Dist: pluggy>=1.5.0
|
|
17
|
+
Requires-Dist: pydantic>=2.0.0
|
|
18
|
+
Requires-Dist: textual>=8,<9
|
|
19
|
+
Requires-Python: >=3.13
|
|
20
|
+
Project-URL: Homepage, https://github.com/smorinlabs/lost-sessions
|
|
21
|
+
Project-URL: Repository, https://github.com/smorinlabs/lost-sessions
|
|
22
|
+
Project-URL: Documentation, https://lost-sessions.readthedocs.io/en/latest/
|
|
23
|
+
Project-URL: Changelog, https://github.com/smorinlabs/lost-sessions/blob/main/CHANGELOG.md
|
|
24
|
+
Project-URL: Issues, https://github.com/smorinlabs/lost-sessions/issues
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
|
|
27
|
+
# lost-sessions
|
|
28
|
+
|
|
29
|
+
Recover the agent sessions a reboot dropped. `lost-sessions` finds Claude Code,
|
|
30
|
+
Codex, Muse and OpenCode sessions that started before the last boot and that no
|
|
31
|
+
session manager will restore, then prints their resume commands or reopens
|
|
32
|
+
them in Herdr or Rex.
|
|
33
|
+
|
|
34
|
+
Agents and session managers are plugins, the way
|
|
35
|
+
[`simonw/llm`](https://llm.datasette.io/en/stable/plugins/) does plugins. The
|
|
36
|
+
six built-ins use the same interface a third-party `lost-sessions-<name>`
|
|
37
|
+
package uses.
|
|
38
|
+
|
|
39
|
+
## Install
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
uv tool install "lost-sessions @ git+https://github.com/smorinlabs/lost-sessions"
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
This installs the latest `main`. Python 3.13 or later. macOS and Linux. Tagged
|
|
46
|
+
releases, and a PyPI package, follow once publishing is set up.
|
|
47
|
+
|
|
48
|
+
## Use
|
|
49
|
+
|
|
50
|
+
| Command | What it does |
|
|
51
|
+
|---|---|
|
|
52
|
+
| `lost-sessions recover` | Interactive menu: choose Print, Rex or Herdr for each dropped session, then run them all |
|
|
53
|
+
| `lost-sessions list` | Numbered list of the dropped sessions in the window |
|
|
54
|
+
| `lost-sessions open REF… --print\|--rex\|--herdr [--focus [REF]]` | Print or open sessions without the menu; `REF@DEST` picks a destination |
|
|
55
|
+
| `lost-sessions workspaces` | The Herdr workspaces and Rex sessions you can open sessions in |
|
|
56
|
+
| `lost-sessions history [--json]` | Every action taken, from `${XDG_STATE_HOME:-~/.local/state}/lost-sessions/history.jsonl` |
|
|
57
|
+
| `lost-sessions inventory [--json]` | Read-only inventory of every agent session on this machine |
|
|
58
|
+
| `lost-sessions plugins [--all]` | Installed plugins, as JSON |
|
|
59
|
+
| `lost-sessions install PKG…` / `uninstall PKG…` | Add or remove plugins in the `uv tool` install |
|
|
60
|
+
|
|
61
|
+
The window is every session that started before the last boot and was active
|
|
62
|
+
since its start: by default 00:00 on the day before the boot. `--since` sets
|
|
63
|
+
that start, as a date (`--since 2026-11-09`) or a duration back from now
|
|
64
|
+
(`--since 3d`); pick an earlier start to widen the window. Running sessions
|
|
65
|
+
and sessions a manager restores are hidden unless `--all`.
|
|
66
|
+
|
|
67
|
+
## Library
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
from lost_sessions.inventory import collect, select
|
|
71
|
+
|
|
72
|
+
inventory = collect() # an Inventory: sessions, census, sources, failed
|
|
73
|
+
for session in select(inventory, live=True):
|
|
74
|
+
print(session.tool, session.id, session.resume)
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Plugins
|
|
78
|
+
|
|
79
|
+
See [Writing plugins](docs/source/reference/plugins.md) for the hooks, the
|
|
80
|
+
`AgentTool` and `SessionManager` contracts, and how to test and install a
|
|
81
|
+
plugin. The design is in
|
|
82
|
+
[design 0006](docs/design/0006-lost-sessions-plugin-design.md).
|
|
83
|
+
|
|
84
|
+
## Develop
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
make bootstrap # bare machines only: just + uv
|
|
88
|
+
just setup # dev environment and git hooks
|
|
89
|
+
just check # tests, lint, types, import contracts
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
See [AGENTS.md](AGENTS.md) for the full workflow.
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# lost-sessions
|
|
2
|
+
|
|
3
|
+
Recover the agent sessions a reboot dropped. `lost-sessions` finds Claude Code,
|
|
4
|
+
Codex, Muse and OpenCode sessions that started before the last boot and that no
|
|
5
|
+
session manager will restore, then prints their resume commands or reopens
|
|
6
|
+
them in Herdr or Rex.
|
|
7
|
+
|
|
8
|
+
Agents and session managers are plugins, the way
|
|
9
|
+
[`simonw/llm`](https://llm.datasette.io/en/stable/plugins/) does plugins. The
|
|
10
|
+
six built-ins use the same interface a third-party `lost-sessions-<name>`
|
|
11
|
+
package uses.
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
uv tool install "lost-sessions @ git+https://github.com/smorinlabs/lost-sessions"
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
This installs the latest `main`. Python 3.13 or later. macOS and Linux. Tagged
|
|
20
|
+
releases, and a PyPI package, follow once publishing is set up.
|
|
21
|
+
|
|
22
|
+
## Use
|
|
23
|
+
|
|
24
|
+
| Command | What it does |
|
|
25
|
+
|---|---|
|
|
26
|
+
| `lost-sessions recover` | Interactive menu: choose Print, Rex or Herdr for each dropped session, then run them all |
|
|
27
|
+
| `lost-sessions list` | Numbered list of the dropped sessions in the window |
|
|
28
|
+
| `lost-sessions open REF… --print\|--rex\|--herdr [--focus [REF]]` | Print or open sessions without the menu; `REF@DEST` picks a destination |
|
|
29
|
+
| `lost-sessions workspaces` | The Herdr workspaces and Rex sessions you can open sessions in |
|
|
30
|
+
| `lost-sessions history [--json]` | Every action taken, from `${XDG_STATE_HOME:-~/.local/state}/lost-sessions/history.jsonl` |
|
|
31
|
+
| `lost-sessions inventory [--json]` | Read-only inventory of every agent session on this machine |
|
|
32
|
+
| `lost-sessions plugins [--all]` | Installed plugins, as JSON |
|
|
33
|
+
| `lost-sessions install PKG…` / `uninstall PKG…` | Add or remove plugins in the `uv tool` install |
|
|
34
|
+
|
|
35
|
+
The window is every session that started before the last boot and was active
|
|
36
|
+
since its start: by default 00:00 on the day before the boot. `--since` sets
|
|
37
|
+
that start, as a date (`--since 2026-11-09`) or a duration back from now
|
|
38
|
+
(`--since 3d`); pick an earlier start to widen the window. Running sessions
|
|
39
|
+
and sessions a manager restores are hidden unless `--all`.
|
|
40
|
+
|
|
41
|
+
## Library
|
|
42
|
+
|
|
43
|
+
```python
|
|
44
|
+
from lost_sessions.inventory import collect, select
|
|
45
|
+
|
|
46
|
+
inventory = collect() # an Inventory: sessions, census, sources, failed
|
|
47
|
+
for session in select(inventory, live=True):
|
|
48
|
+
print(session.tool, session.id, session.resume)
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Plugins
|
|
52
|
+
|
|
53
|
+
See [Writing plugins](docs/source/reference/plugins.md) for the hooks, the
|
|
54
|
+
`AgentTool` and `SessionManager` contracts, and how to test and install a
|
|
55
|
+
plugin. The design is in
|
|
56
|
+
[design 0006](docs/design/0006-lost-sessions-plugin-design.md).
|
|
57
|
+
|
|
58
|
+
## Develop
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
make bootstrap # bare machines only: just + uv
|
|
62
|
+
just setup # dev environment and git hooks
|
|
63
|
+
just check # tests, lint, types, import contracts
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
See [AGENTS.md](AGENTS.md) for the full workflow.
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "lost_sessions"
|
|
3
|
+
version = "0.2.0"
|
|
4
|
+
description = "Recover agent sessions that a reboot dropped, with pluggable session managers"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.13"
|
|
7
|
+
license = "MIT"
|
|
8
|
+
classifiers = [
|
|
9
|
+
"Development Status :: 4 - Beta",
|
|
10
|
+
"Intended Audience :: Developers",
|
|
11
|
+
"Operating System :: OS Independent",
|
|
12
|
+
"Programming Language :: Python :: 3",
|
|
13
|
+
"Programming Language :: Python :: 3.13",
|
|
14
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
15
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
16
|
+
]
|
|
17
|
+
dependencies = [
|
|
18
|
+
"click>=8.1.0",
|
|
19
|
+
"pluggy>=1.5.0",
|
|
20
|
+
"pydantic>=2.0.0",
|
|
21
|
+
"textual>=8,<9",
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
[[project.authors]]
|
|
25
|
+
name = "Steve Morin"
|
|
26
|
+
email = "steve.morin@gmail.com"
|
|
27
|
+
|
|
28
|
+
[project.scripts]
|
|
29
|
+
lost-sessions = "lost_sessions.cli:main"
|
|
30
|
+
|
|
31
|
+
[project.urls]
|
|
32
|
+
Homepage = "https://github.com/smorinlabs/lost-sessions"
|
|
33
|
+
Repository = "https://github.com/smorinlabs/lost-sessions"
|
|
34
|
+
Documentation = "https://lost-sessions.readthedocs.io/en/latest/"
|
|
35
|
+
Changelog = "https://github.com/smorinlabs/lost-sessions/blob/main/CHANGELOG.md"
|
|
36
|
+
Issues = "https://github.com/smorinlabs/lost-sessions/issues"
|
|
37
|
+
|
|
38
|
+
[dependency-groups]
|
|
39
|
+
dev = [
|
|
40
|
+
"pytest>=7.0.0",
|
|
41
|
+
"pytest-cov>=4.1.0",
|
|
42
|
+
"ty>=0.0.1a16",
|
|
43
|
+
"ruff>=0.1.0",
|
|
44
|
+
"types-Pygments",
|
|
45
|
+
"cogapp",
|
|
46
|
+
"bandit>=1.8.0",
|
|
47
|
+
"codespell>=2.4.0",
|
|
48
|
+
"editorconfig-checker>=3.0.0",
|
|
49
|
+
"yamllint>=1.35.0",
|
|
50
|
+
"pip-audit>=2.9.0",
|
|
51
|
+
"twine>=6.1.0",
|
|
52
|
+
"syrupy>=4.8.0",
|
|
53
|
+
"import-linter>=2.0",
|
|
54
|
+
"pytest-xdist>=3.6",
|
|
55
|
+
"pytest-timeout>=2.3",
|
|
56
|
+
"pytest-randomly>=3.15",
|
|
57
|
+
"hypothesis>=6.100",
|
|
58
|
+
]
|
|
59
|
+
docs = [
|
|
60
|
+
"sphinx>=7.0.0",
|
|
61
|
+
"sphinx-rtd-theme",
|
|
62
|
+
"furo",
|
|
63
|
+
"myst-parser",
|
|
64
|
+
"sphinx-autodoc-typehints",
|
|
65
|
+
"cogapp",
|
|
66
|
+
"sphinx-copybutton",
|
|
67
|
+
"sphinx-autobuild",
|
|
68
|
+
"sphinxext-opengraph",
|
|
69
|
+
]
|
|
70
|
+
|
|
71
|
+
[build-system]
|
|
72
|
+
requires = ["uv_build>=0.5,<1.0"]
|
|
73
|
+
build-backend = "uv_build"
|
|
74
|
+
|
|
75
|
+
[tool.uv.build-backend]
|
|
76
|
+
module-name = "lost_sessions"
|
|
77
|
+
module-root = "src"
|
|
78
|
+
|
|
79
|
+
[tool.ruff]
|
|
80
|
+
target-version = "py313"
|
|
81
|
+
line-length = 88
|
|
82
|
+
fix = true
|
|
83
|
+
exclude = [
|
|
84
|
+
".git",
|
|
85
|
+
".venv",
|
|
86
|
+
"__pycache__",
|
|
87
|
+
"build",
|
|
88
|
+
"dist",
|
|
89
|
+
"docs/source/conf.py",
|
|
90
|
+
"src/lost_sessions/_version.py",
|
|
91
|
+
"prototypes",
|
|
92
|
+
]
|
|
93
|
+
|
|
94
|
+
[tool.ruff.lint]
|
|
95
|
+
select = [
|
|
96
|
+
"E",
|
|
97
|
+
"F",
|
|
98
|
+
"I",
|
|
99
|
+
"B",
|
|
100
|
+
"C4",
|
|
101
|
+
"UP",
|
|
102
|
+
"N",
|
|
103
|
+
"RUF",
|
|
104
|
+
"W",
|
|
105
|
+
"YTT",
|
|
106
|
+
"S",
|
|
107
|
+
"ANN",
|
|
108
|
+
]
|
|
109
|
+
ignore = [
|
|
110
|
+
"E501",
|
|
111
|
+
"W293",
|
|
112
|
+
]
|
|
113
|
+
unfixable = []
|
|
114
|
+
|
|
115
|
+
[tool.ruff.lint.pycodestyle]
|
|
116
|
+
max-line-length = 88
|
|
117
|
+
|
|
118
|
+
[tool.ruff.lint.per-file-ignores]
|
|
119
|
+
"__init__.py" = ["F401"]
|
|
120
|
+
"tests/*" = [
|
|
121
|
+
"S101",
|
|
122
|
+
"S105",
|
|
123
|
+
"S106",
|
|
124
|
+
"S603",
|
|
125
|
+
"S607",
|
|
126
|
+
"ANN",
|
|
127
|
+
"F811",
|
|
128
|
+
]
|
|
129
|
+
|
|
130
|
+
[tool.ruff.format]
|
|
131
|
+
quote-style = "double"
|
|
132
|
+
indent-style = "space"
|
|
133
|
+
line-ending = "auto"
|
|
134
|
+
|
|
135
|
+
[tool.pyright]
|
|
136
|
+
include = [
|
|
137
|
+
"src/lost_sessions",
|
|
138
|
+
"tests",
|
|
139
|
+
]
|
|
140
|
+
exclude = [
|
|
141
|
+
"**/node_modules",
|
|
142
|
+
"**/__pycache__",
|
|
143
|
+
]
|
|
144
|
+
typeCheckingMode = "strict"
|
|
145
|
+
pythonVersion = "3.13"
|
|
146
|
+
useLibraryCodeForTypes = true
|
|
147
|
+
reportMissingImports = true
|
|
148
|
+
reportMissingTypeStubs = true
|
|
149
|
+
reportUnknownParameterType = true
|
|
150
|
+
reportUnknownArgumentType = true
|
|
151
|
+
reportUnknownLambdaType = true
|
|
152
|
+
reportUnknownVariableType = true
|
|
153
|
+
reportUnknownMemberType = true
|
|
154
|
+
reportMissingTypeArgument = true
|
|
155
|
+
reportInvalidTypeVarUse = true
|
|
156
|
+
reportUnnecessaryIsInstance = true
|
|
157
|
+
reportUnnecessaryCast = true
|
|
158
|
+
reportUnnecessaryComparison = true
|
|
159
|
+
reportDuplicateImport = true
|
|
160
|
+
reportPrivateUsage = "warning"
|
|
161
|
+
reportUnusedImport = true
|
|
162
|
+
reportUnusedVariable = true
|
|
163
|
+
reportUnusedFunction = true
|
|
164
|
+
reportUnusedClass = true
|
|
165
|
+
|
|
166
|
+
[tool.pyright.defineConstant]
|
|
167
|
+
DEBUG = true
|
|
168
|
+
|
|
169
|
+
[tool.ty.environment]
|
|
170
|
+
python-version = "3.13"
|
|
171
|
+
|
|
172
|
+
[tool.ty.rules]
|
|
173
|
+
ambiguous-protocol-member = "error"
|
|
174
|
+
blanket-ignore-comment = "error"
|
|
175
|
+
invalid-ignore-comment = "error"
|
|
176
|
+
ignore-comment-unknown-rule = "error"
|
|
177
|
+
ineffective-final = "error"
|
|
178
|
+
invalid-enum-member-annotation = "error"
|
|
179
|
+
invalid-named-tuple-override = "error"
|
|
180
|
+
|
|
181
|
+
[tool.ty.terminal]
|
|
182
|
+
error-on-warning = false
|
|
183
|
+
|
|
184
|
+
[tool.bandit]
|
|
185
|
+
exclude_dirs = [
|
|
186
|
+
"tests",
|
|
187
|
+
".venv",
|
|
188
|
+
"build",
|
|
189
|
+
"dist",
|
|
190
|
+
]
|
|
191
|
+
skips = []
|
|
192
|
+
|
|
193
|
+
[tool.pytest.ini_options]
|
|
194
|
+
testpaths = ["tests"]
|
|
195
|
+
python_files = ["test_*.py"]
|
|
196
|
+
markers = [
|
|
197
|
+
"live: requires external services",
|
|
198
|
+
"slow: takes >1s",
|
|
199
|
+
]
|
|
200
|
+
addopts = "-m 'not live and not slow'"
|
|
201
|
+
strict_markers = true
|
|
202
|
+
timeout = 60
|
|
203
|
+
timeout_method = "thread"
|
|
204
|
+
|
|
205
|
+
[tool.coverage.run]
|
|
206
|
+
branch = true
|
|
207
|
+
|
|
208
|
+
[tool.codespell]
|
|
209
|
+
skip = ".git,.venv,build,dist,node_modules,uv.lock,bun.lock,*.lock,docs/_build,*.svg,*.cast,.ruff_cache,.pytest_cache,.mypy_cache,.ty_cache,*.log"
|
|
210
|
+
ignore-words-list = "ans,porjects,unparseable,wit"
|
|
211
|
+
check-filenames = true
|
|
212
|
+
check-hidden = false
|
|
213
|
+
|
|
214
|
+
[tool.importlinter]
|
|
215
|
+
root_package = "lost_sessions"
|
|
216
|
+
include_external_packages = true
|
|
217
|
+
|
|
218
|
+
[[tool.importlinter.contracts]]
|
|
219
|
+
name = "lost-sessions is a CLI: no web framework imports"
|
|
220
|
+
type = "forbidden"
|
|
221
|
+
source_modules = ["lost_sessions"]
|
|
222
|
+
forbidden_modules = [
|
|
223
|
+
"fastapi",
|
|
224
|
+
"starlette",
|
|
225
|
+
"uvicorn",
|
|
226
|
+
]
|
|
227
|
+
|
|
228
|
+
[[tool.importlinter.contracts]]
|
|
229
|
+
name = "core reaches built-in plugins only through the plugin manager"
|
|
230
|
+
type = "forbidden"
|
|
231
|
+
source_modules = [
|
|
232
|
+
"lost_sessions.cli",
|
|
233
|
+
"lost_sessions.hookspecs",
|
|
234
|
+
"lost_sessions.plugins",
|
|
235
|
+
]
|
|
236
|
+
forbidden_modules = ["lost_sessions.default_plugins"]
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
# Copyright (c) 2026, Steve Morin
|
|
2
|
+
#
|
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
4
|
+
# this software and associated documentation files (the "Software"), to deal in
|
|
5
|
+
# the Software without restriction, including without limitation the rights to
|
|
6
|
+
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
7
|
+
# the Software, and to permit persons to whom the Software is furnished to do so,
|
|
8
|
+
# subject to the following conditions:
|
|
9
|
+
#
|
|
10
|
+
# The above copyright notice and this permission notice shall be included in all
|
|
11
|
+
# copies or substantial portions of the Software.
|
|
12
|
+
#
|
|
13
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
15
|
+
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
16
|
+
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
17
|
+
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
18
|
+
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
19
|
+
|
|
20
|
+
[project]
|
|
21
|
+
name = "lost_sessions"
|
|
22
|
+
# ITM-070 — static version (ADR-06 cutover). release-please bumps this.
|
|
23
|
+
version = "0.2.0"
|
|
24
|
+
description = "Recover agent sessions that a reboot dropped, with pluggable session managers"
|
|
25
|
+
readme = "README.md"
|
|
26
|
+
requires-python = ">=3.13"
|
|
27
|
+
license = "MIT" # PEP 639 license expression (ITM-070, round-19).
|
|
28
|
+
authors = [{ name = "Steve Morin", email = "steve.morin@gmail.com" }]
|
|
29
|
+
classifiers = [
|
|
30
|
+
# ITM-070 — round-19. py3.13+ only (per ITM-033 floor); MIT classifier
|
|
31
|
+
# intentionally omitted (PEP 639 license expression above is the single
|
|
32
|
+
# source of truth — duplication would drift).
|
|
33
|
+
"Development Status :: 4 - Beta",
|
|
34
|
+
"Intended Audience :: Developers",
|
|
35
|
+
"Operating System :: OS Independent",
|
|
36
|
+
"Programming Language :: Python :: 3",
|
|
37
|
+
"Programming Language :: Python :: 3.13",
|
|
38
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
39
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
40
|
+
]
|
|
41
|
+
dependencies = [
|
|
42
|
+
"click>=8.1.0",
|
|
43
|
+
"pluggy>=1.5.0", # plugin manager, as in simonw/llm (design Section 4)
|
|
44
|
+
"pydantic>=2.0.0", # every data object is a Pydantic model (design Q-G2)
|
|
45
|
+
"textual>=8,<9", # the `recover` menu; imported lazily
|
|
46
|
+
]
|
|
47
|
+
|
|
48
|
+
# ITM-063 — PEP 735 dependency-groups (round-13 decision). Replaces
|
|
49
|
+
# [project.optional-dependencies] dev+docs. Install via `uv sync --group <name>`.
|
|
50
|
+
[dependency-groups]
|
|
51
|
+
dev = [
|
|
52
|
+
"pytest>=7.0.0",
|
|
53
|
+
"pytest-cov>=4.1.0",
|
|
54
|
+
"ty>=0.0.1a16", # per ADR-03 (pre-1.0; bump as needed)
|
|
55
|
+
"ruff>=0.1.0",
|
|
56
|
+
"types-Pygments",
|
|
57
|
+
"cogapp",
|
|
58
|
+
# WL-001 — hook/CI tools pinned via uv.lock (run with `uv run`, not floating
|
|
59
|
+
# `uvx`), so local hooks and CI use the same version and upgrades arrive as
|
|
60
|
+
# reviewable dependabot PRs instead of surprise breakage.
|
|
61
|
+
"bandit>=1.8.0",
|
|
62
|
+
"codespell>=2.4.0",
|
|
63
|
+
"editorconfig-checker>=3.0.0",
|
|
64
|
+
"yamllint>=1.35.0",
|
|
65
|
+
"pip-audit>=2.9.0", # WL-014 — `just audit` + scheduled dep-audit workflow
|
|
66
|
+
"twine>=6.1.0", # WL-004 — dist metadata check in the build-smoke CI job
|
|
67
|
+
"syrupy>=4.8.0", # WL-023 — CLI --help snapshot tests
|
|
68
|
+
"import-linter>=2.0", # HEX-30 — architectural layer/boundary rules
|
|
69
|
+
# Testing-strategy shortlist (ADR-0019).
|
|
70
|
+
"pytest-xdist>=3.6", # WL-008 — parallel test execution (`-n auto`)
|
|
71
|
+
"pytest-timeout>=2.3", # WL-009 — per-test timeout guard (hung test fails loud)
|
|
72
|
+
"pytest-randomly>=3.15", # WL-010 — randomized order surfaces inter-test coupling
|
|
73
|
+
"hypothesis>=6.100", # WL-013 — property-based tests at parse/serialize seams
|
|
74
|
+
]
|
|
75
|
+
|
|
76
|
+
docs = [
|
|
77
|
+
"sphinx>=7.0.0",
|
|
78
|
+
"sphinx-rtd-theme",
|
|
79
|
+
"furo",
|
|
80
|
+
"myst-parser",
|
|
81
|
+
"sphinx-autodoc-typehints",
|
|
82
|
+
"cogapp",
|
|
83
|
+
"sphinx-copybutton",
|
|
84
|
+
"sphinx-autobuild",
|
|
85
|
+
"sphinxext-opengraph", # https://github.com/wpilibsuite/sphinxext-opengraph
|
|
86
|
+
]
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
[project.scripts]
|
|
90
|
+
lost-sessions = "lost_sessions.cli:main"
|
|
91
|
+
|
|
92
|
+
# ITM-072 — package metadata URLs (round 19).
|
|
93
|
+
[project.urls]
|
|
94
|
+
Homepage = "https://github.com/smorinlabs/lost-sessions"
|
|
95
|
+
Repository = "https://github.com/smorinlabs/lost-sessions"
|
|
96
|
+
Documentation = "https://lost-sessions.readthedocs.io/en/latest/"
|
|
97
|
+
Changelog = "https://github.com/smorinlabs/lost-sessions/blob/main/CHANGELOG.md"
|
|
98
|
+
Issues = "https://github.com/smorinlabs/lost-sessions/issues"
|
|
99
|
+
|
|
100
|
+
# ITM-073 — uv_build (ADR-06 cutover; replaces Hatchling + hatch-vcs).
|
|
101
|
+
[build-system]
|
|
102
|
+
requires = ["uv_build>=0.5,<1.0"]
|
|
103
|
+
build-backend = "uv_build"
|
|
104
|
+
|
|
105
|
+
# ITM-074 — uv_build module configuration.
|
|
106
|
+
# Template uses a flat layout (package at repo root, not src/) — must set
|
|
107
|
+
# module-root = "" so uv_build doesn't expect src/lost_sessions/.
|
|
108
|
+
[tool.uv.build-backend]
|
|
109
|
+
module-name = "lost_sessions"
|
|
110
|
+
# src/ layout (Decision 1) — package now lives at src/lost_sessions/.
|
|
111
|
+
module-root = "src"
|
|
112
|
+
|
|
113
|
+
[tool.ruff]
|
|
114
|
+
# ITM-018 reconciled config (round-2). Merge: template's stricter rule
|
|
115
|
+
# selection + source's ignore rationale. target-version reflects ITM-033
|
|
116
|
+
# Python-floor bump.
|
|
117
|
+
target-version = "py313"
|
|
118
|
+
line-length = 88
|
|
119
|
+
|
|
120
|
+
lint.select = [
|
|
121
|
+
"E", # pycodestyle errors
|
|
122
|
+
"F", # pyflakes
|
|
123
|
+
"I", # isort
|
|
124
|
+
"B", # flake8-bugbear
|
|
125
|
+
"C4", # flake8-comprehensions
|
|
126
|
+
"UP", # pyupgrade
|
|
127
|
+
"N", # pep8-naming
|
|
128
|
+
"RUF", # Ruff-specific rules
|
|
129
|
+
"W", # pycodestyle warnings
|
|
130
|
+
"YTT", # flake8-2020
|
|
131
|
+
"S", # flake8-bandit
|
|
132
|
+
"ANN", # flake8-annotations — enforce annotation *presence* (P03; the gap ty
|
|
133
|
+
# deliberately does NOT check). Scoped to the package via
|
|
134
|
+
# per-file-ignores below; tests/ are exempt.
|
|
135
|
+
]
|
|
136
|
+
|
|
137
|
+
# Source's ignore rationale (round-2): line-length handled by formatter;
|
|
138
|
+
# template-applicable subset adopted (B904/B007 omitted — source-specific
|
|
139
|
+
# patterns that don't apply here).
|
|
140
|
+
lint.ignore = [
|
|
141
|
+
"E501", # line too long (formatter owns this)
|
|
142
|
+
"W293", # whitespace on blank line (formatter owns trim_trailing)
|
|
143
|
+
]
|
|
144
|
+
lint.unfixable = [] # Rules that should not be fixed automatically
|
|
145
|
+
|
|
146
|
+
# Allow autofix behavior for specific rules
|
|
147
|
+
fix = true
|
|
148
|
+
|
|
149
|
+
# Exclude files/folders
|
|
150
|
+
exclude = [
|
|
151
|
+
".git",
|
|
152
|
+
".venv",
|
|
153
|
+
"__pycache__",
|
|
154
|
+
"build",
|
|
155
|
+
"dist",
|
|
156
|
+
"docs/source/conf.py",
|
|
157
|
+
"src/lost_sessions/_version.py",
|
|
158
|
+
"prototypes", # P0004 template-press engine prototypes (own lint config in template-press)
|
|
159
|
+
]
|
|
160
|
+
# Per-file-ignores
|
|
161
|
+
[tool.ruff.lint.pycodestyle]
|
|
162
|
+
max-line-length = 88
|
|
163
|
+
[tool.ruff.lint.per-file-ignores]
|
|
164
|
+
"__init__.py" = ["F401"] # Ignore unused imports in __init__.py files
|
|
165
|
+
"tests/*" = [
|
|
166
|
+
"S101",
|
|
167
|
+
"S105",
|
|
168
|
+
"S106",
|
|
169
|
+
"S603",
|
|
170
|
+
"S607",
|
|
171
|
+
"ANN",
|
|
172
|
+
"F811",
|
|
173
|
+
] # asserts + annotations optional; tests drive the CLI and stand-ins via subprocess;
|
|
174
|
+
# F811: importing a pytest fixture and naming it as a parameter is the normal pattern
|
|
175
|
+
# S101: assert statements removed under -O — security risk if assert is the only gate.
|
|
176
|
+
# S105/S106: hardcoded password strings/variables in code.
|
|
177
|
+
|
|
178
|
+
# ITM-018 — explicit format config (round-2; mirrors source). Values match Ruff
|
|
179
|
+
# defaults but being explicit prevents silent drift if defaults change.
|
|
180
|
+
[tool.ruff.format]
|
|
181
|
+
quote-style = "double"
|
|
182
|
+
indent-style = "space"
|
|
183
|
+
line-ending = "auto"
|
|
184
|
+
|
|
185
|
+
# Pyright/Pylance (IDE) strict type checking — formerly pyrightconfig.json.
|
|
186
|
+
# CI typechecking runs ty (ADR-03); this section drives the VS Code Pyright
|
|
187
|
+
# extension and ad-hoc `pyright` runs.
|
|
188
|
+
[tool.pyright]
|
|
189
|
+
include = ["src/lost_sessions", "tests"]
|
|
190
|
+
exclude = ["**/node_modules", "**/__pycache__"]
|
|
191
|
+
defineConstant = { DEBUG = true }
|
|
192
|
+
typeCheckingMode = "strict"
|
|
193
|
+
pythonVersion = "3.13"
|
|
194
|
+
useLibraryCodeForTypes = true
|
|
195
|
+
reportMissingImports = true
|
|
196
|
+
reportMissingTypeStubs = true
|
|
197
|
+
reportUnknownParameterType = true
|
|
198
|
+
reportUnknownArgumentType = true
|
|
199
|
+
reportUnknownLambdaType = true
|
|
200
|
+
reportUnknownVariableType = true
|
|
201
|
+
reportUnknownMemberType = true
|
|
202
|
+
reportMissingTypeArgument = true
|
|
203
|
+
reportInvalidTypeVarUse = true
|
|
204
|
+
reportUnnecessaryIsInstance = true
|
|
205
|
+
reportUnnecessaryCast = true
|
|
206
|
+
reportUnnecessaryComparison = true
|
|
207
|
+
reportDuplicateImport = true
|
|
208
|
+
reportPrivateUsage = "warning"
|
|
209
|
+
reportUnusedImport = true
|
|
210
|
+
reportUnusedVariable = true
|
|
211
|
+
reportUnusedFunction = true
|
|
212
|
+
reportUnusedClass = true
|
|
213
|
+
|
|
214
|
+
# ty (Astral) is the CI type-check authority (ADR-03). ty is rule-based, not
|
|
215
|
+
# strict-mode-based: its correctness core already defaults to `error`, so this
|
|
216
|
+
# block only promotes the handful of quality rules that ship below `error`.
|
|
217
|
+
# Each was verified against the pinned ty (see uv.lock, 0.0.61): running
|
|
218
|
+
# `ty check --error <rule> src/lost_sessions/` passes for each.
|
|
219
|
+
# `error-on-warning` is set explicitly to false below (not left implicit): a
|
|
220
|
+
# dependency `deprecated` warning must not fail every fork's CI, and pinning the
|
|
221
|
+
# value keeps CI deterministic across ty upgrades (its default is version-
|
|
222
|
+
# sensitive pre-1.0). `strict-literal-narrowing` is left off as an optional
|
|
223
|
+
# stricter knob. See projects/P03-type-precision-uplevel.md.
|
|
224
|
+
[tool.ty.environment]
|
|
225
|
+
python-version = "3.13" # matches requires-python floor; explicit for determinism
|
|
226
|
+
|
|
227
|
+
[tool.ty.rules]
|
|
228
|
+
ambiguous-protocol-member = "error" # core/ports.py exposes Protocol ports
|
|
229
|
+
blanket-ignore-comment = "error" # every `# ty: ignore` must name its rule
|
|
230
|
+
invalid-ignore-comment = "error"
|
|
231
|
+
ignore-comment-unknown-rule = "error"
|
|
232
|
+
ineffective-final = "error"
|
|
233
|
+
invalid-enum-member-annotation = "error"
|
|
234
|
+
invalid-named-tuple-override = "error"
|
|
235
|
+
|
|
236
|
+
[tool.ty.terminal]
|
|
237
|
+
error-on-warning = false # warnings (e.g. `deprecated`) inform; they don't gate CI
|
|
238
|
+
|
|
239
|
+
# ITM-027 — bandit config (round 3). Tests legitimately use `assert`
|
|
240
|
+
# (pytest convention) — exclude_dirs covers tests/.
|
|
241
|
+
[tool.bandit]
|
|
242
|
+
exclude_dirs = ["tests", ".venv", "build", "dist"]
|
|
243
|
+
skips = [] # No global skips; add with rationale only.
|
|
244
|
+
|
|
245
|
+
[tool.pytest.ini_options]
|
|
246
|
+
testpaths = ["tests"]
|
|
247
|
+
python_files = ["test_*.py"]
|
|
248
|
+
# Marker taxonomy (ITM-046, round-9 decision).
|
|
249
|
+
# `live` = requires external services; `slow` = takes >1s.
|
|
250
|
+
# Default `addopts` excludes both — run full suite with `pytest -m ""`.
|
|
251
|
+
markers = ["live: requires external services", "slow: takes >1s"]
|
|
252
|
+
addopts = "-m 'not live and not slow'"
|
|
253
|
+
strict_markers = true
|
|
254
|
+
# WL-009 — a hung test fails loudly instead of burning a CI slot. `thread`
|
|
255
|
+
# method (not the POSIX-only signal default) so the cap also holds on the
|
|
256
|
+
# Windows matrix leg. A genuinely slow test overrides via
|
|
257
|
+
# `@pytest.mark.timeout(N)`.
|
|
258
|
+
timeout = 60
|
|
259
|
+
timeout_method = "thread"
|
|
260
|
+
# WL-010 (pytest-randomly) reorders tests each run with a printed, replayable
|
|
261
|
+
# seed (`-p randomly --randomly-seed=…`) to surface hidden inter-test coupling;
|
|
262
|
+
# it needs no config. Parallelism (WL-008, `-n auto`) is opt-in per invocation
|
|
263
|
+
# (`just test -n auto`, and the CI full-suite + canary runs) rather than in
|
|
264
|
+
# default addopts, so the fast local inner loop keeps clean `-x`/pdb behaviour.
|
|
265
|
+
|
|
266
|
+
# Branch coverage (ADR-0019): count both-arms of every branch, not just line
|
|
267
|
+
# execution, so the Codecov gate (WL-005) catches untested conditionals.
|
|
268
|
+
[tool.coverage.run]
|
|
269
|
+
branch = true
|
|
270
|
+
|
|
271
|
+
# ITM-015 — codespell. US English baseline (default builtin="clear,rare"); empty
|
|
272
|
+
# ignore list — add terms only when intentional. Decided round 9.
|
|
273
|
+
[tool.codespell]
|
|
274
|
+
skip = ".git,.venv,build,dist,node_modules,uv.lock,bun.lock,*.lock,docs/_build,*.svg,*.cast,.ruff_cache,.pytest_cache,.mypy_cache,.ty_cache,*.log"
|
|
275
|
+
# *.log: raw run output, not prose.
|
|
276
|
+
ignore-words-list = "ans,porjects,unparseable,wit"
|
|
277
|
+
# ans: variable name used in prompt-handling code; codespell wants to "fix"
|
|
278
|
+
# it to "and". Whitelist to keep the short variable name.
|
|
279
|
+
# porjects: the deliberate typo example in the superseded ADR 0007.
|
|
280
|
+
# wit: WebAssembly Interface Types, in docs/research/0001.
|
|
281
|
+
check-filenames = true
|
|
282
|
+
check-hidden = false
|
|
283
|
+
|
|
284
|
+
# import-linter: architectural import rules for this package. Run via
|
|
285
|
+
# `uv run lint-imports`; wired into `just check` and CI (import-boundaries).
|
|
286
|
+
[tool.importlinter]
|
|
287
|
+
root_package = "lost_sessions"
|
|
288
|
+
include_external_packages = true
|
|
289
|
+
|
|
290
|
+
[[tool.importlinter.contracts]]
|
|
291
|
+
name = "lost-sessions is a CLI: no web framework imports"
|
|
292
|
+
type = "forbidden"
|
|
293
|
+
source_modules = ["lost_sessions"]
|
|
294
|
+
forbidden_modules = ["fastapi", "starlette", "uvicorn"]
|
|
295
|
+
|
|
296
|
+
[[tool.importlinter.contracts]]
|
|
297
|
+
name = "core reaches built-in plugins only through the plugin manager"
|
|
298
|
+
type = "forbidden"
|
|
299
|
+
source_modules = ["lost_sessions.cli", "lost_sessions.hookspecs", "lost_sessions.plugins"]
|
|
300
|
+
forbidden_modules = ["lost_sessions.default_plugins"]
|