sessionkit 0.2.0__tar.gz → 0.2.1__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.
- sessionkit-0.2.1/PKG-INFO +191 -0
- sessionkit-0.2.1/README.md +171 -0
- sessionkit-0.2.1/pyproject.toml +54 -0
- sessionkit-0.2.0/pyproject.toml → sessionkit-0.2.1/pyproject.toml.orig +17 -8
- {sessionkit-0.2.0 → sessionkit-0.2.1}/src/sessionkit/cli.py +26 -6
- {sessionkit-0.2.0 → sessionkit-0.2.1}/src/sessionkit/setup.py +212 -26
- sessionkit-0.2.0/PKG-INFO +0 -18
- {sessionkit-0.2.0 → sessionkit-0.2.1}/LICENSE +0 -0
- {sessionkit-0.2.0 → sessionkit-0.2.1}/src/sessionkit/__init__.py +0 -0
- {sessionkit-0.2.0 → sessionkit-0.2.1}/src/sessionkit/py.typed +0 -0
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: sessionkit
|
|
3
|
+
Version: 0.2.1
|
|
4
|
+
Summary: Session Kit gives your Claude Code sessions voice, mail, a phone line and a tmux sidebar, in one install.
|
|
5
|
+
Author: Mike Bailey
|
|
6
|
+
Author-email: Mike Bailey <mike@sessionkit.ai>
|
|
7
|
+
License-Expression: AGPL-3.0-or-later
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Requires-Dist: click>=8.1
|
|
11
|
+
Requires-Dist: sessionmail>=0.2.1
|
|
12
|
+
Requires-Dist: sessiontools>=0.2.0
|
|
13
|
+
Requires-Dist: sessionphone[media]>=0.2.1
|
|
14
|
+
Requires-Dist: sessionview>=0.2.0
|
|
15
|
+
Requires-Dist: sessionvoice[capture]>=0.2.1
|
|
16
|
+
Requires-Python: >=3.11, <3.15
|
|
17
|
+
Project-URL: Homepage, https://github.com/sessionkit/sessionkit
|
|
18
|
+
Project-URL: Repository, https://github.com/sessionkit/sessionkit
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
|
|
21
|
+
# Session Kit
|
|
22
|
+
|
|
23
|
+
Session Kit lets your Claude Code sessions mail each other, hear you and
|
|
24
|
+
answer aloud. One install brings the five family packages and their Claude
|
|
25
|
+
Code plugins.
|
|
26
|
+
|
|
27
|
+
## Installation
|
|
28
|
+
|
|
29
|
+
You need macOS and [Claude Code][claude-code]. If you do not have uv yet,
|
|
30
|
+
install it with `curl -LsSf https://astral.sh/uv/install.sh | sh`, then open
|
|
31
|
+
a new terminal (or run `source ~/.local/bin/env`).
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
uv tool install --python 3.14 sessionkit
|
|
35
|
+
claude plugin marketplace add sessionkit/sessionkit
|
|
36
|
+
claude plugin install sessionkit@sessionkit
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
The first line puts the commands on your PATH. The other two install the
|
|
40
|
+
kit's plugin and the five family plugins it depends on. Start Claude Code
|
|
41
|
+
(or restart it) to load them.
|
|
42
|
+
|
|
43
|
+
- **Keep `--python 3.14`.** Without it, uv takes the Python it finds: 3.15,
|
|
44
|
+
where a voice library has no build yet and the install fails, or the
|
|
45
|
+
Command Line Tools' 3.11. The kit is tested on 3.14.
|
|
46
|
+
- **A dialog may ask to install the Command Line Tools.** On a Mac without
|
|
47
|
+
them, the first `uv tool install` opens it. Click Install and let it
|
|
48
|
+
finish: the plugin lines need the git it brings.
|
|
49
|
+
- **Phone calls are installed, not yet walked.** Whether the phone bot
|
|
50
|
+
signs its mail to your Claude follows your mail carrier: [bot mail
|
|
51
|
+
signing][phone-signing].
|
|
52
|
+
|
|
53
|
+
- **The mail hooks start off.** They tell an agent about new mail between
|
|
54
|
+
its tool calls. To turn them on, set `"hooks": true` in
|
|
55
|
+
`~/.sessionkit/mail/settings/mail.json`. On a fresh install the file is
|
|
56
|
+
not there yet, so make it:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
mkdir -p ~/.sessionkit/mail/settings
|
|
60
|
+
echo '{"version": 1, "hooks": true}' > ~/.sessionkit/mail/settings/mail.json
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
If the file is there, change only that key, because it can hold other
|
|
64
|
+
settings:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
perl -pi -e 's/"hooks": *false/"hooks": true/' ~/.sessionkit/mail/settings/mail.json
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Step by step, with every step that needs you marked: [the manual
|
|
71
|
+
install][install].
|
|
72
|
+
|
|
73
|
+
## Quick start
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
sessionkit --version
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
It lists the kit and each family package, with its version:
|
|
80
|
+
|
|
81
|
+
```text
|
|
82
|
+
sessionkit, version 0.2.1
|
|
83
|
+
sessionmail 0.2.1
|
|
84
|
+
sessiontools 0.2.0
|
|
85
|
+
sessionphone 0.2.1
|
|
86
|
+
sessionview 0.2.0
|
|
87
|
+
sessionvoice 0.2.1
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Start Claude with `session start claude`, never a bare `claude`: mail to
|
|
91
|
+
your agent's box, your phone's included, reaches only a session started as
|
|
92
|
+
that agent. `sessionkit setup` makes the box and the agent first: [first
|
|
93
|
+
checks][checks].
|
|
94
|
+
|
|
95
|
+
Then, from your agent inside a Claude Code session:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
session list # your live sessions
|
|
99
|
+
message send <box> -s "PR ready" -f note.md # mail another agent
|
|
100
|
+
mouth say --device default "the build is green" # speak one line
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
- **Mail works inside a session.** `message` and `pager` read who is asking
|
|
104
|
+
from the session. A plain terminal has no session, so they refuse with
|
|
105
|
+
`no seat in the environment ... set an identity` ([first checks][checks]).
|
|
106
|
+
A send never makes a box: the address must have one already ([mail
|
|
107
|
+
between agents][mail]).
|
|
108
|
+
- **The mouth needs a device.** `--device default` is your Mac's default
|
|
109
|
+
output; `mouth devices` lists the others.
|
|
110
|
+
- **To hear you**, the ears need a speech-to-text server: [hearing and
|
|
111
|
+
speaking][hearing].
|
|
112
|
+
|
|
113
|
+
## Uninstall
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
claude plugin marketplace remove sessionkit
|
|
117
|
+
uv tool uninstall sessionkit
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Removing the marketplace removes its six plugins too. The services and your
|
|
121
|
+
data have their own steps: [uninstall][uninstall].
|
|
122
|
+
|
|
123
|
+
## Usage
|
|
124
|
+
|
|
125
|
+
Every command answers `--help`, and each has a page: [components][components].
|
|
126
|
+
|
|
127
|
+
## The family
|
|
128
|
+
|
|
129
|
+
Session Kit installs these together:
|
|
130
|
+
|
|
131
|
+
- [sessionmail][pkg-mail]: mail for coding agents. `message`, `pager` and
|
|
132
|
+
`memory` over one maildir.
|
|
133
|
+
- [sessiontools][pkg-tools]: sessions, status and idle. `session`.
|
|
134
|
+
- [sessionvoice][pkg-voice]: ambient voice. `ears` hears; `mouth` speaks.
|
|
135
|
+
- [sessionphone][pkg-phone]: phone calls to a session over Delta Chat.
|
|
136
|
+
Installed, not yet walked.
|
|
137
|
+
- [sessionview][pkg-view]: a tmux sidebar of your projects and your agents.
|
|
138
|
+
|
|
139
|
+
## Components
|
|
140
|
+
|
|
141
|
+
The commands, services and folders the install adds, and where each
|
|
142
|
+
package logs: [components][components].
|
|
143
|
+
|
|
144
|
+
## Configuration
|
|
145
|
+
|
|
146
|
+
Each package configures itself: [configuration][configuration]. How the
|
|
147
|
+
settings files stack: [the settings guide][settings].
|
|
148
|
+
|
|
149
|
+
## Logs
|
|
150
|
+
|
|
151
|
+
Each package keeps its own log: [logs][logs]. Which one to read for a
|
|
152
|
+
symptom: [troubleshooting][troubleshooting].
|
|
153
|
+
|
|
154
|
+
## Development
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
git clone https://github.com/sessionkit/sessionkit
|
|
158
|
+
cd sessionkit
|
|
159
|
+
make install # editable: uv tool install -e .
|
|
160
|
+
make check # ruff, shellcheck, then the tests
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
The rules for changing the kit: [AGENTS.md][agents].
|
|
164
|
+
|
|
165
|
+
## Status
|
|
166
|
+
|
|
167
|
+
Alpha, 0.2.1. Tested on macOS 27 on Apple silicon; Linux and Windows are
|
|
168
|
+
untested. What you may still hit: [known gaps][gaps].
|
|
169
|
+
|
|
170
|
+
Licence: [AGPL-3.0][licence].
|
|
171
|
+
|
|
172
|
+
[claude-code]: https://claude.ai/code
|
|
173
|
+
[install]: INSTALL.md
|
|
174
|
+
[checks]: INSTALL.md#4-first-checks
|
|
175
|
+
[hearing]: INSTALL.md#5-hearing-and-speaking-it-hears-me-and-speaks
|
|
176
|
+
[mail]: INSTALL.md#mail-between-agents
|
|
177
|
+
[uninstall]: INSTALL.md#8-uninstall
|
|
178
|
+
[gaps]: INSTALL.md#7-known-gaps-you-will-hit
|
|
179
|
+
[components]: docs/components.md
|
|
180
|
+
[logs]: docs/components.md#logs
|
|
181
|
+
[configuration]: docs/configuration.md
|
|
182
|
+
[settings]: docs/settings.md
|
|
183
|
+
[troubleshooting]: docs/troubleshooting.md
|
|
184
|
+
[agents]: AGENTS.md
|
|
185
|
+
[licence]: LICENSE
|
|
186
|
+
[pkg-mail]: https://github.com/sessionkit/sessionmail
|
|
187
|
+
[pkg-tools]: https://github.com/sessionkit/sessiontools
|
|
188
|
+
[pkg-voice]: https://github.com/sessionkit/sessionvoice
|
|
189
|
+
[pkg-phone]: https://github.com/sessionkit/sessionphone
|
|
190
|
+
[phone-signing]: https://github.com/sessionkit/sessionphone/blob/HEAD/docs/bot-mail-signing.md
|
|
191
|
+
[pkg-view]: https://github.com/sessionkit/sessionview
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# Session Kit
|
|
2
|
+
|
|
3
|
+
Session Kit lets your Claude Code sessions mail each other, hear you and
|
|
4
|
+
answer aloud. One install brings the five family packages and their Claude
|
|
5
|
+
Code plugins.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
You need macOS and [Claude Code][claude-code]. If you do not have uv yet,
|
|
10
|
+
install it with `curl -LsSf https://astral.sh/uv/install.sh | sh`, then open
|
|
11
|
+
a new terminal (or run `source ~/.local/bin/env`).
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
uv tool install --python 3.14 sessionkit
|
|
15
|
+
claude plugin marketplace add sessionkit/sessionkit
|
|
16
|
+
claude plugin install sessionkit@sessionkit
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
The first line puts the commands on your PATH. The other two install the
|
|
20
|
+
kit's plugin and the five family plugins it depends on. Start Claude Code
|
|
21
|
+
(or restart it) to load them.
|
|
22
|
+
|
|
23
|
+
- **Keep `--python 3.14`.** Without it, uv takes the Python it finds: 3.15,
|
|
24
|
+
where a voice library has no build yet and the install fails, or the
|
|
25
|
+
Command Line Tools' 3.11. The kit is tested on 3.14.
|
|
26
|
+
- **A dialog may ask to install the Command Line Tools.** On a Mac without
|
|
27
|
+
them, the first `uv tool install` opens it. Click Install and let it
|
|
28
|
+
finish: the plugin lines need the git it brings.
|
|
29
|
+
- **Phone calls are installed, not yet walked.** Whether the phone bot
|
|
30
|
+
signs its mail to your Claude follows your mail carrier: [bot mail
|
|
31
|
+
signing][phone-signing].
|
|
32
|
+
|
|
33
|
+
- **The mail hooks start off.** They tell an agent about new mail between
|
|
34
|
+
its tool calls. To turn them on, set `"hooks": true` in
|
|
35
|
+
`~/.sessionkit/mail/settings/mail.json`. On a fresh install the file is
|
|
36
|
+
not there yet, so make it:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
mkdir -p ~/.sessionkit/mail/settings
|
|
40
|
+
echo '{"version": 1, "hooks": true}' > ~/.sessionkit/mail/settings/mail.json
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
If the file is there, change only that key, because it can hold other
|
|
44
|
+
settings:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
perl -pi -e 's/"hooks": *false/"hooks": true/' ~/.sessionkit/mail/settings/mail.json
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Step by step, with every step that needs you marked: [the manual
|
|
51
|
+
install][install].
|
|
52
|
+
|
|
53
|
+
## Quick start
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
sessionkit --version
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
It lists the kit and each family package, with its version:
|
|
60
|
+
|
|
61
|
+
```text
|
|
62
|
+
sessionkit, version 0.2.1
|
|
63
|
+
sessionmail 0.2.1
|
|
64
|
+
sessiontools 0.2.0
|
|
65
|
+
sessionphone 0.2.1
|
|
66
|
+
sessionview 0.2.0
|
|
67
|
+
sessionvoice 0.2.1
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Start Claude with `session start claude`, never a bare `claude`: mail to
|
|
71
|
+
your agent's box, your phone's included, reaches only a session started as
|
|
72
|
+
that agent. `sessionkit setup` makes the box and the agent first: [first
|
|
73
|
+
checks][checks].
|
|
74
|
+
|
|
75
|
+
Then, from your agent inside a Claude Code session:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
session list # your live sessions
|
|
79
|
+
message send <box> -s "PR ready" -f note.md # mail another agent
|
|
80
|
+
mouth say --device default "the build is green" # speak one line
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
- **Mail works inside a session.** `message` and `pager` read who is asking
|
|
84
|
+
from the session. A plain terminal has no session, so they refuse with
|
|
85
|
+
`no seat in the environment ... set an identity` ([first checks][checks]).
|
|
86
|
+
A send never makes a box: the address must have one already ([mail
|
|
87
|
+
between agents][mail]).
|
|
88
|
+
- **The mouth needs a device.** `--device default` is your Mac's default
|
|
89
|
+
output; `mouth devices` lists the others.
|
|
90
|
+
- **To hear you**, the ears need a speech-to-text server: [hearing and
|
|
91
|
+
speaking][hearing].
|
|
92
|
+
|
|
93
|
+
## Uninstall
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
claude plugin marketplace remove sessionkit
|
|
97
|
+
uv tool uninstall sessionkit
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Removing the marketplace removes its six plugins too. The services and your
|
|
101
|
+
data have their own steps: [uninstall][uninstall].
|
|
102
|
+
|
|
103
|
+
## Usage
|
|
104
|
+
|
|
105
|
+
Every command answers `--help`, and each has a page: [components][components].
|
|
106
|
+
|
|
107
|
+
## The family
|
|
108
|
+
|
|
109
|
+
Session Kit installs these together:
|
|
110
|
+
|
|
111
|
+
- [sessionmail][pkg-mail]: mail for coding agents. `message`, `pager` and
|
|
112
|
+
`memory` over one maildir.
|
|
113
|
+
- [sessiontools][pkg-tools]: sessions, status and idle. `session`.
|
|
114
|
+
- [sessionvoice][pkg-voice]: ambient voice. `ears` hears; `mouth` speaks.
|
|
115
|
+
- [sessionphone][pkg-phone]: phone calls to a session over Delta Chat.
|
|
116
|
+
Installed, not yet walked.
|
|
117
|
+
- [sessionview][pkg-view]: a tmux sidebar of your projects and your agents.
|
|
118
|
+
|
|
119
|
+
## Components
|
|
120
|
+
|
|
121
|
+
The commands, services and folders the install adds, and where each
|
|
122
|
+
package logs: [components][components].
|
|
123
|
+
|
|
124
|
+
## Configuration
|
|
125
|
+
|
|
126
|
+
Each package configures itself: [configuration][configuration]. How the
|
|
127
|
+
settings files stack: [the settings guide][settings].
|
|
128
|
+
|
|
129
|
+
## Logs
|
|
130
|
+
|
|
131
|
+
Each package keeps its own log: [logs][logs]. Which one to read for a
|
|
132
|
+
symptom: [troubleshooting][troubleshooting].
|
|
133
|
+
|
|
134
|
+
## Development
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
git clone https://github.com/sessionkit/sessionkit
|
|
138
|
+
cd sessionkit
|
|
139
|
+
make install # editable: uv tool install -e .
|
|
140
|
+
make check # ruff, shellcheck, then the tests
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
The rules for changing the kit: [AGENTS.md][agents].
|
|
144
|
+
|
|
145
|
+
## Status
|
|
146
|
+
|
|
147
|
+
Alpha, 0.2.1. Tested on macOS 27 on Apple silicon; Linux and Windows are
|
|
148
|
+
untested. What you may still hit: [known gaps][gaps].
|
|
149
|
+
|
|
150
|
+
Licence: [AGPL-3.0][licence].
|
|
151
|
+
|
|
152
|
+
[claude-code]: https://claude.ai/code
|
|
153
|
+
[install]: INSTALL.md
|
|
154
|
+
[checks]: INSTALL.md#4-first-checks
|
|
155
|
+
[hearing]: INSTALL.md#5-hearing-and-speaking-it-hears-me-and-speaks
|
|
156
|
+
[mail]: INSTALL.md#mail-between-agents
|
|
157
|
+
[uninstall]: INSTALL.md#8-uninstall
|
|
158
|
+
[gaps]: INSTALL.md#7-known-gaps-you-will-hit
|
|
159
|
+
[components]: docs/components.md
|
|
160
|
+
[logs]: docs/components.md#logs
|
|
161
|
+
[configuration]: docs/configuration.md
|
|
162
|
+
[settings]: docs/settings.md
|
|
163
|
+
[troubleshooting]: docs/troubleshooting.md
|
|
164
|
+
[agents]: AGENTS.md
|
|
165
|
+
[licence]: LICENSE
|
|
166
|
+
[pkg-mail]: https://github.com/sessionkit/sessionmail
|
|
167
|
+
[pkg-tools]: https://github.com/sessionkit/sessiontools
|
|
168
|
+
[pkg-voice]: https://github.com/sessionkit/sessionvoice
|
|
169
|
+
[pkg-phone]: https://github.com/sessionkit/sessionphone
|
|
170
|
+
[phone-signing]: https://github.com/sessionkit/sessionphone/blob/HEAD/docs/bot-mail-signing.md
|
|
171
|
+
[pkg-view]: https://github.com/sessionkit/sessionview
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "sessionkit"
|
|
3
|
+
version = "0.2.1"
|
|
4
|
+
description = "Session Kit gives your Claude Code sessions voice, mail, a phone line and a tmux sidebar, in one install."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = "AGPL-3.0-or-later"
|
|
7
|
+
license-files = ["LICENSE"]
|
|
8
|
+
classifiers = ["Development Status :: 3 - Alpha"]
|
|
9
|
+
requires-python = ">=3.11,<3.15"
|
|
10
|
+
dependencies = [
|
|
11
|
+
"click>=8.1",
|
|
12
|
+
"sessionmail>=0.2.1",
|
|
13
|
+
"sessiontools>=0.2.0",
|
|
14
|
+
"sessionphone[media]>=0.2.1",
|
|
15
|
+
"sessionview>=0.2.0",
|
|
16
|
+
"sessionvoice[capture]>=0.2.1",
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
[[project.authors]]
|
|
20
|
+
name = "Mike Bailey"
|
|
21
|
+
email = "mike@sessionkit.ai"
|
|
22
|
+
|
|
23
|
+
[project.urls]
|
|
24
|
+
Homepage = "https://github.com/sessionkit/sessionkit"
|
|
25
|
+
Repository = "https://github.com/sessionkit/sessionkit"
|
|
26
|
+
|
|
27
|
+
[project.scripts]
|
|
28
|
+
sessionkit = "sessionkit.cli:main"
|
|
29
|
+
message = "sessionmail.message:cli"
|
|
30
|
+
pager = "sessionmail.pager:cli"
|
|
31
|
+
memory = "sessionmail.memory:cli"
|
|
32
|
+
session = "sessiontools.cli:main"
|
|
33
|
+
sessionphone = "sessionphone.cli:main"
|
|
34
|
+
sessionview = "sessionview:main"
|
|
35
|
+
sessionview-config = "sessionview:main"
|
|
36
|
+
sessionview-session = "sessionview:main"
|
|
37
|
+
sessionview-messages = "sessionview:main"
|
|
38
|
+
ears = "ears.cli:main"
|
|
39
|
+
ears-serve = "ears.serve:main"
|
|
40
|
+
mouth = "mouth.__main__:main"
|
|
41
|
+
talk = "talk.cli:main"
|
|
42
|
+
|
|
43
|
+
[build-system]
|
|
44
|
+
requires = ["uv_build>=0.11.9,<0.12.0"]
|
|
45
|
+
build-backend = "uv_build"
|
|
46
|
+
|
|
47
|
+
[dependency-groups]
|
|
48
|
+
dev = [
|
|
49
|
+
"pytest>=8",
|
|
50
|
+
"ruff>=0.6",
|
|
51
|
+
]
|
|
52
|
+
|
|
53
|
+
[tool.pytest.ini_options]
|
|
54
|
+
testpaths = ["tests"]
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "sessionkit"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.1"
|
|
4
4
|
description = "Session Kit gives your Claude Code sessions voice, mail, a phone line and a tmux sidebar, in one install."
|
|
5
|
+
readme = "README.md"
|
|
5
6
|
license = "AGPL-3.0-or-later"
|
|
6
7
|
license-files = ["LICENSE"]
|
|
7
8
|
classifiers = ["Development Status :: 3 - Alpha"]
|
|
@@ -9,16 +10,24 @@ authors = [
|
|
|
9
10
|
{ name = "Mike Bailey", email = "mike@sessionkit.ai" }
|
|
10
11
|
]
|
|
11
12
|
requires-python = ">=3.11,<3.15"
|
|
12
|
-
# The family, one install. Floored at 0.
|
|
13
|
-
# PyPI's 0.0.1 name placeholders
|
|
13
|
+
# The family, one install. Floored at 0.2.0: the kit needs every family
|
|
14
|
+
# package at 0.2.0 or later, and PyPI's 0.0.1 name placeholders and the
|
|
15
|
+
# 0.1.0 alphas must never satisfy these. sessionmail, sessionphone and
|
|
16
|
+
# sessionvoice are floored at 0.2.1: mail's 0.2.1 judges the doorbell by the
|
|
17
|
+
# drop, phone's 0.2.1 sends unsigned under the drop carrier, and voice's 0.2.1
|
|
18
|
+
# installer sets the drop carrier.
|
|
19
|
+
# sessionphone with its media extra: the bot, calls and `sessionphone web` need it.
|
|
14
20
|
# sessionvoice with its capture extra, so the ears can hear the room.
|
|
21
|
+
# This is the RULED list, kept in tests/_ruled_deps.py with the ruling;
|
|
22
|
+
# tests/test_dependency_gate.py fails when these lines, the built wheel or
|
|
23
|
+
# uv.lock drift from it.
|
|
15
24
|
dependencies = [
|
|
16
25
|
"click>=8.1",
|
|
17
|
-
"sessionmail>=0.1
|
|
18
|
-
"sessiontools>=0.
|
|
19
|
-
"sessionphone>=0.1
|
|
20
|
-
"sessionview>=0.
|
|
21
|
-
"sessionvoice[capture]>=0.1
|
|
26
|
+
"sessionmail>=0.2.1",
|
|
27
|
+
"sessiontools>=0.2.0",
|
|
28
|
+
"sessionphone[media]>=0.2.1",
|
|
29
|
+
"sessionview>=0.2.0",
|
|
30
|
+
"sessionvoice[capture]>=0.2.1",
|
|
22
31
|
]
|
|
23
32
|
|
|
24
33
|
[project.urls]
|
|
@@ -90,13 +90,22 @@ def _shape(check):
|
|
|
90
90
|
help="Your own full mail address (name@host), written as the operator "
|
|
91
91
|
"instead of a mail box.",
|
|
92
92
|
)
|
|
93
|
+
@click.option(
|
|
94
|
+
"--agent",
|
|
95
|
+
metavar="NAME",
|
|
96
|
+
default=operator_setup.DEFAULT_AGENT,
|
|
97
|
+
show_default=True,
|
|
98
|
+
callback=_shape(operator_setup.check_agent_shape),
|
|
99
|
+
help="Your assistant's name: setup writes its agent file and makes its "
|
|
100
|
+
"mail box, so `session start NAME` starts Claude as NAME, hearing mail to NAME.",
|
|
101
|
+
)
|
|
93
102
|
@click.option(
|
|
94
103
|
"--dry-run",
|
|
95
104
|
is_flag=True,
|
|
96
105
|
help="Run every check and print what would be written; write nothing.",
|
|
97
106
|
)
|
|
98
|
-
def setup(mailbox: str | None, address: str | None, dry_run: bool) -> None:
|
|
99
|
-
"""Set up mail on this Mac,
|
|
107
|
+
def setup(mailbox: str | None, address: str | None, agent: str, dry_run: bool) -> None:
|
|
108
|
+
"""Set up mail on this Mac, write you as the operator, and define your assistant.
|
|
100
109
|
|
|
101
110
|
Mail first: it runs sessionmail's mail setup (`message setup`), which
|
|
102
111
|
makes the mail root and your box, and prints what it did and what is
|
|
@@ -109,13 +118,24 @@ def setup(mailbox: str | None, address: str | None, dry_run: bool) -> None:
|
|
|
109
118
|
confirm who you are: then it says how to name yourself, with --address
|
|
110
119
|
or --mailbox. It writes nothing, too, while the operator file an earlier
|
|
111
120
|
release wrote is still in place and none is at the new path: it names
|
|
112
|
-
both.
|
|
113
|
-
|
|
114
|
-
|
|
121
|
+
both.
|
|
122
|
+
|
|
123
|
+
Then your assistant (--agent, default claude): it writes NAME.md where
|
|
124
|
+
`session start` looks for agents (~/.session/agents, unless
|
|
125
|
+
$AGENTS_DIR or ~/.session/agent/config says otherwise), not keyed, and
|
|
126
|
+
asks `message setup` for the box NAME. A file already there, in either
|
|
127
|
+
layout, is left as it is. It ends with the one launch step:
|
|
128
|
+
`session start NAME`.
|
|
129
|
+
|
|
130
|
+
Exit 0 all done, 1 refused or failed, 2 usage, 4 done what it could,
|
|
131
|
+
mail steps left for you. See INSTALL.md, "Operator and administrators"
|
|
132
|
+
and "Mail between agents".
|
|
115
133
|
"""
|
|
116
134
|
if mailbox is not None and address is not None:
|
|
117
135
|
raise click.UsageError("give --mailbox or --address, not both")
|
|
118
|
-
outcome = operator_setup.run_setup(
|
|
136
|
+
outcome = operator_setup.run_setup(
|
|
137
|
+
mailbox=mailbox, address=address, agent=agent, dry_run=dry_run
|
|
138
|
+
)
|
|
119
139
|
for line in outcome.lines:
|
|
120
140
|
click.echo(line, err=outcome.code != 0)
|
|
121
141
|
raise SystemExit(outcome.code)
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
"""`sessionkit setup`: make this host's mail ready,
|
|
1
|
+
"""`sessionkit setup`: make this host's mail ready, write the operator, then
|
|
2
|
+
define the assistant (`run_agent`: the file `session start NAME` resolves, and
|
|
3
|
+
the box it hears, so a seat started that way hears mail to NAME).
|
|
2
4
|
|
|
3
5
|
Mail first. sessionmail's mail setup verb (argv in `MAIL_SETUP_ARGV`) makes
|
|
4
6
|
the mail root and the first boxes, the operator's box among them, and the
|
|
@@ -132,6 +134,21 @@ def check_mailbox_shape(name: str) -> str:
|
|
|
132
134
|
return name
|
|
133
135
|
|
|
134
136
|
|
|
137
|
+
def check_agent_shape(name: str) -> str:
|
|
138
|
+
"""NAME names the agent's mail box, so it is a mail box name. It names
|
|
139
|
+
its file too (`<agents folder>/NAME.md`), so it may not start with `.`:
|
|
140
|
+
`session start --list-agents` hides a dot-file, and a picker never offers it."""
|
|
141
|
+
try:
|
|
142
|
+
check_mailbox_shape(name)
|
|
143
|
+
except ShapeError:
|
|
144
|
+
raise ShapeError(
|
|
145
|
+
f"{name!r} is not an agent name: one plain name, no '/', no '@', no spaces"
|
|
146
|
+
) from None
|
|
147
|
+
if name.startswith("."):
|
|
148
|
+
raise ShapeError(f"{name!r} is not an agent name: it may not start with '.'")
|
|
149
|
+
return name
|
|
150
|
+
|
|
151
|
+
|
|
135
152
|
def check_address_shape(address: str) -> str:
|
|
136
153
|
"""ADDRESS is `local@domain`: exactly one `@`, both sides non-empty, no
|
|
137
154
|
whitespace, and not an example domain (a pasted placeholder)."""
|
|
@@ -303,8 +320,14 @@ def drop_in_text(block: Mapping[str, str]) -> str:
|
|
|
303
320
|
def write_exclusive(target: Path, text: str) -> str | None:
|
|
304
321
|
"""Create TARGET holding TEXT, only if it does not exist. None on
|
|
305
322
|
success, else why not. Never a partial target and never a stray
|
|
306
|
-
`*.json`: the temp's name ends in `.tmp-<pid>`, and it is removed.
|
|
307
|
-
|
|
323
|
+
`*.json`: the temp's name ends in `.tmp-<pid>`, and it is removed. A
|
|
324
|
+
folder that cannot be made (a file in the way, no permission) is a
|
|
325
|
+
reason too, never a traceback: the caller's other halves have run, and
|
|
326
|
+
their lines must still print."""
|
|
327
|
+
try:
|
|
328
|
+
target.parent.mkdir(parents=True, exist_ok=True)
|
|
329
|
+
except OSError as e:
|
|
330
|
+
return f"could not make {target.parent} ({e.strerror or e.__class__.__name__})"
|
|
308
331
|
temp = target.with_name(f".{target.name}.tmp-{os.getpid()}")
|
|
309
332
|
try:
|
|
310
333
|
# A temp by this pid's name is a dead run's leftover (no live process
|
|
@@ -344,17 +367,22 @@ MAIL_LEFT = 4
|
|
|
344
367
|
MAIL_DOC = 'INSTALL.md, "Mail between agents"'
|
|
345
368
|
|
|
346
369
|
|
|
347
|
-
def mail_setup_boxes(
|
|
370
|
+
def mail_setup_boxes(
|
|
371
|
+
mailbox: str | None, address: str | None, agent: str | None
|
|
372
|
+
) -> tuple[str, ...]:
|
|
348
373
|
"""The boxes the installer asks the verb for, as `--box NAME` each.
|
|
349
374
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
375
|
+
`--mailbox NAME` passes NAME, so a named box on a fresh install is made
|
|
376
|
+
before the operator check looks for it. Then the agent's box (`--agent`,
|
|
377
|
+
default `claude`): a seat started with `session start NAME` hears the
|
|
378
|
+
box NAME, so the box must exist for mail to it to land. The verb's own
|
|
379
|
+
floor (the operator's box, `operator.mailbox`, else the installing
|
|
380
|
+
account's name) is made besides. A session's own box is made by its
|
|
381
|
+
pager, never here. The verb is idempotent: a box that exists stays.
|
|
355
382
|
"""
|
|
356
383
|
del address # an address needs no box
|
|
357
|
-
|
|
384
|
+
boxes = [name for name in (mailbox, agent) if name is not None]
|
|
385
|
+
return tuple(dict.fromkeys(boxes))
|
|
358
386
|
|
|
359
387
|
|
|
360
388
|
def mail_setup_argv(boxes: Sequence[str], dry_run: bool) -> list[str]:
|
|
@@ -371,15 +399,18 @@ def _verb() -> str:
|
|
|
371
399
|
return " ".join(MAIL_SETUP_ARGV)
|
|
372
400
|
|
|
373
401
|
|
|
374
|
-
def _no_verb(why: str) -> Outcome:
|
|
375
|
-
"""This sessionmail cannot run the verb: print the exact step instead
|
|
402
|
+
def _no_verb(why: str, boxes: Sequence[str] = ()) -> Outcome:
|
|
403
|
+
"""This sessionmail cannot run the verb: print the exact step instead,
|
|
404
|
+
with the `--box NAME` setup would have passed (the agent's among them),
|
|
405
|
+
so running it later makes the same boxes a run of the verb would."""
|
|
406
|
+
step = " ".join(mail_setup_argv(boxes, dry_run=False))
|
|
376
407
|
return Outcome(
|
|
377
408
|
EXIT_STEPS_LEFT,
|
|
378
409
|
[
|
|
379
410
|
f"Mail not set up: {why}.",
|
|
380
411
|
(
|
|
381
412
|
f"A newer sessionmail has it. Until you have that, set mail up by "
|
|
382
|
-
f"hand ({MAIL_DOC}); once you do, run: {
|
|
413
|
+
f"hand ({MAIL_DOC}); once you do, run: {step}"
|
|
383
414
|
),
|
|
384
415
|
],
|
|
385
416
|
)
|
|
@@ -410,14 +441,15 @@ def run_mail_setup(
|
|
|
410
441
|
"""
|
|
411
442
|
program = MAIL_SETUP_ARGV[0]
|
|
412
443
|
if _find(program, environ) is None:
|
|
413
|
-
return _no_verb(f"`{program}` is not on the PATH")
|
|
444
|
+
return _no_verb(f"`{program}` is not on the PATH", boxes)
|
|
414
445
|
probe = _run([*MAIL_SETUP_ARGV, "--help"], environ)
|
|
415
446
|
if probe is None:
|
|
416
|
-
return _no_verb(f"`{_verb()} --help` did not answer")
|
|
447
|
+
return _no_verb(f"`{_verb()} --help` did not answer", boxes)
|
|
417
448
|
if probe.returncode != 0:
|
|
418
449
|
return _no_verb(
|
|
419
450
|
f"this sessionmail has no `{_verb()}` "
|
|
420
|
-
f"(`{_verb()} --help` exited {probe.returncode})"
|
|
451
|
+
f"(`{_verb()} --help` exited {probe.returncode})",
|
|
452
|
+
boxes,
|
|
421
453
|
)
|
|
422
454
|
|
|
423
455
|
argv = mail_setup_argv(boxes, dry_run)
|
|
@@ -463,6 +495,147 @@ def run_mail_setup(
|
|
|
463
495
|
)
|
|
464
496
|
|
|
465
497
|
|
|
498
|
+
# --- the agent: the file `session start NAME` resolves -------------------------
|
|
499
|
+
|
|
500
|
+
# The assistant setup defines when no `--agent` is given.
|
|
501
|
+
DEFAULT_AGENT = "claude"
|
|
502
|
+
|
|
503
|
+
# Where `session start NAME` looks for agents: sessiontools'
|
|
504
|
+
# `start/agents.py:agents_root`, the same rule, mirrored here so the kit does
|
|
505
|
+
# not import sessiontools' internals. `$AGENTS_DIR`, else
|
|
506
|
+
# `~/.session/agent/config` if that is a directory, else `~/.session/agents`.
|
|
507
|
+
# tests/test_setup.py holds this rule to the installed sessiontools'.
|
|
508
|
+
AGENTS_DIR_ENV = "AGENTS_DIR"
|
|
509
|
+
SESSION_HOME = ".session"
|
|
510
|
+
AGENT_CONFIG_DIR = ("agent", "config")
|
|
511
|
+
AGENTS_DIR_NAME = "agents"
|
|
512
|
+
|
|
513
|
+
# The reader: every agent `session start` would accept, one name a line.
|
|
514
|
+
LIST_AGENTS_ARGV: tuple[str, ...] = ("session", "start", "--list-agents")
|
|
515
|
+
|
|
516
|
+
# The one launch step, in sessionphone's final words: the same line its Delta
|
|
517
|
+
# bot prints. Spelled once: change it here only.
|
|
518
|
+
LAUNCH_SENTENCE = "To hear Delta messages, start Claude with: session start {agent}"
|
|
519
|
+
|
|
520
|
+
# The agent file. No `start:` keys: the seat is NOT keyed. No front matter at
|
|
521
|
+
# all: Claude Code (2.1.304, measured) names an agent by its file, so `name:`
|
|
522
|
+
# and `description:` are not needed. For any NAME but `claude` this text is
|
|
523
|
+
# the agent's prompt; for `claude`, Claude Code's own built-in agent of that
|
|
524
|
+
# name answers, and the file only has to exist for `session start` to find it.
|
|
525
|
+
AGENT_TEXT = """\
|
|
526
|
+
You are {agent}, a Claude Code assistant.
|
|
527
|
+
|
|
528
|
+
Written by `sessionkit setup`. `session start {agent}` starts Claude as
|
|
529
|
+
{agent}, so the session hears mail to the box {agent}. Edit this file as you
|
|
530
|
+
like: setup never writes over it.
|
|
531
|
+
"""
|
|
532
|
+
|
|
533
|
+
|
|
534
|
+
def agents_root(home: Path, environ: Mapping[str, str]) -> Path:
|
|
535
|
+
"""The agents folder `session start` reads, by sessiontools' rule."""
|
|
536
|
+
named = environ.get(AGENTS_DIR_ENV)
|
|
537
|
+
if named:
|
|
538
|
+
return Path(os.path.expanduser(named))
|
|
539
|
+
config = home / SESSION_HOME / Path(*AGENT_CONFIG_DIR)
|
|
540
|
+
if config.is_dir():
|
|
541
|
+
return config
|
|
542
|
+
return home / SESSION_HOME / AGENTS_DIR_NAME
|
|
543
|
+
|
|
544
|
+
|
|
545
|
+
def agent_file(home: Path, environ: Mapping[str, str], agent: str) -> Path:
|
|
546
|
+
"""The file setup writes for AGENT: the flat layout, `<root>/NAME.md`."""
|
|
547
|
+
return agents_root(home, environ) / f"{agent}.md"
|
|
548
|
+
|
|
549
|
+
|
|
550
|
+
def agent_text(agent: str) -> str:
|
|
551
|
+
return AGENT_TEXT.format(agent=agent)
|
|
552
|
+
|
|
553
|
+
|
|
554
|
+
def launch_sentence(agent: str) -> str:
|
|
555
|
+
return LAUNCH_SENTENCE.format(agent=agent)
|
|
556
|
+
|
|
557
|
+
|
|
558
|
+
def read_listed_agents(environ: Mapping[str, str]) -> list[str] | None:
|
|
559
|
+
"""`session start --list-agents`, one name a line, or None when it does
|
|
560
|
+
not answer (absent, non-zero, a timeout): "not verified", never "absent"."""
|
|
561
|
+
done = _run(LIST_AGENTS_ARGV, environ)
|
|
562
|
+
if done is None or done.returncode != 0:
|
|
563
|
+
return None
|
|
564
|
+
return [line.strip() for line in done.stdout.splitlines() if line.strip()]
|
|
565
|
+
|
|
566
|
+
|
|
567
|
+
def run_agent(
|
|
568
|
+
*,
|
|
569
|
+
agent: str,
|
|
570
|
+
dry_run: bool = False,
|
|
571
|
+
home: Path | None = None,
|
|
572
|
+
environ: Mapping[str, str] | None = None,
|
|
573
|
+
) -> Outcome:
|
|
574
|
+
"""Write AGENT's file where `session start` looks, once; then verify.
|
|
575
|
+
|
|
576
|
+
Never clobbers: either layout `session start` reads (`<root>/NAME/NAME.md`,
|
|
577
|
+
which wins, or `<root>/NAME.md`) already there means write nothing. A
|
|
578
|
+
folder `<root>/NAME/` without its NAME.md, or a flat path that is not a
|
|
579
|
+
file, is refused: `session start` could not start NAME from it, and
|
|
580
|
+
writing beside it would not change that. AGENT is already shape-checked.
|
|
581
|
+
"""
|
|
582
|
+
environ = os.environ if environ is None else environ
|
|
583
|
+
home = Path.home() if home is None else home
|
|
584
|
+
root = agents_root(home, environ)
|
|
585
|
+
folder = root / agent
|
|
586
|
+
flat = agent_file(home, environ, agent)
|
|
587
|
+
if folder.is_dir():
|
|
588
|
+
inside = folder / f"{agent}.md"
|
|
589
|
+
if inside.is_file():
|
|
590
|
+
return Outcome(
|
|
591
|
+
EXIT_OK, [f"Agent {agent!r} is already in {inside}; left as it is."]
|
|
592
|
+
)
|
|
593
|
+
return Outcome(
|
|
594
|
+
EXIT_REFUSED,
|
|
595
|
+
[
|
|
596
|
+
(
|
|
597
|
+
f"No agent file written: {folder} exists without {agent}.md "
|
|
598
|
+
f"in it, so session start cannot start {agent!r}."
|
|
599
|
+
),
|
|
600
|
+
f"Put {agent}.md in that folder, or remove it, then run sessionkit setup again.",
|
|
601
|
+
],
|
|
602
|
+
)
|
|
603
|
+
if flat.is_file():
|
|
604
|
+
return Outcome(
|
|
605
|
+
EXIT_OK, [f"Agent {agent!r} is already in {flat}; left as it is."]
|
|
606
|
+
)
|
|
607
|
+
if os.path.lexists(flat):
|
|
608
|
+
return Outcome(
|
|
609
|
+
EXIT_REFUSED,
|
|
610
|
+
[
|
|
611
|
+
f"No agent file written: {flat} exists and is not a file session start can read.",
|
|
612
|
+
"Remove it, then run sessionkit setup again.",
|
|
613
|
+
],
|
|
614
|
+
)
|
|
615
|
+
text = agent_text(agent)
|
|
616
|
+
if dry_run:
|
|
617
|
+
return Outcome(EXIT_OK, [f"Would write {flat}:", text.rstrip("\n")])
|
|
618
|
+
|
|
619
|
+
failed = write_exclusive(flat, text)
|
|
620
|
+
if failed is not None:
|
|
621
|
+
return Outcome(EXIT_REFUSED, [f"No agent file written: {failed}."])
|
|
622
|
+
lines = [f"Wrote {flat}: the agent {agent!r}, not keyed."]
|
|
623
|
+
|
|
624
|
+
listed = read_listed_agents(environ)
|
|
625
|
+
verb = " ".join(LIST_AGENTS_ARGV)
|
|
626
|
+
if listed is None:
|
|
627
|
+
lines.append(f"Written; not verified: `{verb}` did not answer.")
|
|
628
|
+
return Outcome(EXIT_OK, lines)
|
|
629
|
+
if agent not in listed:
|
|
630
|
+
lines.append(
|
|
631
|
+
f"WARNING: `{verb}` does not list {agent!r}: the `session` on your "
|
|
632
|
+
"PATH looks in another agents folder ($AGENTS_DIR, or another sessiontools)."
|
|
633
|
+
)
|
|
634
|
+
return Outcome(EXIT_REFUSED, lines)
|
|
635
|
+
lines.append(f"{verb}: lists {agent!r}.")
|
|
636
|
+
return Outcome(EXIT_OK, lines)
|
|
637
|
+
|
|
638
|
+
|
|
466
639
|
# --- the run ------------------------------------------------------------------
|
|
467
640
|
|
|
468
641
|
|
|
@@ -513,21 +686,23 @@ def run_setup(
|
|
|
513
686
|
*,
|
|
514
687
|
mailbox: str | None = None,
|
|
515
688
|
address: str | None = None,
|
|
689
|
+
agent: str = DEFAULT_AGENT,
|
|
516
690
|
dry_run: bool = False,
|
|
517
691
|
home: Path | None = None,
|
|
518
692
|
environ: Mapping[str, str] | None = None,
|
|
519
693
|
) -> Outcome:
|
|
520
|
-
"""The stranded check, then mail,
|
|
694
|
+
"""The stranded check, then mail, the operator and the agent; all print.
|
|
521
695
|
|
|
522
696
|
An operator file stranded at the 0.1.0 place, with none at the new one,
|
|
523
697
|
stops the whole run before mail: nothing is made or written, one line.
|
|
524
698
|
The verb would otherwise make the operator's box from settings that
|
|
525
|
-
cannot see that file. Past it,
|
|
699
|
+
cannot see that file. Past it, all three always run. Mail goes first
|
|
526
700
|
because the operator is written only for a box that exists, and the
|
|
527
|
-
mail setup verb is what makes the operator's box
|
|
528
|
-
of the
|
|
529
|
-
for you, else 0.
|
|
530
|
-
|
|
701
|
+
mail setup verb is what makes the operator's box and the agent's. The
|
|
702
|
+
exit is the worst of the three: 1 if any refused or failed, else 4 if
|
|
703
|
+
mail left steps for you, else 0. With the agent defined, the run ends on
|
|
704
|
+
the one launch step (LAUNCH_SENTENCE). MAILBOX / ADDRESS / AGENT are
|
|
705
|
+
already shape-checked by the caller.
|
|
531
706
|
"""
|
|
532
707
|
environ = os.environ if environ is None else environ
|
|
533
708
|
resolved = Path.home() if home is None else home
|
|
@@ -538,18 +713,29 @@ def run_setup(
|
|
|
538
713
|
# sessionmail's default carrier, and postfix is the user's opt-in.
|
|
539
714
|
# `--write-maps` is never passed: writing postfix maps is opt-in too.
|
|
540
715
|
mail = run_mail_setup(
|
|
541
|
-
boxes=mail_setup_boxes(mailbox, address
|
|
716
|
+
boxes=mail_setup_boxes(mailbox, address, agent),
|
|
717
|
+
dry_run=dry_run,
|
|
718
|
+
environ=environ,
|
|
542
719
|
)
|
|
543
720
|
operator = run_operator(
|
|
544
721
|
mailbox=mailbox, address=address, dry_run=dry_run, home=home, environ=environ
|
|
545
722
|
)
|
|
546
|
-
|
|
723
|
+
defined = run_agent(agent=agent, dry_run=dry_run, home=resolved, environ=environ)
|
|
724
|
+
codes = (mail.code, operator.code, defined.code)
|
|
725
|
+
if EXIT_REFUSED in codes:
|
|
547
726
|
code = EXIT_REFUSED
|
|
548
|
-
elif EXIT_STEPS_LEFT in
|
|
727
|
+
elif EXIT_STEPS_LEFT in codes:
|
|
549
728
|
code = EXIT_STEPS_LEFT
|
|
550
729
|
else:
|
|
551
730
|
code = EXIT_OK
|
|
552
|
-
|
|
731
|
+
lines = mail.lines + operator.lines + defined.lines
|
|
732
|
+
if dry_run:
|
|
733
|
+
lines.append(
|
|
734
|
+
f"Would ask `{_verb()}` for the agent's box {agent!r} (--box {agent})."
|
|
735
|
+
)
|
|
736
|
+
if defined.code == EXIT_OK:
|
|
737
|
+
lines.append(launch_sentence(agent))
|
|
738
|
+
return Outcome(code, lines)
|
|
553
739
|
|
|
554
740
|
|
|
555
741
|
def run_operator(
|
sessionkit-0.2.0/PKG-INFO
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: sessionkit
|
|
3
|
-
Version: 0.2.0
|
|
4
|
-
Summary: Session Kit gives your Claude Code sessions voice, mail, a phone line and a tmux sidebar, in one install.
|
|
5
|
-
Author: Mike Bailey
|
|
6
|
-
Author-email: Mike Bailey <mike@sessionkit.ai>
|
|
7
|
-
License-Expression: AGPL-3.0-or-later
|
|
8
|
-
License-File: LICENSE
|
|
9
|
-
Classifier: Development Status :: 3 - Alpha
|
|
10
|
-
Requires-Dist: click>=8.1
|
|
11
|
-
Requires-Dist: sessionmail>=0.1.0
|
|
12
|
-
Requires-Dist: sessiontools>=0.1.0
|
|
13
|
-
Requires-Dist: sessionphone>=0.1.0
|
|
14
|
-
Requires-Dist: sessionview>=0.1.0
|
|
15
|
-
Requires-Dist: sessionvoice[capture]>=0.1.0
|
|
16
|
-
Requires-Python: >=3.11, <3.15
|
|
17
|
-
Project-URL: Homepage, https://github.com/sessionkit/sessionkit
|
|
18
|
-
Project-URL: Repository, https://github.com/sessionkit/sessionkit
|
|
File without changes
|
|
File without changes
|
|
File without changes
|