feat(cli): 1.31.5 — JSON peer list lifts role to top level + skill renders it
After 1.31.4 the human renderer surfaced role and groups, but launched- session LLMs still dropped them when they called peer list --json and built their own tables. - Top-level role field. The broker returns role nested under profile.role; the CLI now lifts it to a top-level role field at parse time so it is the second-most-visible JSON field after displayName. profile.role is preserved. - Updated claudemesh skill SKILL.md peer-list section with the full JSON shape (memberPubkey, sessionId, role, profile, isSelf, isThisSession) plus explicit guidance to render role + groups in any peer table inside a launched session. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,25 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 1.31.5 (2026-05-04) — JSON peer list lifts profile.role to top-level + skill guides LLMs to render it
|
||||||
|
|
||||||
|
Two follow-ups after 1.31.4 made the human renderer show role/groups
|
||||||
|
but a launched-session LLM still dropped them when it called
|
||||||
|
`peer list --json` and built its own table.
|
||||||
|
|
||||||
|
- **Top-level `role` field on every peer record.** The broker has
|
||||||
|
always returned role nested under `profile.role`, but downstream
|
||||||
|
consumers (LLMs in launched sessions, jq pipelines, dashboards) kept
|
||||||
|
missing it. The CLI now lifts `profile.role` to a top-level `role`
|
||||||
|
field at parse time, so it's the second thing visible in JSON after
|
||||||
|
`displayName`. The original `profile.role` is preserved for
|
||||||
|
backward compatibility.
|
||||||
|
- **Updated SKILL.md peer-list section** with the full JSON shape
|
||||||
|
(including `memberPubkey`, `sessionId`, `role`, `profile`, `isSelf`,
|
||||||
|
`isThisSession`) and explicit guidance: when listing peers inside a
|
||||||
|
launched session, prefer the human renderer; if you do need JSON,
|
||||||
|
always include `role` and `groups` columns. The previous version of
|
||||||
|
the skill documented six fields and skipped role + identity entirely.
|
||||||
|
|
||||||
## 1.31.4 (2026-05-04) — peer list shows roles and groups
|
## 1.31.4 (2026-05-04) — peer list shows roles and groups
|
||||||
|
|
||||||
`claudemesh peer list` now surfaces each peer's profile-level role
|
`claudemesh peer list` now surfaces each peer's profile-level role
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "claudemesh-cli",
|
"name": "claudemesh-cli",
|
||||||
"version": "1.31.4",
|
"version": "1.31.5",
|
||||||
"description": "Peer mesh for Claude Code sessions — CLI + MCP server.",
|
"description": "Peer mesh for Claude Code sessions — CLI + MCP server.",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"claude-code",
|
"claude-code",
|
||||||
|
|||||||
@@ -328,22 +328,36 @@ claudemesh peer bans # list banned members
|
|||||||
claudemesh peer verify [peer] # 6×5-digit safety numbers
|
claudemesh peer verify [peer] # 6×5-digit safety numbers
|
||||||
```
|
```
|
||||||
|
|
||||||
JSON shape (per peer):
|
JSON shape (per peer) — **render `role` and `groups` whenever you build a table for the user**, they're the highest-signal fields after `displayName`:
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"displayName": "Mou",
|
"displayName": "Mou",
|
||||||
"pubkey": "abc123...",
|
"pubkey": "abc123...", // session pubkey (rotates per claudemesh launch)
|
||||||
|
"memberPubkey": "def456...", // stable identity (same across all sibling sessions)
|
||||||
|
"sessionId": "uuid",
|
||||||
"status": "idle | working | dnd",
|
"status": "idle | working | dnd",
|
||||||
"summary": "string or null",
|
"summary": "string or null",
|
||||||
|
"role": "lead | reviewer | bot | ...", // 1.31.5+: top-level alias of profile.role
|
||||||
"groups": [{ "name": "reviewers", "role": "lead" }],
|
"groups": [{ "name": "reviewers", "role": "lead" }],
|
||||||
"peerType": "claude | telegram | ...",
|
"profile": {
|
||||||
|
"role": "lead",
|
||||||
|
"title": "string or null",
|
||||||
|
"bio": "string or null",
|
||||||
|
"avatar": "emoji or null",
|
||||||
|
"capabilities": ["..."]
|
||||||
|
},
|
||||||
|
"peerType": "claude | telegram | ai | human | connector | ...",
|
||||||
"channel": "claude-code | api | ...",
|
"channel": "claude-code | api | ...",
|
||||||
"model": "claude-opus-4-7 | ...",
|
"model": "claude-opus-4-7 | ...",
|
||||||
"cwd": "/path/to/working/dir or null",
|
"cwd": "/path/to/working/dir or null",
|
||||||
|
"isSelf": true, // peer is one of the caller's own sessions
|
||||||
|
"isThisSession": false, // peer is the exact session running the cli
|
||||||
"stats": { "messagesIn": 0, "messagesOut": 0, "toolCalls": 0, "errors": 0, "uptime": 1200 }
|
"stats": { "messagesIn": 0, "messagesOut": 0, "toolCalls": 0, "errors": 0, "uptime": 1200 }
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**When asked to "list peers" inside a launched session, prefer the human renderer (`claudemesh peer list`, no `--json`) — it already prints role + groups inline next to the name with an explicit `(none)` footer when both are absent. If you do need JSON for parsing, always include `role` and `groups` columns in any rendered table; the user's primary question is usually "who's in what role" and dropping those fields hides the answer.**
|
||||||
|
|
||||||
### `message` — send and inspect messages
|
### `message` — send and inspect messages
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@@ -33,6 +33,10 @@ interface PeerRecord {
|
|||||||
status?: string;
|
status?: string;
|
||||||
summary?: string;
|
summary?: string;
|
||||||
groups: Array<{ name: string; role?: string }>;
|
groups: Array<{ name: string; role?: string }>;
|
||||||
|
/** Top-level convenience alias for `profile.role`. Lifted by the
|
||||||
|
* CLI so JSON consumers see role at the shape's top level instead
|
||||||
|
* of nested under profile. Same value either way. */
|
||||||
|
role?: string;
|
||||||
peerType?: string;
|
peerType?: string;
|
||||||
channel?: string;
|
channel?: string;
|
||||||
model?: string;
|
model?: string;
|
||||||
@@ -110,6 +114,13 @@ async function listPeersForMesh(slug: string): Promise<PeerRecord[]> {
|
|||||||
* tell sender's own sessions from real peers. The broker has always
|
* tell sender's own sessions from real peers. The broker has always
|
||||||
* surfaced a sender's siblings as separate rows because they're separate
|
* surfaced a sender's siblings as separate rows because they're separate
|
||||||
* presence rows; the cli just hadn't been making that visible.
|
* presence rows; the cli just hadn't been making that visible.
|
||||||
|
*
|
||||||
|
* Also lifts `profile.role` to a top-level `role` field. The broker has
|
||||||
|
* always returned role nested under `profile.role`, but downstream JSON
|
||||||
|
* consumers (LLMs in launched sessions, jq pipelines, dashboards) kept
|
||||||
|
* missing it because nothing pointed at the nesting. A dedicated
|
||||||
|
* top-level alias makes the intent unmissable without breaking the
|
||||||
|
* `profile` object's shape for callers that already drill into it.
|
||||||
*/
|
*/
|
||||||
function annotateSelf(
|
function annotateSelf(
|
||||||
peer: PeerRecord,
|
peer: PeerRecord,
|
||||||
@@ -126,7 +137,8 @@ function annotateSelf(
|
|||||||
selfSessionPubkey &&
|
selfSessionPubkey &&
|
||||||
peer.pubkey === selfSessionPubkey
|
peer.pubkey === selfSessionPubkey
|
||||||
);
|
);
|
||||||
return { ...peer, isSelf, isThisSession };
|
const role = peer.profile?.role?.trim() || undefined;
|
||||||
|
return { ...peer, ...(role ? { role } : {}), isSelf, isThisSession };
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function runPeers(flags: PeersFlags): Promise<void> {
|
export async function runPeers(flags: PeersFlags): Promise<void> {
|
||||||
|
|||||||
Reference in New Issue
Block a user