agent-relay-plugin 0.4.16 → 0.4.18

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.
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "agent-relay",
3
3
  "description": "Client connector for Agent Relay — auto-registers Claude Code sessions as agents and enables inter-agent messaging via a lightweight HTTP message bus",
4
- "version": "0.4.16"
4
+ "version": "0.4.18"
5
5
  }
@@ -1,8 +1,16 @@
1
1
  #!/usr/bin/env bash
2
2
  # Agent Relay plugin — approval gate for PreToolUse and PermissionDenied hooks.
3
- # Enforces AGENT_RELAY_APPROVAL policy: open (default), guarded, read-only.
3
+ # Enforces AGENT_RELAY_APPROVAL/profile approval policy: open (default), guarded, read-only.
4
4
 
5
- MODE="${AGENT_RELAY_APPROVAL:-open}"
5
+ script_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
6
+ source "${script_dir}/profile-lib.sh"
7
+ project=$(basename "${PWD:-unknown}")
8
+ if [ -n "$CLAUDE_RIG_NAME" ]; then rig="$CLAUDE_RIG_NAME"
9
+ elif [ -n "$CLAUDE_CONFIG_DIR" ]; then rig=$(basename "$CLAUDE_CONFIG_DIR")
10
+ else rig="default"; fi
11
+ ar_init_profile "claude" "$rig" "$project"
12
+ MODE="${AGENT_RELAY_APPROVAL:-$AR_APPROVAL}"
13
+ MODE="${MODE:-open}"
6
14
  [ "$MODE" = "open" ] && exit 0
7
15
 
8
16
  input=$(cat)
@@ -6,6 +6,8 @@
6
6
  RELAY_URL="$1"
7
7
  AGENT_ID="$2"
8
8
  INTERVAL="${3:-10}"
9
+ script_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
10
+ source "${script_dir}/profile-lib.sh"
9
11
  auth_header_args=()
10
12
  if [ -n "${AGENT_RELAY_TOKEN:-}" ]; then
11
13
  auth_header_args=(-H "X-Agent-Relay-Token: ${AGENT_RELAY_TOKEN}")
@@ -19,6 +21,11 @@ fi
19
21
  # PID file so session-start.sh can find and kill us on /clear or /compact.
20
22
  pid_file="/tmp/agent-relay-poll-${AGENT_ID}.pid"
21
23
  status_file="/tmp/agent-relay-status-${AGENT_ID}.state"
24
+ project=$(basename "${PWD:-unknown}")
25
+ if [ -n "$CLAUDE_RIG_NAME" ]; then rig="$CLAUDE_RIG_NAME"
26
+ elif [ -n "$CLAUDE_CONFIG_DIR" ]; then rig=$(basename "$CLAUDE_CONFIG_DIR")
27
+ else rig="default"; fi
28
+ ar_init_profile "claude" "$rig" "$project"
22
29
  cleanup() { rm -f "$pid_file"; }
23
30
  trap cleanup EXIT
24
31
  echo $$ > "$pid_file"
@@ -42,15 +49,13 @@ current_status() {
42
49
  }
43
50
 
44
51
  re_register() {
45
- local machine rig project caps_csv caps_json approval reg_code
52
+ local machine rig project reg_code
46
53
  machine=$(hostname)
47
54
  project=$(basename "${PWD:-unknown}")
48
55
  if [ -n "$CLAUDE_RIG_NAME" ]; then rig="$CLAUDE_RIG_NAME"
49
56
  elif [ -n "$CLAUDE_CONFIG_DIR" ]; then rig=$(basename "$CLAUDE_CONFIG_DIR")
50
57
  else rig="default"; fi
51
- caps_csv="${AGENT_RELAY_CAPS:-chat}"
52
- caps_json=$(echo "$caps_csv" | jq -R 'split(",") | map(gsub("^\\s+|\\s+$"; ""))')
53
- approval="${AGENT_RELAY_APPROVAL:-open}"
58
+ ar_init_profile "claude" "$rig" "$project"
54
59
 
55
60
  reg_code=$(curl -s -o /dev/null -w '%{http_code}' -X POST "${RELAY_URL}/api/agents" "${auth_header_args[@]}" \
56
61
  -H 'Content-Type: application/json' \
@@ -59,10 +64,11 @@ re_register() {
59
64
  --arg name "$project ($rig @ $machine)" \
60
65
  --arg machine "$machine" \
61
66
  --arg rig "$rig" \
62
- --argjson tags "$(jq -n --arg r "$rig" --arg p "$project" '[$r, $p]')" \
63
- --argjson caps "$caps_json" \
64
- --argjson meta "$(jq -n --arg cwd "$PWD" --arg approval "$approval" '{cwd: $cwd, approvalMode: $approval}')" \
65
- '{id: $id, name: $name, machine: $machine, rig: $rig, tags: $tags, capabilities: $caps, status: "online", meta: $meta}'
67
+ --arg label "$AR_LABEL" \
68
+ --argjson tags "$AR_TAGS_JSON" \
69
+ --argjson caps "$AR_CAPS_JSON" \
70
+ --argjson meta "$AR_META_JSON" \
71
+ '{id: $id, name: $name, machine: $machine, rig: $rig, tags: $tags, capabilities: $caps, status: "online", meta: $meta} + (if $label != "" then {label: $label} else {} end)'
66
72
  )" 2>/dev/null)
67
73
 
68
74
  [ "$reg_code" = "200" ] || [ "$reg_code" = "201" ]
@@ -133,6 +139,9 @@ while true; do
133
139
  echo "$msgs" | jq -c '.[]' | while IFS= read -r msg; do
134
140
  mid=$(printf '%s' "$msg" | jq -r '.id')
135
141
  claimable=$(printf '%s' "$msg" | jq -r '.claimable // false')
142
+ if [ "$(ar_message_matches_channels "$msg")" != "true" ]; then
143
+ continue
144
+ fi
136
145
 
137
146
  if [ "$claimable" = "true" ]; then
138
147
  claim_code=$(curl -s -o /dev/null -w '%{http_code}' -X POST \
@@ -144,7 +153,7 @@ while true; do
144
153
  fi
145
154
  fi
146
155
 
147
- printf '%s' "$msg" | jq -r 'if .type == "system" then "⚠ SYSTEM [msg:\(.id)]: \(.body)" else "[msg:\(.id)] \(.from) → \(.to) | \(.subject // "(no subject)"): \(.body)" end'
156
+ printf '%s' "$msg" | jq -r '(.meta.pairId // empty) as $pair | (if $pair != "" then " [pair:\($pair)]" else "" end) as $pairText | if .type == "system" then "⚠ SYSTEM [msg:\(.id)]\($pairText): \(.body)" else "[msg:\(.id)]\($pairText) \(.from) → \(.to) | \(.subject // "(no subject)"): \(.body)" end'
148
157
 
149
158
  curl -s -X PATCH "${RELAY_URL}/api/messages/${mid}" "${auth_header_args[@]}" \
150
159
  -H 'Content-Type: application/json' \
@@ -0,0 +1,72 @@
1
+ #!/usr/bin/env bash
2
+
3
+ ar_csv_json() {
4
+ printf '%s' "$1" | jq -R 'split(",") | map(gsub("^\\s+|\\s+$"; "")) | map(select(length > 0)) | reduce .[] as $x ([]; if index($x) then . else . + [$x] end)'
5
+ }
6
+
7
+ ar_profile_value() {
8
+ local key="$1"
9
+ printf '%s' "${AR_PROFILE_JSON:-{}}" | jq -r --arg key "$key" '.[$key] // empty | if type == "string" then gsub("^\\s+|\\s+$"; "") else empty end'
10
+ }
11
+
12
+ ar_profile_array() {
13
+ local key="$1"
14
+ printf '%s' "${AR_PROFILE_JSON:-{}}" | jq -c --arg key "$key" 'if (.[$key] | type) == "array" then .[$key] | map(select(type == "string") | gsub("^\\s+|\\s+$"; "") | select(length > 0)) | reduce .[] as $x ([]; if index($x) then . else . + [$x] end) else [] end'
15
+ }
16
+
17
+ ar_init_profile() {
18
+ local provider="$1"
19
+ local rig="$2"
20
+ local project="$3"
21
+ local profiles_file profile_meta extra_tags
22
+
23
+ AR_PROFILE_NAME="${AGENT_RELAY_PROFILE:-}"
24
+ profiles_file="${AGENT_RELAY_PROFILES_FILE:-${HOME:-}/.config/agent-relay/profiles.json}"
25
+ AR_PROFILE_JSON="{}"
26
+ if [ -n "$AR_PROFILE_NAME" ] && [ -f "$profiles_file" ]; then
27
+ AR_PROFILE_JSON=$(jq -c --arg name "$AR_PROFILE_NAME" '.[$name] // {}' "$profiles_file" 2>/dev/null || printf '{}')
28
+ fi
29
+
30
+ if [ -n "${AGENT_RELAY_CAPS:-}" ]; then
31
+ AR_CAPS_JSON=$(ar_csv_json "$AGENT_RELAY_CAPS")
32
+ else
33
+ AR_CAPS_JSON=$(ar_profile_array capabilities)
34
+ if [ "$AR_CAPS_JSON" = "[]" ]; then AR_CAPS_JSON='["chat"]'; fi
35
+ fi
36
+
37
+ if [ -n "${AGENT_RELAY_TAGS:-}" ]; then
38
+ extra_tags=$(ar_csv_json "$AGENT_RELAY_TAGS")
39
+ else
40
+ extra_tags=$(ar_profile_array tags)
41
+ fi
42
+ AR_TAGS_JSON=$(jq -n --arg provider "$provider" --arg rig "$rig" --arg project "$project" --argjson extra "$extra_tags" \
43
+ '[$provider, $rig, $project] + $extra | map(select(length > 0)) | reduce .[] as $x ([]; if index($x) then . else . + [$x] end)')
44
+
45
+ if [ -n "${AGENT_RELAY_CHANNELS:-}" ]; then
46
+ AR_CHANNELS_JSON=$(ar_csv_json "$AGENT_RELAY_CHANNELS")
47
+ else
48
+ AR_CHANNELS_JSON=$(ar_profile_array channels)
49
+ fi
50
+
51
+ AR_LABEL="${AGENT_RELAY_LABEL:-$(ar_profile_value label)}"
52
+ AR_APPROVAL="${AGENT_RELAY_APPROVAL:-$(ar_profile_value approval)}"
53
+ AR_APPROVAL="${AR_APPROVAL:-open}"
54
+ case "$AR_APPROVAL" in
55
+ open|guarded|read-only) ;;
56
+ *) AR_APPROVAL="open" ;;
57
+ esac
58
+ profile_meta=$(printf '%s' "$AR_PROFILE_JSON" | jq -c 'if (.meta | type) == "object" then .meta else {} end' 2>/dev/null || printf '{}')
59
+ AR_META_JSON=$(jq -n \
60
+ --arg cwd "${PWD:-}" \
61
+ --arg approval "$AR_APPROVAL" \
62
+ --arg profile "$AR_PROFILE_NAME" \
63
+ --argjson channels "$AR_CHANNELS_JSON" \
64
+ --argjson profileMeta "$profile_meta" \
65
+ '$profileMeta + {cwd: $cwd, approvalMode: $approval, channels: $channels} + (if $profile != "" then {profile: $profile} else {} end)')
66
+ }
67
+
68
+ ar_message_matches_channels() {
69
+ local msg="$1"
70
+ printf '%s' "$msg" | jq -r --argjson channels "${AR_CHANNELS_JSON:-[]}" \
71
+ '(.channel // "") as $ch | if (($channels | length) == 0) or ($ch == "") or (($channels | index($ch)) != null) then "true" else "false" end'
72
+ }
@@ -6,6 +6,7 @@
6
6
 
7
7
  RELAY_URL="${AGENT_RELAY_URL:-http://localhost:4850}"
8
8
  script_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
9
+ source "${script_dir}/profile-lib.sh"
9
10
  plugin_root="${CLAUDE_PLUGIN_ROOT:-$(cd "${script_dir}/.." && pwd)}"
10
11
  PLUGIN_VERSION=$(
11
12
  jq -r '.version // empty' "${plugin_root}/.claude-plugin/plugin.json" 2>/dev/null ||
@@ -30,6 +31,7 @@ elif [ -n "$CLAUDE_CONFIG_DIR" ]; then
30
31
  else
31
32
  rig="default"
32
33
  fi
34
+ ar_init_profile "claude" "$rig" "$project"
33
35
 
34
36
  if command -v shasum >/dev/null 2>&1; then
35
37
  short_pid=$(printf '%s' "$PPID" | shasum -a 1 | head -c 6)
@@ -57,11 +59,6 @@ if [ -f "$instance_state" ]; then
57
59
  fi
58
60
  printf '%s\n' "$agent_id" > "$instance_state"
59
61
 
60
- # --- Register agent ---
61
- caps_csv="${AGENT_RELAY_CAPS:-chat}"
62
- caps_json=$(echo "$caps_csv" | jq -R 'split(",") | map(gsub("^\\s+|\\s+$"; ""))')
63
- approval="${AGENT_RELAY_APPROVAL:-open}"
64
-
65
62
  reg_code=$(curl -s -o /dev/null -w '%{http_code}' -X POST "${RELAY_URL}/api/agents" "${auth_header_args[@]}" \
66
63
  -H 'Content-Type: application/json' \
67
64
  -d "$(jq -n \
@@ -69,10 +66,11 @@ reg_code=$(curl -s -o /dev/null -w '%{http_code}' -X POST "${RELAY_URL}/api/agen
69
66
  --arg name "$project ($rig @ $machine)" \
70
67
  --arg machine "$machine" \
71
68
  --arg rig "$rig" \
72
- --argjson tags "$(jq -n --arg r "$rig" --arg p "$project" '[$r, $p]')" \
73
- --argjson caps "$caps_json" \
74
- --argjson meta "$(jq -n --arg cwd "$PWD" --arg approval "$approval" '{cwd: $cwd, approvalMode: $approval}')" \
75
- '{id: $id, name: $name, machine: $machine, rig: $rig, tags: $tags, capabilities: $caps, status: "online", meta: $meta}'
69
+ --arg label "$AR_LABEL" \
70
+ --argjson tags "$AR_TAGS_JSON" \
71
+ --argjson caps "$AR_CAPS_JSON" \
72
+ --argjson meta "$AR_META_JSON" \
73
+ '{id: $id, name: $name, machine: $machine, rig: $rig, tags: $tags, capabilities: $caps, status: "online", meta: $meta} + (if $label != "" then {label: $label} else {} end)'
76
74
  )" 2>/dev/null)
77
75
 
78
76
  if [ "$reg_code" != "201" ] && [ "$reg_code" != "200" ]; then
@@ -92,7 +90,7 @@ fi
92
90
 
93
91
  # --- Output context (first stdout = notification injected into conversation) ---
94
92
  approval_note=""
95
- case "$approval" in
93
+ case "$AR_APPROVAL" in
96
94
  guarded) approval_note=" — destructive ops (rm, mv, chmod, kill, force-push) are blocked" ;;
97
95
  read-only) approval_note=" — observe/analyze/report only, no file writes or mutation commands" ;;
98
96
  esac
@@ -100,7 +98,10 @@ esac
100
98
  cat <<CONTEXT
101
99
  Agent Relay active. Your agent ID: ${agent_id}
102
100
  Relay URL: ${RELAY_URL} | Server: ${server_version:-unknown} | Plugin: ${PLUGIN_VERSION}
103
- Approval mode: ${approval}${approval_note}
101
+ Profile: ${AR_PROFILE_NAME:-none} | Label: ${AR_LABEL:-none}
102
+ Tags: $(printf '%s' "$AR_TAGS_JSON" | jq -r 'join(", ")') | Capabilities: $(printf '%s' "$AR_CAPS_JSON" | jq -r 'join(", ")')
103
+ Channels: $(printf '%s' "$AR_CHANNELS_JSON" | jq -r 'if length == 0 then "all" else join(", ") end')
104
+ Approval mode: ${AR_APPROVAL}${approval_note}
104
105
 
105
106
  To send a message:
106
107
  curl -s -X POST ${RELAY_URL}/api/messages${auth_header_example} -H 'Content-Type: application/json' -d '{"from":"${agent_id}","to":"TARGET","body":"MESSAGE"}'
@@ -141,6 +142,22 @@ To find agents by capability:
141
142
  To list online agents:
142
143
  curl -s${auth_header_example} '${RELAY_URL}/api/agents?status=online'
143
144
 
145
+ To pair with one available agent for focused two-party live collaboration:
146
+ If the user types "/pair codex", "/pair status", "/pair send PAIR_ID MESSAGE", "/disconnect", "/status", "/label LABEL", or "/tags TAGS", run the matching agent-relay CLI command.
147
+ agent-relay /pair codex "WORK TO SOLVE TOGETHER"
148
+ agent-relay /pair status
149
+ agent-relay /pair send PAIR_ID "MESSAGE"
150
+ agent-relay /disconnect
151
+ agent-relay /status
152
+ agent-relay /label backend-fixer
153
+ agent-relay /tags backend tests urgent
154
+ If the CLI is unavailable, use the API directly:
155
+ curl -s -X POST ${RELAY_URL}/api/pairs${auth_header_example} -H 'Content-Type: application/json' -d '{"from":"${agent_id}","target":"codex","objective":"WORK TO SOLVE TOGETHER"}'
156
+ Accept an incoming pair invite: curl -s -X POST ${RELAY_URL}/api/pairs/PAIR_ID/accept${auth_header_example} -H 'Content-Type: application/json' -d '{"agentId":"${agent_id}"}'
157
+ Send pair chat: curl -s -X POST ${RELAY_URL}/api/pairs/PAIR_ID/messages${auth_header_example} -H 'Content-Type: application/json' -d '{"from":"${agent_id}","body":"MESSAGE"}'
158
+ Hang up: curl -s -X POST ${RELAY_URL}/api/pairs/PAIR_ID/hangup${auth_header_example} -H 'Content-Type: application/json' -d '{"agentId":"${agent_id}"}'
159
+ Pairing is exclusive: one agent can have only one pending or active pair.
160
+
144
161
  System messages:
145
162
  Messages prefixed with "⚠ SYSTEM" are relay-level instructions (e.g. plugin updates, restart requests).
146
163
  When you see one, follow the instruction immediately — for example, restart the inbox monitor if asked.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-relay-plugin",
3
- "version": "0.4.16",
3
+ "version": "0.4.18",
4
4
  "description": "Claude Code plugin for Agent Relay — auto-registers sessions as agents and enables inter-agent messaging",
5
5
  "type": "module",
6
6
  "license": "AGPL-3.0-or-later",
@@ -17,7 +17,8 @@
17
17
  "files": [
18
18
  ".claude-plugin/",
19
19
  "hooks/",
20
- "monitors/"
20
+ "monitors/",
21
+ "skills/"
21
22
  ],
22
23
  "keywords": [
23
24
  "claude-code",
@@ -0,0 +1,16 @@
1
+ ---
2
+ name: disconnect
3
+ description: End the current Agent Relay pair session. Use when the user invokes /disconnect or asks to hang up, unpair, or disconnect from a paired agent.
4
+ argument-hint: "[PAIR_ID]"
5
+ allowed-tools: [Bash]
6
+ ---
7
+
8
+ # Agent Relay Disconnect
9
+
10
+ Run:
11
+
12
+ ```bash
13
+ agent-relay /disconnect $ARGUMENTS
14
+ ```
15
+
16
+ If no pair id is supplied, the CLI ends the active pair for this session. Report the result briefly.
@@ -0,0 +1,23 @@
1
+ ---
2
+ name: label
3
+ description: Read, set, or clear the current Agent Relay agent label. Use when the user invokes /label or asks to rename this relay agent.
4
+ argument-hint: "[LABEL|--clear]"
5
+ allowed-tools: [Bash]
6
+ ---
7
+
8
+ # Agent Relay Label
9
+
10
+ Run:
11
+
12
+ ```bash
13
+ agent-relay /label $ARGUMENTS
14
+ ```
15
+
16
+ Examples:
17
+
18
+ ```bash
19
+ agent-relay /label backend-fixer
20
+ agent-relay /label --clear
21
+ ```
22
+
23
+ Report the new label or current label briefly.
@@ -0,0 +1,24 @@
1
+ ---
2
+ name: message
3
+ description: Send a normal Agent Relay message to another agent, label, tag, capability, or broadcast target. Use when the user invokes /message or asks to send a one-off relay message.
4
+ argument-hint: "<target> <message> [--subject TEXT] [--channel NAME]"
5
+ allowed-tools: [Bash]
6
+ ---
7
+
8
+ # Agent Relay Message
9
+
10
+ Run:
11
+
12
+ ```bash
13
+ agent-relay /message $ARGUMENTS
14
+ ```
15
+
16
+ Examples:
17
+
18
+ ```bash
19
+ agent-relay /message codex "Can you look at that failing action?"
20
+ agent-relay /message tag:backend "Does anyone see the regression?"
21
+ agent-relay /message broadcast "Standup in five minutes"
22
+ ```
23
+
24
+ Report the sent message id briefly.
@@ -0,0 +1,26 @@
1
+ ---
2
+ name: pair
3
+ description: Start, inspect, accept, reject, or send messages in an Agent Relay two-agent pair session. Use when the user invokes /pair or asks to pair this agent with Codex, Claude, or another relay agent.
4
+ argument-hint: "<target|status|accept|reject|send> [args]"
5
+ allowed-tools: [Bash]
6
+ ---
7
+
8
+ # Agent Relay Pair
9
+
10
+ Run the Agent Relay CLI with the user's arguments:
11
+
12
+ ```bash
13
+ agent-relay /pair $ARGUMENTS
14
+ ```
15
+
16
+ Use this for pair-session commands such as:
17
+
18
+ ```bash
19
+ agent-relay /pair codex "Debug flaky tests"
20
+ agent-relay /pair status
21
+ agent-relay /pair accept PAIR_ID
22
+ agent-relay /pair reject PAIR_ID
23
+ agent-relay /pair send PAIR_ID "What do you see?"
24
+ ```
25
+
26
+ Report the command output briefly. If the CLI cannot detect this session's agent id, rerun with `--agent AGENT_ID` or `--from AGENT_ID` using the Agent Relay ID shown in session context.
@@ -0,0 +1,24 @@
1
+ ---
2
+ name: send-claimable
3
+ description: Send a claimable Agent Relay work item so one matching agent can claim and handle it. Use when the user invokes /send-claimable or wants to enqueue work for another agent.
4
+ argument-hint: "<target> <message> [--subject TEXT] [--channel NAME]"
5
+ allowed-tools: [Bash]
6
+ ---
7
+
8
+ # Agent Relay Send Claimable
9
+
10
+ Run:
11
+
12
+ ```bash
13
+ agent-relay /send-claimable $ARGUMENTS
14
+ ```
15
+
16
+ Examples:
17
+
18
+ ```bash
19
+ agent-relay /send-claimable codex "Claim this and inspect the failing action"
20
+ agent-relay /send-claimable tag:backend "Fix the failing API test"
21
+ agent-relay /send-claimable cap:review "Review the migration patch"
22
+ ```
23
+
24
+ Report the sent claimable message id briefly.
@@ -0,0 +1,16 @@
1
+ ---
2
+ name: status
3
+ description: Show Agent Relay status for this session, including relay health, current agent id, label, tags, readiness, and active pair state. Use when the user invokes /status or asks for relay connection status.
4
+ argument-hint: "[--json]"
5
+ allowed-tools: [Bash]
6
+ ---
7
+
8
+ # Agent Relay Status
9
+
10
+ Run:
11
+
12
+ ```bash
13
+ agent-relay /status $ARGUMENTS
14
+ ```
15
+
16
+ Summarize the current relay connection, agent identity, label, tags, and active pair state.
@@ -0,0 +1,25 @@
1
+ ---
2
+ name: tags
3
+ description: List or update Agent Relay tags for the current session. Use when the user invokes /tags or asks to set, add, remove, or inspect relay tags.
4
+ argument-hint: "[TAG ...|--list|--add TAGS|--remove TAGS]"
5
+ allowed-tools: [Bash]
6
+ ---
7
+
8
+ # Agent Relay Tags
9
+
10
+ Run:
11
+
12
+ ```bash
13
+ agent-relay /tags $ARGUMENTS
14
+ ```
15
+
16
+ Examples:
17
+
18
+ ```bash
19
+ agent-relay /tags
20
+ agent-relay /tags backend tests urgent
21
+ agent-relay /tags --add backend,tests
22
+ agent-relay /tags --remove urgent
23
+ ```
24
+
25
+ Report the resulting tags briefly.