ワンクリックで
skill-zulip
Fetch a Zulip thread via API and write formatted JSON to a file. Invoke for /zulip command.
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
Fetch a Zulip thread via API and write formatted JSON to a file. Invoke for /zulip command.
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
Orchestrate multi-agent implementation with parallel phase execution. Spawns teammates for independent phases and coordinates dependent phases. Includes debugger teammate for error recovery.
Orchestrate multi-agent planning with parallel plan generation. Spawns 2-3 teammates for diverse planning approaches and synthesizes into final plan with trade-off analysis.
Orchestrate multi-agent research with wave-based parallel execution. Spawns 2-4 teammates for diverse investigation angles and synthesizes findings.
Full structural hard-mode orchestration state machine with per-phase dispatch (H1), adversarial verification (H4), convergence policing (H6), territory contracts (H7), and churn detection (H5). Invoke for /orchestrate --hard.
Autonomous state machine that drives a task through its full lifecycle (research -> plan -> implement -> complete) without user confirmation between phases. Invoke for /orchestrate command.
Execute hard-mode implementation with anti-analysis contracts, per-phase dispatch, and territory-aware execution. Invoke for --hard implementation tasks.
| name | skill-zulip |
| description | Fetch a Zulip thread via API and write formatted JSON to a file. Invoke for /zulip command. |
| allowed-tools | Bash, AskUserQuestion |
Direct execution skill that fetches all messages from a Zulip chat thread and writes formatted JSON output (sender, date, content) to a target file. Reads credentials from ~/.zuliprc and parses the Zulip URL to extract channel and topic.
Extract URL and optional output path from the arguments passed to this skill.
Run these bash commands to parse the arguments:
url=$(echo "$ARGUMENTS" | awk '{print $1}')
output_path=$(echo "$ARGUMENTS" | awk 'NF>=2{print $2}')
If $url is empty, print usage and stop:
if [ -z "$url" ]; then
echo "Error: Usage: /zulip <zulip-url> [output-path]"
echo "Example: /zulip https://leanprover.zulipchat.com/#narrow/channel/113489-new-members/topic/some.20topic"
exit 1
fi
Check that the credentials file exists and all required tools are available:
zuliprc="$HOME/.zuliprc"
if [ ! -f "$zuliprc" ]; then
echo "Error: ~/.zuliprc not found. Create it with:"
echo " [api]"
echo " email=your@email.com"
echo " key=your_api_key"
echo " site=https://yourorg.zulipchat.com"
exit 1
fi
for tool in curl jq python3; do
if ! which "$tool" >/dev/null 2>&1; then
echo "Error: Required tool not found: $tool"
exit 1
fi
done
Parse ~/.zuliprc to extract the API email, key, and site URL:
email=$(grep -oP '(?<=email=)\S+' "$zuliprc")
api_key=$(grep -oP '(?<=key=)\S+' "$zuliprc")
site=$(grep -oP '(?<=site=)\S+' "$zuliprc" | sed 's|/$||')
if [ -z "$email" ] || [ -z "$api_key" ] || [ -z "$site" ]; then
echo "Error: ~/.zuliprc is missing required fields (email, key, site)"
exit 1
fi
Extract the base URL, channel/stream segment, topic segment, and optional message anchor from the URL.
Note: Zulip uses #narrow/channel/ in newer versions and #narrow/stream/ in older ones — both must be handled.
base=$(echo "$url" | grep -oP 'https?://[^/#]+')
channel_raw=$(echo "$url" | grep -oP '(?<=#narrow/(?:channel|stream)/)[^/]+')
topic_raw=$(echo "$url" | grep -oP '(?<=/topic/)[^/]+')
near_id=$(echo "$url" | grep -oP '(?<=/near/)[0-9]+' || true)
if [ -z "$base" ] || [ -z "$channel_raw" ]; then
echo "Error: Could not parse Zulip URL. Expected format:"
echo " https://REALM.zulipchat.com/#narrow/channel/ID-stream-name/topic/topic.20text"
exit 1
fi
if [ -z "$topic_raw" ]; then
echo "Error: URL does not contain a topic segment. Provide a thread URL (not just a channel URL)."
exit 1
fi
Convert the raw URL segments into a usable stream name and topic string.
Zulip uses dot-encoding in URL fragments: .20 means space, .2C means comma, etc. These are different from standard percent-encoding. The conversion is: .XX -> %XX, then standard URL-decode.
channel_slug=$(echo "$channel_raw" | sed 's/^[0-9]*-//')
stream_name=$(echo "$channel_slug" | tr '-' ' ')
topic=$(python3 -c "
import re, sys
from urllib.parse import unquote
raw = sys.argv[1]
pct = re.sub(r'\.([0-9A-Fa-f]{2})', lambda m: '%' + m.group(1), raw)
print(unquote(pct))
" "$topic_raw")
echo "Fetching thread: stream='$stream_name', topic='$topic'"
if [ -n "$near_id" ]; then
echo "Anchor: message ID $near_id"
fi
If no output path was provided in the arguments, prompt the user to choose where to send the output.
Use AskUserQuestion to present two choices:
Ask: "Where should the Zulip thread output go? Enter '1' to display inline, or '2' to write to a file."
If the user chooses option 2 (write to file), ask a follow-up question: "Enter output file path (e.g., /tmp/thread.json or ~/thread.json):"
Set output_path to the entered path, or set use_stdout=true if option 1 was chosen.
If output_path was already provided in the arguments (Step 1), skip this step entirely.
Build the narrow parameter, URL-encode it, and call the Zulip REST API.
narrow=$(python3 -c "
import json, sys
print(json.dumps([
{'operator': 'stream', 'operand': sys.argv[1]},
{'operator': 'topic', 'operand': sys.argv[2]}
]))
" "$stream_name" "$topic")
narrow_encoded=$(python3 -c "
from urllib.parse import quote
import sys
print(quote(sys.argv[1]))
" "$narrow")
anchor="${near_id:-newest}"
echo "Calling Zulip API..."
response=$(curl -s --user "$email:$api_key" \
"${site}/api/v1/messages?anchor=${anchor}&num_before=5000&num_after=0&narrow=${narrow_encoded}")
api_result=$(echo "$response" | jq -r '.result' 2>/dev/null)
if [ "$api_result" != "success" ]; then
api_msg=$(echo "$response" | jq -r '.msg' 2>/dev/null || echo "Unknown error")
echo "Error: Zulip API returned error: $api_msg"
exit 1
fi
msg_count=$(echo "$response" | jq '.messages | length' 2>/dev/null || echo 0)
echo "Fetched $msg_count messages."
Extract sender, date, and content from the API response and write to the destination.
Format each message as a JSON object with three fields: sender (full name), date (ISO 8601 date string from Unix timestamp), and content (raw HTML).
formatted=$(echo "$response" | jq '[.messages[] | {
sender: .sender_full_name,
date: (.timestamp | todate),
content: .content
}]')
If use_stdout is true, or if output_path is still empty after Step 6, print $formatted directly to stdout.
Otherwise write to file:
mkdir -p "$(dirname "$output_path")"
echo "$formatted" > "$output_path"
Print a completion summary. If output was written to a file:
echo ""
echo "Thread written to: $output_path"
echo "Messages: $msg_count"
echo "Stream: $stream_name"
echo "Topic: $topic"
If output was printed to stdout:
echo ""
echo "---"
echo "Stream: $stream_name | Topic: $topic | Messages: $msg_count"
[] — this is valid outputmkdir -p $(dirname "$output_path") before writing