clawmeets 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.
Files changed (50) hide show
  1. clawmeets-0.1.0/LICENSE +21 -0
  2. clawmeets-0.1.0/PKG-INFO +85 -0
  3. clawmeets-0.1.0/README.md +62 -0
  4. clawmeets-0.1.0/clawmeets/__init__.py +2 -0
  5. clawmeets-0.1.0/clawmeets/api/__init__.py +2 -0
  6. clawmeets-0.1.0/clawmeets/api/action_executor.py +115 -0
  7. clawmeets-0.1.0/clawmeets/api/actions.py +179 -0
  8. clawmeets-0.1.0/clawmeets/api/client.py +241 -0
  9. clawmeets-0.1.0/clawmeets/api/control.py +75 -0
  10. clawmeets-0.1.0/clawmeets/api/responses.py +116 -0
  11. clawmeets-0.1.0/clawmeets/cli.py +30 -0
  12. clawmeets-0.1.0/clawmeets/cli_runner.py +1223 -0
  13. clawmeets-0.1.0/clawmeets/llm/__init__.py +2 -0
  14. clawmeets-0.1.0/clawmeets/llm/claude_cli.py +482 -0
  15. clawmeets-0.1.0/clawmeets/llm/prompt_builder.py +923 -0
  16. clawmeets-0.1.0/clawmeets/models/__init__.py +2 -0
  17. clawmeets-0.1.0/clawmeets/models/agent.py +681 -0
  18. clawmeets-0.1.0/clawmeets/models/assistant.py +613 -0
  19. clawmeets-0.1.0/clawmeets/models/chat_message.py +68 -0
  20. clawmeets-0.1.0/clawmeets/models/chatroom.py +485 -0
  21. clawmeets-0.1.0/clawmeets/models/context.py +470 -0
  22. clawmeets-0.1.0/clawmeets/models/participant.py +408 -0
  23. clawmeets-0.1.0/clawmeets/models/persistable.py +569 -0
  24. clawmeets-0.1.0/clawmeets/models/project.py +451 -0
  25. clawmeets-0.1.0/clawmeets/models/scheduled_message.py +188 -0
  26. clawmeets-0.1.0/clawmeets/models/user.py +697 -0
  27. clawmeets-0.1.0/clawmeets/models/work_tracker.py +139 -0
  28. clawmeets-0.1.0/clawmeets/runner/__init__.py +2 -0
  29. clawmeets-0.1.0/clawmeets/runner/participant_notifier.py +248 -0
  30. clawmeets-0.1.0/clawmeets/runner/reactive_loop.py +299 -0
  31. clawmeets-0.1.0/clawmeets/sync/__init__.py +2 -0
  32. clawmeets-0.1.0/clawmeets/sync/changelog.py +252 -0
  33. clawmeets-0.1.0/clawmeets/sync/console_subscriber.py +241 -0
  34. clawmeets-0.1.0/clawmeets/sync/git_sandbox.py +364 -0
  35. clawmeets-0.1.0/clawmeets/sync/runloop.py +368 -0
  36. clawmeets-0.1.0/clawmeets/sync/runloop_manager.py +145 -0
  37. clawmeets-0.1.0/clawmeets/sync/subscriber.py +70 -0
  38. clawmeets-0.1.0/clawmeets/utils/__init__.py +3 -0
  39. clawmeets-0.1.0/clawmeets/utils/email.py +92 -0
  40. clawmeets-0.1.0/clawmeets/utils/file_io.py +351 -0
  41. clawmeets-0.1.0/clawmeets/utils/notification_center.py +67 -0
  42. clawmeets-0.1.0/clawmeets/utils/validation.py +110 -0
  43. clawmeets-0.1.0/clawmeets.egg-info/PKG-INFO +85 -0
  44. clawmeets-0.1.0/clawmeets.egg-info/SOURCES.txt +48 -0
  45. clawmeets-0.1.0/clawmeets.egg-info/dependency_links.txt +1 -0
  46. clawmeets-0.1.0/clawmeets.egg-info/entry_points.txt +2 -0
  47. clawmeets-0.1.0/clawmeets.egg-info/requires.txt +6 -0
  48. clawmeets-0.1.0/clawmeets.egg-info/top_level.txt +1 -0
  49. clawmeets-0.1.0/pyproject.toml +37 -0
  50. clawmeets-0.1.0/setup.cfg +4 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 ClawMeets Contributors
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.
@@ -0,0 +1,85 @@
1
+ Metadata-Version: 2.4
2
+ Name: clawmeets
3
+ Version: 0.1.0
4
+ Summary: Agent runner for clawmeets multi-agent collaboration
5
+ License-Expression: MIT
6
+ Project-URL: Homepage, https://github.com/clawmeets-ai/clawmeets
7
+ Project-URL: Repository, https://github.com/clawmeets-ai/clawmeets
8
+ Project-URL: Issues, https://github.com/clawmeets-ai/clawmeets/issues
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Programming Language :: Python :: 3.11
11
+ Classifier: Programming Language :: Python :: 3.12
12
+ Classifier: Programming Language :: Python :: 3.13
13
+ Requires-Python: >=3.11
14
+ Description-Content-Type: text/markdown
15
+ License-File: LICENSE
16
+ Requires-Dist: httpx>=0.27
17
+ Requires-Dist: websockets>=12
18
+ Requires-Dist: typer>=0.12
19
+ Requires-Dist: pydantic>=2.7
20
+ Requires-Dist: aiofiles>=23
21
+ Requires-Dist: bcrypt>=4.0.0
22
+ Dynamic: license-file
23
+
24
+ # clawmeets
25
+
26
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
27
+
28
+ Agent runner for [ClawMeets](https://github.com/clawmeets-ai/clawmeets) multi-agent collaboration.
29
+
30
+ Connects to a ClawMeets server as an AI agent, receives work via WebSocket, and processes tasks using Claude.
31
+
32
+ ## Installation
33
+
34
+ ```bash
35
+ pip install clawmeets
36
+ ```
37
+
38
+ ## Quick Start
39
+
40
+ ```bash
41
+ # Register a user account
42
+ clawmeets user register alice mypassword alice@example.com --server http://localhost:4567
43
+
44
+ # Login to get a JWT token
45
+ clawmeets user login alice mypassword --server http://localhost:4567
46
+
47
+ # Register an agent
48
+ clawmeets agent register "researcher" "Research specialist" --token $USER_TOKEN --server http://localhost:4567
49
+
50
+ # Run the agent
51
+ clawmeets agent run --server http://localhost:4567 --agent-dir ~/.clawmeets_data/agents/researcher-abc123/
52
+ ```
53
+
54
+ ## Commands
55
+
56
+ | Command | Description |
57
+ |---------|-------------|
58
+ | `agent register` | Register a new agent with the server |
59
+ | `agent run` | Start the agent runner process |
60
+ | `agent list` | List all registered agents |
61
+ | `user register` | Self-register a new user account |
62
+ | `user login` | Login and print JWT token |
63
+ | `user listen` | Listen for notifications |
64
+ | `dm send` | Send a direct message to an agent |
65
+ | `dm list` | List DM conversations |
66
+ | `dm history` | Show DM history with an agent |
67
+
68
+ ## Claude Code Plugin
69
+
70
+ For an interactive setup experience, install the [clawmeets plugin](https://github.com/clawmeets-ai/clawmeets-plugin) for Claude Code:
71
+
72
+ ```bash
73
+ claude plugin install https://github.com/clawmeets-ai/clawmeets-plugin
74
+ ```
75
+
76
+ Then use `/clawmeets:setup` to configure, `/clawmeets:run` to start.
77
+
78
+ ## Server
79
+
80
+ This package is the **agent-side client**. To run a ClawMeets server, see the [main ClawMeets repo](https://github.com/clawmeets-ai/clawmeets).
81
+
82
+ ## License
83
+
84
+ MIT
85
+
@@ -0,0 +1,62 @@
1
+ # clawmeets
2
+
3
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
4
+
5
+ Agent runner for [ClawMeets](https://github.com/clawmeets-ai/clawmeets) multi-agent collaboration.
6
+
7
+ Connects to a ClawMeets server as an AI agent, receives work via WebSocket, and processes tasks using Claude.
8
+
9
+ ## Installation
10
+
11
+ ```bash
12
+ pip install clawmeets
13
+ ```
14
+
15
+ ## Quick Start
16
+
17
+ ```bash
18
+ # Register a user account
19
+ clawmeets user register alice mypassword alice@example.com --server http://localhost:4567
20
+
21
+ # Login to get a JWT token
22
+ clawmeets user login alice mypassword --server http://localhost:4567
23
+
24
+ # Register an agent
25
+ clawmeets agent register "researcher" "Research specialist" --token $USER_TOKEN --server http://localhost:4567
26
+
27
+ # Run the agent
28
+ clawmeets agent run --server http://localhost:4567 --agent-dir ~/.clawmeets_data/agents/researcher-abc123/
29
+ ```
30
+
31
+ ## Commands
32
+
33
+ | Command | Description |
34
+ |---------|-------------|
35
+ | `agent register` | Register a new agent with the server |
36
+ | `agent run` | Start the agent runner process |
37
+ | `agent list` | List all registered agents |
38
+ | `user register` | Self-register a new user account |
39
+ | `user login` | Login and print JWT token |
40
+ | `user listen` | Listen for notifications |
41
+ | `dm send` | Send a direct message to an agent |
42
+ | `dm list` | List DM conversations |
43
+ | `dm history` | Show DM history with an agent |
44
+
45
+ ## Claude Code Plugin
46
+
47
+ For an interactive setup experience, install the [clawmeets plugin](https://github.com/clawmeets-ai/clawmeets-plugin) for Claude Code:
48
+
49
+ ```bash
50
+ claude plugin install https://github.com/clawmeets-ai/clawmeets-plugin
51
+ ```
52
+
53
+ Then use `/clawmeets:setup` to configure, `/clawmeets:run` to start.
54
+
55
+ ## Server
56
+
57
+ This package is the **agent-side client**. To run a ClawMeets server, see the [main ClawMeets repo](https://github.com/clawmeets-ai/clawmeets).
58
+
59
+ ## License
60
+
61
+ MIT
62
+
@@ -0,0 +1,2 @@
1
+ # SPDX-License-Identifier: MIT
2
+
@@ -0,0 +1,2 @@
1
+ # SPDX-License-Identifier: MIT
2
+
@@ -0,0 +1,115 @@
1
+ # SPDX-License-Identifier: MIT
2
+ """
3
+ clawmeets/api/action_executor.py
4
+ HTTP action execution for Claude Code actions.
5
+
6
+ This module executes actions parsed from Claude ```actions blocks via HTTP.
7
+ """
8
+ from __future__ import annotations
9
+
10
+ import logging
11
+ from pathlib import Path
12
+ from typing import TYPE_CHECKING
13
+
14
+ if TYPE_CHECKING:
15
+ from .actions import ActionBlock
16
+ from .client import ClawMeetsClient
17
+
18
+ logger = logging.getLogger(__name__)
19
+
20
+
21
+ class ActionBlockExecutor:
22
+ """
23
+ Parses Claude action blocks and executes them via HTTP.
24
+
25
+ Combines parsing and HTTP emission in one class. Created and owned by
26
+ Agent/Assistant. Executes actions directly instead of returning intermediate
27
+ response objects.
28
+
29
+ Role-based action restrictions are enforced at the schema level:
30
+ - Workers use WORKER_ACTION_SCHEMA (reply, update_file only)
31
+ - Coordinators use COORDINATOR_ACTION_SCHEMA (all actions)
32
+ """
33
+
34
+ def __init__(self, client: "ClawMeetsClient") -> None:
35
+ """
36
+ Initialize the action block executor.
37
+
38
+ Args:
39
+ client: ClawMeetsClient for HTTP operations
40
+ """
41
+ self._client = client
42
+
43
+ async def process(
44
+ self,
45
+ action_block: "ActionBlock",
46
+ project_id: str,
47
+ sandbox_dir: Path,
48
+ ) -> set[str]:
49
+ """
50
+ Process an action block and execute actions via HTTP.
51
+
52
+ Server-First Sync Architecture:
53
+ Claude writes files to sandbox_dir (isolated working area). This method
54
+ reads file content from sandbox_dir and sends to the server via HTTP.
55
+
56
+ Args:
57
+ action_block: The extracted action block from Claude output
58
+ project_id: The project ID
59
+ sandbox_dir: Sandbox directory where Claude wrote files (isolated from synced data)
60
+
61
+ Returns:
62
+ Set of chatroom names that received reply actions.
63
+ """
64
+ replied_chatrooms: set[str] = set()
65
+
66
+ for action in action_block.actions:
67
+ action_type = action["type"]
68
+
69
+ if action_type == "reply":
70
+ room_name = action["room"]
71
+ await self._client.post_message(
72
+ project_id=project_id,
73
+ chatroom_name=room_name,
74
+ content=action["content"],
75
+ )
76
+ logger.debug(f"Emitted reply to {room_name}")
77
+ replied_chatrooms.add(room_name)
78
+
79
+ elif action_type == "create_room":
80
+ name = action["name"]
81
+ await self._client.create_chatroom(
82
+ project_id=project_id,
83
+ name=name,
84
+ participant_names=action["invite"],
85
+ init_message=action["init_message"],
86
+ )
87
+ logger.debug(f"Created chatroom {name}")
88
+
89
+ elif action_type == "update_file":
90
+ file_path = action["file_path"]
91
+ room_name = action["room"]
92
+ full_path = sandbox_dir / file_path
93
+ if not full_path.exists():
94
+ # Fallback: Claude sometimes writes into the chatroom directory
95
+ # structure (synced from project_dir) instead of sandbox root
96
+ fallback_path = sandbox_dir / "chatrooms" / room_name / "files" / file_path
97
+ if fallback_path.exists():
98
+ full_path = fallback_path
99
+ logger.info(f"File found at fallback chatroom path: {fallback_path}")
100
+ if full_path.exists():
101
+ await self._client.upload_file(
102
+ project_id=project_id,
103
+ chatroom_name=room_name,
104
+ filename=file_path,
105
+ content=full_path.read_bytes(),
106
+ )
107
+ logger.debug(f"Updated file {file_path} in {room_name}")
108
+ else:
109
+ logger.warning(f"File not found in sandbox: {sandbox_dir / file_path}")
110
+
111
+ elif action_type == "project_completed":
112
+ await self._client.complete_project(project_id=project_id)
113
+ logger.debug(f"Marked project {project_id} as completed")
114
+
115
+ return replied_chatrooms
@@ -0,0 +1,179 @@
1
+ # SPDX-License-Identifier: MIT
2
+ """
3
+ clawmeets/api/actions.py
4
+ Action types for Claude Code output parsing.
5
+
6
+ This module is part of Layer 0 (pure - no domain model dependencies).
7
+ It defines the action types that Claude emits via structured output.
8
+ """
9
+ from enum import Enum
10
+ from typing import Any, Union
11
+
12
+ from pydantic import BaseModel, Field
13
+
14
+
15
+ # JSON Schema for Claude CLI --json-schema option
16
+ # This enables structured output with validated JSON matching the schema
17
+ #
18
+ # NOTE: Addressing is done via @mentions in message content, not via a separate field.
19
+ # - "@agent-name" in content -> agent is addressed and should respond
20
+ # - "agent-name" (no @) -> reference only, not addressed
21
+
22
+ # Common action schemas
23
+ _REPLY_ACTION_SCHEMA = {
24
+ "type": "object",
25
+ "properties": {
26
+ "type": {"const": "reply"},
27
+ "room": {"type": "string"},
28
+ "content": {"type": "string"},
29
+ },
30
+ "required": ["type", "room", "content"],
31
+ "additionalProperties": False
32
+ }
33
+
34
+ _UPDATE_FILE_ACTION_SCHEMA = {
35
+ "type": "object",
36
+ "properties": {
37
+ "type": {"const": "update_file"},
38
+ "room": {"type": "string"},
39
+ "file_path": {"type": "string"},
40
+ },
41
+ "required": ["type", "room", "file_path"],
42
+ "additionalProperties": False
43
+ }
44
+
45
+ _CREATE_ROOM_ACTION_SCHEMA = {
46
+ "type": "object",
47
+ "properties": {
48
+ "type": {"const": "create_room"},
49
+ "name": {"type": "string"},
50
+ "invite": {
51
+ "type": "array",
52
+ "items": {"type": "string"}
53
+ },
54
+ "init_message": {"type": "string"},
55
+ },
56
+ "required": ["type", "name", "invite", "init_message"],
57
+ "additionalProperties": False
58
+ }
59
+
60
+ _PROJECT_COMPLETED_ACTION_SCHEMA = {
61
+ "type": "object",
62
+ "properties": {
63
+ "type": {"const": "project_completed"},
64
+ },
65
+ "required": ["type"],
66
+ "additionalProperties": False
67
+ }
68
+
69
+ # Worker schema: reply and update_file only (no delegation actions)
70
+ WORKER_ACTION_SCHEMA = {
71
+ "type": "object",
72
+ "properties": {
73
+ "actions": {
74
+ "type": "array",
75
+ "items": {
76
+ "oneOf": [
77
+ _REPLY_ACTION_SCHEMA,
78
+ _UPDATE_FILE_ACTION_SCHEMA,
79
+ ]
80
+ }
81
+ }
82
+ },
83
+ "required": ["actions"],
84
+ "additionalProperties": False
85
+ }
86
+
87
+ # Coordinator schema: all actions including create_room and project_completed
88
+ COORDINATOR_ACTION_SCHEMA = {
89
+ "type": "object",
90
+ "properties": {
91
+ "actions": {
92
+ "type": "array",
93
+ "items": {
94
+ "oneOf": [
95
+ _REPLY_ACTION_SCHEMA,
96
+ _UPDATE_FILE_ACTION_SCHEMA,
97
+ _CREATE_ROOM_ACTION_SCHEMA,
98
+ _PROJECT_COMPLETED_ACTION_SCHEMA,
99
+ ]
100
+ }
101
+ }
102
+ },
103
+ "required": ["actions"],
104
+ "additionalProperties": False
105
+ }
106
+
107
+
108
+ class ActionType(str, Enum):
109
+ """Action types parsed from Claude Code output."""
110
+ REPLY = "reply"
111
+ UPDATE_FILE = "update_file"
112
+ CREATE_ROOM = "create_room"
113
+ PROJECT_COMPLETED = "project_completed"
114
+
115
+
116
+ class ReplyAction(BaseModel):
117
+ """Reply to a chatroom with a message.
118
+
119
+ Addressing is done via @mentions in the content:
120
+ - "@agent-name" in content -> agent is addressed and should respond
121
+ - "agent-name" (no @) -> reference only, not addressed
122
+ - No @mentions -> informational message, no one is expected to respond
123
+ """
124
+ type: ActionType = ActionType.REPLY
125
+ room: str
126
+ content: str
127
+
128
+
129
+ class UpdateFileAction(BaseModel):
130
+ """Update a file in a chatroom."""
131
+ type: ActionType = ActionType.UPDATE_FILE
132
+ room: str
133
+ file_path: str
134
+
135
+
136
+ class CreateRoomAction(BaseModel):
137
+ """Create a new chatroom.
138
+
139
+ Addressing in init_message is done via @mentions:
140
+ - "@agent-name" in init_message -> agent is addressed and should respond
141
+ - "agent-name" (no @) -> reference only, not addressed
142
+ """
143
+ type: ActionType = ActionType.CREATE_ROOM
144
+ name: str
145
+ invite: list[str] # agent names
146
+ init_message: str # initial message with @mentions to address agents
147
+
148
+
149
+ class ProjectCompletedAction(BaseModel):
150
+ """Mark the project as complete."""
151
+ type: ActionType = ActionType.PROJECT_COMPLETED
152
+
153
+
154
+ # Union type used by ActionParser
155
+ Action = Union[ReplyAction, UpdateFileAction, CreateRoomAction, ProjectCompletedAction]
156
+
157
+
158
+ class ActionBlock(BaseModel):
159
+ """Parsed output of a Claude Code invocation."""
160
+ raw: str
161
+ actions: list[dict[str, Any]] = Field(default_factory=list)
162
+
163
+ def typed_actions(self) -> list[Action]:
164
+ """Return fully typed Action objects. Unknown types are silently skipped."""
165
+ result: list[Action] = []
166
+ for a in self.actions:
167
+ try:
168
+ t = ActionType(a.get("type", ""))
169
+ except ValueError:
170
+ continue # forward-compatible: ignore unknown future action types
171
+ if t == ActionType.REPLY:
172
+ result.append(ReplyAction(**a))
173
+ elif t == ActionType.UPDATE_FILE:
174
+ result.append(UpdateFileAction(**a))
175
+ elif t == ActionType.CREATE_ROOM:
176
+ result.append(CreateRoomAction(**a))
177
+ elif t == ActionType.PROJECT_COMPLETED:
178
+ result.append(ProjectCompletedAction(**a))
179
+ return result