aidevops 3.13.95 → 3.14.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -1
- package/VERSION +1 -1
- package/aidevops.sh +44 -26
- package/package.json +1 -1
- package/setup.sh +25 -21
- package/aidevops-init-lib.sh +0 -1411
- package/aidevops-repos-lib.sh +0 -700
- package/aidevops-skills-plugin-lib.sh +0 -697
- package/aidevops-status-lib.sh +0 -141
- package/aidevops-update-lib.sh +0 -512
- package/aidevops-upgrade-planning-lib.sh +0 -370
- package/setup-modules/agent-deploy.sh +0 -1035
- package/setup-modules/agent-runtime.sh +0 -287
- package/setup-modules/config.sh +0 -478
- package/setup-modules/core.sh +0 -736
- package/setup-modules/mcp-setup.sh +0 -947
- package/setup-modules/migrations.sh +0 -1688
- package/setup-modules/plugins.sh +0 -728
- package/setup-modules/post-setup.sh +0 -301
- package/setup-modules/schedulers-linux.sh +0 -386
- package/setup-modules/schedulers-platform.sh +0 -1072
- package/setup-modules/schedulers-pulse.sh +0 -978
- package/setup-modules/schedulers.sh +0 -565
- package/setup-modules/shell-env.sh +0 -1240
- package/setup-modules/tool-beads.sh +0 -324
- package/setup-modules/tool-install.sh +0 -2134
package/aidevops-status-lib.sh
DELETED
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# SPDX-License-Identifier: MIT
|
|
3
|
-
# SPDX-FileCopyrightText: 2025-2026 Marcus Quinn
|
|
4
|
-
# =============================================================================
|
|
5
|
-
# aidevops Status Library — status command helper functions
|
|
6
|
-
# =============================================================================
|
|
7
|
-
# Helper functions for `aidevops status`, extracted from aidevops.sh to keep
|
|
8
|
-
# the CLI orchestrator below the large-file gate while preserving behaviour.
|
|
9
|
-
#
|
|
10
|
-
# Usage: source "${INSTALL_DIR}/aidevops-status-lib.sh"
|
|
11
|
-
# Part of aidevops framework: https://aidevops.sh
|
|
12
|
-
|
|
13
|
-
# Apply strict mode only when executed directly (not when sourced)
|
|
14
|
-
[[ "${BASH_SOURCE[0]}" == "${0}" ]] && set -euo pipefail
|
|
15
|
-
|
|
16
|
-
# Include guard
|
|
17
|
-
[[ -n "${_AIDEVOPS_STATUS_LIB_LOADED:-}" ]] && return 0
|
|
18
|
-
_AIDEVOPS_STATUS_LIB_LOADED=1
|
|
19
|
-
|
|
20
|
-
if [[ -z "${SCRIPT_DIR:-}" ]]; then
|
|
21
|
-
_lib_path="${BASH_SOURCE[0]%/*}"
|
|
22
|
-
[[ "$_lib_path" == "${BASH_SOURCE[0]}" ]] && _lib_path="."
|
|
23
|
-
SCRIPT_DIR="$(cd "$_lib_path" && pwd)"
|
|
24
|
-
unset _lib_path
|
|
25
|
-
fi
|
|
26
|
-
|
|
27
|
-
_status_recommended_tools() {
|
|
28
|
-
print_header "Recommended Tools"
|
|
29
|
-
if [[ "$(uname)" == "Darwin" ]]; then
|
|
30
|
-
check_dir "/Applications/Tabby.app" && print_success "Tabby terminal" || print_warning "Tabby terminal - not installed"
|
|
31
|
-
if check_dir "/Applications/Zed.app"; then
|
|
32
|
-
print_success "Zed editor"
|
|
33
|
-
check_dir "$HOME/Library/Application Support/Zed/extensions/installed/opencode" && print_success " └─ OpenCode extension" || print_warning " └─ OpenCode extension - not installed"
|
|
34
|
-
else print_warning "Zed editor - not installed"; fi
|
|
35
|
-
else
|
|
36
|
-
check_cmd tabby && print_success "Tabby terminal" || print_warning "Tabby terminal - not installed"
|
|
37
|
-
if check_cmd zed; then
|
|
38
|
-
print_success "Zed editor"
|
|
39
|
-
check_dir "$HOME/.local/share/zed/extensions/installed/opencode" && print_success " └─ OpenCode extension" || print_warning " └─ OpenCode extension - not installed"
|
|
40
|
-
else print_warning "Zed editor - not installed"; fi
|
|
41
|
-
fi
|
|
42
|
-
echo ""
|
|
43
|
-
return 0
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
_status_ai_tools() {
|
|
47
|
-
print_header "AI Tools & MCPs"
|
|
48
|
-
check_cmd opencode && print_success "OpenCode CLI" || print_warning "OpenCode CLI - not installed"
|
|
49
|
-
if check_cmd auggie; then
|
|
50
|
-
check_file "$HOME/.augment/session.json" && print_success "Augment Context Engine (authenticated)" || print_warning "Augment Context Engine (not authenticated)"
|
|
51
|
-
else print_warning "Augment Context Engine - not installed"; fi
|
|
52
|
-
check_cmd bd && print_success "Beads CLI (task graph)" || print_warning "Beads CLI (bd) - not installed"
|
|
53
|
-
echo ""
|
|
54
|
-
return 0
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
_status_dev_envs() {
|
|
58
|
-
print_header "Development Environments"
|
|
59
|
-
check_dir "$INSTALL_DIR/python-env/dspy-env" && print_success "DSPy Python environment" || print_warning "DSPy Python environment - not created"
|
|
60
|
-
check_cmd dspyground && print_success "DSPyGround" || print_warning "DSPyGround - not installed"
|
|
61
|
-
echo ""
|
|
62
|
-
return 0
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
_status_ai_configs() {
|
|
66
|
-
print_header "AI Assistant Configurations"
|
|
67
|
-
local ai_configs=("$HOME/.config/opencode/opencode.json:OpenCode" "$HOME/.claude/commands:Claude Code CLI" "$HOME/CLAUDE.md:Claude Code memory")
|
|
68
|
-
for config in "${ai_configs[@]}"; do
|
|
69
|
-
local path="${config%%:*}" name="${config##*:}"
|
|
70
|
-
[[ -e "$path" ]] && print_success "$name" || print_warning "$name - not configured"
|
|
71
|
-
done
|
|
72
|
-
echo ""
|
|
73
|
-
return 0
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
# Status command
|
|
77
|
-
cmd_status() {
|
|
78
|
-
print_header "AI DevOps Framework Status"
|
|
79
|
-
echo "=========================="
|
|
80
|
-
echo ""
|
|
81
|
-
local current_version
|
|
82
|
-
current_version=$(get_version)
|
|
83
|
-
local remote_version
|
|
84
|
-
remote_version=$(get_remote_version)
|
|
85
|
-
print_header "Version"
|
|
86
|
-
echo " Installed: $current_version"
|
|
87
|
-
echo " Latest: $remote_version"
|
|
88
|
-
if [[ "$current_version" != "$remote_version" && "$remote_version" != "unknown" ]]; then
|
|
89
|
-
print_warning "Update available! Run: aidevops update"
|
|
90
|
-
elif [[ "$current_version" == "$remote_version" ]]; then print_success "Up to date"; fi
|
|
91
|
-
echo ""
|
|
92
|
-
print_header "Installation"
|
|
93
|
-
check_dir "$INSTALL_DIR" && print_success "Repository: $INSTALL_DIR" || print_error "Repository: Not found at $INSTALL_DIR"
|
|
94
|
-
if check_dir "$AGENTS_DIR"; then
|
|
95
|
-
local agent_count
|
|
96
|
-
agent_count=$(find "$AGENTS_DIR" -name "*.md" -type f 2>/dev/null | wc -l | tr -d ' ')
|
|
97
|
-
print_success "Agents: $AGENTS_DIR ($agent_count files)"
|
|
98
|
-
else print_error "Agents: Not deployed"; fi
|
|
99
|
-
echo ""
|
|
100
|
-
print_header "Required Dependencies"
|
|
101
|
-
for cmd in git curl jq ssh; do check_cmd "$cmd" && print_success "$cmd" || print_error "$cmd - not installed"; done
|
|
102
|
-
echo ""
|
|
103
|
-
print_header "Optional Dependencies"
|
|
104
|
-
check_cmd sshpass && print_success "sshpass" || print_warning "sshpass - not installed (needed for password SSH)"
|
|
105
|
-
echo ""
|
|
106
|
-
_status_recommended_tools
|
|
107
|
-
print_header "Git CLI Tools"
|
|
108
|
-
check_cmd gh && print_success "GitHub CLI (gh)" || print_warning "GitHub CLI (gh) - not installed"
|
|
109
|
-
check_cmd glab && print_success "GitLab CLI (glab)" || print_warning "GitLab CLI (glab) - not installed"
|
|
110
|
-
check_cmd tea && print_success "Gitea CLI (tea)" || print_warning "Gitea CLI (tea) - not installed"
|
|
111
|
-
echo ""
|
|
112
|
-
_status_ai_tools
|
|
113
|
-
_status_dev_envs
|
|
114
|
-
_status_ai_configs
|
|
115
|
-
print_header "SSH Configuration"
|
|
116
|
-
check_file "$HOME/.ssh/id_ed25519" && print_success "Ed25519 SSH key" || print_warning "Ed25519 SSH key - not found"
|
|
117
|
-
echo ""
|
|
118
|
-
print_header "Commit Signing"
|
|
119
|
-
local signing_format signing_key signing_enabled
|
|
120
|
-
signing_format=$(git config --global gpg.format 2>/dev/null || echo "")
|
|
121
|
-
signing_key=$(git config --global user.signingkey 2>/dev/null || echo "")
|
|
122
|
-
signing_enabled=$(git config --global commit.gpgsign 2>/dev/null || echo "")
|
|
123
|
-
if [[ "$signing_format" == "ssh" && -n "$signing_key" && "$signing_enabled" == "true" ]]; then
|
|
124
|
-
print_success "SSH commit signing enabled"
|
|
125
|
-
if check_file "$HOME/.ssh/allowed_signers"; then
|
|
126
|
-
print_success "Allowed signers file configured"
|
|
127
|
-
else
|
|
128
|
-
print_warning "No allowed_signers file — run: aidevops signing setup"
|
|
129
|
-
fi
|
|
130
|
-
else
|
|
131
|
-
print_warning "Commit signing not configured — run: aidevops signing setup"
|
|
132
|
-
fi
|
|
133
|
-
echo ""
|
|
134
|
-
# t2424/GH#20030: Pulse operational counters (pre-dispatch aborts, etc.)
|
|
135
|
-
local stats_helper="$AGENTS_DIR/scripts/pulse-stats-helper.sh"
|
|
136
|
-
if [[ -x "$stats_helper" ]]; then
|
|
137
|
-
print_header "Pulse Stats"
|
|
138
|
-
"$stats_helper" status 2>/dev/null || print_info " (no stats recorded yet)"
|
|
139
|
-
echo ""
|
|
140
|
-
fi
|
|
141
|
-
}
|
package/aidevops-update-lib.sh
DELETED
|
@@ -1,512 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# SPDX-License-Identifier: MIT
|
|
3
|
-
# SPDX-FileCopyrightText: 2025-2026 Marcus Quinn
|
|
4
|
-
# =============================================================================
|
|
5
|
-
# aidevops Update Library — update command helper functions
|
|
6
|
-
# =============================================================================
|
|
7
|
-
# Helper functions for `aidevops update`, extracted from aidevops.sh to keep
|
|
8
|
-
# the CLI orchestrator below the large-file gate while preserving behaviour.
|
|
9
|
-
#
|
|
10
|
-
# Usage: source "${INSTALL_DIR}/aidevops-update-lib.sh"
|
|
11
|
-
# Part of aidevops framework: https://aidevops.sh
|
|
12
|
-
|
|
13
|
-
# Apply strict mode only when executed directly (not when sourced)
|
|
14
|
-
[[ "${BASH_SOURCE[0]}" == "${0}" ]] && set -euo pipefail
|
|
15
|
-
|
|
16
|
-
# Include guard
|
|
17
|
-
[[ -n "${_AIDEVOPS_UPDATE_LIB_LOADED:-}" ]] && return 0
|
|
18
|
-
_AIDEVOPS_UPDATE_LIB_LOADED=1
|
|
19
|
-
|
|
20
|
-
if [[ -z "${SCRIPT_DIR:-}" ]]; then
|
|
21
|
-
_lib_path="${BASH_SOURCE[0]%/*}"
|
|
22
|
-
[[ "$_lib_path" == "${BASH_SOURCE[0]}" ]] && _lib_path="."
|
|
23
|
-
SCRIPT_DIR="$(cd "$_lib_path" && pwd)"
|
|
24
|
-
unset _lib_path
|
|
25
|
-
fi
|
|
26
|
-
|
|
27
|
-
_AIDEVOPS_UPDATE_TRUE=true
|
|
28
|
-
|
|
29
|
-
_update_fresh_install() {
|
|
30
|
-
print_warning "Repository not found, performing fresh install..."
|
|
31
|
-
local tmp_setup
|
|
32
|
-
# t2997: drop .sh — XXXXXX must be at end for BSD mktemp.
|
|
33
|
-
tmp_setup=$(mktemp "${TMPDIR:-/tmp}/aidevops-setup-XXXXXX") || {
|
|
34
|
-
print_error "Failed to create temp file for setup script"
|
|
35
|
-
return 1
|
|
36
|
-
}
|
|
37
|
-
trap 'rm -f "${tmp_setup:-}"' RETURN
|
|
38
|
-
if curl -fsSL "https://raw.githubusercontent.com/marcusquinn/aidevops/main/setup.sh" -o "$tmp_setup" 2>/dev/null && [[ -s "$tmp_setup" ]]; then
|
|
39
|
-
chmod +x "$tmp_setup"
|
|
40
|
-
bash "$tmp_setup"
|
|
41
|
-
local setup_exit=$?
|
|
42
|
-
rm -f "$tmp_setup"
|
|
43
|
-
[[ $setup_exit -ne 0 ]] && return 1
|
|
44
|
-
else
|
|
45
|
-
rm -f "$tmp_setup"
|
|
46
|
-
print_error "Failed to download setup script"
|
|
47
|
-
print_info "Try: git clone https://github.com/marcusquinn/aidevops.git $INSTALL_DIR && bash $INSTALL_DIR/setup.sh"
|
|
48
|
-
return 1
|
|
49
|
-
fi
|
|
50
|
-
return 0
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
_update_sync_projects() {
|
|
54
|
-
local skip="$1" current_ver="$2"
|
|
55
|
-
echo ""
|
|
56
|
-
print_header "Syncing Initialized Projects"
|
|
57
|
-
if [[ "$skip" == "$_AIDEVOPS_UPDATE_TRUE" ]]; then
|
|
58
|
-
print_info "Project sync skipped (--skip-project-sync)"
|
|
59
|
-
return 0
|
|
60
|
-
fi
|
|
61
|
-
local repos_needing_upgrade=()
|
|
62
|
-
while IFS= read -r repo_path; do
|
|
63
|
-
[[ -z "$repo_path" ]] && continue
|
|
64
|
-
[[ -d "$repo_path" ]] && check_repo_needs_upgrade "$repo_path" && repos_needing_upgrade+=("$repo_path")
|
|
65
|
-
done < <(get_registered_repos)
|
|
66
|
-
_update_sync_agent_source_repos "$current_ver" || true
|
|
67
|
-
if [[ ${#repos_needing_upgrade[@]} -eq 0 ]]; then
|
|
68
|
-
print_success "All registered projects are up to date"
|
|
69
|
-
return 0
|
|
70
|
-
fi
|
|
71
|
-
local synced=0 skipped=0 failed=0
|
|
72
|
-
for repo in "${repos_needing_upgrade[@]}"; do
|
|
73
|
-
[[ ! -f "$repo/.aidevops.json" ]] && {
|
|
74
|
-
skipped=$((skipped + 1))
|
|
75
|
-
continue
|
|
76
|
-
}
|
|
77
|
-
local did_sync=false
|
|
78
|
-
if command -v jq &>/dev/null; then
|
|
79
|
-
local temp_file="${repo}/.aidevops.json.tmp"
|
|
80
|
-
if jq --arg version "$current_ver" '.version = $version' "$repo/.aidevops.json" >"$temp_file" 2>/dev/null && [[ -s "$temp_file" ]]; then
|
|
81
|
-
mv "$temp_file" "$repo/.aidevops.json"
|
|
82
|
-
local features
|
|
83
|
-
features=$(jq -r '[.features | to_entries[] | select(.value == true) | .key] | join(",")' "$repo/.aidevops.json" 2>/dev/null || echo "")
|
|
84
|
-
register_repo "$repo" "$current_ver" "$features"
|
|
85
|
-
did_sync=true
|
|
86
|
-
else rm -f "$temp_file"; fi
|
|
87
|
-
fi
|
|
88
|
-
if [[ "$did_sync" != "$_AIDEVOPS_UPDATE_TRUE" ]]; then
|
|
89
|
-
sed -i '' "s/\"version\": *\"[^\"]*\"/\"version\": \"$current_ver\"/" "$repo/.aidevops.json" 2>/dev/null && did_sync=true
|
|
90
|
-
fi
|
|
91
|
-
[[ "$did_sync" == "$_AIDEVOPS_UPDATE_TRUE" ]] && synced=$((synced + 1)) || failed=$((failed + 1))
|
|
92
|
-
done
|
|
93
|
-
[[ $synced -gt 0 ]] && print_success "Synced $synced project(s) to v$current_ver"
|
|
94
|
-
[[ $skipped -gt 0 ]] && print_info "Skipped $skipped uninitialized project(s) (run 'aidevops init' in each to enable)"
|
|
95
|
-
[[ $failed -gt 0 ]] && print_warning "$failed project(s) failed to sync (jq missing or write error)"
|
|
96
|
-
return 0
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
_update_sync_agent_source_repos() {
|
|
100
|
-
local current_ver="$1"
|
|
101
|
-
local synced=0 skipped=0 failed=0
|
|
102
|
-
local repo
|
|
103
|
-
|
|
104
|
-
while IFS= read -r repo; do
|
|
105
|
-
[[ -z "$repo" ]] && continue
|
|
106
|
-
if [[ ! -d "$repo" ]]; then
|
|
107
|
-
skipped=$((skipped + 1))
|
|
108
|
-
continue
|
|
109
|
-
fi
|
|
110
|
-
if seed_agent_source_repo_templates "$repo"; then
|
|
111
|
-
synced=$((synced + 1))
|
|
112
|
-
if [[ -f "$repo/.aidevops.json" ]] && command -v jq &>/dev/null; then
|
|
113
|
-
local temp_file="${repo}/.aidevops.json.tmp"
|
|
114
|
-
jq --arg version "$current_ver" '.version = $version | .agent_source = true' "$repo/.aidevops.json" >"$temp_file" 2>/dev/null && mv "$temp_file" "$repo/.aidevops.json" || rm -f "$temp_file"
|
|
115
|
-
fi
|
|
116
|
-
else
|
|
117
|
-
failed=$((failed + 1))
|
|
118
|
-
fi
|
|
119
|
-
done < <(get_agent_source_repos)
|
|
120
|
-
|
|
121
|
-
[[ $synced -gt 0 ]] && print_success "Synced $synced agent-source repo template(s)"
|
|
122
|
-
[[ $skipped -gt 0 ]] && print_info "Skipped $skipped unavailable agent-source repo(s)"
|
|
123
|
-
[[ $failed -gt 0 ]] && print_warning "$failed agent-source repo template sync(s) failed"
|
|
124
|
-
return 0
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
_update_check_planning() {
|
|
128
|
-
echo ""
|
|
129
|
-
print_header "Checking Planning Templates"
|
|
130
|
-
local repos_needing_planning=()
|
|
131
|
-
while IFS= read -r repo_path; do
|
|
132
|
-
[[ -z "$repo_path" || ! -d "$repo_path" ]] && continue
|
|
133
|
-
if [[ -f "$repo_path/.aidevops.json" ]]; then
|
|
134
|
-
local has_planning
|
|
135
|
-
has_planning=$(grep -o '"planning": *true' "$repo_path/.aidevops.json" 2>/dev/null || true)
|
|
136
|
-
[[ -n "$has_planning" ]] && check_planning_needs_upgrade "$repo_path" && repos_needing_planning+=("$repo_path")
|
|
137
|
-
fi
|
|
138
|
-
done < <(get_registered_repos)
|
|
139
|
-
if [[ ${#repos_needing_planning[@]} -eq 0 ]]; then
|
|
140
|
-
print_success "All planning templates are up to date"
|
|
141
|
-
return 0
|
|
142
|
-
fi
|
|
143
|
-
echo ""
|
|
144
|
-
print_warning "${#repos_needing_planning[@]} project(s) have outdated planning templates:"
|
|
145
|
-
for repo in "${repos_needing_planning[@]}"; do
|
|
146
|
-
local repo_name
|
|
147
|
-
repo_name=$(basename "$repo")
|
|
148
|
-
local todo_ver
|
|
149
|
-
todo_ver=$(grep -A1 "TOON:meta" "$repo/TODO.md" 2>/dev/null | tail -1 | cut -d',' -f1)
|
|
150
|
-
echo " - $repo_name (v${todo_ver:-none})"
|
|
151
|
-
done
|
|
152
|
-
local template_ver
|
|
153
|
-
template_ver=$(grep -A1 "TOON:meta" "$AGENTS_DIR/templates/todo-template.md" 2>/dev/null | tail -1 | cut -d',' -f1)
|
|
154
|
-
echo ""
|
|
155
|
-
echo " Latest template: v${template_ver} (adds risk field, active session time estimates)"
|
|
156
|
-
echo ""
|
|
157
|
-
read -r -p "Upgrade planning templates in these projects? [y/N] " response
|
|
158
|
-
if [[ "$response" =~ ^[Yy]$ ]]; then
|
|
159
|
-
for repo in "${repos_needing_planning[@]}"; do
|
|
160
|
-
print_info "Upgrading $(basename "$repo")..."
|
|
161
|
-
(cd "$repo" && cmd_upgrade_planning --force) || print_warning "Failed to upgrade $(basename "$repo")"
|
|
162
|
-
done
|
|
163
|
-
else print_info "Run 'aidevops upgrade-planning' in each project to upgrade manually"; fi
|
|
164
|
-
return 0
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
_update_check_tools() {
|
|
168
|
-
echo ""
|
|
169
|
-
print_header "Checking Key Tools"
|
|
170
|
-
local tool_check_script="$AGENTS_DIR/scripts/tool-version-check.sh"
|
|
171
|
-
if [[ ! -f "$tool_check_script" ]]; then
|
|
172
|
-
print_info "Tool version check not available (run setup first)"
|
|
173
|
-
return 0
|
|
174
|
-
fi
|
|
175
|
-
local stale_count=0 stale_tools=""
|
|
176
|
-
local key_tool_cmds="opencode gh"
|
|
177
|
-
local key_tool_pkgs="opencode-ai brew:gh"
|
|
178
|
-
local idx=0
|
|
179
|
-
for cmd_name in $key_tool_cmds; do
|
|
180
|
-
local pkg_ref
|
|
181
|
-
pkg_ref=$(echo "$key_tool_pkgs" | cut -d' ' -f$((idx + 1)))
|
|
182
|
-
idx=$((idx + 1))
|
|
183
|
-
local installed="" latest=""
|
|
184
|
-
command -v "$cmd_name" &>/dev/null || continue
|
|
185
|
-
installed=$("$cmd_name" --version 2>/dev/null | head -1 | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1)
|
|
186
|
-
[[ -z "$installed" ]] && continue
|
|
187
|
-
if [[ "$pkg_ref" == brew:* ]]; then
|
|
188
|
-
local brew_pkg="${pkg_ref#brew:}"
|
|
189
|
-
local brew_bin=""
|
|
190
|
-
brew_bin=$(command -v brew 2>/dev/null || true)
|
|
191
|
-
if [[ -n "$brew_bin" && -x "$brew_bin" ]]; then
|
|
192
|
-
latest=$(_timeout_cmd 30 "$brew_bin" info --json=v2 "$brew_pkg" | jq -r '.formulae[0].versions.stable // empty' || true)
|
|
193
|
-
elif [[ "$brew_pkg" == "gh" ]] && command -v gh &>/dev/null; then latest=$(get_public_release_tag "cli/cli"); fi
|
|
194
|
-
else latest=$(_timeout_cmd 30 npm view "$pkg_ref" version || true); fi
|
|
195
|
-
[[ -z "$latest" ]] && continue
|
|
196
|
-
[[ "$installed" != "$latest" ]] && {
|
|
197
|
-
stale_tools="${stale_tools:+$stale_tools, }$cmd_name ($installed -> $latest)"
|
|
198
|
-
((++stale_count))
|
|
199
|
-
}
|
|
200
|
-
done
|
|
201
|
-
if [[ "$stale_count" -eq 0 ]]; then
|
|
202
|
-
print_success "Key tools are up to date"
|
|
203
|
-
else
|
|
204
|
-
print_warning "$stale_count tool(s) have updates: $stale_tools"
|
|
205
|
-
echo ""
|
|
206
|
-
read -r -p "Run full tool update check? [y/N] " response
|
|
207
|
-
[[ "$response" =~ ^[Yy]$ ]] && bash "$tool_check_script" --update || print_info "Run 'aidevops update-tools --update' to update later"
|
|
208
|
-
fi
|
|
209
|
-
return 0
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
# Check for stale Homebrew-installed copy after git update (GH#11470)
|
|
213
|
-
# Self-heal broken OpenCode runtime symlinks (t2172). A single dangling
|
|
214
|
-
# symlink in ~/.config/opencode/{command,agent,skills,tool}/ blocks new
|
|
215
|
-
# OpenCode sessions with "Failed to parse command ...". Running on every
|
|
216
|
-
# update is cheap (find+rm on 4 small dirs) and catches orphans left
|
|
217
|
-
# behind when users delete private agent source clones without going
|
|
218
|
-
# through `agent-sources-helper.sh remove`. Fail-open — must never
|
|
219
|
-
# break the update cron.
|
|
220
|
-
_update_sweep_opencode_symlinks() {
|
|
221
|
-
local sym_helper="${HOME}/.aidevops/agents/scripts/agent-sources-helper.sh"
|
|
222
|
-
[[ -x "$sym_helper" ]] || return 0
|
|
223
|
-
"$sym_helper" cleanup-broken-symlinks >/dev/null 2>&1 || true
|
|
224
|
-
return 0
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
_update_check_homebrew() {
|
|
228
|
-
command -v brew &>/dev/null || return 0
|
|
229
|
-
brew list aidevops &>/dev/null 2>&1 || return 0
|
|
230
|
-
local brew_version=""
|
|
231
|
-
brew_version=$(brew info aidevops --json=v2 2>/dev/null | jq -r '.formulae[0].installed[0].version // empty' 2>/dev/null || true)
|
|
232
|
-
[[ -z "$brew_version" ]] && return 0
|
|
233
|
-
local current_version
|
|
234
|
-
current_version=$(get_version)
|
|
235
|
-
[[ -z "$current_version" ]] && return 0
|
|
236
|
-
if [[ "$brew_version" != "$current_version" ]]; then
|
|
237
|
-
echo ""
|
|
238
|
-
print_warning "Homebrew-installed copy is outdated ($brew_version vs $current_version)"
|
|
239
|
-
print_info "The Homebrew wrapper should prefer your git copy, but if your PATH"
|
|
240
|
-
print_info "resolves the Homebrew libexec copy directly, you'll run the old version."
|
|
241
|
-
echo ""
|
|
242
|
-
read -r -p "Run 'brew upgrade aidevops' now? [y/N] " response
|
|
243
|
-
if [[ "$response" =~ ^[Yy]$ ]]; then
|
|
244
|
-
brew upgrade aidevops 2>&1 || print_warning "brew upgrade failed — run manually: brew upgrade aidevops"
|
|
245
|
-
else
|
|
246
|
-
print_info "Run 'brew upgrade aidevops' to sync the Homebrew copy"
|
|
247
|
-
fi
|
|
248
|
-
fi
|
|
249
|
-
return 0
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
# t2926 / GH#21102: Re-check setsid on every 'aidevops update' run.
|
|
253
|
-
# setsid (from util-linux) is required to detach pulse workers into their own
|
|
254
|
-
# process group — without it, every pulse restart sends SIGHUP to its PGID,
|
|
255
|
-
# killing in-flight workers. This check runs even when setup.sh is skipped
|
|
256
|
-
# (already up-to-date path), so Homebrew drift doesn't silently break workers.
|
|
257
|
-
_update_check_setsid() {
|
|
258
|
-
command -v setsid >/dev/null 2>&1 && return 0
|
|
259
|
-
|
|
260
|
-
# setsid is missing. On macOS with Homebrew, auto-install util-linux.
|
|
261
|
-
# Use a boolean flag to avoid repeating the OS literal string.
|
|
262
|
-
local _on_mac=false
|
|
263
|
-
[[ "$(uname -s)" == Darwin* ]] && _on_mac=true
|
|
264
|
-
if $_on_mac && command -v brew >/dev/null 2>&1; then
|
|
265
|
-
print_info "setsid not found — installing util-linux for worker PGID isolation (GH#21102)"
|
|
266
|
-
if brew install util-linux 2>&1 | tail -3; then
|
|
267
|
-
local brew_prefix=""
|
|
268
|
-
brew_prefix="$(brew --prefix 2>/dev/null || true)"
|
|
269
|
-
local keg_setsid="${brew_prefix}/opt/util-linux/bin/setsid"
|
|
270
|
-
local link_target="${brew_prefix}/bin/setsid"
|
|
271
|
-
if [[ -x "$keg_setsid" && ! -e "$link_target" ]]; then
|
|
272
|
-
ln -s "$keg_setsid" "$link_target" && \
|
|
273
|
-
print_success "Symlinked setsid: $keg_setsid → $link_target"
|
|
274
|
-
fi
|
|
275
|
-
if command -v setsid >/dev/null 2>&1; then
|
|
276
|
-
print_success "setsid installed at $(command -v setsid) (worker PGID isolation enabled)"
|
|
277
|
-
else
|
|
278
|
-
print_error "util-linux installed but setsid still not in PATH — check brew --prefix"
|
|
279
|
-
fi
|
|
280
|
-
else
|
|
281
|
-
print_error "brew install util-linux failed — workers will share pulse PGID until resolved"
|
|
282
|
-
fi
|
|
283
|
-
elif $_on_mac; then
|
|
284
|
-
print_error "setsid not found — worker isolation broken; install Homebrew then run: brew install util-linux"
|
|
285
|
-
else
|
|
286
|
-
print_error "setsid not found — worker isolation broken; install util-linux via your distro package manager"
|
|
287
|
-
fi
|
|
288
|
-
|
|
289
|
-
return 0
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
# GH#21735: Notify operator when framework workflow templates change.
|
|
293
|
-
# When .agents/templates/workflows/*.yml or *-reusable.yml workflows change
|
|
294
|
-
# in a framework update, downstream repos that use these as workflow_call
|
|
295
|
-
# callers may have drifted from the new template. Detection and remediation
|
|
296
|
-
# both already exist (`aidevops check-workflows`, `aidevops sync-workflows
|
|
297
|
-
# --apply`); the gap was the notification surface — operators only learned
|
|
298
|
-
# of drift when downstream CI failed (canonical incident: a managed
|
|
299
|
-
# downstream repo's issue-sync.yml failed silently after the upstream
|
|
300
|
-
# template added a new input).
|
|
301
|
-
#
|
|
302
|
-
# This check inspects the SHA-window diff for changes to workflow caller
|
|
303
|
-
# templates and reusable workflows, prints a warning, and emits a daily
|
|
304
|
-
# advisory so the next session greeting surfaces it if the operator
|
|
305
|
-
# misses the inline output.
|
|
306
|
-
#
|
|
307
|
-
# Args: $1=old_sha, $2=new_sha
|
|
308
|
-
# Returns: 0 (always — informational only, never breaks update)
|
|
309
|
-
_update_check_workflow_drift() {
|
|
310
|
-
local old_sha="$1"
|
|
311
|
-
local new_sha="$2"
|
|
312
|
-
[[ -z "$old_sha" || -z "$new_sha" || "$old_sha" == "$new_sha" ]] && return 0
|
|
313
|
-
# `.git` is a directory in a regular repo and a file in a worktree;
|
|
314
|
-
# `-e` covers both so the helper is testable from a worktree.
|
|
315
|
-
[[ ! -e "$INSTALL_DIR/.git" ]] && return 0
|
|
316
|
-
|
|
317
|
-
# Files that propagate to downstream caller workflows OR are themselves
|
|
318
|
-
# reusable workflow definitions referenced by downstream callers.
|
|
319
|
-
# Internal .github/workflows/*.yml hotfixes (e.g. self-test runs) are
|
|
320
|
-
# intentionally skipped to avoid false-positive nags.
|
|
321
|
-
local relevant_files
|
|
322
|
-
relevant_files=$(git -C "$INSTALL_DIR" diff --name-only "$old_sha" "$new_sha" -- \
|
|
323
|
-
'.agents/templates/workflows/' \
|
|
324
|
-
'.github/workflows/' \
|
|
325
|
-
2>/dev/null \
|
|
326
|
-
| grep -E '(\.agents/templates/workflows/.*\.ya?ml$|\.github/workflows/.*-reusable\.ya?ml$)' \
|
|
327
|
-
|| true)
|
|
328
|
-
[[ -z "$relevant_files" ]] && return 0
|
|
329
|
-
|
|
330
|
-
local file_count
|
|
331
|
-
file_count=$(printf '%s\n' "$relevant_files" | wc -l | tr -d ' ')
|
|
332
|
-
echo ""
|
|
333
|
-
print_warning "Workflow templates updated ($file_count file(s)) — downstream callers may have drifted."
|
|
334
|
-
print_info " Detect drift: aidevops check-workflows"
|
|
335
|
-
print_info " Apply fix: aidevops sync-workflows --apply [--repo OWNER/REPO]"
|
|
336
|
-
|
|
337
|
-
# Persist as advisory so the next session greeting surfaces it even if
|
|
338
|
-
# the operator misses the inline warning. Day-stamped ID makes repeated
|
|
339
|
-
# updates within the same day idempotent (one advisory per day);
|
|
340
|
-
# 'aidevops security dismiss <id>' silences a specific day's advisory.
|
|
341
|
-
_update_emit_workflow_drift_advisory "$relevant_files" || true
|
|
342
|
-
return 0
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
# Companion to _update_check_workflow_drift — separated for testability.
|
|
346
|
-
# Args: $1=relevant_files (newline-separated)
|
|
347
|
-
# Returns: 0 (always — fail-open; advisory write must never break update)
|
|
348
|
-
_update_emit_workflow_drift_advisory() {
|
|
349
|
-
local relevant_files="$1"
|
|
350
|
-
local advisories_dir="${HOME}/.aidevops/advisories"
|
|
351
|
-
local adv_id
|
|
352
|
-
adv_id="workflow-drift-$(date +%Y%m%d)"
|
|
353
|
-
local dismissed_file="$advisories_dir/dismissed.txt"
|
|
354
|
-
|
|
355
|
-
# Skip if today's advisory was already dismissed.
|
|
356
|
-
if [[ -f "$dismissed_file" ]] && grep -qxF "$adv_id" "$dismissed_file" 2>/dev/null; then
|
|
357
|
-
return 0
|
|
358
|
-
fi
|
|
359
|
-
|
|
360
|
-
mkdir -p "$advisories_dir" 2>/dev/null || return 0
|
|
361
|
-
local adv_file="$advisories_dir/${adv_id}.advisory"
|
|
362
|
-
|
|
363
|
-
{
|
|
364
|
-
printf 'Workflow templates changed — downstream caller workflows may have drifted.\n'
|
|
365
|
-
printf '\n'
|
|
366
|
-
printf 'Files changed in this update:\n'
|
|
367
|
-
printf '%s\n' "$relevant_files" | sed 's|^| |'
|
|
368
|
-
printf '\n'
|
|
369
|
-
printf 'Detect drift: aidevops check-workflows\n'
|
|
370
|
-
printf 'Apply fix: aidevops sync-workflows --apply [--repo OWNER/REPO]\n'
|
|
371
|
-
printf 'Background: reference/reusable-workflows.md\n'
|
|
372
|
-
} >"$adv_file" 2>/dev/null || return 0
|
|
373
|
-
return 0
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
# Verify supply chain signature after pulling framework updates.
|
|
377
|
-
# Checks that the HEAD commit is signed by the trusted maintainer key.
|
|
378
|
-
# Non-blocking: warns on failure, does not abort the update.
|
|
379
|
-
_update_verify_signature() {
|
|
380
|
-
local signing_helper="$AGENTS_DIR/scripts/signing-setup.sh"
|
|
381
|
-
|
|
382
|
-
# Cannot verify if the helper script is not yet deployed
|
|
383
|
-
if [[ ! -f "$signing_helper" ]]; then
|
|
384
|
-
return 0
|
|
385
|
-
fi
|
|
386
|
-
|
|
387
|
-
local result
|
|
388
|
-
result=$(bash "$signing_helper" verify-update "$INSTALL_DIR" 2>/dev/null || echo "UNKNOWN")
|
|
389
|
-
|
|
390
|
-
case "$result" in
|
|
391
|
-
VERIFIED)
|
|
392
|
-
print_success "Supply chain verified: HEAD commit is signed by trusted maintainer"
|
|
393
|
-
;;
|
|
394
|
-
UNSIGNED)
|
|
395
|
-
print_warning "HEAD commit is not signed — cannot verify supply chain integrity"
|
|
396
|
-
print_info "This is expected for older releases. Signed commits start from v3.6.21+"
|
|
397
|
-
;;
|
|
398
|
-
UNTRUSTED)
|
|
399
|
-
print_warning "HEAD commit is signed but by an untrusted key"
|
|
400
|
-
print_info "Run 'aidevops signing setup' to configure signature verification"
|
|
401
|
-
;;
|
|
402
|
-
BAD_SIGNATURE)
|
|
403
|
-
print_error "HEAD commit has a BAD signature — update may be compromised"
|
|
404
|
-
print_info "Verify manually: cd $INSTALL_DIR && git log --show-signature -1"
|
|
405
|
-
;;
|
|
406
|
-
UNVERIFIABLE)
|
|
407
|
-
# Signing not configured yet — silent, do not nag
|
|
408
|
-
;;
|
|
409
|
-
esac
|
|
410
|
-
return 0
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
# One-shot, idempotent migration of supervisor.* → orchestration.* in settings.json (t2946).
|
|
414
|
-
# Safe: reads value from supervisor.* only when orchestration.* key is absent.
|
|
415
|
-
# Logs to ~/.aidevops/logs/settings-migration.log.
|
|
416
|
-
_migrate_settings_supervisor_to_orchestration() {
|
|
417
|
-
local _settings_file="${HOME}/.config/aidevops/settings.json"
|
|
418
|
-
local _log_file="${HOME}/.aidevops/logs/settings-migration.log"
|
|
419
|
-
|
|
420
|
-
if ! command -v jq >/dev/null 2>&1; then
|
|
421
|
-
return 0
|
|
422
|
-
fi
|
|
423
|
-
if [[ ! -f "$_settings_file" ]]; then
|
|
424
|
-
return 0
|
|
425
|
-
fi
|
|
426
|
-
if ! jq . "$_settings_file" >/dev/null 2>&1; then
|
|
427
|
-
return 0
|
|
428
|
-
fi
|
|
429
|
-
|
|
430
|
-
# Check if supervisor.pulse_interval_seconds exists and orchestration.pulse_interval_seconds is absent.
|
|
431
|
-
local _has_sv _has_orch
|
|
432
|
-
_has_sv=$(jq -r 'if .supervisor.pulse_interval_seconds != null then "yes" else "no" end' "$_settings_file" 2>/dev/null)
|
|
433
|
-
_has_orch=$(jq -r 'if .orchestration.pulse_interval_seconds != null then "yes" else "no" end' "$_settings_file" 2>/dev/null)
|
|
434
|
-
|
|
435
|
-
if [[ "$_has_sv" != "yes" ]]; then
|
|
436
|
-
return 0
|
|
437
|
-
fi
|
|
438
|
-
|
|
439
|
-
local _ts
|
|
440
|
-
_ts=$(date -u +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date +%Y-%m-%dT%H:%M:%SZ)
|
|
441
|
-
mkdir -p "$(dirname "$_log_file")" 2>/dev/null || true
|
|
442
|
-
|
|
443
|
-
local _tmp
|
|
444
|
-
_tmp=$(mktemp 2>/dev/null) || return 0
|
|
445
|
-
|
|
446
|
-
if [[ "$_has_orch" == "no" ]]; then
|
|
447
|
-
# Migrate: copy supervisor.pulse_interval_seconds to orchestration.pulse_interval_seconds,
|
|
448
|
-
# then remove supervisor.pulse_interval_seconds.
|
|
449
|
-
local _sv_val
|
|
450
|
-
_sv_val=$(jq -r '.supervisor.pulse_interval_seconds' "$_settings_file" 2>/dev/null)
|
|
451
|
-
if jq --argjson v "$_sv_val" \
|
|
452
|
-
'(.orchestration.pulse_interval_seconds) = $v | del(.supervisor.pulse_interval_seconds)' \
|
|
453
|
-
"$_settings_file" >"$_tmp" 2>/dev/null && [[ -s "$_tmp" ]]; then
|
|
454
|
-
mv "$_tmp" "$_settings_file"
|
|
455
|
-
printf '[%s] migrated supervisor.pulse_interval_seconds=%s → orchestration.pulse_interval_seconds\n' \
|
|
456
|
-
"$_ts" "$_sv_val" >>"$_log_file" 2>/dev/null || true
|
|
457
|
-
print_info "Settings migrated: supervisor.pulse_interval_seconds → orchestration.pulse_interval_seconds ($_sv_val)"
|
|
458
|
-
else
|
|
459
|
-
rm -f "$_tmp"
|
|
460
|
-
fi
|
|
461
|
-
else
|
|
462
|
-
# Both present: orchestration wins, remove the stale supervisor key.
|
|
463
|
-
local _orch_val
|
|
464
|
-
_orch_val=$(jq -r '.orchestration.pulse_interval_seconds' "$_settings_file" 2>/dev/null)
|
|
465
|
-
if jq 'del(.supervisor.pulse_interval_seconds)' \
|
|
466
|
-
"$_settings_file" >"$_tmp" 2>/dev/null && [[ -s "$_tmp" ]]; then
|
|
467
|
-
mv "$_tmp" "$_settings_file"
|
|
468
|
-
printf '[%s] removed stale supervisor.pulse_interval_seconds (orchestration.pulse_interval_seconds=%s wins)\n' \
|
|
469
|
-
"$_ts" "$_orch_val" >>"$_log_file" 2>/dev/null || true
|
|
470
|
-
print_info "Settings cleaned: removed stale supervisor.pulse_interval_seconds (orchestration value $_orch_val kept)"
|
|
471
|
-
else
|
|
472
|
-
rm -f "$_tmp"
|
|
473
|
-
fi
|
|
474
|
-
fi
|
|
475
|
-
return 0
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
_update_check_daemon_health() {
|
|
479
|
-
local helper="$HOME/.aidevops/agents/scripts/auto-update-helper.sh"
|
|
480
|
-
[[ -x "$helper" ]] || return 0
|
|
481
|
-
local advisory_dir="$HOME/.aidevops/advisories"
|
|
482
|
-
local advisory_file="$advisory_dir/daemon-disabled.advisory"
|
|
483
|
-
|
|
484
|
-
local hc_rc=0
|
|
485
|
-
"$helper" health-check --quiet >/dev/null 2>&1 || hc_rc=$?
|
|
486
|
-
|
|
487
|
-
if [[ "$hc_rc" -eq 0 ]]; then
|
|
488
|
-
# Healthy — clear any stale advisory.
|
|
489
|
-
[[ -f "$advisory_file" ]] && rm -f "$advisory_file"
|
|
490
|
-
return 0
|
|
491
|
-
fi
|
|
492
|
-
|
|
493
|
-
# Unhealthy — warn on stderr and write advisory.
|
|
494
|
-
mkdir -p "$advisory_dir" 2>/dev/null || return 0
|
|
495
|
-
local fix_cmd="aidevops auto-update enable"
|
|
496
|
-
[[ "$hc_rc" -eq 1 ]] && fix_cmd="aidevops auto-update check"
|
|
497
|
-
cat >"$advisory_file" <<EOF
|
|
498
|
-
auto-update daemon is not running normally on this runner. Without it, this
|
|
499
|
-
runner falls behind the fleet and may dispatch workers that fail because of
|
|
500
|
-
bugs already fixed upstream. See cross-runner-coordination.md §4.4.
|
|
501
|
-
|
|
502
|
-
Diagnose: aidevops auto-update health-check
|
|
503
|
-
Fix: ${fix_cmd}
|
|
504
|
-
EOF
|
|
505
|
-
|
|
506
|
-
if [[ "$hc_rc" -eq 1 ]]; then
|
|
507
|
-
print_warning "Auto-update daemon is stalled. Fix: ${fix_cmd}"
|
|
508
|
-
else
|
|
509
|
-
print_warning "Auto-update daemon is not running. Fix: ${fix_cmd}"
|
|
510
|
-
fi
|
|
511
|
-
return 0
|
|
512
|
-
}
|