Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .agents/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@ Read subagents on-demand. Full index: `subagent-index.toon`.
| Bundles | `bundles/*.json`, `scripts/bundle-helper.sh`, `tools/context/model-routing.md` |
| Model routing | `tools/context/model-routing.md`, `reference/orchestration.md` |
| Orchestration | `reference/orchestration.md`, `tools/ai-assistants/headless-dispatch.md`, `scripts/commands/pulse.md`, `scripts/commands/dashboard.md` |
| Upstream watch | `scripts/upstream-watch-helper.sh`, `configs/upstream-watch.json` |
| Agent/MCP dev | `tools/build-agent/build-agent.md`, `tools/build-mcp/build-mcp.md`, `tools/mcp-toolkit/mcporter.md` |
| Framework | `aidevops/architecture.md`, `scripts/commands/skills.md` |

Expand All @@ -231,8 +232,9 @@ Key capabilities (details in `reference/orchestration.md`, `reference/services.m
- **Memory**: cross-session SQLite FTS5 (`/remember`, `/recall`)
- **Orchestration**: supervisor dispatch, pulse scheduler, auto-pickup, cross-repo issue/PR/TODO visibility
- **Contribution watch**: monitors external issues/PRs for new comments needing reply. `contribution-watch-helper.sh seed|scan|status|install|uninstall`. Prompt-injection-safe — automated scans are deterministic (no LLM), comment bodies only shown in interactive sessions after `prompt-guard-helper.sh scan`.
- **Upstream watch**: monitors external repos we've borrowed ideas/code from for new releases. `upstream-watch-helper.sh add|remove|check|ack|status`. Shows release diffs and changelogs between our last-seen version and latest. Distinct from skill imports (code we pulled in) and contribution watch (repos we filed issues on) — this tracks "inspiration repos" for passive monitoring. Config: `.agents/configs/upstream-watch.json`.
- **Skills**: `aidevops skills`, `/skills`
- **Auto-update**: GitHub poll + daily skill/repo sync
- **Auto-update**: GitHub poll + daily skill/upstream watch/OpenClaw/tool freshness checks (via `auto-update-helper.sh`). Repo sync runs separately via `aidevops repo-sync` scheduler.
- **Browser**: Playwright, dev-browser (persistent login)
- **Quality**: Write-time per-edit linting → `linters-local.sh` → `/pr review` → `/postflight`. Fix violations at edit time, not commit time. See `prompts/build.txt` "Write-Time Quality Enforcement". Bundle `skip_gates` filter irrelevant checks per project type.
- **Sessions**: `/session-review`, `/checkpoint`, compaction resilience
Expand Down
4 changes: 4 additions & 0 deletions .agents/configs/upstream-watch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"$comment": "Upstream repos to watch for releases and significant changes. Managed by upstream-watch-helper.sh (t1426). Entry schema: {slug, description, relevance, default_branch, added_at}. State (last_release_seen, last_commit_seen, last_checked, updates_pending) stored in ~/.aidevops/cache/upstream-watch-state.json.",
"repos": []
}
2 changes: 2 additions & 0 deletions .agents/reference/services.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,8 @@ Automatic polling for new releases. Checks GitHub every 10 minutes and runs `aid

**Daily skill refresh**: Each auto-update check also runs a 24h-gated skill freshness check. If >24h have passed since the last check, `skill-update-helper.sh --auto-update --quiet` pulls upstream changes for all imported skills. State is tracked in `~/.aidevops/cache/auto-update-state.json` (`last_skill_check`, `skill_updates_applied`). Disable with `AIDEVOPS_SKILL_AUTO_UPDATE=false`; adjust frequency with `AIDEVOPS_SKILL_FRESHNESS_HOURS=<hours>` (default: 24). View skill check status with `aidevops auto-update status`.

**Upstream watch**: Alongside skill refresh, `upstream-watch-helper.sh check` monitors external repos we've borrowed ideas/code from for new releases. Unlike skill imports (code we pulled in) or contribution watch (repos we filed issues on), this tracks "inspiration repos" — repos we want to passively monitor for improvements relevant to our implementation. Config: `configs/upstream-watch.json`. State: `~/.aidevops/cache/upstream-watch-state.json`. Run `upstream-watch-helper.sh status` to see watched repos, `upstream-watch-helper.sh check` to check for updates, `upstream-watch-helper.sh ack <slug>` after reviewing.

**Repo version wins on update**: When `aidevops update` runs, shared agents in `~/.aidevops/agents/` are overwritten by the repo version. Only `custom/` and `draft/` directories are preserved. Imported skills stored outside these directories will be overwritten. To keep a skill across updates, either re-import it after each update or move it to `custom/`.

## Repo Sync
Expand Down
2 changes: 2 additions & 0 deletions .agents/reference/settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ Controls automatic update behavior for aidevops, skills, tools, and OpenClaw.
| `auto_update.tool_idle_hours` | number | `6` | `AIDEVOPS_TOOL_IDLE_HOURS` | Required user idle time (hours) before tool updates run. Prevents updates during active work. |
| `auto_update.openclaw_auto_update` | boolean | `true` | `AIDEVOPS_OPENCLAW_AUTO_UPDATE` | Enable daily OpenClaw update checks (only if openclaw CLI is installed). |
| `auto_update.openclaw_freshness_hours` | number | `24` | `AIDEVOPS_OPENCLAW_FRESHNESS_HOURS` | Hours between OpenClaw update checks. |
| `auto_update.upstream_watch` | boolean | `true` | `AIDEVOPS_UPSTREAM_WATCH` | Enable daily upstream repo watch checks. Monitors external repos for new releases. |
| `auto_update.upstream_watch_hours` | number | `24` | `AIDEVOPS_UPSTREAM_WATCH_HOURS` | Hours between upstream watch checks. |

### supervisor

Expand Down
126 changes: 126 additions & 0 deletions .agents/scripts/auto-update-helper.sh
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@
# AIDEVOPS_TOOL_AUTO_UPDATE=false Disable 6-hourly tool freshness check
# AIDEVOPS_TOOL_FRESHNESS_HOURS=6 Hours between tool checks (default: 6)
# AIDEVOPS_TOOL_IDLE_HOURS=6 Required user idle hours before tool updates (default: 6)
# AIDEVOPS_UPSTREAM_WATCH=false Disable daily upstream repo watch check
# AIDEVOPS_UPSTREAM_WATCH_HOURS=24 Hours between upstream watch checks (default: 24)
Comment thread
coderabbitai[bot] marked this conversation as resolved.
#
# Logs: ~/.aidevops/logs/auto-update.log

Expand Down Expand Up @@ -71,6 +73,7 @@ readonly DEFAULT_SKILL_FRESHNESS_HOURS=24
readonly DEFAULT_OPENCLAW_FRESHNESS_HOURS=24
readonly DEFAULT_TOOL_FRESHNESS_HOURS=6
readonly DEFAULT_TOOL_IDLE_HOURS=6
readonly DEFAULT_UPSTREAM_WATCH_HOURS=24
readonly LAUNCHD_LABEL="com.aidevops.aidevops-auto-update"
readonly LAUNCHD_DIR="$HOME/Library/LaunchAgents"
readonly LAUNCHD_PLIST="${LAUNCHD_DIR}/${LAUNCHD_LABEL}.plist"
Expand Down Expand Up @@ -859,6 +862,120 @@ update_tool_check_timestamp() {
return 0
}

#######################################
# Check upstream-watched repos for new releases (24h gate)
# Called from cmd_check after tool freshness check.
# Respects config: aidevops config set updates.upstream_watch false
#######################################
check_upstream_watch() {
# Opt-out via config (env var or config file)
if ! is_feature_enabled upstream_watch; then
log_info "Upstream watch disabled via config"
return 0
fi

local freshness_hours
freshness_hours=$(get_feature_toggle upstream_watch_hours "$DEFAULT_UPSTREAM_WATCH_HOURS")
if ! [[ "$freshness_hours" =~ ^[0-9]+$ ]] || [[ "$freshness_hours" -eq 0 ]]; then
log_warn "updates.upstream_watch_hours='${freshness_hours}' is not a positive integer — using default (${DEFAULT_UPSTREAM_WATCH_HOURS}h)"
freshness_hours="$DEFAULT_UPSTREAM_WATCH_HOURS"
fi
local freshness_seconds=$((freshness_hours * 3600))

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

# Determine if check is needed
local needs_check=true
if [[ -n "$last_upstream_check" ]]; then
local last_epoch now_epoch elapsed
if [[ "$(uname)" == "Darwin" ]]; then
last_epoch=$(TZ=UTC date -j -f "%Y-%m-%dT%H:%M:%SZ" "$last_upstream_check" "+%s" 2>/dev/null || echo "0")
else
last_epoch=$(date -d "$last_upstream_check" "+%s" 2>/dev/null || echo "0")
fi
now_epoch=$(date +%s)
elapsed=$((now_epoch - last_epoch))

if [[ $elapsed -lt $freshness_seconds ]]; then
log_info "Upstream watch checked ${elapsed}s ago (gate: ${freshness_seconds}s) — skipping"
needs_check=false
fi
fi

if [[ "$needs_check" != "true" ]]; then
return 0
fi

# Locate upstream-watch-helper.sh (respect AIDEVOPS_AGENTS_DIR)
local agents_dir="${AIDEVOPS_AGENTS_DIR:-$HOME/.aidevops/agents}"
local upstream_watch_script="${agents_dir}/scripts/upstream-watch-helper.sh"
if [[ ! -x "$upstream_watch_script" ]]; then
upstream_watch_script="$INSTALL_DIR/.agents/scripts/upstream-watch-helper.sh"
fi

if [[ ! -x "$upstream_watch_script" ]]; then
log_info "upstream-watch-helper.sh not found — skipping upstream watch check"
return 0
fi

# Check if upstream-watch.json has any repos
local watch_config="${agents_dir}/configs/upstream-watch.json"
if [[ ! -f "$watch_config" ]]; then
log_info "No upstream watch config found — skipping"
update_upstream_watch_timestamp
return 0
fi

local repo_count
repo_count=$(jq '.repos | length' "$watch_config" 2>/dev/null || echo "0")
if [[ "$repo_count" -eq 0 ]]; then
log_info "No repos in upstream watchlist — skipping"
update_upstream_watch_timestamp
return 0
fi

log_info "Running daily upstream watch check (${repo_count} repos)..."
if "$upstream_watch_script" check >>"$LOG_FILE" 2>&1; then
log_info "Upstream watch check complete"
update_upstream_watch_timestamp
else
log_warn "Upstream watch check had errors (exit code: $?) — will retry next run"
fi
return 0
}

#######################################
# Record last_upstream_watch_check timestamp in state file
#######################################
update_upstream_watch_timestamp() {
local timestamp
timestamp="$(date -u +%Y-%m-%dT%H:%M:%SZ)"

if command -v jq &>/dev/null; then
local tmp_state
tmp_state=$(mktemp)
trap 'rm -f "${tmp_state:-}"' RETURN

if [[ -f "$STATE_FILE" ]]; then
if ! jq --arg ts "$timestamp" \
'. + {last_upstream_watch_check: $ts}' \
"$STATE_FILE" >"$tmp_state" 2>&1; then
log_warn "Failed to update upstream watch timestamp (jq error on state file)"
return 1
fi
mv "$tmp_state" "$STATE_FILE"
else
jq -n --arg ts "$timestamp" \
'{last_upstream_watch_check: $ts}' >"$STATE_FILE"
fi
fi
return 0
}

#######################################
# One-shot check and update
# This is what the cron job calls
Expand Down Expand Up @@ -897,6 +1014,7 @@ cmd_check() {
check_skill_freshness
check_openclaw_freshness
check_tool_freshness
check_upstream_watch
return 0
fi

Expand Down Expand Up @@ -927,6 +1045,7 @@ cmd_check() {
check_skill_freshness
check_openclaw_freshness
check_tool_freshness
check_upstream_watch
return 0
fi

Expand All @@ -941,6 +1060,7 @@ cmd_check() {
check_skill_freshness
check_openclaw_freshness
check_tool_freshness
check_upstream_watch
return 1
fi

Expand All @@ -951,6 +1071,7 @@ cmd_check() {
check_skill_freshness
check_openclaw_freshness
check_tool_freshness
check_upstream_watch
return 1
fi

Expand All @@ -960,6 +1081,7 @@ cmd_check() {
check_skill_freshness
check_openclaw_freshness
check_tool_freshness
check_upstream_watch
return 1
fi

Expand All @@ -986,6 +1108,7 @@ cmd_check() {
check_skill_freshness
check_openclaw_freshness
check_tool_freshness
check_upstream_watch
return 1
fi

Expand All @@ -998,6 +1121,9 @@ cmd_check() {
# Run 6-hourly tool freshness check (idle-gated)
check_tool_freshness

# Run daily upstream watch check (24h gate)
check_upstream_watch

return 0
}

Expand Down
4 changes: 3 additions & 1 deletion .agents/scripts/shared-constants.sh
Original file line number Diff line number Diff line change
Expand Up @@ -1446,6 +1446,8 @@ _ft_env_map() {
repo_sync) echo "AIDEVOPS_REPO_SYNC" ;;
openclaw_auto_update) echo "AIDEVOPS_OPENCLAW_AUTO_UPDATE" ;;
openclaw_freshness_hours) echo "AIDEVOPS_OPENCLAW_FRESHNESS_HOURS" ;;
upstream_watch) echo "AIDEVOPS_UPSTREAM_WATCH" ;;
upstream_watch_hours) echo "AIDEVOPS_UPSTREAM_WATCH_HOURS" ;;
max_interactive_sessions) echo "AIDEVOPS_MAX_SESSIONS" ;;
*) echo "" ;;
esac
Expand Down Expand Up @@ -1478,7 +1480,7 @@ _load_feature_toggles_legacy() {
done <"$FEATURE_TOGGLES_USER"
fi

local toggle_keys="auto_update update_interval skill_auto_update skill_freshness_hours tool_auto_update tool_freshness_hours tool_idle_hours supervisor_pulse repo_sync openclaw_auto_update openclaw_freshness_hours manage_opencode_config manage_claude_config session_greeting safety_hooks shell_aliases onboarding_prompt"
local toggle_keys="auto_update update_interval skill_auto_update skill_freshness_hours tool_auto_update tool_freshness_hours tool_idle_hours supervisor_pulse repo_sync openclaw_auto_update openclaw_freshness_hours upstream_watch upstream_watch_hours manage_opencode_config manage_claude_config session_greeting safety_hooks shell_aliases onboarding_prompt"
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
local tk env_var env_val
for tk in $toggle_keys; do
env_var=$(_ft_env_map "$tk")
Expand Down
Loading