From 016caca398c5b450db14c928f69b6927a6ac557f Mon Sep 17 00:00:00 2001 From: Sihao Liu Date: Sat, 28 Mar 2026 18:28:04 -0700 Subject: [PATCH 1/2] Add ask-gemini skill and tool-filtered monitor subcommands Introduce ask-gemini skill that wraps the Gemini CLI in non-interactive mode with built-in web-search instruction, defaulting to gemini-3.1-pro-preview. Supports --gemini-model and --gemini-timeout flags. Add tool metadata tagging (tool: codex / tool: gemini) to both ask-codex and ask-gemini invocations so the monitor can distinguish them. Extend humanize monitor with codex and gemini subcommands that filter skill invocations by tool type. The existing skill subcommand continues to show all invocations. Bump version to 1.17.0. --- .claude-plugin/marketplace.json | 2 +- .claude-plugin/plugin.json | 2 +- README.md | 14 +- scripts/ask-codex.sh | 5 + scripts/ask-gemini.sh | 385 ++++++++++++++++++++++++++++++++ scripts/humanize.sh | 16 +- scripts/lib/monitor-skill.sh | 108 +++++++-- skills/ask-gemini/SKILL.md | 61 +++++ 8 files changed, 571 insertions(+), 22 deletions(-) create mode 100755 scripts/ask-gemini.sh create mode 100644 skills/ask-gemini/SKILL.md diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 588e3b1d..e4e1adf3 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -8,7 +8,7 @@ "name": "humanize", "source": "./", "description": "Humanize - An iterative development plugin that uses Codex to review Claude's work. Creates a feedback loop where Claude implements plans and Codex independently reviews progress, ensuring quality through continuous refinement.", - "version": "1.16.0" + "version": "1.17.0" } ] } diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index 0f9ab32b..bbab412d 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "humanize", "description": "Humanize - An iterative development plugin that uses Codex to review Claude's work. Creates a feedback loop where Claude implements plans and Codex independently reviews progress, ensuring quality through continuous refinement.", - "version": "1.16.0", + "version": "1.17.0", "author": { "name": "humania-org" }, diff --git a/README.md b/README.md index dd961c9a..0a93dd30 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Humanize -**Current Version: 1.16.0** +**Current Version: 1.17.0** > Derived from the [GAAC (GitHub-as-a-Context)](https://github.com/SihaoLiu/gaac) project. @@ -55,10 +55,18 @@ Requires [codex CLI](https://github.com/openai/codex) for review. See the full [ /humanize:start-rlcr-loop docs/plan.md ``` -4. **Monitor progress**: +4. **Consult Gemini** for deep web research (requires Gemini CLI): + ```bash + /humanize:ask-gemini What are the latest best practices for X? + ``` + +5. **Monitor progress**: ```bash source /scripts/humanize.sh - humanize monitor rlcr + humanize monitor rlcr # RLCR loop + humanize monitor skill # All skill invocations (codex + gemini) + humanize monitor codex # Codex invocations only + humanize monitor gemini # Gemini invocations only ``` ## Monitor Dashboard diff --git a/scripts/ask-codex.sh b/scripts/ask-codex.sh index ac26fc32..bea40b4e 100755 --- a/scripts/ask-codex.sh +++ b/scripts/ask-codex.sh @@ -234,6 +234,7 @@ $QUESTION - Effort: $CODEX_EFFORT - Timeout: ${CODEX_TIMEOUT}s - Timestamp: $TIMESTAMP +- Tool: codex EOF # ======================================== @@ -317,6 +318,7 @@ if [[ $CODEX_EXIT_CODE -eq 124 ]]; then # Save metadata even on timeout cat > "$SKILL_DIR/metadata.md" << EOF --- +tool: codex model: $CODEX_MODEL effort: $CODEX_EFFORT timeout: $CODEX_TIMEOUT @@ -343,6 +345,7 @@ if [[ $CODEX_EXIT_CODE -ne 0 ]]; then # Save metadata cat > "$SKILL_DIR/metadata.md" << EOF --- +tool: codex model: $CODEX_MODEL effort: $CODEX_EFFORT timeout: $CODEX_TIMEOUT @@ -368,6 +371,7 @@ if [[ ! -s "$CODEX_STDOUT_FILE" ]]; then cat > "$SKILL_DIR/metadata.md" << EOF --- +tool: codex model: $CODEX_MODEL effort: $CODEX_EFFORT timeout: $CODEX_TIMEOUT @@ -390,6 +394,7 @@ cp "$CODEX_STDOUT_FILE" "$SKILL_DIR/output.md" # Save metadata cat > "$SKILL_DIR/metadata.md" << EOF --- +tool: codex model: $CODEX_MODEL effort: $CODEX_EFFORT timeout: $CODEX_TIMEOUT diff --git a/scripts/ask-gemini.sh b/scripts/ask-gemini.sh new file mode 100755 index 00000000..489bc4db --- /dev/null +++ b/scripts/ask-gemini.sh @@ -0,0 +1,385 @@ +#!/usr/bin/env bash +# +# Ask Gemini - One-shot consultation with Gemini CLI +# +# Sends a question or task to gemini in non-interactive mode and returns +# the response. Gemini is always instructed to leverage Google Search +# for deep web research. +# +# Usage: +# ask-gemini.sh [--gemini-model MODEL] [--gemini-timeout SECONDS] [question...] +# +# Output: +# stdout: Gemini's response (for Claude to read) +# stderr: Status/debug info (model, log paths) +# +# Storage: +# Project-local: .humanize/skill//{input,output,metadata}.md +# Cache: ~/.cache/humanize//skill-/gemini-run.{cmd,out,log} +# + +set -euo pipefail + +# ======================================== +# Source Shared Libraries +# ======================================== + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" && pwd)" + +# Source portable timeout wrapper +source "$SCRIPT_DIR/portable-timeout.sh" + +# ======================================== +# Default Configuration +# ======================================== + +DEFAULT_GEMINI_MODEL="gemini-3.1-pro-preview" +DEFAULT_ASK_GEMINI_TIMEOUT=3600 + +GEMINI_MODEL="$DEFAULT_GEMINI_MODEL" +GEMINI_TIMEOUT="$DEFAULT_ASK_GEMINI_TIMEOUT" + +# ======================================== +# Help +# ======================================== + +show_help() { + cat << 'HELP_EOF' +ask-gemini - One-shot deep-research consultation with Gemini + +USAGE: + /humanize:ask-gemini [OPTIONS] + +OPTIONS: + --gemini-model + Gemini model name (default: gemini-3.1-pro-preview) + --gemini-timeout + Timeout for the Gemini query in seconds (default: 3600) + -h, --help Show this help message + +DESCRIPTION: + Sends a one-shot question or task to the Gemini CLI in non-interactive + mode (-p). The prompt is augmented with an instruction to perform web + research via Google Search, making this ideal for deep-research tasks + that benefit from up-to-date internet information. + + The response is saved to .humanize/skill//output.md for reference. + +EXAMPLES: + /humanize:ask-gemini What are the latest best practices for Rust error handling? + /humanize:ask-gemini --gemini-model gemini-2.5-pro Review recent CVEs for OpenSSL 3.x + /humanize:ask-gemini --gemini-timeout 600 Compare React Server Components vs Astro Islands + +ENVIRONMENT: + HUMANIZE_GEMINI_YOLO + Set to "true" or "1" to auto-approve all Gemini tool calls (--yolo). + Default behaviour uses --sandbox mode. +HELP_EOF + exit 0 +} + +# ======================================== +# Parse Arguments +# ======================================== + +QUESTION_PARTS=() +OPTIONS_DONE=false + +while [[ $# -gt 0 ]]; do + if [[ "$OPTIONS_DONE" == "true" ]]; then + QUESTION_PARTS+=("$1") + shift + continue + fi + case $1 in + -h|--help) + show_help + ;; + --) + OPTIONS_DONE=true + shift + ;; + --gemini-model) + if [[ -z "${2:-}" ]]; then + echo "Error: --gemini-model requires a MODEL argument" >&2 + exit 1 + fi + GEMINI_MODEL="$2" + shift 2 + ;; + --gemini-timeout) + if [[ -z "${2:-}" ]]; then + echo "Error: --gemini-timeout requires a number argument (seconds)" >&2 + exit 1 + fi + if ! [[ "$2" =~ ^[0-9]+$ ]]; then + echo "Error: --gemini-timeout must be a positive integer (seconds), got: $2" >&2 + exit 1 + fi + GEMINI_TIMEOUT="$2" + shift 2 + ;; + -*) + echo "Error: Unknown option: $1" >&2 + echo "Use --help for usage information" >&2 + exit 1 + ;; + *) + QUESTION_PARTS+=("$1") + OPTIONS_DONE=true + shift + ;; + esac +done + +# Join question parts into a single string +QUESTION="${QUESTION_PARTS[*]}" + +# ======================================== +# Validate Prerequisites +# ======================================== + +if ! command -v gemini &>/dev/null; then + echo "Error: 'gemini' command is not installed or not in PATH" >&2 + echo "" >&2 + echo "Please install Gemini CLI: npm install -g @anthropic-ai/gemini-cli or https://github.com/anthropics/gemini-cli" >&2 + echo "Then retry: /humanize:ask-gemini " >&2 + exit 1 +fi + +if [[ -z "$QUESTION" ]]; then + echo "Error: No question or task provided" >&2 + echo "" >&2 + echo "Usage: /humanize:ask-gemini [OPTIONS] " >&2 + echo "" >&2 + echo "For help: /humanize:ask-gemini --help" >&2 + exit 1 +fi + +# Validate model name for safety (alphanumeric, hyphen, underscore, dot) +if [[ ! "$GEMINI_MODEL" =~ ^[a-zA-Z0-9._-]+$ ]]; then + echo "Error: Gemini model contains invalid characters" >&2 + echo " Model: $GEMINI_MODEL" >&2 + echo " Only alphanumeric, hyphen, underscore, dot allowed" >&2 + exit 1 +fi + +# ======================================== +# Detect Project Root +# ======================================== + +if git rev-parse --show-toplevel &>/dev/null; then + PROJECT_ROOT=$(git rev-parse --show-toplevel) +else + PROJECT_ROOT="${CLAUDE_PROJECT_DIR:-$(pwd)}" +fi + +# ======================================== +# Create Storage Directories +# ======================================== + +TIMESTAMP=$(date +%Y-%m-%d_%H-%M-%S) +UNIQUE_ID="${TIMESTAMP}-$$-$(head -c 4 /dev/urandom | od -An -tx1 | tr -d ' \n')" + +# Project-local storage: .humanize/skill// +SKILL_DIR="$PROJECT_ROOT/.humanize/skill/$UNIQUE_ID" +mkdir -p "$SKILL_DIR" + +# Cache storage: ~/.cache/humanize//skill-/ +SANITIZED_PROJECT_PATH=$(echo "$PROJECT_ROOT" | sed 's/[^a-zA-Z0-9._-]/-/g' | sed 's/--*/-/g') +CACHE_BASE="${XDG_CACHE_HOME:-$HOME/.cache}" +CACHE_DIR="$CACHE_BASE/humanize/$SANITIZED_PROJECT_PATH/skill-$UNIQUE_ID" +if ! mkdir -p "$CACHE_DIR" 2>/dev/null; then + CACHE_DIR="$SKILL_DIR/cache" + mkdir -p "$CACHE_DIR" + echo "ask-gemini: warning: home cache not writable, using $CACHE_DIR" >&2 +fi + +# ======================================== +# Save Input +# ======================================== + +cat > "$SKILL_DIR/input.md" << EOF +# Ask Gemini Input + +## Question + +$QUESTION + +## Configuration + +- Model: $GEMINI_MODEL +- Timeout: ${GEMINI_TIMEOUT}s +- Timestamp: $TIMESTAMP +- Tool: gemini +EOF + +# ======================================== +# Build Gemini Command +# ======================================== + +GEMINI_ARGS=("-m" "$GEMINI_MODEL") + +# Determine approval mode +if [[ "${HUMANIZE_GEMINI_YOLO:-}" == "true" ]] || [[ "${HUMANIZE_GEMINI_YOLO:-}" == "1" ]]; then + GEMINI_ARGS+=("--yolo") +else + GEMINI_ARGS+=("--sandbox") +fi + +# Use text output format for clean stdout +GEMINI_ARGS+=("-o" "text") + +# Build the augmented prompt with web-search instruction +AUGMENTED_PROMPT="You MUST use Google Search to find the most up-to-date and accurate information before answering. Perform thorough web research. Cite sources where possible. + +--- + +$QUESTION" + +# ======================================== +# Save Debug Command +# ======================================== + +GEMINI_CMD_FILE="$CACHE_DIR/gemini-run.cmd" +GEMINI_STDOUT_FILE="$CACHE_DIR/gemini-run.out" +GEMINI_STDERR_FILE="$CACHE_DIR/gemini-run.log" + +{ + echo "# Gemini ask-gemini invocation debug info" + echo "# Timestamp: $(date -u +%Y-%m-%dT%H:%M:%SZ)" + echo "# Working directory: $PROJECT_ROOT" + echo "# Timeout: $GEMINI_TIMEOUT seconds" + echo "" + echo "gemini ${GEMINI_ARGS[*]} -p \"\"" + echo "" + echo "# Prompt content:" + echo "$AUGMENTED_PROMPT" +} > "$GEMINI_CMD_FILE" + +# ======================================== +# Run Gemini +# ======================================== + +echo "ask-gemini: model=$GEMINI_MODEL timeout=${GEMINI_TIMEOUT}s" >&2 +echo "ask-gemini: cache=$CACHE_DIR" >&2 +echo "ask-gemini: running gemini -p ..." >&2 + +# Portable epoch-to-ISO8601 formatter +epoch_to_iso() { + local epoch="$1" + date -u -d "@$epoch" +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || + date -u -r "$epoch" +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || + echo "unknown" +} + +START_TIME=$(date +%s) + +GEMINI_EXIT_CODE=0 +run_with_timeout "$GEMINI_TIMEOUT" gemini "${GEMINI_ARGS[@]}" -p "$AUGMENTED_PROMPT" \ + > "$GEMINI_STDOUT_FILE" 2> "$GEMINI_STDERR_FILE" || GEMINI_EXIT_CODE=$? + +END_TIME=$(date +%s) +DURATION=$((END_TIME - START_TIME)) + +echo "ask-gemini: exit_code=$GEMINI_EXIT_CODE duration=${DURATION}s" >&2 + +# ======================================== +# Handle Results +# ======================================== + +if [[ $GEMINI_EXIT_CODE -eq 124 ]]; then + echo "Error: Gemini timed out after ${GEMINI_TIMEOUT} seconds" >&2 + echo "" >&2 + echo "Try increasing the timeout:" >&2 + echo " /humanize:ask-gemini --gemini-timeout $((GEMINI_TIMEOUT * 2)) " >&2 + echo "" >&2 + echo "Debug logs: $CACHE_DIR" >&2 + + cat > "$SKILL_DIR/metadata.md" << EOF +--- +tool: gemini +model: $GEMINI_MODEL +timeout: $GEMINI_TIMEOUT +exit_code: 124 +duration: ${DURATION}s +status: timeout +started_at: $(epoch_to_iso "$START_TIME") +--- +EOF + exit 124 +fi + +if [[ $GEMINI_EXIT_CODE -ne 0 ]]; then + echo "Error: Gemini exited with code $GEMINI_EXIT_CODE" >&2 + if [[ -s "$GEMINI_STDERR_FILE" ]]; then + echo "" >&2 + echo "Gemini stderr (last 20 lines):" >&2 + tail -20 "$GEMINI_STDERR_FILE" >&2 + fi + echo "" >&2 + echo "Debug logs: $CACHE_DIR" >&2 + + cat > "$SKILL_DIR/metadata.md" << EOF +--- +tool: gemini +model: $GEMINI_MODEL +timeout: $GEMINI_TIMEOUT +exit_code: $GEMINI_EXIT_CODE +duration: ${DURATION}s +status: error +started_at: $(epoch_to_iso "$START_TIME") +--- +EOF + exit "$GEMINI_EXIT_CODE" +fi + +if [[ ! -s "$GEMINI_STDOUT_FILE" ]]; then + echo "Error: Gemini returned empty response" >&2 + if [[ -s "$GEMINI_STDERR_FILE" ]]; then + echo "" >&2 + echo "Gemini stderr (last 20 lines):" >&2 + tail -20 "$GEMINI_STDERR_FILE" >&2 + fi + echo "" >&2 + echo "Debug logs: $CACHE_DIR" >&2 + + cat > "$SKILL_DIR/metadata.md" << EOF +--- +tool: gemini +model: $GEMINI_MODEL +timeout: $GEMINI_TIMEOUT +exit_code: 0 +duration: ${DURATION}s +status: empty_response +started_at: $(epoch_to_iso "$START_TIME") +--- +EOF + exit 1 +fi + +# ======================================== +# Save Output and Metadata +# ======================================== + +cp "$GEMINI_STDOUT_FILE" "$SKILL_DIR/output.md" + +cat > "$SKILL_DIR/metadata.md" << EOF +--- +tool: gemini +model: $GEMINI_MODEL +timeout: $GEMINI_TIMEOUT +exit_code: 0 +duration: ${DURATION}s +status: success +started_at: $(epoch_to_iso "$START_TIME") +--- +EOF + +echo "ask-gemini: response saved to $SKILL_DIR/output.md" >&2 + +# ======================================== +# Output Response +# ======================================== + +cat "$GEMINI_STDOUT_FILE" diff --git a/scripts/humanize.sh b/scripts/humanize.sh index a3492844..c5ac3f20 100755 --- a/scripts/humanize.sh +++ b/scripts/humanize.sh @@ -1182,13 +1182,21 @@ humanize() { skill) _humanize_monitor_skill "$@" ;; + codex) + _humanize_monitor_skill --tool-filter codex "$@" + ;; + gemini) + _humanize_monitor_skill --tool-filter gemini "$@" + ;; *) - echo "Usage: humanize monitor " + echo "Usage: humanize monitor " echo "" echo "Subcommands:" echo " rlcr Monitor the latest RLCR loop log from .humanize/rlcr" echo " pr Monitor the latest PR loop from .humanize/pr-loop" - echo " skill Monitor ask-codex skill invocations from .humanize/skill" + echo " skill Monitor all skill invocations (codex + gemini)" + echo " codex Monitor ask-codex skill invocations only" + echo " gemini Monitor ask-gemini skill invocations only" echo "" echo "Features:" echo " - Fixed status bar showing session info, round progress, model config" @@ -1205,7 +1213,9 @@ humanize() { echo "Commands:" echo " monitor rlcr Monitor the latest RLCR loop log" echo " monitor pr Monitor the latest PR loop" - echo " monitor skill Monitor ask-codex skill invocations" + echo " monitor skill Monitor all skill invocations (codex + gemini)" + echo " monitor codex Monitor ask-codex skill invocations only" + echo " monitor gemini Monitor ask-gemini skill invocations only" return 1 ;; esac diff --git a/scripts/lib/monitor-skill.sh b/scripts/lib/monitor-skill.sh index 218fab92..8803f139 100644 --- a/scripts/lib/monitor-skill.sh +++ b/scripts/lib/monitor-skill.sh @@ -3,15 +3,18 @@ # monitor-skill.sh - Skill monitor for humanize # # Provides the _humanize_monitor_skill function for monitoring -# ask-codex skill invocations from .humanize/skill directory. +# skill invocations (ask-codex, ask-gemini) from .humanize/skill directory. # # This file is sourced by humanize.sh and depends on: # - monitor-common.sh (monitor_get_yaml_value, monitor_format_timestamp, etc.) # - humanize.sh (humanize_split_to_array) -# Monitor ask-codex skill invocations from .humanize/skill +# Monitor skill invocations from .humanize/skill # Shows a fixed status bar with aggregate stats and latest invocation details, # with live output display in the scrollable area below. +# +# Accepts --tool-filter to show only invocations from a +# specific tool. Without the filter, all invocations are shown. _humanize_monitor_skill() { # Enable 0-indexed arrays in zsh for bash compatibility # no_monitor suppresses background job notifications ([1] PID) @@ -23,11 +26,16 @@ _humanize_monitor_skill() { local check_interval=2 local status_bar_height=9 local once_mode=false + local tool_filter="" # Parse arguments while [[ $# -gt 0 ]]; do case "$1" in --once) once_mode=true; shift ;; + --tool-filter) + tool_filter="${2:-}" + shift 2 + ;; *) shift ;; esac done @@ -35,10 +43,37 @@ _humanize_monitor_skill() { # Check if .humanize/skill exists if [[ ! -d "$skill_dir" ]]; then echo "Error: $skill_dir directory not found in current directory" - echo "Run /humanize:ask-codex first to create skill invocations" + echo "Run /humanize:ask-codex or /humanize:ask-gemini first to create skill invocations" return 1 fi + # Determine the tool for a given invocation directory. + # Reads metadata.md first (completed), falls back to input.md (running). + # Returns: codex, gemini, or unknown + _skill_get_tool() { + local dir="$1" + if [[ -f "$dir/metadata.md" ]]; then + local t=$(monitor_get_yaml_value "tool" "$dir/metadata.md") + [[ -n "$t" ]] && { echo "$t"; return; } + fi + if [[ -f "$dir/input.md" ]]; then + local t=$(grep -E '^- Tool:' "$dir/input.md" 2>/dev/null | sed 's/- Tool: //') + [[ -n "$t" ]] && { echo "$t"; return; } + fi + echo "unknown" + } + + # Check whether a directory passes the current tool filter. + # Returns 0 (pass) or 1 (skip). + _skill_passes_filter() { + [[ -z "$tool_filter" ]] && return 0 + local t=$(_skill_get_tool "$1") + [[ "$t" == "$tool_filter" ]] && return 0 + # Legacy invocations without a tool tag are treated as codex + [[ "$t" == "unknown" && "$tool_filter" == "codex" ]] && return 0 + return 1 + } + # List all valid skill invocation directories sorted newest-first # Skill dirs use YYYY-MM-DD_HH-MM-SS or YYYY-MM-DD_HH-MM-SS-PID-RANDOM naming _skill_list_dirs_sorted() { @@ -47,7 +82,9 @@ _humanize_monitor_skill() { [[ -z "$d" ]] && continue [[ ! -d "$d" ]] && continue local name=$(basename "$d") - [[ "$name" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}_[0-9]{2}-[0-9]{2}-[0-9]{2} ]] && dirs+=("$d") + [[ "$name" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}_[0-9]{2}-[0-9]{2}-[0-9]{2} ]] || continue + _skill_passes_filter "$d" || continue + dirs+=("$d") done < <(find "$skill_dir" -mindepth 1 -maxdepth 1 -type d 2>/dev/null) printf '%s\n' "${dirs[@]}" | sort -r } @@ -88,6 +125,7 @@ _humanize_monitor_skill() { [[ ! -d "$d" ]] && continue local name=$(basename "$d") [[ ! "$name" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}_[0-9]{2}-[0-9]{2}-[0-9]{2} ]] && continue + _skill_passes_filter "$d" || continue ((total++)) if [[ -f "$d/metadata.md" ]]; then local st=$(monitor_get_yaml_value "status" "$d/metadata.md") @@ -127,6 +165,7 @@ _humanize_monitor_skill() { # Find the best file to monitor for a skill invocation # Searches both global cache (~/.cache/humanize/), local cache ($dir/cache/), # and project-local files (.humanize/skill/) for the best content. + # Supports both codex (codex-run.*) and gemini (gemini-run.*) cache files. _skill_find_monitored_file() { local dir="$1" local gcache=$(_skill_find_cache_dir "$dir") @@ -134,18 +173,29 @@ _humanize_monitor_skill() { local is_running=false [[ ! -f "$dir/metadata.md" ]] && is_running=true + # Determine which tool produced this invocation for cache file naming + local inv_tool=$(_skill_get_tool "$dir") + local run_prefix="codex-run" + [[ "$inv_tool" == "gemini" ]] && run_prefix="gemini-run" + # Helper: check a cache directory for best file # Args: cache_dir, prefer_log (true for running, false for completed) _check_cache_files() { local c="$1" prefer_log="$2" [[ ! -d "$c" ]] && return if [[ "$prefer_log" == "true" ]]; then + [[ -f "$c/${run_prefix}.log" && -s "$c/${run_prefix}.log" ]] && { echo "$c/${run_prefix}.log"; return; } + [[ -f "$c/${run_prefix}.out" && -s "$c/${run_prefix}.out" ]] && { echo "$c/${run_prefix}.out"; return; } + [[ -f "$c/${run_prefix}.log" ]] && { echo "$c/${run_prefix}.log"; return; } + # Fallback: try the other prefix for legacy/mixed invocations [[ -f "$c/codex-run.log" && -s "$c/codex-run.log" ]] && { echo "$c/codex-run.log"; return; } - [[ -f "$c/codex-run.out" && -s "$c/codex-run.out" ]] && { echo "$c/codex-run.out"; return; } - [[ -f "$c/codex-run.log" ]] && { echo "$c/codex-run.log"; return; } + [[ -f "$c/gemini-run.log" && -s "$c/gemini-run.log" ]] && { echo "$c/gemini-run.log"; return; } else + [[ -f "$c/${run_prefix}.out" && -s "$c/${run_prefix}.out" ]] && { echo "$c/${run_prefix}.out"; return; } + [[ -f "$c/${run_prefix}.log" && -s "$c/${run_prefix}.log" ]] && { echo "$c/${run_prefix}.log"; return; } + # Fallback [[ -f "$c/codex-run.out" && -s "$c/codex-run.out" ]] && { echo "$c/codex-run.out"; return; } - [[ -f "$c/codex-run.log" && -s "$c/codex-run.log" ]] && { echo "$c/codex-run.log"; return; } + [[ -f "$c/gemini-run.out" && -s "$c/gemini-run.out" ]] && { echo "$c/gemini-run.out"; return; } fi } @@ -166,6 +216,15 @@ _humanize_monitor_skill() { echo "" } + # Build the monitor title based on filter + _skill_monitor_title() { + case "$tool_filter" in + codex) echo " Humanize Skill Monitor [codex]" ;; + gemini) echo " Humanize Skill Monitor [gemini]" ;; + *) echo " Humanize Skill Monitor" ;; + esac + } + # Draw the status bar at the top _skill_draw_status_bar() { local latest_dir="$1" @@ -186,17 +245,21 @@ _humanize_monitor_skill() { # Parse latest invocation metadata local inv_status="running" model="N/A" effort="N/A" duration="N/A" started_at="N/A" + local inv_tool="unknown" if [[ -n "$latest_dir" && -f "$latest_dir/metadata.md" ]]; then inv_status=$(monitor_get_yaml_value "status" "$latest_dir/metadata.md") model=$(monitor_get_yaml_value "model" "$latest_dir/metadata.md") effort=$(monitor_get_yaml_value "effort" "$latest_dir/metadata.md") duration=$(monitor_get_yaml_value "duration" "$latest_dir/metadata.md") started_at=$(monitor_get_yaml_value "started_at" "$latest_dir/metadata.md") + inv_tool=$(monitor_get_yaml_value "tool" "$latest_dir/metadata.md") elif [[ -n "$latest_dir" && -f "$latest_dir/input.md" ]]; then model=$(grep -E '^- Model:' "$latest_dir/input.md" 2>/dev/null | sed 's/- Model: //') effort=$(grep -E '^- Effort:' "$latest_dir/input.md" 2>/dev/null | sed 's/- Effort: //') + inv_tool=$(grep -E '^- Tool:' "$latest_dir/input.md" 2>/dev/null | sed 's/- Tool: //') fi inv_status="${inv_status:-unknown}"; model="${model:-N/A}"; effort="${effort:-N/A}" + inv_tool="${inv_tool:-unknown}" # Status color local status_color="$dim" @@ -235,11 +298,19 @@ _humanize_monitor_skill() { cache_display="...${cache_display: -$csuffix_len}" fi + # Model display: for gemini, no effort; for codex, show (effort) + local model_display="$model" + if [[ "$inv_tool" == "gemini" ]] || [[ "$effort" == "N/A" ]]; then + model_display="$model" + else + model_display="$model ($effort)" + fi + tput sc tput cup 0 0 # Line 1: Title - printf "${bg}${bold}%-${term_width}s${reset}${clr_eol}\n" " Humanize Skill Monitor" + printf "${bg}${bold}%-${term_width}s${reset}${clr_eol}\n" "$(_skill_monitor_title)" # Line 2: Aggregate stats printf "${cyan}Total:${reset} ${bold}${total}${reset} invocations" [[ "$success" -gt 0 ]] && printf " | ${green}${success} success${reset}" @@ -248,8 +319,8 @@ _humanize_monitor_skill() { [[ "$empty" -gt 0 ]] && printf " | ${yellow}${empty} empty${reset}" [[ "$running" -gt 0 ]] && printf " | ${yellow}${running} running${reset}" printf "${clr_eol}\n" - # Line 3: Focused invocation status + model + duration - printf "${magenta}Focused:${reset} ${status_color}%s${reset} | ${yellow}Model:${reset} %s (%s) | ${cyan}Duration:${reset} %s${clr_eol}\n" "$inv_status" "$model" "$effort" "${duration:-N/A}" + # Line 3: Focused invocation status + tool + model + duration + printf "${magenta}Focused:${reset} ${status_color}%s${reset} | ${dim}[%s]${reset} ${yellow}Model:${reset} %s | ${cyan}Duration:${reset} %s${clr_eol}\n" "$inv_status" "$inv_tool" "$model_display" "${duration:-N/A}" # Line 4: Started at printf "${cyan}Started:${reset} %s${clr_eol}\n" "$start_display" # Line 5: Question @@ -269,7 +340,9 @@ _humanize_monitor_skill() { if [[ "$once_mode" == "true" ]]; then local latest=$(_skill_find_latest_dir) if [[ -z "$latest" ]]; then - echo "No skill invocations found in $skill_dir" + local filter_msg="" + [[ -n "$tool_filter" ]] && filter_msg=" (filter: $tool_filter)" + echo "No skill invocations found in $skill_dir$filter_msg" return 1 fi @@ -283,24 +356,29 @@ _humanize_monitor_skill() { local -a stats humanize_split_to_array stats "$(_skill_count_stats)" local inv_status="running" model="N/A" effort="N/A" duration="N/A" started_at="N/A" + local inv_tool="unknown" if [[ -f "$focus_dir/metadata.md" ]]; then inv_status=$(monitor_get_yaml_value "status" "$focus_dir/metadata.md") model=$(monitor_get_yaml_value "model" "$focus_dir/metadata.md") effort=$(monitor_get_yaml_value "effort" "$focus_dir/metadata.md") duration=$(monitor_get_yaml_value "duration" "$focus_dir/metadata.md") started_at=$(monitor_get_yaml_value "started_at" "$focus_dir/metadata.md") + inv_tool=$(monitor_get_yaml_value "tool" "$focus_dir/metadata.md") fi + inv_tool="${inv_tool:-unknown}" local question=$(_skill_get_question "$focus_dir") local cache_dir=$(_skill_find_cache_dir "$focus_dir") + local title=$(_skill_monitor_title) echo "==========================================" - echo " Humanize Skill Monitor" + echo "$title" echo "==========================================" echo "" echo "Total Invocations: ${stats[0]}" echo " Success: ${stats[1]} Error: ${stats[2]} Timeout: ${stats[3]} Empty: ${stats[4]} Running: ${stats[5]}" echo "" echo "Focused: $(basename "$focus_dir")" + echo " Tool: ${inv_tool}" echo " Status: ${inv_status:-unknown}" echo " Model: ${model:-N/A} (${effort:-N/A})" echo " Duration: ${duration:-N/A}" @@ -329,14 +407,16 @@ _humanize_monitor_skill() { while IFS= read -r d; do [[ -z "$d" ]] && continue local name=$(basename "$d") - local st="running" dur="" + local st="running" dur="" t="?" if [[ -f "$d/metadata.md" ]]; then st=$(monitor_get_yaml_value "status" "$d/metadata.md") dur=$(monitor_get_yaml_value "duration" "$d/metadata.md") + t=$(monitor_get_yaml_value "tool" "$d/metadata.md") fi + t="${t:-?}" local q=$(_skill_get_question "$d") [[ ${#q} -gt 50 ]] && q="${q:0:47}..." - printf " %-38s %-14s %-6s %s\n" "$name" "$st" "$dur" "$q" + printf " %-38s %-7s %-14s %-6s %s\n" "$name" "[$t]" "$st" "$dur" "$q" ((count++)) [[ $count -ge 10 ]] && break done < <(_skill_list_dirs_sorted) diff --git a/skills/ask-gemini/SKILL.md b/skills/ask-gemini/SKILL.md new file mode 100644 index 00000000..e31cdd57 --- /dev/null +++ b/skills/ask-gemini/SKILL.md @@ -0,0 +1,61 @@ +--- +name: ask-gemini +description: Consult Gemini as an independent expert with deep web research. Sends a question or task to Gemini CLI and returns a research-backed response. +argument-hint: "[--gemini-model MODEL] [--gemini-timeout SECONDS] [question or task]" +allowed-tools: "Bash(${CLAUDE_PLUGIN_ROOT}/scripts/ask-gemini.sh:*)" +--- + +# Ask Gemini + +Send a question or task to Gemini and return a research-backed response. +Gemini is always instructed to perform web research via Google Search, +making this ideal for deep-research tasks that benefit from up-to-date +internet information. + +## How to Use + +Do not pass free-form user text to the shell unquoted. The question or task may contain spaces or shell metacharacters such as `(`, `)`, `;`, `#`, `*`, or `[`. + +If the user only supplied a question or task, execute: + +```bash +"${CLAUDE_PLUGIN_ROOT}/scripts/ask-gemini.sh" "$ARGUMENTS" +``` + +If the user supplied flags such as `--gemini-model` or `--gemini-timeout`, reconstruct the command so those flags remain separate shell arguments and the remaining free-form question is passed as one quoted final argument. + +Example: + +```bash +"${CLAUDE_PLUGIN_ROOT}/scripts/ask-gemini.sh" --gemini-model gemini-2.5-pro "What are the latest Rust async runtime benchmarks?" +``` + +Never run this unsafe form: + +```bash +"${CLAUDE_PLUGIN_ROOT}/scripts/ask-gemini.sh" $ARGUMENTS +``` + +because the shell will re-parse the question text and can fail before `ask-gemini.sh` starts. + +## Interpreting Output + +- The script outputs Gemini's response to **stdout** and status info to **stderr** +- Read the stdout output carefully and incorporate Gemini's response into your answer +- Gemini's responses are research-backed with web sources; relay source citations when available +- If the script exits with a non-zero code, report the error to the user + +## Error Handling + +| Exit Code | Meaning | +|-----------|---------| +| 0 | Success - Gemini response is in stdout | +| 1 | Validation error (missing gemini, empty question, invalid flags) | +| 124 | Timeout - suggest using `--gemini-timeout` with a larger value | +| Other | Gemini process error - report the exit code and any stderr output | + +## Notes + +- The response is saved to `.humanize/skill//output.md` for reference +- Default model is `gemini-3.1-pro-preview` with a 3600-second timeout +- Gemini is always instructed to perform Google Search for up-to-date information From 883e3f5bb8106cea4153d9f5e469b2fa7a8d6849 Mon Sep 17 00:00:00 2001 From: Sihao Liu Date: Sat, 28 Mar 2026 18:29:34 -0700 Subject: [PATCH 2/2] Revert version back to 1.16.0 to match dev branch --- .claude-plugin/marketplace.json | 2 +- .claude-plugin/plugin.json | 2 +- README.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index e4e1adf3..588e3b1d 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -8,7 +8,7 @@ "name": "humanize", "source": "./", "description": "Humanize - An iterative development plugin that uses Codex to review Claude's work. Creates a feedback loop where Claude implements plans and Codex independently reviews progress, ensuring quality through continuous refinement.", - "version": "1.17.0" + "version": "1.16.0" } ] } diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index bbab412d..0f9ab32b 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "humanize", "description": "Humanize - An iterative development plugin that uses Codex to review Claude's work. Creates a feedback loop where Claude implements plans and Codex independently reviews progress, ensuring quality through continuous refinement.", - "version": "1.17.0", + "version": "1.16.0", "author": { "name": "humania-org" }, diff --git a/README.md b/README.md index 0a93dd30..bd7f9145 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Humanize -**Current Version: 1.17.0** +**Current Version: 1.16.0** > Derived from the [GAAC (GitHub-as-a-Context)](https://github.com/SihaoLiu/gaac) project.