Initial commit
This commit is contained in:
81
hooks/generate_message.sh
Executable file
81
hooks/generate_message.sh
Executable file
@@ -0,0 +1,81 @@
|
||||
#!/bin/bash
|
||||
|
||||
# メッセージ生成スクリプト
|
||||
|
||||
# プラグインのディレクトリを取得
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
HOOKS_DIR="$SCRIPT_DIR"
|
||||
NOTIFY_LOG_PATH="$HOOKS_DIR/notify.log"
|
||||
|
||||
# 標準入力からメッセージ要約を取得
|
||||
SUMMARY=$(cat)
|
||||
|
||||
EXISTING_EXAMPLES=$(cat <<'EOT'
|
||||
- 対応を完了しました。
|
||||
- 修正を反映しました。
|
||||
- 最終確認をお願いします。
|
||||
- 確認事項があります。
|
||||
- 次の点についてご確認ください。
|
||||
- ご回答をお願いします。
|
||||
- 追加の情報が必要です。
|
||||
- ご指示をお願いします。
|
||||
- ご確認ください。
|
||||
- 以上です。
|
||||
EOT
|
||||
)
|
||||
|
||||
# 直近10件のログを取得
|
||||
RECENT_LOGS=""
|
||||
if [ -f "$NOTIFY_LOG_PATH" ]; then
|
||||
RECENT_LOGS=$(tail -n 10 "$NOTIFY_LOG_PATH" | cut -d'|' -f2)
|
||||
fi
|
||||
|
||||
# Claude APIへのプロンプトを作成
|
||||
PROMPT="あなたは無個性で中立的なアシスタントです。以下のタスク要約に対し、
|
||||
感情やキャラクター性を排した、簡潔で平易な敬体のメッセージを50文字以内で生成してください。
|
||||
|
||||
前提: これはAgentからユーザーへの切り替え時の発言です(質問がある時または対応完了時)。
|
||||
|
||||
タスク要約: $SUMMARY
|
||||
|
||||
【必須要件】
|
||||
- 無個性・中立。感情やキャラクター性を出さない。
|
||||
- です/ます調。過度に仰々しい敬語や比喩は避ける。
|
||||
- 記号・絵文字・感嘆符を使わない。平易な語彙を用いる。
|
||||
- 着手・開始・実行・進める等の未来時制の表現は使わない。
|
||||
- 成果の報告または質問の提示に限定する。
|
||||
|
||||
【重複回避】
|
||||
- 最近の発言(下記)と同義反復や語句の重複を避ける。
|
||||
$RECENT_LOGS
|
||||
|
||||
【発言パターン例】
|
||||
$EXISTING_EXAMPLES
|
||||
|
||||
【出力形式】
|
||||
- 何をしたかが具体的にわかるよう、端的に記載する(前後の解説・記号・引用符なし)。"
|
||||
|
||||
# フォールバック文言の判定(質問/確認が含まれるかで出し分け)
|
||||
if echo "$SUMMARY" | grep -Eq '[\??]|質問|確認|教えて|不明点|ご回答'; then
|
||||
FALLBACK_MESSAGE="ご確認をお願いします。"
|
||||
else
|
||||
FALLBACK_MESSAGE="対応を完了しました。"
|
||||
fi
|
||||
|
||||
# Claude CLIコマンドを使用してメッセージ生成
|
||||
if command -v claude >/dev/null 2>&1; then
|
||||
# プロンプトを直接パイプで渡す
|
||||
RESPONSE=$(echo "$PROMPT" | claude -p - 2>/dev/null)
|
||||
# レスポンスが取得できた場合
|
||||
if [ -n "$RESPONSE" ] && [ "$RESPONSE" != "" ]; then
|
||||
echo "$RESPONSE" | head -n 2
|
||||
else
|
||||
# Claude CLIが失敗した場合のフォールバック
|
||||
echo "$FALLBACK_MESSAGE"
|
||||
fi
|
||||
else
|
||||
# Claude CLIが利用できない場合のフォールバック
|
||||
echo "$FALLBACK_MESSAGE"
|
||||
fi
|
||||
|
||||
|
||||
15
hooks/hooks.json
Normal file
15
hooks/hooks.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"hooks": {
|
||||
"Stop": [
|
||||
{
|
||||
"matcher": "",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/notify.sh"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
90
hooks/notify.sh
Executable file
90
hooks/notify.sh
Executable file
@@ -0,0 +1,90 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 音声通知スクリプト
|
||||
# 最後のメッセージを取得し、要約してメッセージを読み上げ
|
||||
|
||||
# 標準入力からJSONを読み取る
|
||||
INPUT=$(cat)
|
||||
|
||||
# プラグインのディレクトリを取得
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PLUGIN_DIR="$(dirname "$SCRIPT_DIR")"
|
||||
HOOKS_DIR="$SCRIPT_DIR"
|
||||
NOTIFY_LOG_PATH="$HOOKS_DIR/notify.log"
|
||||
|
||||
# ログディレクトリの作成
|
||||
mkdir -p "$HOOKS_DIR"
|
||||
|
||||
# トランスクリプトを処理(.jsonl形式に対応)
|
||||
TRANSCRIPT_PATH=$(echo "$INPUT" | jq -r '.transcript_path')
|
||||
if [ -f "$TRANSCRIPT_PATH" ]; then
|
||||
# 最後のアシスタントメッセージのみを取得(全文)
|
||||
TEMP_FILE=$(mktemp)
|
||||
|
||||
if command -v tail >/dev/null 2>&1; then
|
||||
tail -r "$TRANSCRIPT_PATH" > "$TEMP_FILE"
|
||||
|
||||
LAST_MESSAGE=""
|
||||
while IFS= read -r line; do
|
||||
# JSON形式の妥当性をチェック
|
||||
if echo "$line" | jq -e . >/dev/null 2>&1; then
|
||||
if echo "$line" | jq -e '.type == "assistant"' >/dev/null 2>&1; then
|
||||
LAST_MESSAGE=$(echo "$line" | jq -r '.message.content[]? | select(.type == "text") | .text' 2>/dev/null)
|
||||
break
|
||||
fi
|
||||
fi
|
||||
done < "$TEMP_FILE"
|
||||
|
||||
rm -f "$TEMP_FILE"
|
||||
fi
|
||||
|
||||
# メッセージが取得できた場合の処理
|
||||
if [ -n "$LAST_MESSAGE" ]; then
|
||||
# 最後のメッセージの1行目を取得(最大100文字)
|
||||
MESSAGE=$(echo "$LAST_MESSAGE" | head -n 1 | head -c 100 | sed 's/[[:space:]]*$//')
|
||||
|
||||
# メッセージが空の場合はデフォルト文例
|
||||
if [ -z "$MESSAGE" ]; then
|
||||
MESSAGE="処理が完了しました。"
|
||||
fi
|
||||
|
||||
# プロジェクト名を取得して追加
|
||||
PROJECT_PATH=$(echo "$INPUT" | jq -r '.cwd // empty')
|
||||
if [ -n "$PROJECT_PATH" ]; then
|
||||
PROJECT_MANAGER="$HOOKS_DIR/project_manager.sh"
|
||||
if [ -f "$PROJECT_MANAGER" ]; then
|
||||
PROJECT_NAME=$(bash "$PROJECT_MANAGER" get "$PROJECT_PATH" 2>/dev/null || echo "")
|
||||
if [ -n "$PROJECT_NAME" ]; then
|
||||
MESSAGE="${PROJECT_NAME}のたくしです。${MESSAGE}"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# 通知実行
|
||||
# terminal-notifier使用(Macネイティブ通知)
|
||||
if command -v terminal-notifier >/dev/null 2>&1; then
|
||||
terminal-notifier -message "$MESSAGE" -title "Assistant" >/dev/null 2>&1 &
|
||||
fi
|
||||
|
||||
# 音声出力(Style-Bert-VITS2)
|
||||
TTS_SCRIPT="$HOOKS_DIR/tts_bert_vits.sh"
|
||||
if [ -f "$TTS_SCRIPT" ]; then
|
||||
nohup bash "$TTS_SCRIPT" "$MESSAGE" >/dev/null 2>&1 &
|
||||
fi
|
||||
|
||||
# ログ保存(タイムスタンプ|発言のみ、改行除去)
|
||||
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
|
||||
CLEAN_MESSAGE=$(echo "$MESSAGE" | tr -d '\n\r' | sed 's/[[:space:]]\+/ /g' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
|
||||
echo "$TIMESTAMP|$CLEAN_MESSAGE" >> "$NOTIFY_LOG_PATH"
|
||||
|
||||
# ログを10件までに制限
|
||||
if [ -f "$NOTIFY_LOG_PATH" ]; then
|
||||
TEMP_LOG=$(mktemp)
|
||||
tail -n 10 "$NOTIFY_LOG_PATH" > "$TEMP_LOG"
|
||||
mv "$TEMP_LOG" "$NOTIFY_LOG_PATH"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
echo '{"decision": "approve"}'
|
||||
exit 0
|
||||
126
hooks/project_manager.sh
Executable file
126
hooks/project_manager.sh
Executable file
@@ -0,0 +1,126 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Takushiプロジェクト名管理スクリプト
|
||||
# 使用方法: ./project_manager.sh [action] [args...]
|
||||
# action: set, get, init
|
||||
|
||||
CONFIG_DIR="$HOME/.config/takushi_notifier"
|
||||
PROJECT_NAMES_FILE="$CONFIG_DIR/project_names.conf"
|
||||
|
||||
# 設定ディレクトリの作成
|
||||
init_config() {
|
||||
mkdir -p "$CONFIG_DIR"
|
||||
if [ ! -f "$PROJECT_NAMES_FILE" ]; then
|
||||
touch "$PROJECT_NAMES_FILE"
|
||||
fi
|
||||
}
|
||||
|
||||
# プロジェクト名設定
|
||||
set_project_name() {
|
||||
local project_path="$1"
|
||||
local project_name="$2"
|
||||
|
||||
# 引数チェック
|
||||
if [ -z "$project_path" ] || [ -z "$project_name" ]; then
|
||||
echo "エラー: プロジェクトパスと名前を指定してください"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
init_config
|
||||
|
||||
# 一時ファイルを作成
|
||||
local temp_file=$(mktemp)
|
||||
local var_name="PROJECT_NAME_${project_path//\//_}"
|
||||
local updated=false
|
||||
|
||||
# 設定ファイルを読み込み、該当行を更新
|
||||
if [ -f "$PROJECT_NAMES_FILE" ]; then
|
||||
while IFS= read -r line; do
|
||||
if [[ "$line" =~ ^${var_name}= ]]; then
|
||||
echo "${var_name}=\"$project_name\"" >> "$temp_file"
|
||||
updated=true
|
||||
else
|
||||
echo "$line" >> "$temp_file"
|
||||
fi
|
||||
done < "$PROJECT_NAMES_FILE"
|
||||
fi
|
||||
|
||||
# 新規追加の場合
|
||||
if [ "$updated" = false ]; then
|
||||
echo "${var_name}=\"$project_name\"" >> "$temp_file"
|
||||
fi
|
||||
|
||||
# 一時ファイルで設定ファイルを上書き
|
||||
mv "$temp_file" "$PROJECT_NAMES_FILE"
|
||||
|
||||
echo "プロジェクト「${project_name}」を設定しました (パス: ${project_path})"
|
||||
}
|
||||
|
||||
# プロジェクト名取得
|
||||
get_project_name() {
|
||||
local project_path="$1"
|
||||
|
||||
if [ -z "$project_path" ]; then
|
||||
echo "エラー: プロジェクトパスを指定してください"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
init_config
|
||||
|
||||
if [ -f "$PROJECT_NAMES_FILE" ]; then
|
||||
local current_path="$project_path"
|
||||
|
||||
# 現在のパスから親階層まで順に探索
|
||||
while [ -n "$current_path" ]; do
|
||||
local var_name="PROJECT_NAME_${current_path//\//_}"
|
||||
local project_name=$(grep "^${var_name}=" "$PROJECT_NAMES_FILE" | cut -d'=' -f2- | tr -d '"')
|
||||
|
||||
if [ -n "$project_name" ]; then
|
||||
echo "$project_name"
|
||||
return
|
||||
fi
|
||||
|
||||
# 親ディレクトリに移動
|
||||
if [ "$current_path" = "/" ] || [ "$current_path" = "." ]; then
|
||||
break
|
||||
fi
|
||||
current_path=$(dirname "$current_path")
|
||||
if [ "$current_path" = "." ]; then
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
echo ""
|
||||
}
|
||||
|
||||
# メイン処理
|
||||
case "${1:-get}" in
|
||||
"set")
|
||||
if [ -z "$2" ] || [ -z "$3" ]; then
|
||||
echo "エラー: プロジェクトパスと名前を指定してください"
|
||||
echo "使用方法: $0 set [path] [name]"
|
||||
exit 1
|
||||
fi
|
||||
set_project_name "$2" "$3"
|
||||
;;
|
||||
"get")
|
||||
if [ -z "$2" ]; then
|
||||
echo "エラー: プロジェクトパスを指定してください"
|
||||
echo "使用方法: $0 get [path]"
|
||||
exit 1
|
||||
fi
|
||||
get_project_name "$2"
|
||||
;;
|
||||
"init")
|
||||
init_config
|
||||
echo "設定ディレクトリを初期化しました: $CONFIG_DIR"
|
||||
;;
|
||||
*)
|
||||
echo "使用方法: $0 [set|get|init] [args...]"
|
||||
echo " set [path] [name] : プロジェクト名を設定"
|
||||
echo " get [path] : プロジェクト名を取得"
|
||||
echo " init : 設定ディレクトリを初期化"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
107
hooks/tts_bert_vits.sh
Executable file
107
hooks/tts_bert_vits.sh
Executable file
@@ -0,0 +1,107 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Style-Bert-VITS2 音声合成スクリプト
|
||||
# 使用方法: ./tts_bert_vits.sh "読み上げたいテキスト"
|
||||
|
||||
# API認証情報
|
||||
CF_ACCESS_CLIENT_ID="78daf18f4b82f77f12a0bfec004ab4ce.access"
|
||||
CF_ACCESS_CLIENT_SECRET="cded896f04ee01c47f5098cebcd3118ed09ad1bc3666f1d59cc5912b2e724020"
|
||||
API_BASE_URL="https://bert-vits-web.vildas.org"
|
||||
|
||||
# モデル設定(固定)
|
||||
MODEL_NAME="izawa_toiyomi"
|
||||
MODEL_FILE="model_assets/izawa_toiyomi/izawa_toiyomi_e100_s5000.safetensors"
|
||||
|
||||
# 設定ファイルの読み込み
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
# グローバル設定ファイルを読み込み
|
||||
GLOBAL_CONFIG="$HOME/.config/takushi_notifier/volume.conf"
|
||||
|
||||
# グローバル設定ファイルが存在する場合は読み込み
|
||||
if [ -f "$GLOBAL_CONFIG" ]; then
|
||||
. "$GLOBAL_CONFIG"
|
||||
fi
|
||||
|
||||
# 音量設定(設定ファイルで AFPLAY_VOLUME を 0.0〜1.0 で指定可能。)
|
||||
AFPLAY_VOLUME="${AFPLAY_VOLUME:-1.0}"
|
||||
|
||||
# 引数チェック
|
||||
if [ $# -eq 0 ]; then
|
||||
echo "エラー: テキストを指定してください"
|
||||
echo "使用方法: $0 \"読み上げたいテキスト\""
|
||||
exit 1
|
||||
fi
|
||||
|
||||
TEXT="$1"
|
||||
SAVE_FILE="${2:-}" # 第2引数で出力ファイル名を指定可能(省略時は一時ファイル)
|
||||
|
||||
# 出力ファイルの設定
|
||||
if [ -z "$SAVE_FILE" ]; then
|
||||
# ファイル名が指定されていない場合は一時ファイルを使用(macOS対応)
|
||||
OUTPUT_FILE=$(mktemp).wav
|
||||
if [ $? -ne 0 ] || [ -z "$OUTPUT_FILE" ]; then
|
||||
echo "エラー: 一時ファイルの作成に失敗しました"
|
||||
exit 1
|
||||
fi
|
||||
TEMP_FILE=true
|
||||
else
|
||||
OUTPUT_FILE="$SAVE_FILE"
|
||||
TEMP_FILE=false
|
||||
fi
|
||||
|
||||
# 1. G2P処理でmoraToneListを取得
|
||||
G2P_RESULT=$(curl -s -X POST "${API_BASE_URL}/api/g2p" \
|
||||
-H "CF-Access-Client-Id: ${CF_ACCESS_CLIENT_ID}" \
|
||||
-H "CF-Access-Client-Secret: ${CF_ACCESS_CLIENT_SECRET}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"text\": \"${TEXT}\"}")
|
||||
|
||||
# エラーチェック
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "エラー: G2P処理に失敗しました"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 2. 音声合成
|
||||
SYNTHESIS_JSON=$(cat <<EOF
|
||||
{
|
||||
"model": "${MODEL_NAME}",
|
||||
"modelFile": "${MODEL_FILE}",
|
||||
"text": "${TEXT}",
|
||||
"moraToneList": ${G2P_RESULT}
|
||||
}
|
||||
EOF
|
||||
)
|
||||
|
||||
# 音声合成リクエスト
|
||||
HTTP_STATUS=$(curl -s -X POST "${API_BASE_URL}/api/synthesis" \
|
||||
-H "CF-Access-Client-Id: ${CF_ACCESS_CLIENT_ID}" \
|
||||
-H "CF-Access-Client-Secret: ${CF_ACCESS_CLIENT_SECRET}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Accept: audio/wav" \
|
||||
-d "${SYNTHESIS_JSON}" \
|
||||
--output "${OUTPUT_FILE}" \
|
||||
-w "%{http_code}")
|
||||
|
||||
# HTTPステータスコードチェック
|
||||
if [ -n "$HTTP_STATUS" ] && [ "$HTTP_STATUS" -eq 200 ]; then
|
||||
if [ -f "${OUTPUT_FILE}" ]; then
|
||||
if [ "$TEMP_FILE" = true ]; then
|
||||
# 一時ファイルの場合は自動再生して削除
|
||||
afplay -v "${AFPLAY_VOLUME}" "${OUTPUT_FILE}"
|
||||
rm "${OUTPUT_FILE}"
|
||||
else
|
||||
# ファイル保存の場合
|
||||
echo "ファイル: ${OUTPUT_FILE}"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
echo "エラー: 音声合成に失敗しました (HTTPステータス: ${HTTP_STATUS})"
|
||||
if [ -f "${OUTPUT_FILE}" ]; then
|
||||
echo "エラー内容:"
|
||||
cat "${OUTPUT_FILE}"
|
||||
rm "${OUTPUT_FILE}"
|
||||
fi
|
||||
exit 1
|
||||
fi
|
||||
85
hooks/volume_manager.sh
Executable file
85
hooks/volume_manager.sh
Executable file
@@ -0,0 +1,85 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Takushi音量管理スクリプト
|
||||
# 使用方法: ./volume_manager.sh [action] [args...]
|
||||
# action: set, get, init
|
||||
|
||||
CONFIG_DIR="$HOME/.config/takushi_notifier"
|
||||
CONFIG_FILE="$CONFIG_DIR/volume.conf"
|
||||
DEFAULT_VOLUME=50
|
||||
|
||||
# 設定ディレクトリの作成
|
||||
init_config() {
|
||||
mkdir -p "$CONFIG_DIR"
|
||||
if [ ! -f "$CONFIG_FILE" ]; then
|
||||
echo "AFPLAY_VOLUME=0.5" > "$CONFIG_FILE"
|
||||
echo "VOLUME_PERCENT=$DEFAULT_VOLUME" >> "$CONFIG_FILE"
|
||||
fi
|
||||
}
|
||||
|
||||
# 音量設定(0-100を0.0-1.0に変換)
|
||||
set_volume() {
|
||||
local volume_percent="$1"
|
||||
|
||||
# 引数チェック
|
||||
if ! [[ "$volume_percent" =~ ^[0-9]+$ ]]; then
|
||||
echo "エラー: 音量は0-100の整数で指定してください"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$volume_percent" -lt 0 ] || [ "$volume_percent" -gt 100 ]; then
|
||||
echo "エラー: 音量は0-100の範囲で指定してください"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 0-100を0.0-1.0に変換
|
||||
local volume_float=$(echo "scale=2; $volume_percent / 100" | bc -l)
|
||||
|
||||
# 設定ファイルを更新
|
||||
init_config
|
||||
cat > "$CONFIG_FILE" << EOF
|
||||
AFPLAY_VOLUME=$volume_float
|
||||
VOLUME_PERCENT=$volume_percent
|
||||
EOF
|
||||
|
||||
echo "Takushi音量を${volume_percent}%に設定しました (afplay: ${volume_float})"
|
||||
}
|
||||
|
||||
# 現在の音量取得
|
||||
get_volume() {
|
||||
init_config
|
||||
if [ -f "$CONFIG_FILE" ]; then
|
||||
source "$CONFIG_FILE"
|
||||
echo "現在のTakushi音量: ${VOLUME_PERCENT}% (afplay: ${AFPLAY_VOLUME})"
|
||||
else
|
||||
echo "設定ファイルが見つかりません。初期化します..."
|
||||
init_config
|
||||
echo "現在のTakushi音量: ${DEFAULT_VOLUME}% (afplay: 0.5)"
|
||||
fi
|
||||
}
|
||||
|
||||
# メイン処理
|
||||
case "${1:-get}" in
|
||||
"set")
|
||||
if [ -z "$2" ]; then
|
||||
echo "エラー: 音量を指定してください"
|
||||
echo "使用方法: $0 set [0-100]"
|
||||
exit 1
|
||||
fi
|
||||
set_volume "$2"
|
||||
;;
|
||||
"get")
|
||||
get_volume
|
||||
;;
|
||||
"init")
|
||||
init_config
|
||||
echo "設定ディレクトリを初期化しました: $CONFIG_DIR"
|
||||
;;
|
||||
*)
|
||||
echo "使用方法: $0 [set|get|init] [volume]"
|
||||
echo " set [0-100] : 音量を設定"
|
||||
echo " get : 現在の音量を表示"
|
||||
echo " init : 設定ディレクトリを初期化"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
Reference in New Issue
Block a user