Skip to content

feat: add idle-gated tool auto-update to keep all installed tools fresh#2145

Merged
marcusquinn merged 3 commits intomainfrom
feature/tool-auto-update
Feb 22, 2026
Merged

feat: add idle-gated tool auto-update to keep all installed tools fresh#2145
marcusquinn merged 3 commits intomainfrom
feature/tool-auto-update

Conversation

@marcusquinn
Copy link
Owner

@marcusquinn marcusquinn commented Feb 22, 2026

Summary

  • Adds automatic tool freshness checking to auto-update-helper.sh — keeps all npm, brew, and pip tools installed by setup.sh up to date (OpenCode, gh, shellcheck, osgrep, MCP servers, etc.)
  • Only runs when user has been idle 6+ hours (sleeping/away) to avoid disrupting active work
  • Cross-platform idle detection: macOS IOKit, Linux xprintidle/dbus/w(1), headless servers always eligible

Details

New functions in auto-update-helper.sh

Function Purpose
get_user_idle_seconds() Cross-platform user idle time detection
check_tool_freshness() 6h time gate + idle gate, delegates to tool-version-check.sh --update
update_tool_check_timestamp() Records check timestamp + lifetime update count in state file

Idle detection strategy

Platform Method Notes
macOS ioreg -c IOHIDSystem HIDIdleTime Nanosecond precision, works over SSH
Linux X11 xprintidle Millisecond precision, requires $DISPLAY
Linux Wayland dbus-send GNOME ScreenSaver GetActiveTime seconds
Linux TTY/SSH w -h session idle parsing Handles 3:42, 2days, 23:15m formats
Headless server Always idle (999999s) No display + no TTY users = no human
Fallback 0 (assume active) Safe default — never updates unexpectedly

Configuration

Env var Default Purpose
AIDEVOPS_TOOL_AUTO_UPDATE true Set false to disable
AIDEVOPS_TOOL_FRESHNESS_HOURS 6 Hours between checks
AIDEVOPS_TOOL_IDLE_HOURS 6 Required idle hours before updating

Testing

  • ShellCheck: clean (only pre-existing SC1091 info for external source)
  • Bash syntax: OK
  • Idle detection: tested live on macOS (correct 4s idle while typing)
  • status command: shows tool check state + current idle time + eligibility
  • help command: updated with new env vars and workflow docs

Summary by CodeRabbit

  • New Features
    • Added cross-platform user idle detection
    • Implemented 6-hourly tool freshness checks triggered only when user is idle
    • Extended status display to show tool check timestamps, update counts, and current idle time with update eligibility indicator

Adds check_tool_freshness() to auto-update-helper.sh that runs
tool-version-check.sh --update every 6 hours, but only when the user
has been idle for 6+ hours (sleeping/away). Covers all npm, brew, and
pip tools installed by setup.sh (OpenCode, gh, shellcheck, osgrep, etc).

Cross-platform idle detection:
- macOS: IOKit HIDIdleTime (works even over SSH)
- Linux X11: xprintidle
- Linux Wayland: dbus-send to GNOME ScreenSaver
- Linux TTY/SSH: w(1) session idle parsing
- Headless servers: always idle (no human at keyboard)

Configurable via AIDEVOPS_TOOL_AUTO_UPDATE, AIDEVOPS_TOOL_FRESHNESS_HOURS,
and AIDEVOPS_TOOL_IDLE_HOURS environment variables.
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 22, 2026

Warning

Rate limit exceeded

@marcusquinn has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 15 minutes and 19 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

Walkthrough

A new idle-gated tool freshness workflow has been integrated into the auto-update helper script. Cross-platform user idle detection and 6-hourly tool freshness checks execute only when idle thresholds are exceeded. State tracking persists tool update activity and timestamps, with integration points throughout cmd_check and cmd_status flows.

Changes

Cohort / File(s) Summary
Tool Freshness Workflow Integration
.agents/scripts/auto-update-helper.sh
Added three new functions: get_user_idle_seconds() for cross-platform idle detection with macOS/Linux fallbacks; check_tool_freshness() for idle-gated, 6-hourly tool freshness checks invoking tool-version-check.sh; update_tool_check_timestamp() for state persistence (last_tool_check, tool_updates_applied). Integrated check_tool_freshness() calls into cmd_check paths (no-update, up-to-date, update-start, error) and post-update flows. Extended cmd_status() output to display last_tool_check timestamp, tool_updates_applied count, and current idle time with eligibility messaging. Added configuration defaults (TOOL_FRESHNESS_HOURS=6, TOOL_IDLE_HOURS=6) and environment variable documentation (AIDEVOPS_TOOL_AUTO_UPDATE, AIDEVOPS_TOOL_FRESHNESS_HOURS, AIDEVOPS_TOOL_IDLE_HOURS).

Sequence Diagram(s)

sequenceDiagram
    participant User as User/System
    participant Script as auto-update-helper.sh
    participant StateFile as State File
    participant ToolCheck as tool-version-check.sh
    participant Status as Status Output

    User->>Script: Trigger update flow (cmd_check)
    Script->>Script: get_user_idle_seconds()
    Script->>Script: Check TOOL_IDLE_HOURS threshold
    
    alt User Idle Exceeds Threshold
        Script->>StateFile: Check last_tool_check timestamp
        StateFile-->>Script: Return last check time
        
        alt 6+ Hours Since Last Check
            Script->>ToolCheck: Invoke --update --quiet
            ToolCheck-->>Script: Return update status
            Script->>StateFile: update_tool_check_timestamp()
            StateFile-->>Script: Persist last_tool_check & tool_updates_applied
        else Within Freshness Window
            Script->>Script: Skip tool update
        end
    else User Not Idle
        Script->>Script: Skip idle-gated check
    end
    
    Script->>Status: cmd_status() with idle/update metrics
    Status-->>User: Display last_tool_check, tool_updates_applied, idle time
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

  • #1995 — Adds OpenClaw freshness-check functions with identical state/timestamp-handling and cmd_check/cmd_status integration patterns to the same auto-update-helper.sh script.
  • #955 — Introduces cmd_check and cmd_status command flows that are now extended here with tool freshness integration points.
  • #56 — Implements tool-version-check.sh CLI and setup hooks that are invoked by the new check_tool_freshness() workflow.

Poem

Idle users, fresh tools bloom,
Six-hour checks dispel the gloom—
Cross-platform sleuth detects the rest,
State persists, automation's blessed. 🛠️✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and concisely describes the main change: adding idle-gated tool auto-update functionality to keep installed tools fresh.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/tool-auto-update

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist
Copy link

Summary of Changes

Hello @marcusquinn, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces an intelligent auto-update mechanism for development tools, designed to keep the environment fresh without interrupting active user sessions. By leveraging cross-platform idle detection, it ensures that updates for various package managers are performed discreetly in the background, improving system maintenance and reducing manual overhead for users.

Highlights

  • Idle-Gated Tool Auto-Update: Implemented a new feature to automatically check and update installed tools (npm, brew, pip) every 6 hours, but only when the user has been idle for at least 6 hours to prevent disruption.
  • Cross-Platform Idle Detection: Added robust idle detection mechanisms supporting macOS (IOKit), Linux (xprintidle, dbus, w(1) command parsing), and a fallback for headless servers, ensuring broad compatibility.
  • Configurable Environment Variables: Introduced new environment variables (AIDEVOPS_TOOL_AUTO_UPDATE, AIDEVOPS_TOOL_FRESHNESS_HOURS, AIDEVOPS_TOOL_IDLE_HOURS) to allow users to customize or disable the tool auto-update behavior.
  • Enhanced Status Reporting: Updated the status command to display the last tool check timestamp, lifetime tool updates applied, and the current user idle time with an indication of tool update eligibility.
Changelog
  • .agents/scripts/auto-update-helper.sh
    • Added documentation for the new 6-hourly tool freshness check.
    • Introduced new environment variables for configuring tool auto-update behavior.
    • Defined default values for tool freshness and idle hours.
    • Implemented get_user_idle_seconds for cross-platform idle time detection.
    • Implemented check_tool_freshness to manage the idle-gated tool update process.
    • Implemented update_tool_check_timestamp to record tool update activity.
    • Integrated check_tool_freshness into various execution paths of the cmd_check function.
    • Added a direct call to check_tool_freshness in the cmd_check function.
    • Enhanced the cmd_status function to display tool update status and user idle time.
    • Added a status message for disabled tool auto-update in cmd_status.
    • Updated help output to include new environment variables and workflow details for tool auto-updates.
Activity
  • No specific activity (comments, reviews, progress) has been recorded for this pull request yet.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@github-actions
Copy link

🔍 Code Quality Report

�[0;35m[MONITOR]�[0m Code Review Monitoring Report

�[0;34m[INFO]�[0m Latest Quality Status:
SonarCloud: 0 bugs, 0 vulnerabilities, 40 code smells

�[0;34m[INFO]�[0m Recent monitoring activity:
Sun Feb 22 15:51:39 UTC 2026: Code review monitoring started
Sun Feb 22 15:51:39 UTC 2026: SonarCloud - Bugs: 0, Vulnerabilities: 0, Code Smells: 40

📈 Current Quality Metrics

  • BUGS: 0
  • CODE SMELLS: 40
  • VULNERABILITIES: 0

Generated on: Sun Feb 22 15:51:42 UTC 2026


Generated by AI DevOps Framework Code Review Monitoring

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

The pull request introduces an idle-gated tool auto-update feature to auto-update-helper.sh, which is a valuable addition for maintaining tool freshness without disrupting active work. The implementation of cross-platform idle detection is comprehensive. However, the review identified several adherence issues with the repository's shell scripting rules, specifically regarding variable declarations and error suppression. Additionally, a potential bug related to octal interpretation in Bash arithmetic was found in the idle time parsing logic, which could lead to script errors when processing times with leading zeros.

Comment on lines +665 to +681
local parsed=0
if [[ "$idle_field" =~ ^([0-9]+)days$ ]]; then
local d="${BASH_REMATCH[1]}"
parsed=$((d * 86400))
elif [[ "$idle_field" =~ ^([0-9]+):([0-9]+)m$ ]]; then
# hours:minutes format (e.g. "23:15m")
local hh="${BASH_REMATCH[1]}" mm="${BASH_REMATCH[2]}"
parsed=$((hh * 3600 + mm * 60))
elif [[ "$idle_field" =~ ^([0-9]+):([0-9]+)$ ]]; then
# minutes:seconds format (e.g. "3:42")
local mm="${BASH_REMATCH[1]}" ss="${BASH_REMATCH[2]}"
parsed=$((mm * 60 + ss))
elif [[ "$idle_field" =~ ^([0-9]+)\.([0-9]+)s$ ]]; then
parsed="${BASH_REMATCH[1]}"
elif [[ "$idle_field" =~ ^([0-9]+)s$ ]]; then
parsed="${BASH_REMATCH[1]}"
fi

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

This block contains two issues:

  1. Octal Interpretation Bug: In Bash arithmetic $(()), numbers with leading zeros (like 08 or 09) are interpreted as octal. This will cause a syntax error if w returns times like 08:42. Using the 10# prefix forces base-10 interpretation.
  2. Rule Violation: Local variables should be declared and assigned separately.
			local parsed
			parsed=0
			if [[ "$idle_field" =~ ^([0-9]+)days$ ]]; then
				local d
				d="${BASH_REMATCH[1]}"
				parsed=$((10#$d * 86400))
			elif [[ "$idle_field" =~ ^([0-9]+):([0-9]+)m$ ]]; then
				# hours:minutes format (e.g. "23:15m")
				local hh mm
				hh="${BASH_REMATCH[1]}"
				mm="${BASH_REMATCH[2]}"
				parsed=$((10#$hh * 3600 + 10#$mm * 60))
			elif [[ "$idle_field" =~ ^([0-9]+):([0-9]+)$ ]]; then
				# minutes:seconds format (e.g. "3:42")
				local mm ss
				mm="${BASH_REMATCH[1]}"
				ss="${BASH_REMATCH[2]}"
				parsed=$((10#$mm * 60 + 10#$ss))
			elif [[ "$idle_field" =~ ^([0-9]+)\.([0-9]+)s$ ]]; then
				parsed=$((10#${BASH_REMATCH[1]}))
			elif [[ "$idle_field" =~ ^([0-9]+)s$ ]]; then
				parsed=$((10#${BASH_REMATCH[1]}))
			fi
References
  1. When declaring and assigning local variables in shell scripts, separate the local declaration from the assignment if command substitution is involved, to ensure exit code safety. For consistency, this pattern should be maintained even for simple assignments.

Comment on lines +653 to +655
local min_idle=999999
local found_user=false
local idle_field

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

According to the repository rules, variables should be declared and assigned separately to ensure exit code safety and maintain consistency.

Suggested change
local min_idle=999999
local found_user=false
local idle_field
local min_idle
min_idle=999999
local found_user
found_user=false
local idle_field
References
  1. When declaring and assigning local variables in shell scripts, separate the local declaration from the assignment if command substitution is involved, to ensure exit code safety. For consistency, this pattern should be maintained even for simple assignments.

[[ "$line" =~ ^USER ]] && continue
[[ "$line" =~ ^[[:space:]]*$ ]] && continue
# Extract idle field (5th column)
idle_field=$(echo "$line" | awk '{print $5}')

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Calling awk inside a loop can be inefficient in shell scripts. Since the output of w is space-delimited, you can use the built-in read command to extract the required field more efficiently.

Suggested change
idle_field=$(echo "$line" | awk '{print $5}')
read -r _ _ _ _ idle_field _ <<< "$line"
References
  1. In shell scripts, use pure Bash parameter expansion for string manipulations like calculating indentation within loops to avoid spawning subshells and improve performance.

Comment on lines +720 to +734
local freshness_hours="${AIDEVOPS_TOOL_FRESHNESS_HOURS:-$DEFAULT_TOOL_FRESHNESS_HOURS}"
if ! [[ "$freshness_hours" =~ ^[0-9]+$ ]] || [[ "$freshness_hours" -eq 0 ]]; then
log_warn "AIDEVOPS_TOOL_FRESHNESS_HOURS='${freshness_hours}' is not a positive integer — using default (${DEFAULT_TOOL_FRESHNESS_HOURS}h)"
freshness_hours="$DEFAULT_TOOL_FRESHNESS_HOURS"
fi
local freshness_seconds=$((freshness_hours * 3600))

# Read last tool check timestamp from state file
local last_tool_check=""
if [[ -f "$STATE_FILE" ]] && command -v jq &>/dev/null; then
last_tool_check=$(jq -r '.last_tool_check // empty' "$STATE_FILE" 2>/dev/null || true)
fi

# Determine if check is needed (time gate)
local needs_check=true

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Multiple local variables in this block are assigned during declaration, which violates the repository rules for variable declaration.

Suggested change
local freshness_hours="${AIDEVOPS_TOOL_FRESHNESS_HOURS:-$DEFAULT_TOOL_FRESHNESS_HOURS}"
if ! [[ "$freshness_hours" =~ ^[0-9]+$ ]] || [[ "$freshness_hours" -eq 0 ]]; then
log_warn "AIDEVOPS_TOOL_FRESHNESS_HOURS='${freshness_hours}' is not a positive integer — using default (${DEFAULT_TOOL_FRESHNESS_HOURS}h)"
freshness_hours="$DEFAULT_TOOL_FRESHNESS_HOURS"
fi
local freshness_seconds=$((freshness_hours * 3600))
# Read last tool check timestamp from state file
local last_tool_check=""
if [[ -f "$STATE_FILE" ]] && command -v jq &>/dev/null; then
last_tool_check=$(jq -r '.last_tool_check // empty' "$STATE_FILE" 2>/dev/null || true)
fi
# Determine if check is needed (time gate)
local needs_check=true
local freshness_hours
freshness_hours="${AIDEVOPS_TOOL_FRESHNESS_HOURS:-$DEFAULT_TOOL_FRESHNESS_HOURS}"
if ! [[ "$freshness_hours" =~ ^[0-9]+$ ]] || [[ "$freshness_hours" -eq 0 ]]; then
log_warn "AIDEVOPS_TOOL_FRESHNESS_HOURS='${freshness_hours}' is not a positive integer — using default (${DEFAULT_TOOL_FRESHNESS_HOURS}h)"
freshness_hours="$DEFAULT_TOOL_FRESHNESS_HOURS"
fi
local freshness_seconds
freshness_seconds=$((freshness_hours * 3600))
# Read last tool check timestamp from state file
local last_tool_check
last_tool_check=""
if [[ -f "$STATE_FILE" ]] && command -v jq &>/dev/null; then
last_tool_check=$(jq -r '.last_tool_check // empty' "$STATE_FILE" 2>/dev/null || true)
fi
# Determine if check is needed (time gate)
local needs_check
needs_check=true
References
  1. When declaring and assigning local variables in shell scripts, separate the local declaration from the assignment if command substitution is involved, to ensure exit code safety. For consistency, this pattern should be maintained even for simple assignments.

Comment on lines +756 to +773
local idle_hours="${AIDEVOPS_TOOL_IDLE_HOURS:-$DEFAULT_TOOL_IDLE_HOURS}"
if ! [[ "$idle_hours" =~ ^[0-9]+$ ]] || [[ "$idle_hours" -eq 0 ]]; then
log_warn "AIDEVOPS_TOOL_IDLE_HOURS='${idle_hours}' is not a positive integer — using default (${DEFAULT_TOOL_IDLE_HOURS}h)"
idle_hours="$DEFAULT_TOOL_IDLE_HOURS"
fi
local idle_threshold_seconds=$((idle_hours * 3600))

local user_idle_seconds
user_idle_seconds=$(get_user_idle_seconds)
if [[ $user_idle_seconds -lt $idle_threshold_seconds ]]; then
local idle_h=$((user_idle_seconds / 3600))
local idle_m=$(((user_idle_seconds % 3600) / 60))
log_info "User idle ${idle_h}h${idle_m}m (need ${idle_hours}h) — deferring tool updates"
return 0
fi

# Locate tool-version-check.sh
local tool_check_script="$HOME/.aidevops/agents/scripts/tool-version-check.sh"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Adhere to the repository rules by declaring and assigning local variables separately.

	local idle_hours
	idle_hours="${AIDEVOPS_TOOL_IDLE_HOURS:-$DEFAULT_TOOL_IDLE_HOURS}"
	if ! [[ "$idle_hours" =~ ^[0-9]+$ ]] || [[ "$idle_hours" -eq 0 ]]; then
		log_warn "AIDEVOPS_TOOL_IDLE_HOURS='${idle_hours}' is not a positive integer — using default (${DEFAULT_TOOL_IDLE_HOURS}h)"
		idle_hours="$DEFAULT_TOOL_IDLE_HOURS"
	fi
	local idle_threshold_seconds
	idle_threshold_seconds=$((idle_hours * 3600))

	local user_idle_seconds
	user_idle_seconds=$(get_user_idle_seconds)
	if [[ $user_idle_seconds -lt $idle_threshold_seconds ]]; then
		local idle_h
		idle_h=$((user_idle_seconds / 3600))
		local idle_m
		idle_m=$(((user_idle_seconds % 3600) / 60))
		log_info "User idle ${idle_h}h${idle_m}m (need ${idle_hours}h) — deferring tool updates"
		return 0
	fi

	# Locate tool-version-check.sh
	local tool_check_script
	tool_check_script="$HOME/.aidevops/agents/scripts/tool-version-check.sh"
References
  1. When declaring and assigning local variables in shell scripts, separate the local declaration from the assignment if command substitution is involved, to ensure exit code safety. For consistency, this pattern should be maintained even for simple assignments.

fi

# Count updates from output (best-effort: count lines with "Updated" or arrow)
local tool_updates=0

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Declare and assign separately per repository rules.

Suggested change
local tool_updates=0
local tool_updates
tool_updates=0
References
  1. When declaring and assigning local variables in shell scripts, separate the local declaration from the assignment if command substitution is involved, to ensure exit code safety. For consistency, this pattern should be maintained even for simple assignments.

# Args: $1 = number of tool updates applied (default: 0)
#######################################
update_tool_check_timestamp() {
local updates_count="${1:-0}"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Declare and assign separately per repository rules.

Suggested change
local updates_count="${1:-0}"
local updates_count
updates_count="${1:-0}"
References
  1. When declaring and assigning local variables in shell scripts, separate the local declaration from the assignment if command substitution is involved, to ensure exit code safety. For consistency, this pattern should be maintained even for simple assignments.

--argjson count "$updates_count" \
'. + {last_tool_check: $ts} |
.tool_updates_applied = ((.tool_updates_applied // 0) + $count)' \
"$STATE_FILE" >"$tmp_state" 2>/dev/null && mv "$tmp_state" "$STATE_FILE"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The repository rules prohibit blanket suppression of errors with 2>/dev/null when jq is used, to ensure that actual syntax or system errors remain visible for debugging.

Suggested change
"$STATE_FILE" >"$tmp_state" 2>/dev/null && mv "$tmp_state" "$STATE_FILE"
"$STATE_FILE" >"$tmp_state" && mv "$tmp_state" "$STATE_FILE"
References
  1. In shell scripts with 'set -e', use '|| true' to prevent the script from exiting when a command like 'jq' fails on an optional lookup. Do not suppress stderr with '2>/dev/null' so that actual syntax or system errors remain visible for debugging.

… loop efficiency

- Fix octal interpretation bug: use 10# prefix for w(1) idle time parsing
  to handle leading zeros (e.g. '08:09' would fail as invalid octal)
- Separate local variable declarations from assignments per repo conventions
- Replace awk in w(1) parsing loop with read builtin for efficiency
@github-actions
Copy link

🔍 Code Quality Report

�[0;35m[MONITOR]�[0m Code Review Monitoring Report

�[0;34m[INFO]�[0m Latest Quality Status:
SonarCloud: 0 bugs, 0 vulnerabilities, 40 code smells

�[0;34m[INFO]�[0m Recent monitoring activity:
Sun Feb 22 15:57:11 UTC 2026: Code review monitoring started
Sun Feb 22 15:57:12 UTC 2026: SonarCloud - Bugs: 0, Vulnerabilities: 0, Code Smells: 40

📈 Current Quality Metrics

  • BUGS: 0
  • CODE SMELLS: 40
  • VULNERABILITIES: 0

Generated on: Sun Feb 22 15:57:14 UTC 2026


Generated by AI DevOps Framework Code Review Monitoring

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (2)
.agents/scripts/auto-update-helper.sh (2)

389-465: Consider extracting the shared time-gate and timestamp-update patterns into helpers.

The timestamp-parse + elapsed-check block and the jq-based state-file update are duplicated three times across check_skill_freshness / check_openclaw_freshness / check_tool_freshness and their corresponding update_*_check_timestamp functions. A pair of helpers (e.g., _is_freshness_elapsed <state_key> <threshold_secs> and _update_check_timestamp <state_key> <count>) would cut ~90 lines and make adding a fourth freshness gate trivial.

Also applies to: 502-599, 713-810

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.agents/scripts/auto-update-helper.sh around lines 389 - 465, The code
duplicates timestamp-parse/elapsed-check and jq-based state updates across
check_skill_freshness, check_openclaw_freshness, check_tool_freshness and their
update_*_check_timestamp helpers; extract that logic into two helpers (e.g.,
_is_freshness_elapsed <state_key> <threshold_secs> that reads STATE_FILE via jq,
parses ISO timestamp into epoch (handle Darwin vs Linux using date), computes
elapsed and returns 0/1 plus elapsed in a variable, and _update_check_timestamp
<state_key> <count> that atomically updates/creates STATE_FILE with jq setting
the key to current UTC ISO timestamp and an optional count field), then replace
the duplicated blocks in check_skill_freshness (and the other check_* functions)
to call _is_freshness_elapsed before running checks and call
_update_check_timestamp at the end with the skill_updates value; keep existing
log messages and feature flags (AIDEVOPS_SKILL_AUTO_UPDATE,
AIDEVOPS_SKILL_FRESHNESS_HOURS) and continue to use skill-update-helper.sh
invocation unchanged.

649-692: w fallback without -h may set min_idle=0 due to the uptime header line.

When w -h is unavailable and the fallback w 2>/dev/null runs, the first output line is the uptime summary (e.g., " 12:34 up 2 days, ..."). It doesn't match the ^USER or blank-line guards, so it is processed: $5 won't match any idle format leaving parsed=0, but found_user=true gets set and min_idle drops to 0. The function then returns 0 (assume active), quietly suppressing tool updates on systems without w -h support.

Since w -h is near-universal, this is low-risk, but a simple uptime-line guard would make the fallback more robust.

♻️ Add an uptime header guard
 		while IFS= read -r line; do
 			# Skip header lines
 			[[ "$line" =~ ^USER ]] && continue
 			[[ "$line" =~ ^[[:space:]]*$ ]] && continue
+			# Skip uptime summary line (starts with space + digit, e.g. " 12:34:56 up ...")
+			[[ "$line" =~ ^[[:space:]]+[0-9] ]] && continue
 			# Extract idle field (5th column)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.agents/scripts/auto-update-helper.sh around lines 649 - 692, The loop
parsing w output can treat the uptime summary line as a user line when w -h is
missing, causing parsed to remain 0 and min_idle to be set to 0; inside the
while read loop (which uses variables min_idle, found_user, idle_field, parsed),
add a guard to skip uptime-summary lines before extracting idle_field — e.g.
check and continue on lines matching the uptime pattern like
^[[:space:]]*[0-9]+:[0-9]+[[:space:]]+up (or containing " up " and "load
average") so the uptime header is ignored and only real user lines set
found_user and parsed.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.agents/scripts/auto-update-helper.sh:
- Around line 636-647: The dbus call is using
org.gnome.ScreenSaver.GetActiveTime which reports screensaver runtime rather
than session idle time; change the dbus-send invocation that currently calls
GetActiveTime to call GetSessionIdleTime and parse its numeric return into the
existing idle_secs variable (the dbus-send line and the awk that reads into
idle_secs), then keep the same validation (^[0-9]+$ and -gt 0) and echo/return
behavior so idle detection reflects true session idle time.
- Around line 796-800: The current command substitution for tool_updates
captures duplicate output when grep returns non-zero, producing "0\n0" and
breaking the numeric test; change the assignment so the grep -cE pipeline is
inside the substitution but the fallback runs outside it (e.g., run
tool_updates=$(echo "$update_output" | grep -cE '(Updated|→|->)' 2>/dev/null) ||
tool_updates=0) so that when set -o pipefail causes a non-match exit status the
external || handler assigns a clean 0; update the code that sets tool_updates
(referencing the variables tool_updates and update_output and the grep -cE
pipeline) and keep the redirected stderr 2>/dev/null as before.
- Around line 1242-1247: The echo block in cmd_status uses
idle_threshold="${AIDEVOPS_TOOL_IDLE_HOURS:-$DEFAULT_TOOL_IDLE_HOURS}" directly
in arithmetic, which can crash if the env var is non-integer; mirror the integer
validation used in check_tool_freshness by normalizing/validating
AIDEVOPS_TOOL_IDLE_HOURS into an integer (falling back to
DEFAULT_TOOL_IDLE_HOURS) before computing $((idle_threshold * 3600)), e.g.,
reuse the same parsing/regex or normalization logic from the
check_tool_freshness function so cmd_status uses a safe integer idle_threshold.

---

Nitpick comments:
In @.agents/scripts/auto-update-helper.sh:
- Around line 389-465: The code duplicates timestamp-parse/elapsed-check and
jq-based state updates across check_skill_freshness, check_openclaw_freshness,
check_tool_freshness and their update_*_check_timestamp helpers; extract that
logic into two helpers (e.g., _is_freshness_elapsed <state_key> <threshold_secs>
that reads STATE_FILE via jq, parses ISO timestamp into epoch (handle Darwin vs
Linux using date), computes elapsed and returns 0/1 plus elapsed in a variable,
and _update_check_timestamp <state_key> <count> that atomically updates/creates
STATE_FILE with jq setting the key to current UTC ISO timestamp and an optional
count field), then replace the duplicated blocks in check_skill_freshness (and
the other check_* functions) to call _is_freshness_elapsed before running checks
and call _update_check_timestamp at the end with the skill_updates value; keep
existing log messages and feature flags (AIDEVOPS_SKILL_AUTO_UPDATE,
AIDEVOPS_SKILL_FRESHNESS_HOURS) and continue to use skill-update-helper.sh
invocation unchanged.
- Around line 649-692: The loop parsing w output can treat the uptime summary
line as a user line when w -h is missing, causing parsed to remain 0 and
min_idle to be set to 0; inside the while read loop (which uses variables
min_idle, found_user, idle_field, parsed), add a guard to skip uptime-summary
lines before extracting idle_field — e.g. check and continue on lines matching
the uptime pattern like ^[[:space:]]*[0-9]+:[0-9]+[[:space:]]+up (or containing
" up " and "load average") so the uptime header is ignored and only real user
lines set found_user and parsed.

…idation

- Use GetSessionIdleTime instead of GetActiveTime for GNOME dbus idle
  detection (GetActiveTime only measures screensaver runtime)
- Fix grep -c pipefail issue: use { grep || true; } subshell to avoid
  producing '0\n0' when no lines match under set -o pipefail
- Validate idle_threshold in cmd_status to prevent arithmetic error
  with invalid AIDEVOPS_TOOL_IDLE_HOURS values under set -e
@github-actions
Copy link

🔍 Code Quality Report

�[0;35m[MONITOR]�[0m Code Review Monitoring Report

�[0;34m[INFO]�[0m Latest Quality Status:
SonarCloud: 0 bugs, 0 vulnerabilities, 40 code smells

�[0;34m[INFO]�[0m Recent monitoring activity:
Sun Feb 22 16:03:24 UTC 2026: Code review monitoring started
Sun Feb 22 16:03:25 UTC 2026: SonarCloud - Bugs: 0, Vulnerabilities: 0, Code Smells: 40

📈 Current Quality Metrics

  • BUGS: 0
  • CODE SMELLS: 40
  • VULNERABILITIES: 0

Generated on: Sun Feb 22 16:03:27 UTC 2026


Generated by AI DevOps Framework Code Review Monitoring

@sonarqubecloud
Copy link

@marcusquinn
Copy link
Owner Author

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 22, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant