diff --git a/bin/rbpkg_ci_mux b/bin/rbpkg_ci_mux
new file mode 100755
index 0000000..628ab65
--- /dev/null
+++ b/bin/rbpkg_ci_mux
@@ -0,0 +1,78 @@
+#!/usr/bin/env ruby
+load "#{File.dirname(File.dirname(__FILE__))}/lib/rbpkg.rb"
+
+$ARGS = ARGV
+
+def shift
+ usage if $ARGS == []
+ arg = $ARGS[0]
+ $ARGS = $ARGS[1..] || []
+ arg
+end
+
+verbose_level = 2 # print command output
+case $ARGS[0]
+when "-v"
+ verbose_level = 3 # print all messages
+ shift
+when "-q"
+ verbose_level = 1 # print commands
+ shift
+when "-qq"
+ verbose_level = 0 # print nothing
+ shift
+end
+
+Rbpkg.init("rbpkg_ci_mux", verbose_level, Rbpkg.ci_log_dir, Rbpkg.ci_status_dir)
+
+def usage()
+ STDERR.puts "Usage: #{File.basename($0)} REPO BRANCH COMMIT REMOTE ..."
+ exit 1
+end
+
+def rbpkg_ci_mux
+ clean = false
+ while case $ARGS[0]
+ when "--clean"
+ clean = true
+ shift
+ when "-h"
+ usage
+ when "--help"
+ usage
+ when "--upgrade"
+ Rbpkg.upgrade_self
+ shift
+ end
+ true
+ end
+ repo = shift
+ branch = shift
+ commit = shift
+ threads = []
+ hosts = $ARGS
+ if clean
+ cmd! "rm -rf #{sh_quote(ci_dir)}"
+ hosts.each do |host|
+ cmd! "ssh #{sh_quote(host)} rbpkg_ci --clean"
+ end
+ end
+ hosts.each do |host|
+ Thread.report_on_exception = false
+ threads << Thread.new do
+ Rbpkg.ci_remote(repo, branch, commit, host)
+ end
+ end
+ threads.each do |t|
+ t.join
+ end
+end
+
+begin
+ rbpkg_ci_mux
+ Rbpkg::Log.ok_all
+rescue => error
+ verbose(-1, error.backtrace.reverse.join("\n"))
+ verbose(-1, "#{error.class.name}: #{error.message}")
+ exit 1
+end