meshcode 2.1.0__tar.gz → 2.1.2__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 (30) hide show
  1. {meshcode-2.1.0 → meshcode-2.1.2}/PKG-INFO +16 -21
  2. {meshcode-2.1.0 → meshcode-2.1.2}/README.md +15 -20
  3. {meshcode-2.1.0 → meshcode-2.1.2}/meshcode/__init__.py +1 -1
  4. {meshcode-2.1.0 → meshcode-2.1.2}/meshcode/comms_v4.py +24 -16
  5. {meshcode-2.1.0 → meshcode-2.1.2}/meshcode/meshcode_mcp/server.py +35 -133
  6. meshcode-2.1.2/meshcode/meshcode_mcp/test_server_wrapper.py +117 -0
  7. {meshcode-2.1.0 → meshcode-2.1.2}/meshcode.egg-info/PKG-INFO +16 -21
  8. {meshcode-2.1.0 → meshcode-2.1.2}/meshcode.egg-info/SOURCES.txt +2 -1
  9. {meshcode-2.1.0 → meshcode-2.1.2}/pyproject.toml +1 -1
  10. {meshcode-2.1.0 → meshcode-2.1.2}/meshcode/cli.py +0 -0
  11. {meshcode-2.1.0 → meshcode-2.1.2}/meshcode/invites.py +0 -0
  12. {meshcode-2.1.0 → meshcode-2.1.2}/meshcode/launcher.py +0 -0
  13. {meshcode-2.1.0 → meshcode-2.1.2}/meshcode/launcher_install.py +0 -0
  14. {meshcode-2.1.0 → meshcode-2.1.2}/meshcode/meshcode_mcp/__init__.py +0 -0
  15. {meshcode-2.1.0 → meshcode-2.1.2}/meshcode/meshcode_mcp/__main__.py +0 -0
  16. {meshcode-2.1.0 → meshcode-2.1.2}/meshcode/meshcode_mcp/backend.py +0 -0
  17. {meshcode-2.1.0 → meshcode-2.1.2}/meshcode/meshcode_mcp/realtime.py +0 -0
  18. {meshcode-2.1.0 → meshcode-2.1.2}/meshcode/meshcode_mcp/test_backend.py +0 -0
  19. {meshcode-2.1.0 → meshcode-2.1.2}/meshcode/meshcode_mcp/test_realtime.py +0 -0
  20. {meshcode-2.1.0 → meshcode-2.1.2}/meshcode/preferences.py +0 -0
  21. {meshcode-2.1.0 → meshcode-2.1.2}/meshcode/protocol_v2.py +0 -0
  22. {meshcode-2.1.0 → meshcode-2.1.2}/meshcode/run_agent.py +0 -0
  23. {meshcode-2.1.0 → meshcode-2.1.2}/meshcode/secrets.py +0 -0
  24. {meshcode-2.1.0 → meshcode-2.1.2}/meshcode/self_update.py +0 -0
  25. {meshcode-2.1.0 → meshcode-2.1.2}/meshcode/setup_clients.py +0 -0
  26. {meshcode-2.1.0 → meshcode-2.1.2}/meshcode.egg-info/dependency_links.txt +0 -0
  27. {meshcode-2.1.0 → meshcode-2.1.2}/meshcode.egg-info/entry_points.txt +0 -0
  28. {meshcode-2.1.0 → meshcode-2.1.2}/meshcode.egg-info/requires.txt +0 -0
  29. {meshcode-2.1.0 → meshcode-2.1.2}/meshcode.egg-info/top_level.txt +0 -0
  30. {meshcode-2.1.0 → meshcode-2.1.2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: meshcode
3
- Version: 2.1.0
3
+ Version: 2.1.2
4
4
  Summary: Real-time communication between AI agents — Supabase-backed CLI
5
5
  Author-email: MeshCode <hello@meshcode.io>
6
6
  License: MIT
@@ -43,23 +43,18 @@ Requires Python 3.9+. Works on macOS, Linux, Windows.
43
43
 
44
44
  ---
45
45
 
46
- ## Quickstart (the 5-command happy path)
46
+ ## Quickstart (3 commands)
47
47
 
48
48
  ```bash
49
- # 1. Sign up at https://meshcode.io and copy the mc_xxx key shown once on onboarding
50
- # 2. Store it in your OS keychain (once, forever)
49
+ # 1. Sign up at https://meshcode.io and copy your API key
50
+ # 2. Login (stores key in OS keychain, once forever)
51
51
  meshcode login mc_xxxxxxxxxxxxxxxxxxxxxxxx
52
52
 
53
- # 3. Create a workspace for your first agent
54
- meshcode setup my-project commander
55
-
56
- # 4. Launch the agent (this opens your editor with the MCP server wired up)
57
- meshcode run commander
58
-
59
- # 5. Watch it come online in the dashboard at https://meshcode.io/dashboard
53
+ # 3. Connect your first agent (auto-setups workspace + launches editor)
54
+ meshcode go commander
60
55
  ```
61
56
 
62
- That's it. Tell the agent what to do inside the editor it will coordinate with any other MeshCode agents it can see on the mesh.
57
+ That's it. `meshcode go` handles everything: auth check, workspace creation, editor launch. Watch it come online at https://meshcode.io/dashboard.
63
58
 
64
59
  ---
65
60
 
@@ -90,14 +85,10 @@ Each agent is a *real* Claude/Cursor/Cline window. MeshCode gives them shared me
90
85
  ```bash
91
86
  meshcode login mc_xxx # once, forever
92
87
 
93
- meshcode setup my-project commander # ~/meshcode/my-project-commander/
94
- meshcode setup my-project backend
95
- meshcode setup my-project frontend
96
-
97
88
  # in three separate terminals:
98
- meshcode run commander
99
- meshcode run backend
100
- meshcode run frontend
89
+ meshcode go commander
90
+ meshcode go backend
91
+ meshcode go frontend
101
92
  ```
102
93
 
103
94
  Inside any window, tell the agent to coordinate with the others. They self-orchestrate through the mesh loop (`meshcode_wait` blocks until a peer sends a message, `meshcode_done` reports progress).
@@ -141,6 +132,7 @@ meshcode revoke-member my-project <user> # kick a member instantly
141
132
  | Command | Example | Description |
142
133
  |---|---|---|
143
134
  | `meshcode login <key>` | `meshcode login mc_abc123` | Store your API key in the OS keychain (once). |
135
+ | `meshcode go <agent>` | `meshcode go backend` | **Recommended.** One command: auth + setup + launch. |
144
136
  | `meshcode setup <project> <agent>` | `meshcode setup my-project backend` | Create a workspace and wire up the MCP config for your editor. |
145
137
  | `meshcode run <agent>` | `meshcode run backend` | Launch the editor with this agent's MCP server attached. |
146
138
  | `meshcode invite <project> <agent>` | `meshcode invite my-project designer --role "UI" --days 7` | Generate a join URL to share. |
@@ -223,11 +215,14 @@ The dashboard shows each agent's memories in the Memory tab, where users can vie
223
215
 
224
216
  The web dashboard at [meshcode.io/dashboard](https://meshcode.io/dashboard) shows:
225
217
 
226
- - **Agents panel**: real-time status (online/offline/working), heartbeat, role, and reconnect button
218
+ - **Agents panel**: real-time status (online/waiting/working/idle/offline), auto-detected via CPU monitoring — no manual status updates needed
227
219
  - **Chat**: message history between agents with threading and broadcast highlighting
220
+ - **Mesh Graph**: live force-directed visualization of your agent team — nodes pulse by status, edges light up when messages flow. Hierarchy layout with commander at top.
221
+ - **Agent Replay**: rewind and replay any agent's session like a timeline. See every tool call, message, and status change. Powered by database-level triggers — works automatically for all agents.
228
222
  - **Tasks board**: shared task list with status, priority, assignee, and progress
229
223
  - **Memory**: per-agent persistent key-value store, editable by the user
230
- - **Health dot**: green/yellow/red indicator per meshwork based on agent availability
224
+ - **Live Spectator**: share a public link to let anyone watch your mesh team work in real-time. No login required.
225
+ - **Onboarding Wizard**: interactive 4-step setup checklist for new users with live detection of each step
231
226
 
232
227
  ---
233
228
 
@@ -18,23 +18,18 @@ Requires Python 3.9+. Works on macOS, Linux, Windows.
18
18
 
19
19
  ---
20
20
 
21
- ## Quickstart (the 5-command happy path)
21
+ ## Quickstart (3 commands)
22
22
 
23
23
  ```bash
24
- # 1. Sign up at https://meshcode.io and copy the mc_xxx key shown once on onboarding
25
- # 2. Store it in your OS keychain (once, forever)
24
+ # 1. Sign up at https://meshcode.io and copy your API key
25
+ # 2. Login (stores key in OS keychain, once forever)
26
26
  meshcode login mc_xxxxxxxxxxxxxxxxxxxxxxxx
27
27
 
28
- # 3. Create a workspace for your first agent
29
- meshcode setup my-project commander
30
-
31
- # 4. Launch the agent (this opens your editor with the MCP server wired up)
32
- meshcode run commander
33
-
34
- # 5. Watch it come online in the dashboard at https://meshcode.io/dashboard
28
+ # 3. Connect your first agent (auto-setups workspace + launches editor)
29
+ meshcode go commander
35
30
  ```
36
31
 
37
- That's it. Tell the agent what to do inside the editor it will coordinate with any other MeshCode agents it can see on the mesh.
32
+ That's it. `meshcode go` handles everything: auth check, workspace creation, editor launch. Watch it come online at https://meshcode.io/dashboard.
38
33
 
39
34
  ---
40
35
 
@@ -65,14 +60,10 @@ Each agent is a *real* Claude/Cursor/Cline window. MeshCode gives them shared me
65
60
  ```bash
66
61
  meshcode login mc_xxx # once, forever
67
62
 
68
- meshcode setup my-project commander # ~/meshcode/my-project-commander/
69
- meshcode setup my-project backend
70
- meshcode setup my-project frontend
71
-
72
63
  # in three separate terminals:
73
- meshcode run commander
74
- meshcode run backend
75
- meshcode run frontend
64
+ meshcode go commander
65
+ meshcode go backend
66
+ meshcode go frontend
76
67
  ```
77
68
 
78
69
  Inside any window, tell the agent to coordinate with the others. They self-orchestrate through the mesh loop (`meshcode_wait` blocks until a peer sends a message, `meshcode_done` reports progress).
@@ -116,6 +107,7 @@ meshcode revoke-member my-project <user> # kick a member instantly
116
107
  | Command | Example | Description |
117
108
  |---|---|---|
118
109
  | `meshcode login <key>` | `meshcode login mc_abc123` | Store your API key in the OS keychain (once). |
110
+ | `meshcode go <agent>` | `meshcode go backend` | **Recommended.** One command: auth + setup + launch. |
119
111
  | `meshcode setup <project> <agent>` | `meshcode setup my-project backend` | Create a workspace and wire up the MCP config for your editor. |
120
112
  | `meshcode run <agent>` | `meshcode run backend` | Launch the editor with this agent's MCP server attached. |
121
113
  | `meshcode invite <project> <agent>` | `meshcode invite my-project designer --role "UI" --days 7` | Generate a join URL to share. |
@@ -198,11 +190,14 @@ The dashboard shows each agent's memories in the Memory tab, where users can vie
198
190
 
199
191
  The web dashboard at [meshcode.io/dashboard](https://meshcode.io/dashboard) shows:
200
192
 
201
- - **Agents panel**: real-time status (online/offline/working), heartbeat, role, and reconnect button
193
+ - **Agents panel**: real-time status (online/waiting/working/idle/offline), auto-detected via CPU monitoring — no manual status updates needed
202
194
  - **Chat**: message history between agents with threading and broadcast highlighting
195
+ - **Mesh Graph**: live force-directed visualization of your agent team — nodes pulse by status, edges light up when messages flow. Hierarchy layout with commander at top.
196
+ - **Agent Replay**: rewind and replay any agent's session like a timeline. See every tool call, message, and status change. Powered by database-level triggers — works automatically for all agents.
203
197
  - **Tasks board**: shared task list with status, priority, assignee, and progress
204
198
  - **Memory**: per-agent persistent key-value store, editable by the user
205
- - **Health dot**: green/yellow/red indicator per meshwork based on agent availability
199
+ - **Live Spectator**: share a public link to let anyone watch your mesh team work in real-time. No login required.
200
+ - **Onboarding Wizard**: interactive 4-step setup checklist for new users with live detection of each step
206
201
 
207
202
  ---
208
203
 
@@ -1,2 +1,2 @@
1
1
  """MeshCode — Real-time communication between AI agents."""
2
- __version__ = "2.1.0"
2
+ __version__ = "2.1.2"
@@ -1612,44 +1612,52 @@ def login(api_key):
1612
1612
 
1613
1613
 
1614
1614
  def show_help():
1615
- print("""
1615
+ try:
1616
+ from meshcode import __version__ as _v
1617
+ except Exception:
1618
+ _v = "?"
1619
+ print(f"""
1616
1620
  ╔═══════════════════════════════════════════════════════════╗
1617
- ║ MeshCode v4Supabase-backed Agent Network
1621
+ ║ MeshCode v{_v}Persistent AI Agent Teams
1618
1622
  ╚═══════════════════════════════════════════════════════════╝
1619
1623
 
1620
- CORE:
1621
- register <proj> <name> [role] Join project
1624
+ QUICK START:
1625
+ login <api_key> Authenticate (get key at meshcode.io)
1626
+ go <agent> [--project <name>] Connect agent in one command
1627
+
1628
+ MESSAGING:
1622
1629
  send <proj> <from>:<to> <message> Send message
1623
- broadcast <proj> <from> <message> Send to all
1624
- read <proj> <name> Read pending
1625
- check Hook auto-check
1630
+ broadcast <proj> <from> <message> Send to all agents
1631
+ read <proj> <name> Read pending messages
1632
+ check Hook auto-check (non-destructive peek)
1626
1633
  watch <proj> <name> [interval] [timeout] Wait for messages
1627
1634
 
1628
1635
  STATUS:
1629
1636
  board <proj> Agent status board
1630
- update <proj> <name> <status> [task] Update status
1631
1637
  status [proj] Overview
1632
- projects List projects
1638
+ projects | list | ls List your meshworks
1633
1639
 
1634
1640
  HISTORY:
1635
1641
  history <proj> [count] [agent1,agent2] Conversation log
1636
1642
 
1637
- QUICK START:
1638
- go <agent> [--project <name>] One command to connect (recommended)
1639
- login <api_key> Authenticate with API key
1643
+ TEAM:
1644
+ invite <proj> <agent> [--role "..."] Invite teammate
1645
+ join <token> Accept invite
1646
+ members <proj> List team members
1640
1647
 
1641
1648
  SETUP (advanced):
1642
1649
  setup <proj> <name> [role] Create workspace (auto by 'go')
1643
1650
  run <agent> [--project <name>] Launch agent (auto by 'go')
1651
+ register <proj> <name> [role] Register agent manually
1644
1652
  setup <client> <proj> <name> [role] Legacy: global MCP config
1645
- Clients: claude-desktop
1646
1653
 
1647
- CLEANUP:
1654
+ ADMIN:
1648
1655
  clear <proj> <name> Clear inbox
1649
1656
  unregister <proj> <name> Leave project
1657
+ prefs View/set preferences
1650
1658
 
1651
- BACKEND: Supabase (meshcode schema)
1652
- PLATFORM: macOS, Windows, Linux
1659
+ FEATURES: Auto-status (5 states), Agent Replay, Live Spectator,
1660
+ Mesh Graph, Session Recording, Hot-reload
1653
1661
 
1654
1662
  Run `meshcode <command> --help` for help on a specific command.
1655
1663
  """)
@@ -422,12 +422,10 @@ def with_working_status(func):
422
422
  return func(*args, **kwargs)
423
423
  finally:
424
424
  if not skip:
425
- global _last_tool_at, _processing_timer
425
+ global _last_tool_at
426
426
  _last_tool_at = _time.time()
427
- _set_state("processing", "processing...")
428
- _processing_timer = _threading.Timer(_PROCESSING_COOLDOWN_S, _processing_to_online)
429
- _processing_timer.daemon = True
430
- _processing_timer.start()
427
+ # Don't flip to online here — CPU-based detection in heartbeat
428
+ # will handle the transition when LLM stops generating
431
429
  return swrapper
432
430
 
433
431
 
@@ -565,23 +563,21 @@ def _build_instructions() -> str:
565
563
  )
566
564
  base = f"""You are agent "{AGENT_NAME}" in meshwork "{PROJECT_NAME}".{role_block}{launch_block}
567
565
 
568
- BEHAVIOR LOOP (your default state never exit unless told):
569
- 1. Act on task/message 2. meshcode_send if needed 3. meshcode_wait()
570
- 4. On message back to 1. On timeout meshcode_wait(2x timeout, cap 1800s).
571
- Reset to 60s on any message. On got_done exit loop, summarize, stop.
572
- 5. Only break loop if: user says stop, fatal error, or "tell the human X".
566
+ PERMANENT LOOP (MANDATORY#1 RULE):
567
+ You MUST stay in meshcode_wait() forever. After EVERY action, call meshcode_wait().
568
+ NEVER exit. NEVER stop. NEVER say "standing by" without calling meshcode_wait().
569
+ The ONLY exits: user says "stop", got_done signal, or fatal error.
570
+ Timeout? Call meshcode_wait() again with 2x timeout (cap 1800s).
571
+
572
+ LOOP: act → meshcode_send if needed → meshcode_wait() → repeat.
573
573
 
574
574
  RULES:
575
- - NEVER use CLI commands (meshcode watch, meshcode read, meshcode send) in bash.
576
- Use ONLY the MCP tools: meshcode_check, meshcode_read, meshcode_send, meshcode_wait.
577
- CLI commands are for humans in terminal. You have MCP tools — use them.
578
- - Tasks > messages. Use meshcode_tasks/claim/complete for trackable work.
579
- - Messages <100 tokens, signal-only. Long content → create task instead.
580
- - No empty acks ("OK"/"Got it"). No prose padding. JSON reports only.
581
- - Poll meshcode_tasks() before each meshcode_wait().
582
- - Threading: pass in_reply_to with original msg id.
583
- - sensitive=True for credentials/secrets/PII in meshcode_send.
584
- - No feedback loops: stop if >10 messages on same topic.
575
+ - Use MCP tools only (never CLI commands in bash).
576
+ - Tasks > messages. Claim tasks via meshcode_tasks/task_claim/task_complete.
577
+ - Messages <100 tokens. Long content create task.
578
+ - No empty acks. JSON reports only.
579
+ - Threading: pass in_reply_to.
580
+ - sensitive=True for secrets/PII.
585
581
 
586
582
  SESSION START:
587
583
  1. meshcode_set_status(status="online", task="ready") — announce you're online
@@ -755,7 +751,9 @@ def _heartbeat_thread_fn():
755
751
  except Exception as e:
756
752
  log.warning(f"lease renewal failed: {e}")
757
753
 
758
- _heartbeat_stop.wait(30) # sleep but interruptible on shutdown
754
+ # Heartbeat must be well under the 180s decay threshold
755
+ hb_interval = 30
756
+ _heartbeat_stop.wait(hb_interval)
759
757
 
760
758
 
761
759
  @asynccontextmanager
@@ -833,14 +831,7 @@ except Exception:
833
831
  @with_working_status
834
832
  def meshcode_send(to: str, message: Any, in_reply_to: Optional[str] = None,
835
833
  sensitive: bool = False) -> Dict[str, Any]:
836
- """Send a message to an agent. Use "agent@meshwork" for cross-mesh.
837
-
838
- Args:
839
- to: Recipient agent name (or agent@meshwork for cross-mesh).
840
- message: String or dict payload.
841
- in_reply_to: Original message id for threading.
842
- sensitive: True to hide from public exports.
843
- """
834
+ """Send message. Use "agent@meshwork" for cross-mesh. sensitive=True hides from exports."""
844
835
  if isinstance(message, str):
845
836
  payload: Dict[str, Any] = {"text": message}
846
837
  elif isinstance(message, dict):
@@ -884,16 +875,7 @@ def meshcode_broadcast(payload: Dict[str, Any]) -> Dict[str, Any]:
884
875
  @mcp.tool()
885
876
  @with_working_status
886
877
  def meshcode_read(include_acks: bool = False) -> Dict[str, Any]:
887
- """Read and mark-read all pending messages. ACKs senders.
888
-
889
- Returns a split shape: `messages` (real msgs), `acks`, and `done_signals`
890
- — so you don't have to filter by type yourself.
891
-
892
- Args:
893
- include_acks: If False (default), type='ack' rows are dropped entirely
894
- from the response. Acks are bookkeeping noise and most callers
895
- don't want them in their LLM context.
896
- """
878
+ """Read and consume pending messages. Returns {messages, acks, done_signals}."""
897
879
  raw = be.read_inbox(_PROJECT_ID, AGENT_NAME)
898
880
  normalized = [
899
881
  {
@@ -916,13 +898,7 @@ def meshcode_read(include_acks: bool = False) -> Dict[str, Any]:
916
898
  @mcp.tool()
917
899
  @with_working_status
918
900
  def meshcode_history(limit: int = 20, agent_filter: Optional[str] = None) -> Dict[str, Any]:
919
- """View recent message history (both read and unread). Use when you need
920
- context from past conversations or lost messages after context compression.
921
-
922
- Args:
923
- limit: Max messages to return (default 20).
924
- agent_filter: Optional agent name to filter (shows messages to/from that agent).
925
- """
901
+ """View past messages (read+unread). Optional agent_filter."""
926
902
  raw = be.get_history(_PROJECT_ID, limit=limit, agent_filter=agent_filter or "")
927
903
  messages = [
928
904
  {
@@ -942,12 +918,7 @@ def meshcode_history(limit: int = 20, agent_filter: Optional[str] = None) -> Dic
942
918
  @mcp.tool()
943
919
  @with_working_status
944
920
  def meshcode_read_message(msg_id: str) -> Dict[str, Any]:
945
- """Fetch a specific message by ID. Use when you need to re-read a message
946
- after context was compressed or session restarted.
947
-
948
- Args:
949
- msg_id: The UUID of the message to fetch.
950
- """
921
+ """Fetch a specific message by its UUID."""
951
922
  msg = be.get_message_by_id(_PROJECT_ID, msg_id)
952
923
  if not msg:
953
924
  return {"error": "message not found", "msg_id": msg_id}
@@ -1043,20 +1014,7 @@ async def _meshcode_wait_inner(actual_timeout: int, include_acks: bool) -> Dict[
1043
1014
  # Realtime unavailable — plain sleep fallback so we still honor timeout.
1044
1015
  await asyncio.sleep(actual_timeout)
1045
1016
 
1046
- # On timeout, check for unclaimed tasks assigned to this agent
1047
- timeout_result: Dict[str, Any] = {"timed_out": True}
1048
- try:
1049
- api_key = _get_api_key()
1050
- open_tasks = be.task_list(api_key, _PROJECT_ID, AGENT_NAME, status_filter="open")
1051
- if isinstance(open_tasks, dict) and open_tasks.get("ok"):
1052
- my_tasks = [t for t in open_tasks.get("tasks", [])
1053
- if t.get("assignee") == AGENT_NAME and not t.get("claimed_by")]
1054
- if my_tasks:
1055
- timeout_result["unclaimed_tasks"] = len(my_tasks)
1056
- timeout_result["hint"] = f"You have {len(my_tasks)} unclaimed task(s) assigned to you. Run meshcode_tasks(status_filter='open') to see them."
1057
- except Exception:
1058
- pass
1059
- return timeout_result
1017
+ return {"timed_out": True}
1060
1018
 
1061
1019
 
1062
1020
  @mcp.tool()
@@ -1085,12 +1043,7 @@ def meshcode_done(reason: str) -> Dict[str, Any]:
1085
1043
  @mcp.tool()
1086
1044
  @with_working_status
1087
1045
  def meshcode_check(include_acks: bool = False) -> Dict[str, Any]:
1088
- """Non-blocking: returns pending message count + any new messages.
1089
-
1090
- Checks realtime buffer first, then falls back to DB if buffer is empty
1091
- but there are pending messages (handles messages that arrived before
1092
- the realtime listener connected).
1093
- """
1046
+ """Peek at inbox (non-destructive). Returns pending count + new messages."""
1094
1047
  pending = be.count_pending(_PROJECT_ID, AGENT_NAME)
1095
1048
  # Peek at realtime buffer WITHOUT draining — check is non-destructive
1096
1049
  realtime_buffered = _REALTIME.peek() if _REALTIME else []
@@ -1151,9 +1104,7 @@ def meshcode_status() -> Dict[str, Any]:
1151
1104
  @mcp.tool()
1152
1105
  @with_working_status
1153
1106
  def meshcode_register(role: str = "") -> Dict[str, Any]:
1154
- """Re-register this agent. Use if disconnected or
1155
- want to update your role description.
1156
- """
1107
+ """Re-register agent (update role)."""
1157
1108
  return be.register_agent(PROJECT_NAME, AGENT_NAME, role or AGENT_ROLE)
1158
1109
 
1159
1110
 
@@ -1171,10 +1122,7 @@ def meshcode_set_status(status: str, task: str = "") -> Dict[str, Any]:
1171
1122
  @mcp.tool()
1172
1123
  @with_working_status
1173
1124
  def meshcode_init(project: str, agent: str, role: str = "") -> Dict[str, Any]:
1174
- """Re-initialize MCP session for different project/agent. Use ONLY
1175
- if you need to dynamically switch context — normally the env vars set at
1176
- server start are correct.
1177
- """
1125
+ """Switch project/agent context. Rarely needed."""
1178
1126
  global PROJECT_NAME, AGENT_NAME, AGENT_ROLE, _PROJECT_ID
1179
1127
  PROJECT_NAME = project
1180
1128
  AGENT_NAME = agent
@@ -1191,18 +1139,7 @@ def meshcode_init(project: str, agent: str, role: str = "") -> Dict[str, Any]:
1191
1139
  @with_working_status
1192
1140
  def meshcode_task_create(title: str, description: str = "", assignee: str = "*",
1193
1141
  priority: str = "normal", parent_task_id: Optional[str] = None) -> Dict[str, Any]:
1194
- """Create a task in the shared backlog. Usually called by
1195
- the commander) want to assign work that needs tracking.
1196
-
1197
- Args:
1198
- title: Short title of the task.
1199
- description: Longer description / acceptance criteria.
1200
- assignee: Agent name to assign to, or "*" for any agent (anyone can claim).
1201
- priority: 'low' / 'normal' / 'high' / 'urgent'.
1202
- parent_task_id: Optional parent task id for nesting subtasks.
1203
-
1204
- Returns: {"ok": true, "task_id": "...", "title": "..."}
1205
- """
1142
+ """Create task. assignee="*" for any, priority: low/normal/high/urgent."""
1206
1143
  api_key = _get_api_key()
1207
1144
  result = be.task_create(api_key, _PROJECT_ID, AGENT_NAME, title,
1208
1145
  description=description, assignee=assignee,
@@ -1239,13 +1176,7 @@ def meshcode_tasks(status_filter: Optional[str] = None) -> Dict[str, Any]:
1239
1176
  @mcp.tool()
1240
1177
  @with_working_status
1241
1178
  def meshcode_task_claim(task_id: str) -> Dict[str, Any]:
1242
- """Atomically claim an open task. Fails if already claimed by someone
1243
- else (or assigned to a different agent), this returns an error and another
1244
- task should be picked.
1245
-
1246
- Args:
1247
- task_id: The uuid of the task you want to claim.
1248
- """
1179
+ """Claim an open task. Fails if already claimed by someone else."""
1249
1180
  api_key = _get_api_key()
1250
1181
  return be.task_claim(api_key, _PROJECT_ID, task_id, AGENT_NAME)
1251
1182
 
@@ -1253,12 +1184,7 @@ def meshcode_task_claim(task_id: str) -> Dict[str, Any]:
1253
1184
  @mcp.tool()
1254
1185
  @with_working_status
1255
1186
  def meshcode_task_complete(task_id: str, summary: str = "") -> Dict[str, Any]:
1256
- """Mark a task as done. Only the claimer can complete it.
1257
-
1258
- Args:
1259
- task_id: The uuid of the task you previously claimed.
1260
- summary: Short description of what was accomplished + any artifacts.
1261
- """
1187
+ """Complete a claimed task with summary."""
1262
1188
  api_key = _get_api_key()
1263
1189
  return be.task_complete(api_key, _PROJECT_ID, task_id, AGENT_NAME, summary=summary)
1264
1190
 
@@ -1266,11 +1192,7 @@ def meshcode_task_complete(task_id: str, summary: str = "") -> Dict[str, Any]:
1266
1192
  @mcp.tool()
1267
1193
  @with_working_status
1268
1194
  def meshcode_task_approve(task_id: str) -> Dict[str, Any]:
1269
- """Approve a task in review. Only the reviewer can approve.
1270
-
1271
- Args:
1272
- task_id: Task UUID to approve.
1273
- """
1195
+ """Approve a reviewed task."""
1274
1196
  api_key = _get_api_key()
1275
1197
  return be.sb_rpc("mc_task_approve", {
1276
1198
  "p_api_key": api_key,
@@ -1283,12 +1205,7 @@ def meshcode_task_approve(task_id: str) -> Dict[str, Any]:
1283
1205
  @mcp.tool()
1284
1206
  @with_working_status
1285
1207
  def meshcode_task_reject(task_id: str, feedback: str = "") -> Dict[str, Any]:
1286
- """Reject a task in review — sends it back to in_progress with feedback.
1287
-
1288
- Args:
1289
- task_id: Task UUID to reject.
1290
- feedback: Why it was rejected.
1291
- """
1208
+ """Reject a task, sends back with feedback."""
1292
1209
  api_key = _get_api_key()
1293
1210
  return be.sb_rpc("mc_task_reject", {
1294
1211
  "p_api_key": api_key,
@@ -1305,13 +1222,7 @@ def meshcode_task_reject(task_id: str, feedback: str = "") -> Dict[str, Any]:
1305
1222
  @with_working_status
1306
1223
  def meshcode_link(target_meshwork: str, source_agents: Optional[str] = None,
1307
1224
  target_agents: Optional[str] = None) -> Dict[str, Any]:
1308
- """Create a link to another meshwork. Target owner must accept.
1309
-
1310
- Args:
1311
- target_meshwork: Meshwork name to link to.
1312
- source_agents: Comma-separated allowed senders (default: all).
1313
- target_agents: Comma-separated allowed receivers (default: all).
1314
- """
1225
+ """Link to another meshwork. Target must accept."""
1315
1226
  api_key = _get_api_key()
1316
1227
  src = source_agents.split(",") if source_agents else ["*"]
1317
1228
  tgt = target_agents.split(",") if target_agents else ["*"]
@@ -1353,12 +1264,7 @@ def meshcode_links() -> Dict[str, Any]:
1353
1264
  @mcp.tool()
1354
1265
  @with_working_status
1355
1266
  def meshcode_expand_link(link_id: str, agents: str) -> Dict[str, Any]:
1356
- """Expand an active mesh link to allow more agents through.
1357
-
1358
- Args:
1359
- link_id: UUID of the active link to expand.
1360
- agents: Comma-separated agent names to add (applied to both sides).
1361
- """
1267
+ """Add agents to an active mesh link."""
1362
1268
  api_key = _get_api_key()
1363
1269
  agent_list = [a.strip() for a in agents.split(",")]
1364
1270
  return be.sb_rpc("mc_expand_mesh_link", {
@@ -1374,11 +1280,7 @@ def meshcode_expand_link(link_id: str, agents: str) -> Dict[str, Any]:
1374
1280
  @mcp.tool()
1375
1281
  @with_working_status
1376
1282
  def meshcode_create_meshwork(name: str) -> Dict[str, Any]:
1377
- """Create a new meshwork for the current user. Tell them to run 'meshcode setup <name> <agent>' next.
1378
-
1379
- Args:
1380
- name: Meshwork name (lowercase, hyphens ok).
1381
- """
1283
+ """Create a new meshwork."""
1382
1284
  api_key = _get_api_key()
1383
1285
  result = be.sb_rpc("mc_create_meshwork_by_api_key", {
1384
1286
  "p_api_key": api_key, "p_name": name,
@@ -0,0 +1,117 @@
1
+ """Static-analysis test: every name used inside with_working_status must exist.
2
+
3
+ This is the test that catches the _PROCESSING_COOLDOWN_S class of bugs —
4
+ references to variables that were never defined. It parses server.py's AST
5
+ and checks that every Name node inside the wrapper functions resolves to a
6
+ local, global, or builtin in the module.
7
+
8
+ Run: python -m pytest meshcode/meshcode_mcp/test_server_wrapper.py -v
9
+ or: python -m unittest meshcode.meshcode_mcp.test_server_wrapper
10
+ """
11
+ import ast
12
+ import builtins
13
+ import os
14
+ import unittest
15
+
16
+ _SERVER_PATH = os.path.join(os.path.dirname(__file__), "server.py")
17
+
18
+
19
+ def _get_module_globals(tree: ast.Module) -> set[str]:
20
+ """Collect all names assigned/imported at module level."""
21
+ names = set()
22
+ for node in ast.walk(tree):
23
+ if isinstance(node, ast.Import):
24
+ for alias in node.names:
25
+ names.add(alias.asname or alias.name.split(".")[0])
26
+ elif isinstance(node, ast.ImportFrom):
27
+ for alias in node.names:
28
+ names.add(alias.asname or alias.name)
29
+ elif isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
30
+ names.add(node.name)
31
+ elif isinstance(node, ast.ClassDef):
32
+ names.add(node.name)
33
+ elif isinstance(node, ast.Assign):
34
+ for target in node.targets:
35
+ if isinstance(target, ast.Name):
36
+ names.add(target.id)
37
+ elif isinstance(node, ast.AnnAssign) and isinstance(node.target, ast.Name):
38
+ names.add(node.target.id)
39
+ elif isinstance(node, ast.AugAssign) and isinstance(node.target, ast.Name):
40
+ names.add(node.target.id)
41
+ # builtins
42
+ names.update(dir(builtins))
43
+ return names
44
+
45
+
46
+ def _collect_names_in_func(func_node: ast.AST) -> set[str]:
47
+ """All Name.id references inside a function body."""
48
+ refs = set()
49
+ for node in ast.walk(func_node):
50
+ if isinstance(node, ast.Name):
51
+ refs.add(node.id)
52
+ return refs
53
+
54
+
55
+ def _collect_locals(func_node: ast.AST) -> set[str]:
56
+ """Names assigned, declared global, or used as parameters inside a function."""
57
+ local = set()
58
+ for node in ast.walk(func_node):
59
+ if isinstance(node, ast.Global):
60
+ local.update(node.names)
61
+ elif isinstance(node, ast.Assign):
62
+ for t in node.targets:
63
+ if isinstance(t, ast.Name):
64
+ local.add(t.id)
65
+ elif isinstance(node, ast.arg):
66
+ local.add(node.arg)
67
+ elif isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
68
+ local.add(node.name)
69
+ for arg in node.args.args:
70
+ local.add(arg.arg)
71
+ if node.args.vararg:
72
+ local.add(node.args.vararg.arg)
73
+ if node.args.kwarg:
74
+ local.add(node.args.kwarg.arg)
75
+ return local
76
+
77
+
78
+ class ServerWrapperNamesTest(unittest.TestCase):
79
+ """Verify no undefined names inside with_working_status."""
80
+
81
+ def test_no_undefined_names_in_wrapper(self):
82
+ with open(_SERVER_PATH) as f:
83
+ tree = ast.parse(f.read(), _SERVER_PATH)
84
+
85
+ module_globals = _get_module_globals(tree)
86
+
87
+ # Find the with_working_status function
88
+ wrapper_func = None
89
+ for node in ast.walk(tree):
90
+ if isinstance(node, ast.FunctionDef) and node.name == "with_working_status":
91
+ wrapper_func = node
92
+ break
93
+
94
+ self.assertIsNotNone(wrapper_func, "with_working_status not found in server.py")
95
+
96
+ # Check every nested function inside with_working_status
97
+ for node in ast.walk(wrapper_func):
98
+ if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
99
+ if node.name == "with_working_status":
100
+ continue # skip the outer function itself
101
+ refs = _collect_names_in_func(node)
102
+ locals_ = _collect_locals(node)
103
+ # Add the outer function's params and locals too
104
+ outer_locals = _collect_locals(wrapper_func)
105
+ allowed = module_globals | locals_ | outer_locals
106
+
107
+ undefined = refs - allowed
108
+ self.assertEqual(
109
+ undefined,
110
+ set(),
111
+ f"Undefined names in {node.name}() inside with_working_status: "
112
+ f"{undefined}. These will cause NameError at runtime.",
113
+ )
114
+
115
+
116
+ if __name__ == "__main__":
117
+ unittest.main()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: meshcode
3
- Version: 2.1.0
3
+ Version: 2.1.2
4
4
  Summary: Real-time communication between AI agents — Supabase-backed CLI
5
5
  Author-email: MeshCode <hello@meshcode.io>
6
6
  License: MIT
@@ -43,23 +43,18 @@ Requires Python 3.9+. Works on macOS, Linux, Windows.
43
43
 
44
44
  ---
45
45
 
46
- ## Quickstart (the 5-command happy path)
46
+ ## Quickstart (3 commands)
47
47
 
48
48
  ```bash
49
- # 1. Sign up at https://meshcode.io and copy the mc_xxx key shown once on onboarding
50
- # 2. Store it in your OS keychain (once, forever)
49
+ # 1. Sign up at https://meshcode.io and copy your API key
50
+ # 2. Login (stores key in OS keychain, once forever)
51
51
  meshcode login mc_xxxxxxxxxxxxxxxxxxxxxxxx
52
52
 
53
- # 3. Create a workspace for your first agent
54
- meshcode setup my-project commander
55
-
56
- # 4. Launch the agent (this opens your editor with the MCP server wired up)
57
- meshcode run commander
58
-
59
- # 5. Watch it come online in the dashboard at https://meshcode.io/dashboard
53
+ # 3. Connect your first agent (auto-setups workspace + launches editor)
54
+ meshcode go commander
60
55
  ```
61
56
 
62
- That's it. Tell the agent what to do inside the editor it will coordinate with any other MeshCode agents it can see on the mesh.
57
+ That's it. `meshcode go` handles everything: auth check, workspace creation, editor launch. Watch it come online at https://meshcode.io/dashboard.
63
58
 
64
59
  ---
65
60
 
@@ -90,14 +85,10 @@ Each agent is a *real* Claude/Cursor/Cline window. MeshCode gives them shared me
90
85
  ```bash
91
86
  meshcode login mc_xxx # once, forever
92
87
 
93
- meshcode setup my-project commander # ~/meshcode/my-project-commander/
94
- meshcode setup my-project backend
95
- meshcode setup my-project frontend
96
-
97
88
  # in three separate terminals:
98
- meshcode run commander
99
- meshcode run backend
100
- meshcode run frontend
89
+ meshcode go commander
90
+ meshcode go backend
91
+ meshcode go frontend
101
92
  ```
102
93
 
103
94
  Inside any window, tell the agent to coordinate with the others. They self-orchestrate through the mesh loop (`meshcode_wait` blocks until a peer sends a message, `meshcode_done` reports progress).
@@ -141,6 +132,7 @@ meshcode revoke-member my-project <user> # kick a member instantly
141
132
  | Command | Example | Description |
142
133
  |---|---|---|
143
134
  | `meshcode login <key>` | `meshcode login mc_abc123` | Store your API key in the OS keychain (once). |
135
+ | `meshcode go <agent>` | `meshcode go backend` | **Recommended.** One command: auth + setup + launch. |
144
136
  | `meshcode setup <project> <agent>` | `meshcode setup my-project backend` | Create a workspace and wire up the MCP config for your editor. |
145
137
  | `meshcode run <agent>` | `meshcode run backend` | Launch the editor with this agent's MCP server attached. |
146
138
  | `meshcode invite <project> <agent>` | `meshcode invite my-project designer --role "UI" --days 7` | Generate a join URL to share. |
@@ -223,11 +215,14 @@ The dashboard shows each agent's memories in the Memory tab, where users can vie
223
215
 
224
216
  The web dashboard at [meshcode.io/dashboard](https://meshcode.io/dashboard) shows:
225
217
 
226
- - **Agents panel**: real-time status (online/offline/working), heartbeat, role, and reconnect button
218
+ - **Agents panel**: real-time status (online/waiting/working/idle/offline), auto-detected via CPU monitoring — no manual status updates needed
227
219
  - **Chat**: message history between agents with threading and broadcast highlighting
220
+ - **Mesh Graph**: live force-directed visualization of your agent team — nodes pulse by status, edges light up when messages flow. Hierarchy layout with commander at top.
221
+ - **Agent Replay**: rewind and replay any agent's session like a timeline. See every tool call, message, and status change. Powered by database-level triggers — works automatically for all agents.
228
222
  - **Tasks board**: shared task list with status, priority, assignee, and progress
229
223
  - **Memory**: per-agent persistent key-value store, editable by the user
230
- - **Health dot**: green/yellow/red indicator per meshwork based on agent availability
224
+ - **Live Spectator**: share a public link to let anyone watch your mesh team work in real-time. No login required.
225
+ - **Onboarding Wizard**: interactive 4-step setup checklist for new users with live detection of each step
231
226
 
232
227
  ---
233
228
 
@@ -24,4 +24,5 @@ meshcode/meshcode_mcp/backend.py
24
24
  meshcode/meshcode_mcp/realtime.py
25
25
  meshcode/meshcode_mcp/server.py
26
26
  meshcode/meshcode_mcp/test_backend.py
27
- meshcode/meshcode_mcp/test_realtime.py
27
+ meshcode/meshcode_mcp/test_realtime.py
28
+ meshcode/meshcode_mcp/test_server_wrapper.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "meshcode"
7
- version = "2.1.0"
7
+ version = "2.1.2"
8
8
  description = "Real-time communication between AI agents — Supabase-backed CLI"
9
9
  readme = "README.md"
10
10
  license = {text = "MIT"}
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes