tiroir 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.
- tiroir-0.1.0/.github/workflows/ci.yml +32 -0
- tiroir-0.1.0/.gitignore +5 -0
- tiroir-0.1.0/LICENSE +21 -0
- tiroir-0.1.0/PKG-INFO +119 -0
- tiroir-0.1.0/README.md +102 -0
- tiroir-0.1.0/pyproject.toml +41 -0
- tiroir-0.1.0/src/tiroir/__init__.py +1 -0
- tiroir-0.1.0/src/tiroir/work.py +435 -0
- tiroir-0.1.0/tests/test_work.py +789 -0
- tiroir-0.1.0/uv.lock +107 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
name: ci
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
test:
|
|
13
|
+
strategy:
|
|
14
|
+
fail-fast: false
|
|
15
|
+
matrix:
|
|
16
|
+
os: [ubuntu-latest, macos-latest]
|
|
17
|
+
python: ["3.11", "3.13"]
|
|
18
|
+
runs-on: ${{ matrix.os }}
|
|
19
|
+
timeout-minutes: 10
|
|
20
|
+
steps:
|
|
21
|
+
- uses: actions/checkout@v4
|
|
22
|
+
with:
|
|
23
|
+
persist-credentials: false
|
|
24
|
+
- uses: astral-sh/setup-uv@v6
|
|
25
|
+
with:
|
|
26
|
+
python-version: ${{ matrix.python }}
|
|
27
|
+
- name: Lint
|
|
28
|
+
run: uv run --extra dev ruff check . && uv run --extra dev ruff format --check .
|
|
29
|
+
- name: Tests
|
|
30
|
+
run: uv run --extra dev pytest -q
|
|
31
|
+
- name: Build
|
|
32
|
+
run: uv build
|
tiroir-0.1.0/.gitignore
ADDED
tiroir-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 tiroir
|
|
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.
|
tiroir-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: tiroir
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Fork a tiroir hub's agent session to your own machine and carry on in Claude Code.
|
|
5
|
+
Author-email: tiroir <202564+tiroir@users.noreply.github.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Classifier: Environment :: Console
|
|
9
|
+
Classifier: Operating System :: MacOS
|
|
10
|
+
Classifier: Operating System :: POSIX
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Requires-Python: >=3.11
|
|
13
|
+
Provides-Extra: dev
|
|
14
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
15
|
+
Requires-Dist: ruff>=0.6; extra == 'dev'
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
|
|
18
|
+
# tiroir
|
|
19
|
+
|
|
20
|
+
`tiroir work` takes an agent session that runs on a tiroir hub and gives you your own copy of it
|
|
21
|
+
on your laptop, in Claude Code, with the whole conversation so far. You carry on from where it
|
|
22
|
+
stands, under your own name, with your own tools.
|
|
23
|
+
|
|
24
|
+
It works with a **tiroir hub**: a server that shows shared markdown documents ("sheaves") and
|
|
25
|
+
keeps the transcripts of the agent sessions working on them. Without such a hub this command has
|
|
26
|
+
nothing to do.
|
|
27
|
+
|
|
28
|
+
## What you need
|
|
29
|
+
|
|
30
|
+
- macOS or Linux (Windows is not supported yet);
|
|
31
|
+
- [uv](https://docs.astral.sh/uv/) (or pipx), to run the command;
|
|
32
|
+
- [Claude Code](https://docs.claude.com/en/docs/claude-code), with the `claude` command on your
|
|
33
|
+
`PATH`;
|
|
34
|
+
- an account on the hub's front, and a sheaf that is open to you.
|
|
35
|
+
|
|
36
|
+
## Run it
|
|
37
|
+
|
|
38
|
+
Copy the command from the sheaf's page (☰ menu, **Work on this**, **Fork to your machine**), or
|
|
39
|
+
type it with the sheaf's address:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
uvx tiroir work https://hub.example.com/list/acme/site/plan.md
|
|
43
|
+
uvx tiroir work https://hub.example.com/list/acme/site/plan.md --cwd ~/work/acme-site
|
|
44
|
+
uvx tiroir work https://hub.example.com/list/acme/site/plan.md --dry-run
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
To keep it installed instead: `uv tool install tiroir`, then `tiroir work <address>`.
|
|
48
|
+
|
|
49
|
+
## What it does, on your laptop only
|
|
50
|
+
|
|
51
|
+
1. Downloads the transcript of the sheaf's live session from the hub, as you. Only a sheaf that
|
|
52
|
+
is open to you works.
|
|
53
|
+
2. Stores it in `~/.claude/projects/<your folder>/<session id>.jsonl`, readable by you alone. It
|
|
54
|
+
replaces an older copy when the new transcript extends it (the session grew since your last
|
|
55
|
+
fork). Claude Code needs exactly one copy of a session, so any other copy of it, in this
|
|
56
|
+
folder or another one (sheaves of one project can share one session), is kept aside once the
|
|
57
|
+
new one is written, as `<session id>.jsonl.kept-<UTC time>` in its own folder, and named in one
|
|
58
|
+
line. No other copy is deleted, and a fork you made earlier keeps working: it holds its own
|
|
59
|
+
copy of the conversation. Claude Code no longer reads a kept file; delete it yourself when you
|
|
60
|
+
no longer want it.
|
|
61
|
+
3. Writes on the sheaf that you forked it, from which machine, with the new session's id.
|
|
62
|
+
4. Starts `claude --resume <id> --fork-session --session-id <new id>` in the folder you gave with
|
|
63
|
+
`--cwd` (by default the folder you are in). That is a new session: your messages go to your
|
|
64
|
+
copy, never back to the hub's.
|
|
65
|
+
|
|
66
|
+
What it does not do: it never runs anything on the hub's machine, never changes the session it
|
|
67
|
+
copied, and never sends your messages anywhere but your own Claude Code. `--dry-run` downloads and
|
|
68
|
+
stores the transcript, records nothing on the sheaf, and prints the `claude` command instead of
|
|
69
|
+
starting it.
|
|
70
|
+
|
|
71
|
+
The transcript holds everything the session saw, including tool results: keep it as private as
|
|
72
|
+
the sheaf itself.
|
|
73
|
+
|
|
74
|
+
## Your sign-in token
|
|
75
|
+
|
|
76
|
+
`tiroir work` signs in to the hub with the session token of your account on the front the hub
|
|
77
|
+
sits behind. It reads it from the file `~/.config/tiroir/front-token`, or from the variable
|
|
78
|
+
`TIROIR_FRONT_TOKEN` when that is set. It sends it only to the sheaf's own host, and never prints
|
|
79
|
+
it.
|
|
80
|
+
|
|
81
|
+
To get it, sign in to the front in your browser, open the developer tools, and copy the value of
|
|
82
|
+
the cookie `token` for the front's address (Chrome: Application > Cookies). Then, on a Mac:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
mkdir -p ~/.config/tiroir
|
|
86
|
+
pbpaste > ~/.config/tiroir/front-token # with the token on the clipboard
|
|
87
|
+
chmod 600 ~/.config/tiroir/front-token
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
On Linux, use `xclip -selection clipboard -o` instead of `pbpaste`. The file must be readable by
|
|
91
|
+
you alone (`chmod 600`); `tiroir work` refuses it otherwise. The token expires with your session
|
|
92
|
+
on the front: when `tiroir work` says you are not signed in, copy a fresh one the same way.
|
|
93
|
+
|
|
94
|
+
## When something is refused
|
|
95
|
+
|
|
96
|
+
`tiroir work` says why in one sentence and exits with code 2. The reasons:
|
|
97
|
+
- you are not signed in;
|
|
98
|
+
- the sheaf is not open to you, or has no session on the hub;
|
|
99
|
+
- the address is not a sheaf's;
|
|
100
|
+
- the token file is readable by others;
|
|
101
|
+
- a link sits under the session's file name.
|
|
102
|
+
|
|
103
|
+
When the hub refuses to record the fork, `tiroir work` gives the hub's reason. The transcript is
|
|
104
|
+
then already on your laptop, and Claude Code is not started: run the same command again once the
|
|
105
|
+
reason is fixed.
|
|
106
|
+
|
|
107
|
+
## Development
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
uv run --extra dev pytest
|
|
111
|
+
uv run --extra dev ruff check . && uv run --extra dev ruff format --check .
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
The tests run the command against a fake hub on 127.0.0.1 and never touch your real
|
|
115
|
+
`~/.claude`.
|
|
116
|
+
|
|
117
|
+
## License
|
|
118
|
+
|
|
119
|
+
MIT, see `LICENSE`.
|
tiroir-0.1.0/README.md
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# tiroir
|
|
2
|
+
|
|
3
|
+
`tiroir work` takes an agent session that runs on a tiroir hub and gives you your own copy of it
|
|
4
|
+
on your laptop, in Claude Code, with the whole conversation so far. You carry on from where it
|
|
5
|
+
stands, under your own name, with your own tools.
|
|
6
|
+
|
|
7
|
+
It works with a **tiroir hub**: a server that shows shared markdown documents ("sheaves") and
|
|
8
|
+
keeps the transcripts of the agent sessions working on them. Without such a hub this command has
|
|
9
|
+
nothing to do.
|
|
10
|
+
|
|
11
|
+
## What you need
|
|
12
|
+
|
|
13
|
+
- macOS or Linux (Windows is not supported yet);
|
|
14
|
+
- [uv](https://docs.astral.sh/uv/) (or pipx), to run the command;
|
|
15
|
+
- [Claude Code](https://docs.claude.com/en/docs/claude-code), with the `claude` command on your
|
|
16
|
+
`PATH`;
|
|
17
|
+
- an account on the hub's front, and a sheaf that is open to you.
|
|
18
|
+
|
|
19
|
+
## Run it
|
|
20
|
+
|
|
21
|
+
Copy the command from the sheaf's page (☰ menu, **Work on this**, **Fork to your machine**), or
|
|
22
|
+
type it with the sheaf's address:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
uvx tiroir work https://hub.example.com/list/acme/site/plan.md
|
|
26
|
+
uvx tiroir work https://hub.example.com/list/acme/site/plan.md --cwd ~/work/acme-site
|
|
27
|
+
uvx tiroir work https://hub.example.com/list/acme/site/plan.md --dry-run
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
To keep it installed instead: `uv tool install tiroir`, then `tiroir work <address>`.
|
|
31
|
+
|
|
32
|
+
## What it does, on your laptop only
|
|
33
|
+
|
|
34
|
+
1. Downloads the transcript of the sheaf's live session from the hub, as you. Only a sheaf that
|
|
35
|
+
is open to you works.
|
|
36
|
+
2. Stores it in `~/.claude/projects/<your folder>/<session id>.jsonl`, readable by you alone. It
|
|
37
|
+
replaces an older copy when the new transcript extends it (the session grew since your last
|
|
38
|
+
fork). Claude Code needs exactly one copy of a session, so any other copy of it, in this
|
|
39
|
+
folder or another one (sheaves of one project can share one session), is kept aside once the
|
|
40
|
+
new one is written, as `<session id>.jsonl.kept-<UTC time>` in its own folder, and named in one
|
|
41
|
+
line. No other copy is deleted, and a fork you made earlier keeps working: it holds its own
|
|
42
|
+
copy of the conversation. Claude Code no longer reads a kept file; delete it yourself when you
|
|
43
|
+
no longer want it.
|
|
44
|
+
3. Writes on the sheaf that you forked it, from which machine, with the new session's id.
|
|
45
|
+
4. Starts `claude --resume <id> --fork-session --session-id <new id>` in the folder you gave with
|
|
46
|
+
`--cwd` (by default the folder you are in). That is a new session: your messages go to your
|
|
47
|
+
copy, never back to the hub's.
|
|
48
|
+
|
|
49
|
+
What it does not do: it never runs anything on the hub's machine, never changes the session it
|
|
50
|
+
copied, and never sends your messages anywhere but your own Claude Code. `--dry-run` downloads and
|
|
51
|
+
stores the transcript, records nothing on the sheaf, and prints the `claude` command instead of
|
|
52
|
+
starting it.
|
|
53
|
+
|
|
54
|
+
The transcript holds everything the session saw, including tool results: keep it as private as
|
|
55
|
+
the sheaf itself.
|
|
56
|
+
|
|
57
|
+
## Your sign-in token
|
|
58
|
+
|
|
59
|
+
`tiroir work` signs in to the hub with the session token of your account on the front the hub
|
|
60
|
+
sits behind. It reads it from the file `~/.config/tiroir/front-token`, or from the variable
|
|
61
|
+
`TIROIR_FRONT_TOKEN` when that is set. It sends it only to the sheaf's own host, and never prints
|
|
62
|
+
it.
|
|
63
|
+
|
|
64
|
+
To get it, sign in to the front in your browser, open the developer tools, and copy the value of
|
|
65
|
+
the cookie `token` for the front's address (Chrome: Application > Cookies). Then, on a Mac:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
mkdir -p ~/.config/tiroir
|
|
69
|
+
pbpaste > ~/.config/tiroir/front-token # with the token on the clipboard
|
|
70
|
+
chmod 600 ~/.config/tiroir/front-token
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
On Linux, use `xclip -selection clipboard -o` instead of `pbpaste`. The file must be readable by
|
|
74
|
+
you alone (`chmod 600`); `tiroir work` refuses it otherwise. The token expires with your session
|
|
75
|
+
on the front: when `tiroir work` says you are not signed in, copy a fresh one the same way.
|
|
76
|
+
|
|
77
|
+
## When something is refused
|
|
78
|
+
|
|
79
|
+
`tiroir work` says why in one sentence and exits with code 2. The reasons:
|
|
80
|
+
- you are not signed in;
|
|
81
|
+
- the sheaf is not open to you, or has no session on the hub;
|
|
82
|
+
- the address is not a sheaf's;
|
|
83
|
+
- the token file is readable by others;
|
|
84
|
+
- a link sits under the session's file name.
|
|
85
|
+
|
|
86
|
+
When the hub refuses to record the fork, `tiroir work` gives the hub's reason. The transcript is
|
|
87
|
+
then already on your laptop, and Claude Code is not started: run the same command again once the
|
|
88
|
+
reason is fixed.
|
|
89
|
+
|
|
90
|
+
## Development
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
uv run --extra dev pytest
|
|
94
|
+
uv run --extra dev ruff check . && uv run --extra dev ruff format --check .
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
The tests run the command against a fake hub on 127.0.0.1 and never touch your real
|
|
98
|
+
`~/.claude`.
|
|
99
|
+
|
|
100
|
+
## License
|
|
101
|
+
|
|
102
|
+
MIT, see `LICENSE`.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "tiroir"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Fork a tiroir hub's agent session to your own machine and carry on in Claude Code."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = "MIT"
|
|
7
|
+
license-files = ["LICENSE"]
|
|
8
|
+
authors = [{ name = "tiroir", email = "202564+tiroir@users.noreply.github.com" }]
|
|
9
|
+
requires-python = ">=3.11"
|
|
10
|
+
dependencies = []
|
|
11
|
+
classifiers = [
|
|
12
|
+
"Environment :: Console",
|
|
13
|
+
"Operating System :: POSIX",
|
|
14
|
+
"Operating System :: MacOS",
|
|
15
|
+
"Programming Language :: Python :: 3",
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
[project.scripts]
|
|
19
|
+
tiroir = "tiroir.work:main"
|
|
20
|
+
|
|
21
|
+
[project.optional-dependencies]
|
|
22
|
+
dev = [
|
|
23
|
+
"pytest>=8.0",
|
|
24
|
+
"ruff>=0.6",
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
[tool.ruff]
|
|
28
|
+
line-length = 100
|
|
29
|
+
|
|
30
|
+
[tool.ruff.lint]
|
|
31
|
+
select = ["E", "F", "I", "B", "UP", "SIM", "RUF"]
|
|
32
|
+
|
|
33
|
+
[tool.pytest.ini_options]
|
|
34
|
+
testpaths = ["tests"]
|
|
35
|
+
|
|
36
|
+
[build-system]
|
|
37
|
+
requires = ["hatchling>=1.27"]
|
|
38
|
+
build-backend = "hatchling.build"
|
|
39
|
+
|
|
40
|
+
[tool.hatch.build.targets.wheel]
|
|
41
|
+
packages = ["src/tiroir"]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""tiroir on your own machine: the laptop side of a tiroir hub."""
|