tommy-orchestrator 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.
- tommy_orchestrator-0.1.0/.gitignore +30 -0
- tommy_orchestrator-0.1.0/PKG-INFO +367 -0
- tommy_orchestrator-0.1.0/README.md +354 -0
- tommy_orchestrator-0.1.0/pyproject.toml +33 -0
- tommy_orchestrator-0.1.0/tommy/__init__.py +2 -0
- tommy_orchestrator-0.1.0/tommy/cli.py +513 -0
- tommy_orchestrator-0.1.0/tommy/config.py +137 -0
- tommy_orchestrator-0.1.0/tommy/control.py +296 -0
- tommy_orchestrator-0.1.0/tommy/discovery/__init__.py +1 -0
- tommy_orchestrator-0.1.0/tommy/discovery/harnesses.py +157 -0
- tommy_orchestrator-0.1.0/tommy/discovery/mcp.py +120 -0
- tommy_orchestrator-0.1.0/tommy/install.py +78 -0
- tommy_orchestrator-0.1.0/tommy/mcp_server.py +467 -0
- tommy_orchestrator-0.1.0/tommy/prompt.py +116 -0
- tommy_orchestrator-0.1.0/tommy/prompts/core.md +273 -0
- tommy_orchestrator-0.1.0/tommy/slash_commands/memnos-constraint.md +20 -0
- tommy_orchestrator-0.1.0/tommy/slash_commands/memnos-pin.md +15 -0
- tommy_orchestrator-0.1.0/tommy/slash_commands/memnos-recall.md +15 -0
- tommy_orchestrator-0.1.0/tommy/slash_commands/memnos-save.md +19 -0
- tommy_orchestrator-0.1.0/tommy/slash_commands/memnos.md +52 -0
- tommy_orchestrator-0.1.0/tommy/tommy.conf.default +31 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
__pycache__/
|
|
2
|
+
*.py[cod]
|
|
3
|
+
*.egg-info/
|
|
4
|
+
dist/
|
|
5
|
+
build/
|
|
6
|
+
.venv/
|
|
7
|
+
venv/
|
|
8
|
+
.env
|
|
9
|
+
.env.*
|
|
10
|
+
!.env.example
|
|
11
|
+
*.bak
|
|
12
|
+
*.prerestructure-bak
|
|
13
|
+
.memnos/
|
|
14
|
+
.claude/
|
|
15
|
+
~/.memnos
|
|
16
|
+
memnos.yaml
|
|
17
|
+
*.log
|
|
18
|
+
.DS_Store
|
|
19
|
+
.idea/
|
|
20
|
+
.vscode/
|
|
21
|
+
*.db
|
|
22
|
+
*.db-shm
|
|
23
|
+
*.db-wal
|
|
24
|
+
graphify-out/
|
|
25
|
+
neo4j_data/
|
|
26
|
+
qdrant_data/
|
|
27
|
+
evolution_data/
|
|
28
|
+
run_canary.sh
|
|
29
|
+
run_consolidate.sh
|
|
30
|
+
docs/assets/.cc-replay/
|
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: tommy-orchestrator
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Tommy — a personal coding orchestrator built on memnos
|
|
5
|
+
Requires-Python: >=3.10
|
|
6
|
+
Requires-Dist: click>=8.1
|
|
7
|
+
Requires-Dist: fastmcp>=2.0
|
|
8
|
+
Requires-Dist: httpx>=0.27
|
|
9
|
+
Requires-Dist: memnos-sdk>=0.1.21
|
|
10
|
+
Provides-Extra: dev
|
|
11
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
|
|
14
|
+
# Tommy — memnos-native coding orchestrator
|
|
15
|
+
|
|
16
|
+
Tommy is a lightweight CLI orchestrator that sits between your editor and your
|
|
17
|
+
coding harness (Claude Code, Codex, etc.). It enriches every task with
|
|
18
|
+
long-term memory from [memnos](../../README.md), routes work to the right
|
|
19
|
+
harness, and lets you steer a running sub-agent mid-run without waiting for it
|
|
20
|
+
to finish.
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
┌─────────────┐ tommy --mcp ┌──────────────────────────────────────────┐
|
|
24
|
+
│ Editor / │ ─────────────→ │ Tommy (stdio process) │
|
|
25
|
+
│ IDE │ ←────────────── │ 8 MCP tools · memnos · harness mgr │
|
|
26
|
+
└─────────────┘ JSON-RPC/stdio └──────────────────────┬───────────────────┘
|
|
27
|
+
│ Popen
|
|
28
|
+
TOMMY_CTRL_PORT
|
|
29
|
+
│
|
|
30
|
+
┌──────────────▼──────────────┐
|
|
31
|
+
│ Harness (Claude Code, etc.) │
|
|
32
|
+
│ progress / wrap_up / abort │
|
|
33
|
+
└─────────────────────────────┘
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
**Key design decisions:**
|
|
37
|
+
- Tommy is a **stdio subprocess**, not a daemon or HTTP server. The editor
|
|
38
|
+
spawns it (`tommy --mcp`) and owns its lifecycle.
|
|
39
|
+
- **memnos is the only persistent server.** Tommy talks to memnos for memory;
|
|
40
|
+
it exposes no long-lived listening ports.
|
|
41
|
+
- A **TCP loopback control channel** (`TOMMY_CTRL_PORT`) is opened
|
|
42
|
+
*transiently* — one ephemeral `127.0.0.1:0` socket per dispatch, closed
|
|
43
|
+
when the sub-agent exits. This lets Tommy send `wrap_up` / `abort` /
|
|
44
|
+
`pivot` to a running sub-agent and receive live progress without polling.
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Requirements
|
|
49
|
+
|
|
50
|
+
| Dependency | Version |
|
|
51
|
+
|-----------|---------|
|
|
52
|
+
| Python | ≥ 3.10 |
|
|
53
|
+
| [uv](https://docs.astral.sh/uv/) | any recent |
|
|
54
|
+
| memnos | installed and running (HTTP or stdio) |
|
|
55
|
+
| A supported harness | Claude Code (`claude`), Codex, etc. |
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Install
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
# From the memnos repo root (editable, uv-managed):
|
|
63
|
+
uv tool install -e ~/git/memnos/agents/tommy
|
|
64
|
+
|
|
65
|
+
# Verify:
|
|
66
|
+
tommy --version
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
> **Note:** always use `--force` when pyproject.toml dependencies change:
|
|
70
|
+
> ```bash
|
|
71
|
+
> uv tool install -e ~/git/memnos/agents/tommy --force
|
|
72
|
+
> ```
|
|
73
|
+
|
|
74
|
+
### First-time setup
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
tommy --install
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
This creates `~/.memnos/agents/tommy/tommy.conf` with sensible defaults.
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## Configuration
|
|
85
|
+
|
|
86
|
+
Edit `~/.memnos/agents/tommy/tommy.conf`:
|
|
87
|
+
|
|
88
|
+
```ini
|
|
89
|
+
# Who you are
|
|
90
|
+
TOMMY_USER=YourName
|
|
91
|
+
ORG=your-org
|
|
92
|
+
|
|
93
|
+
# memnos namespaces
|
|
94
|
+
TOMMY_NS=user:yourname:tommy # where Tommy journals its own sessions
|
|
95
|
+
DEFAULT_NS=org:your-org:engineering # default namespace for new memories
|
|
96
|
+
|
|
97
|
+
# Model & harness
|
|
98
|
+
DEFAULT_MODEL=claude-sonnet-4-5
|
|
99
|
+
HARNESS=claude # claude | codex | auto
|
|
100
|
+
SMART_ROUTING=on
|
|
101
|
+
|
|
102
|
+
# Projects — format: key:Name:JIRA_PROJECT:absolute/path/to/repo
|
|
103
|
+
# (one per line, comma-separated)
|
|
104
|
+
PROJECTS=\
|
|
105
|
+
myapp:MyApp:MYAPP:~/git/myapp,\
|
|
106
|
+
platform:Platform:PLAT:~/git/platform,\
|
|
107
|
+
infra:Infra:PLAT:~/git/infra
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Project fields
|
|
111
|
+
|
|
112
|
+
| Field | Meaning |
|
|
113
|
+
|-------|---------|
|
|
114
|
+
| `key` | Short identifier used in `tommy --project <key>` |
|
|
115
|
+
| `Name` | Human-readable label |
|
|
116
|
+
| `JIRA_PROJECT` | Jira project key (used in commit messages, ticket links) |
|
|
117
|
+
| `path` | Absolute path — the workspace Tommy gives to the harness |
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## Usage
|
|
122
|
+
|
|
123
|
+
### CLI
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
# Launch harness with memory context
|
|
127
|
+
tommy
|
|
128
|
+
|
|
129
|
+
# Activate a project (workspace + namespace auto-set)
|
|
130
|
+
tommy --project myapp
|
|
131
|
+
|
|
132
|
+
# List configured projects
|
|
133
|
+
tommy --list-projects
|
|
134
|
+
|
|
135
|
+
# List detected harnesses
|
|
136
|
+
tommy --list-harnesses
|
|
137
|
+
|
|
138
|
+
# Upgrade (respects uv/pipx/pip — never mixes installers)
|
|
139
|
+
tommy --upgrade
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### MCP stdio mode (for editors)
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
tommy --mcp
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
The editor spawns this process, sends JSON-RPC over stdin/stdout, and kills
|
|
149
|
+
the process when done. In MCP mode Tommy itself has no persistent listening
|
|
150
|
+
port — but each `tommy_dispatch` call opens a transient `127.0.0.1:0` TCP
|
|
151
|
+
control channel that is closed when the sub-agent exits.
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
## Editor integration
|
|
156
|
+
|
|
157
|
+
### Claude Desktop
|
|
158
|
+
|
|
159
|
+
Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
|
|
160
|
+
|
|
161
|
+
```json
|
|
162
|
+
{
|
|
163
|
+
"mcpServers": {
|
|
164
|
+
"tommy": {
|
|
165
|
+
"command": "tommy",
|
|
166
|
+
"args": ["--mcp"]
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Cursor
|
|
173
|
+
|
|
174
|
+
Add to `.cursor/mcp.json` in your project root (or the global
|
|
175
|
+
`~/.cursor/mcp.json`):
|
|
176
|
+
|
|
177
|
+
```json
|
|
178
|
+
{
|
|
179
|
+
"servers": {
|
|
180
|
+
"tommy": {
|
|
181
|
+
"command": "tommy",
|
|
182
|
+
"args": ["--mcp"]
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### VS Code + Continue
|
|
189
|
+
|
|
190
|
+
In `.continue/config.json`:
|
|
191
|
+
|
|
192
|
+
```json
|
|
193
|
+
{
|
|
194
|
+
"mcpServers": [
|
|
195
|
+
{
|
|
196
|
+
"name": "tommy",
|
|
197
|
+
"command": "tommy",
|
|
198
|
+
"args": ["--mcp"]
|
|
199
|
+
}
|
|
200
|
+
]
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### Zed
|
|
205
|
+
|
|
206
|
+
In `~/.config/zed/settings.json`:
|
|
207
|
+
|
|
208
|
+
```json
|
|
209
|
+
{
|
|
210
|
+
"context_servers": {
|
|
211
|
+
"tommy": {
|
|
212
|
+
"command": {
|
|
213
|
+
"path": "tommy",
|
|
214
|
+
"args": ["--mcp"]
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## MCP tools
|
|
224
|
+
|
|
225
|
+
| Tool | Description |
|
|
226
|
+
|------|-------------|
|
|
227
|
+
| `tommy_recall` | Query memnos memory for context |
|
|
228
|
+
| `tommy_remember` | Persist a fact / decision to memnos |
|
|
229
|
+
| `tommy_dispatch` | Launch a harness task (async by default) |
|
|
230
|
+
| `tommy_status` | Check a running task's output / exit code |
|
|
231
|
+
| `tommy_control` | Send `wrap_up` / `abort` / `pivot` / `answer` to a running task |
|
|
232
|
+
| `tommy_switch_project` | Set active project (workspace + namespace) |
|
|
233
|
+
| `tommy_route` | Dry-run: which harness would Tommy pick? |
|
|
234
|
+
| `tommy_list_harnesses` | Available harnesses + active routing config |
|
|
235
|
+
|
|
236
|
+
### `tommy_dispatch`
|
|
237
|
+
|
|
238
|
+
```
|
|
239
|
+
tommy_dispatch(
|
|
240
|
+
task="refactor the auth module to use PKCE",
|
|
241
|
+
harness="auto", # or "claude", "codex", …
|
|
242
|
+
workspace="/path/to/repo",
|
|
243
|
+
async_run=True, # return task_id immediately
|
|
244
|
+
inject_memory=True, # prepend memnos recall to prompt
|
|
245
|
+
)
|
|
246
|
+
→ {"task_id": "a3f1b2c4", "status": "running", "harness": "claude"}
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
### `tommy_control` — steer a running task
|
|
250
|
+
|
|
251
|
+
```
|
|
252
|
+
# Ask the harness to wrap up (gives it 60 s to finish gracefully)
|
|
253
|
+
tommy_control(task_id="a3f1b2c4", action="wrap_up", budget_seconds=60)
|
|
254
|
+
|
|
255
|
+
# Stop immediately
|
|
256
|
+
tommy_control(task_id="a3f1b2c4", action="abort")
|
|
257
|
+
|
|
258
|
+
# Redirect to a different goal mid-run
|
|
259
|
+
tommy_control(task_id="a3f1b2c4", action="pivot",
|
|
260
|
+
message="focus only on the login flow, skip registration")
|
|
261
|
+
|
|
262
|
+
# Answer a question the harness asked
|
|
263
|
+
tommy_control(task_id="a3f1b2c4", action="answer", message="yes, overwrite")
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
The harness receives the message over a **TCP loopback control channel**
|
|
267
|
+
(`TOMMY_CTRL_PORT` env var) — no polling required.
|
|
268
|
+
|
|
269
|
+
---
|
|
270
|
+
|
|
271
|
+
## Control channel (for harness authors)
|
|
272
|
+
|
|
273
|
+
If you write a custom harness in Python, connect back to Tommy using the
|
|
274
|
+
bundled `ControlClient`:
|
|
275
|
+
|
|
276
|
+
```python
|
|
277
|
+
from tommy.control import ControlClient
|
|
278
|
+
|
|
279
|
+
def handle_tommy_message(msg: dict) -> None:
|
|
280
|
+
if msg["type"] == "wrap_up":
|
|
281
|
+
# save state and exit within msg["budget_seconds"]
|
|
282
|
+
...
|
|
283
|
+
elif msg["type"] == "abort":
|
|
284
|
+
raise SystemExit(1)
|
|
285
|
+
elif msg["type"] == "pivot":
|
|
286
|
+
current_goal = msg["new_goal"]
|
|
287
|
+
elif msg["type"] == "answer":
|
|
288
|
+
# Reply to a question you sent via client.question()
|
|
289
|
+
user_answer = msg["text"]
|
|
290
|
+
|
|
291
|
+
client = ControlClient(on_control=handle_tommy_message)
|
|
292
|
+
|
|
293
|
+
# Report progress
|
|
294
|
+
client.progress(25, "parsed 250 / 1000 files")
|
|
295
|
+
client.checkpoint("analysis", "found 3 duplicate patterns")
|
|
296
|
+
|
|
297
|
+
# Send a question to Tommy/user; answer arrives via the on_control callback as {"type": "answer", "text": ...}
|
|
298
|
+
client.question("Should I overwrite existing tests?", options=["yes", "no"])
|
|
299
|
+
|
|
300
|
+
client.done("refactoring complete — 12 files changed")
|
|
301
|
+
client.close()
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
The client auto-reads `TOMMY_CTRL_PORT` from the environment.
|
|
305
|
+
|
|
306
|
+
**Protocol (newline-delimited JSON):**
|
|
307
|
+
|
|
308
|
+
| Direction | `type` | Extra fields |
|
|
309
|
+
|-----------|--------|-------------|
|
|
310
|
+
| Harness → Tommy | `progress` | `pct`, `detail` |
|
|
311
|
+
| Harness → Tommy | `checkpoint` | `phase`, `summary` |
|
|
312
|
+
| Harness → Tommy | `done` | `summary` |
|
|
313
|
+
| Harness → Tommy | `error` | `message` |
|
|
314
|
+
| Harness → Tommy | `question` | `text`, `options` |
|
|
315
|
+
| Tommy → Harness | `wrap_up` | `reason`, `budget_seconds` |
|
|
316
|
+
| Tommy → Harness | `abort` | — |
|
|
317
|
+
| Tommy → Harness | `pivot` | `new_goal` |
|
|
318
|
+
| Tommy → Harness | `answer` | `text` |
|
|
319
|
+
|
|
320
|
+
The control channel uses TCP loopback (`127.0.0.1`), which works on macOS,
|
|
321
|
+
Linux, and Windows without any extra setup.
|
|
322
|
+
|
|
323
|
+
---
|
|
324
|
+
|
|
325
|
+
## Upgrade
|
|
326
|
+
|
|
327
|
+
```bash
|
|
328
|
+
tommy --upgrade
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
Tommy detects whether it was installed with `uv`, `pipx`, or `pip` and uses
|
|
332
|
+
the same tool to upgrade — so the venv is never mixed.
|
|
333
|
+
|
|
334
|
+
To upgrade manually with uv:
|
|
335
|
+
|
|
336
|
+
```bash
|
|
337
|
+
uv tool install -e ~/git/memnos/agents/tommy --force
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
---
|
|
341
|
+
|
|
342
|
+
## Project structure
|
|
343
|
+
|
|
344
|
+
```
|
|
345
|
+
agents/tommy/
|
|
346
|
+
├── README.md ← you are here
|
|
347
|
+
├── pyproject.toml
|
|
348
|
+
└── tommy/
|
|
349
|
+
├── __init__.py
|
|
350
|
+
├── cli.py ← click entrypoint, _launch_harness
|
|
351
|
+
├── config.py ← TommyConfig, ProjectEntry
|
|
352
|
+
├── control.py ← ControlServer + ControlClient (TCP IPC)
|
|
353
|
+
├── install.py ← tommy --install
|
|
354
|
+
├── mcp_server.py ← FastMCP stdio server, 8 tools
|
|
355
|
+
├── prompt.py ← memnos-enriched system prompt builder
|
|
356
|
+
└── discovery/
|
|
357
|
+
└── harnesses.py ← auto-detect installed harnesses
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
---
|
|
361
|
+
|
|
362
|
+
## Roadmap
|
|
363
|
+
|
|
364
|
+
- [ ] Supervision loop: idle + wall-clock timeout with automatic `wrap_up`
|
|
365
|
+
- [ ] Smart harness routing by task type (coding vs. research vs. review)
|
|
366
|
+
- [ ] memnos lease heartbeat while harness is running
|
|
367
|
+
- [ ] Multi-harness fan-out (run two harnesses in parallel, merge outputs)
|