gentle-agent-extensions 1.0.1 → 1.2.0
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 +177 -287
- package/package.json +17 -14
- package/resources/agents/architecture-council.md +158 -0
- package/resources/overlays/orchestrator.overlay.md +162 -0
- package/resources/routing/architecture-council.md +65 -0
- package/resources/skills/architecture-quest/SKILL.md +238 -0
- package/resources/skills/gentle-agent-extension/SKILL.md +329 -0
- package/src/extension.ts +544 -0
- package/.manifest.json +0 -15
- package/agents/.gitkeep +0 -0
- package/assets/.gitkeep +0 -0
- package/docs/conventions.md +0 -278
- package/extensions/gentle-agent-extensions.ts +0 -547
- package/mcp/.gitkeep +0 -31
- package/overlays/.gitkeep +0 -0
- package/routing/.gitkeep +0 -0
- package/scripts/doctor.sh +0 -509
- package/scripts/install.sh +0 -206
- package/scripts/sync.sh +0 -513
- package/skills/gentle-agent-extension/SKILL.md +0 -360
- package/tests/gentle-extensions.test.ts +0 -653
package/scripts/install.sh
DELETED
|
@@ -1,206 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# install.sh - Install gentle-agent-extensions
|
|
3
|
-
#
|
|
4
|
-
# This script sets up the project and configures the Pi extension.
|
|
5
|
-
# It supports multiple installation profiles:
|
|
6
|
-
# - pi (default): Standard Pi installation at ~/.pi/agent
|
|
7
|
-
# - gentle-shell: Isolated Gentle Shell at ~/.local/share/gentle-shell/agent
|
|
8
|
-
# - gentle-shell-linked: Gentle Shell with --link at ~/.pi/agent
|
|
9
|
-
|
|
10
|
-
set -euo pipefail
|
|
11
|
-
|
|
12
|
-
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
13
|
-
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
|
14
|
-
|
|
15
|
-
# Colors for output
|
|
16
|
-
RED='\033[0;31m'
|
|
17
|
-
GREEN='\033[0;32m'
|
|
18
|
-
YELLOW='\033[1;33m'
|
|
19
|
-
BLUE='\033[0;34m'
|
|
20
|
-
NC='\033[0m' # No Color
|
|
21
|
-
|
|
22
|
-
log_info() { echo -e "${BLUE}[INFO]${NC} $*"; }
|
|
23
|
-
log_success() { echo -e "${GREEN}[SUCCESS]${NC} $*"; }
|
|
24
|
-
log_warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
|
|
25
|
-
log_error() { echo -e "${RED}[ERROR]${NC} $*"; }
|
|
26
|
-
|
|
27
|
-
usage() {
|
|
28
|
-
cat <<EOF
|
|
29
|
-
Usage: $0 [OPTIONS]
|
|
30
|
-
|
|
31
|
-
Install gentle-agent-extensions for Pi / Gentle Pi.
|
|
32
|
-
|
|
33
|
-
Options:
|
|
34
|
-
--profile PROFILE Installation profile: pi, gentle-shell, gentle-shell-linked (default: pi)
|
|
35
|
-
--agent-home PATH Explicit agent home directory (overrides profile)
|
|
36
|
-
--dry-run Show what would be done without making changes
|
|
37
|
-
--force Overwrite existing extension file
|
|
38
|
-
-h, --help Show this help
|
|
39
|
-
|
|
40
|
-
Examples:
|
|
41
|
-
$0 # Install for standard Pi
|
|
42
|
-
$0 --profile gentle-shell # Install for isolated Gentle Shell
|
|
43
|
-
$0 --agent-home ~/.pi/agent # Explicit agent home
|
|
44
|
-
$0 --dry-run # Preview changes
|
|
45
|
-
EOF
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
PROFILE="pi"
|
|
49
|
-
AGENT_HOME=""
|
|
50
|
-
DRY_RUN=false
|
|
51
|
-
FORCE=false
|
|
52
|
-
|
|
53
|
-
while [[ $# -gt 0 ]]; do
|
|
54
|
-
case $1 in
|
|
55
|
-
--profile)
|
|
56
|
-
PROFILE="$2"
|
|
57
|
-
shift 2
|
|
58
|
-
;;
|
|
59
|
-
--agent-home)
|
|
60
|
-
AGENT_HOME="$2"
|
|
61
|
-
shift 2
|
|
62
|
-
;;
|
|
63
|
-
--dry-run)
|
|
64
|
-
DRY_RUN=true
|
|
65
|
-
shift
|
|
66
|
-
;;
|
|
67
|
-
--force)
|
|
68
|
-
FORCE=true
|
|
69
|
-
shift
|
|
70
|
-
;;
|
|
71
|
-
-h|--help)
|
|
72
|
-
usage
|
|
73
|
-
exit 0
|
|
74
|
-
;;
|
|
75
|
-
*)
|
|
76
|
-
log_error "Unknown option: $1"
|
|
77
|
-
usage
|
|
78
|
-
exit 1
|
|
79
|
-
;;
|
|
80
|
-
esac
|
|
81
|
-
done
|
|
82
|
-
|
|
83
|
-
# Determine agent home based on profile
|
|
84
|
-
if [[ -z "$AGENT_HOME" ]]; then
|
|
85
|
-
case "$PROFILE" in
|
|
86
|
-
pi)
|
|
87
|
-
AGENT_HOME="${HOME}/.pi/agent"
|
|
88
|
-
;;
|
|
89
|
-
gentle-shell)
|
|
90
|
-
AGENT_HOME="${HOME}/.local/share/gentle-shell/agent"
|
|
91
|
-
;;
|
|
92
|
-
gentle-shell-linked)
|
|
93
|
-
AGENT_HOME="${HOME}/.pi/agent"
|
|
94
|
-
;;
|
|
95
|
-
*)
|
|
96
|
-
log_error "Unknown profile: $PROFILE"
|
|
97
|
-
exit 1
|
|
98
|
-
;;
|
|
99
|
-
esac
|
|
100
|
-
fi
|
|
101
|
-
|
|
102
|
-
EXTENSION_SOURCE="${PROJECT_ROOT}/extensions/gentle-agent-extensions.ts"
|
|
103
|
-
EXTENSION_TARGET="${AGENT_HOME}/extensions/gentle-agent-extensions.ts"
|
|
104
|
-
|
|
105
|
-
log_info "Installing gentle-agent-extensions"
|
|
106
|
-
log_info "Profile: $PROFILE"
|
|
107
|
-
log_info "Agent home: $AGENT_HOME"
|
|
108
|
-
log_info "Project root: $PROJECT_ROOT"
|
|
109
|
-
|
|
110
|
-
if [[ "$DRY_RUN" == "true" ]]; then
|
|
111
|
-
log_warn "DRY RUN - no changes will be made"
|
|
112
|
-
fi
|
|
113
|
-
|
|
114
|
-
# Check source extension exists
|
|
115
|
-
if [[ ! -f "$EXTENSION_SOURCE" ]]; then
|
|
116
|
-
log_error "Extension source not found: $EXTENSION_SOURCE"
|
|
117
|
-
exit 1
|
|
118
|
-
fi
|
|
119
|
-
|
|
120
|
-
# Create agent home extensions directory
|
|
121
|
-
if [[ "$DRY_RUN" == "false" ]]; then
|
|
122
|
-
mkdir -p "$(dirname "$EXTENSION_TARGET")"
|
|
123
|
-
fi
|
|
124
|
-
|
|
125
|
-
# Check if extension already exists
|
|
126
|
-
if [[ -f "$EXTENSION_TARGET" && "$FORCE" == "false" && "$DRY_RUN" == "false" ]]; then
|
|
127
|
-
log_warn "Extension already exists at $EXTENSION_TARGET"
|
|
128
|
-
log_warn "Use --force to overwrite or remove it first"
|
|
129
|
-
exit 1
|
|
130
|
-
fi
|
|
131
|
-
|
|
132
|
-
# Copy extension
|
|
133
|
-
if [[ "$DRY_RUN" == "true" ]]; then
|
|
134
|
-
log_info "Would copy: $EXTENSION_SOURCE -> $EXTENSION_TARGET"
|
|
135
|
-
else
|
|
136
|
-
cp "$EXTENSION_SOURCE" "$EXTENSION_TARGET"
|
|
137
|
-
log_success "Extension installed to $EXTENSION_TARGET"
|
|
138
|
-
fi
|
|
139
|
-
|
|
140
|
-
# Create project agent directories
|
|
141
|
-
PROJECT_AGENTS_DIR="${PROJECT_ROOT}/.pi/agents"
|
|
142
|
-
PROJECT_SUBAGENTS_DIR="${PROJECT_ROOT}/.pi/subagents"
|
|
143
|
-
|
|
144
|
-
if [[ "$DRY_RUN" == "true" ]]; then
|
|
145
|
-
log_info "Would create directories: $PROJECT_AGENTS_DIR, $PROJECT_SUBAGENTS_DIR"
|
|
146
|
-
else
|
|
147
|
-
mkdir -p "$PROJECT_AGENTS_DIR" "$PROJECT_SUBAGENTS_DIR"
|
|
148
|
-
log_success "Created project agent directories"
|
|
149
|
-
fi
|
|
150
|
-
|
|
151
|
-
# Initialize manifest if not exists
|
|
152
|
-
MANIFEST="${PROJECT_ROOT}/.manifest.json"
|
|
153
|
-
if [[ ! -f "$MANIFEST" ]]; then
|
|
154
|
-
if [[ "$DRY_RUN" == "true" ]]; then
|
|
155
|
-
log_info "Would create manifest: $MANIFEST"
|
|
156
|
-
else
|
|
157
|
-
cat > "$MANIFEST" <<MANIFEST_EOF
|
|
158
|
-
{
|
|
159
|
-
"version": "1.0",
|
|
160
|
-
"description": "Manifest of files managed by gentle-agent-extensions",
|
|
161
|
-
"managedPaths": {
|
|
162
|
-
"extensions": ["extensions/gentle-agent-extensions.ts"],
|
|
163
|
-
"agents": [],
|
|
164
|
-
"routing": [],
|
|
165
|
-
"overlays": [],
|
|
166
|
-
"skills": [],
|
|
167
|
-
"scripts": [],
|
|
168
|
-
"assets": []
|
|
169
|
-
},
|
|
170
|
-
"installProfiles": {
|
|
171
|
-
"$PROFILE": { "agentHome": "$AGENT_HOME" }
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
MANIFEST_EOF
|
|
175
|
-
log_success "Created manifest: $MANIFEST"
|
|
176
|
-
fi
|
|
177
|
-
else
|
|
178
|
-
log_info "Manifest already exists: $MANIFEST"
|
|
179
|
-
fi
|
|
180
|
-
|
|
181
|
-
# Add extension to Pi settings if not already present
|
|
182
|
-
SETTINGS_FILE="${AGENT_HOME}/settings.json"
|
|
183
|
-
if [[ "$DRY_RUN" == "false" ]]; then
|
|
184
|
-
if [[ -f "$SETTINGS_FILE" ]]; then
|
|
185
|
-
# Check if extension is already in settings
|
|
186
|
-
if ! grep -q "gentle-agent-extensions" "$SETTINGS_FILE"; then
|
|
187
|
-
log_warn "Extension not in Pi settings. Add it manually or run:"
|
|
188
|
-
log_warn " pi install file:$PROJECT_ROOT"
|
|
189
|
-
log_warn "Or add to $SETTINGS_FILE:"
|
|
190
|
-
log_warn ' { "packages": [{ "source": "file:'"$PROJECT_ROOT"'" }] }'
|
|
191
|
-
else
|
|
192
|
-
log_success "Extension already configured in Pi settings"
|
|
193
|
-
fi
|
|
194
|
-
else
|
|
195
|
-
log_warn "Pi settings not found at $SETTINGS_FILE"
|
|
196
|
-
log_warn "Run 'pi install file:$PROJECT_ROOT' to register the extension"
|
|
197
|
-
fi
|
|
198
|
-
fi
|
|
199
|
-
|
|
200
|
-
log_success "Installation complete!"
|
|
201
|
-
log_info ""
|
|
202
|
-
log_info "Next steps:"
|
|
203
|
-
log_info " 1. Register the extension with Pi: pi install file:$PROJECT_ROOT"
|
|
204
|
-
log_info " 2. Run sync: $PROJECT_ROOT/scripts/sync.sh"
|
|
205
|
-
log_info " 3. Verify: $PROJECT_ROOT/scripts/doctor.sh"
|
|
206
|
-
log_info " 4. Start Pi and run /gentle-extensions:status"
|
package/scripts/sync.sh
DELETED
|
@@ -1,513 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# sync.sh - Synchronize gentle-agent-extensions with active Pi profile
|
|
3
|
-
#
|
|
4
|
-
# This script synchronizes custom agents, overlays, routing, skills, and MCP servers
|
|
5
|
-
# with the active Pi installation. It is idempotent and supports dry-run/check modes.
|
|
6
|
-
|
|
7
|
-
set -euo pipefail
|
|
8
|
-
|
|
9
|
-
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
10
|
-
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
|
11
|
-
|
|
12
|
-
# Colors
|
|
13
|
-
RED='\033[0;31m'
|
|
14
|
-
GREEN='\033[0;32m'
|
|
15
|
-
YELLOW='\033[1;33m'
|
|
16
|
-
BLUE='\033[0;34m'
|
|
17
|
-
NC='\033[0m'
|
|
18
|
-
|
|
19
|
-
log_info() { echo -e "${BLUE}[INFO]${NC} $*"; }
|
|
20
|
-
log_success() { echo -e "${GREEN}[SUCCESS]${NC} $*"; }
|
|
21
|
-
log_warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
|
|
22
|
-
log_error() { echo -e "${RED}[ERROR]${NC} $*"; }
|
|
23
|
-
|
|
24
|
-
usage() {
|
|
25
|
-
cat <<EOF
|
|
26
|
-
Usage: $0 [OPTIONS]
|
|
27
|
-
|
|
28
|
-
Synchronize gentle-agent-extensions with the active Pi profile.
|
|
29
|
-
|
|
30
|
-
Options:
|
|
31
|
-
--profile PROFILE Installation profile: pi, gentle-shell, gentle-shell-linked (default: pi)
|
|
32
|
-
--agent-home PATH Explicit agent home directory (overrides profile)
|
|
33
|
-
--dry-run Show what would be done without making changes
|
|
34
|
-
--check Validate configuration and report issues (exit code 0 if ok, 1 if issues)
|
|
35
|
-
-h, --help Show this help
|
|
36
|
-
|
|
37
|
-
Examples:
|
|
38
|
-
$0 # Full sync
|
|
39
|
-
$0 --dry-run # Preview changes
|
|
40
|
-
$0 --check # Validate only
|
|
41
|
-
$0 --profile gentle-shell
|
|
42
|
-
EOF
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
PROFILE="pi"
|
|
46
|
-
AGENT_HOME=""
|
|
47
|
-
DRY_RUN=false
|
|
48
|
-
CHECK_MODE=false
|
|
49
|
-
|
|
50
|
-
while [[ $# -gt 0 ]]; do
|
|
51
|
-
case $1 in
|
|
52
|
-
--profile)
|
|
53
|
-
PROFILE="$2"
|
|
54
|
-
shift 2
|
|
55
|
-
;;
|
|
56
|
-
--agent-home)
|
|
57
|
-
AGENT_HOME="$2"
|
|
58
|
-
shift 2
|
|
59
|
-
;;
|
|
60
|
-
--dry-run)
|
|
61
|
-
DRY_RUN=true
|
|
62
|
-
shift
|
|
63
|
-
;;
|
|
64
|
-
--check)
|
|
65
|
-
CHECK_MODE=true
|
|
66
|
-
shift
|
|
67
|
-
;;
|
|
68
|
-
-h|--help)
|
|
69
|
-
usage
|
|
70
|
-
exit 0
|
|
71
|
-
;;
|
|
72
|
-
*)
|
|
73
|
-
log_error "Unknown option: $1"
|
|
74
|
-
usage
|
|
75
|
-
exit 1
|
|
76
|
-
;;
|
|
77
|
-
esac
|
|
78
|
-
done
|
|
79
|
-
|
|
80
|
-
# Determine agent home
|
|
81
|
-
if [[ -z "$AGENT_HOME" ]]; then
|
|
82
|
-
case "$PROFILE" in
|
|
83
|
-
pi) AGENT_HOME="${HOME}/.pi/agent" ;;
|
|
84
|
-
gentle-shell) AGENT_HOME="${HOME}/.local/share/gentle-shell/agent" ;;
|
|
85
|
-
gentle-shell-linked) AGENT_HOME="${HOME}/.pi/agent" ;;
|
|
86
|
-
*) log_error "Unknown profile: $PROFILE"; exit 1 ;;
|
|
87
|
-
esac
|
|
88
|
-
fi
|
|
89
|
-
|
|
90
|
-
log_info "Syncing gentle-agent-extensions"
|
|
91
|
-
log_info "Profile: $PROFILE"
|
|
92
|
-
log_info "Agent home: $AGENT_HOME"
|
|
93
|
-
log_info "Project root: $PROJECT_ROOT"
|
|
94
|
-
|
|
95
|
-
if [[ "$DRY_RUN" == "true" ]]; then
|
|
96
|
-
log_warn "DRY RUN mode - no changes will be made"
|
|
97
|
-
fi
|
|
98
|
-
|
|
99
|
-
if [[ "$CHECK_MODE" == "true" ]]; then
|
|
100
|
-
log_info "CHECK mode - validation only"
|
|
101
|
-
fi
|
|
102
|
-
|
|
103
|
-
# Track changes and issues
|
|
104
|
-
CHANGES=0
|
|
105
|
-
ISSUES=0
|
|
106
|
-
WARNINGS=0
|
|
107
|
-
|
|
108
|
-
# Source directories
|
|
109
|
-
AGENTS_SRC="${PROJECT_ROOT}/agents"
|
|
110
|
-
OVERLAYS_SRC="${PROJECT_ROOT}/overlays"
|
|
111
|
-
ROUTING_SRC="${PROJECT_ROOT}/routing"
|
|
112
|
-
SKILLS_SRC="${PROJECT_ROOT}/skills"
|
|
113
|
-
MCP_SRC="${PROJECT_ROOT}/mcp"
|
|
114
|
-
|
|
115
|
-
# Target directories
|
|
116
|
-
AGENT_HOME_EXTENSIONS="${AGENT_HOME}/extensions"
|
|
117
|
-
PROJECT_AGENTS_DIR="${PROJECT_ROOT}/.pi/agents"
|
|
118
|
-
PROJECT_SUBAGENTS_DIR="${PROJECT_ROOT}/.pi/subagents"
|
|
119
|
-
|
|
120
|
-
# Manifest
|
|
121
|
-
MANIFEST="${PROJECT_ROOT}/.manifest.json"
|
|
122
|
-
|
|
123
|
-
# Helper: read manifest
|
|
124
|
-
read_manifest() {
|
|
125
|
-
if [[ -f "$MANIFEST" ]]; then
|
|
126
|
-
cat "$MANIFEST"
|
|
127
|
-
else
|
|
128
|
-
echo '{"version":"1.0","managedPaths":{}}'
|
|
129
|
-
fi
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
# Helper: write manifest
|
|
133
|
-
write_manifest() {
|
|
134
|
-
if [[ "$DRY_RUN" == "false" && "$CHECK_MODE" == "false" ]]; then
|
|
135
|
-
echo "$1" > "$MANIFEST"
|
|
136
|
-
fi
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
# Helper: update manifest path list
|
|
140
|
-
update_manifest_paths() {
|
|
141
|
-
local key="$1"
|
|
142
|
-
local paths_json="$2"
|
|
143
|
-
local manifest
|
|
144
|
-
manifest=$(read_manifest)
|
|
145
|
-
if command -v jq &> /dev/null; then
|
|
146
|
-
echo "$manifest" | jq --arg key "$key" --argjson paths "$paths_json" '.managedPaths[$key] = $paths'
|
|
147
|
-
else
|
|
148
|
-
log_warn "jq not available, manifest not updated (install jq for full manifest support)"
|
|
149
|
-
echo "$manifest"
|
|
150
|
-
fi
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
# 1. Validate project structure
|
|
154
|
-
log_info "Validating project structure..."
|
|
155
|
-
|
|
156
|
-
for dir in "$AGENTS_SRC" "$OVERLAYS_SRC" "$ROUTING_SRC" "$SKILLS_SRC" "$MCP_SRC"; do
|
|
157
|
-
if [[ ! -d "$dir" ]]; then
|
|
158
|
-
log_warn "Directory missing: $dir"
|
|
159
|
-
((WARNINGS++))
|
|
160
|
-
fi
|
|
161
|
-
done
|
|
162
|
-
|
|
163
|
-
# 2. Sync agents
|
|
164
|
-
log_info "Syncing agents..."
|
|
165
|
-
|
|
166
|
-
if [[ -d "$AGENTS_SRC" ]]; then
|
|
167
|
-
if [[ "$DRY_RUN" == "false" && "$CHECK_MODE" == "false" ]]; then
|
|
168
|
-
mkdir -p "$PROJECT_AGENTS_DIR"
|
|
169
|
-
fi
|
|
170
|
-
|
|
171
|
-
MANAGED_AGENTS=()
|
|
172
|
-
|
|
173
|
-
for agent_file in "$AGENTS_SRC"/*.md; do
|
|
174
|
-
[[ -f "$agent_file" ]] || continue
|
|
175
|
-
|
|
176
|
-
agent_name=$(basename "$agent_file" .md)
|
|
177
|
-
target_file="${PROJECT_AGENTS_DIR}/${agent_name}.md"
|
|
178
|
-
|
|
179
|
-
if ! head -20 "$agent_file" | grep -q "^---$"; then
|
|
180
|
-
log_warn "Agent $agent_name: missing frontmatter delimiter"
|
|
181
|
-
((WARNINGS++))
|
|
182
|
-
fi
|
|
183
|
-
|
|
184
|
-
if ! grep -q "^name:" "$agent_file"; then
|
|
185
|
-
log_warn "Agent $agent_name: missing 'name' in frontmatter"
|
|
186
|
-
((WARNINGS++))
|
|
187
|
-
fi
|
|
188
|
-
|
|
189
|
-
if [[ "$DRY_RUN" == "true" ]]; then
|
|
190
|
-
if [[ -f "$target_file" ]]; then
|
|
191
|
-
if ! cmp -s "$agent_file" "$target_file"; then
|
|
192
|
-
log_info "Would update: $agent_name -> $target_file"
|
|
193
|
-
((CHANGES++))
|
|
194
|
-
else
|
|
195
|
-
log_info "Unchanged: $agent_name"
|
|
196
|
-
fi
|
|
197
|
-
else
|
|
198
|
-
log_info "Would create: $agent_name -> $target_file"
|
|
199
|
-
((CHANGES++))
|
|
200
|
-
fi
|
|
201
|
-
elif [[ "$CHECK_MODE" == "false" ]]; then
|
|
202
|
-
if [[ -f "$target_file" ]]; then
|
|
203
|
-
if ! cmp -s "$agent_file" "$target_file"; then
|
|
204
|
-
cp "$agent_file" "$target_file"
|
|
205
|
-
log_success "Updated agent: $agent_name"
|
|
206
|
-
((CHANGES++))
|
|
207
|
-
else
|
|
208
|
-
log_info "Unchanged: $agent_name"
|
|
209
|
-
fi
|
|
210
|
-
else
|
|
211
|
-
cp "$agent_file" "$target_file"
|
|
212
|
-
log_success "Created agent: $agent_name"
|
|
213
|
-
((CHANGES++))
|
|
214
|
-
fi
|
|
215
|
-
fi
|
|
216
|
-
|
|
217
|
-
MANAGED_AGENTS+=("$target_file")
|
|
218
|
-
done
|
|
219
|
-
|
|
220
|
-
if [[ "$DRY_RUN" == "false" && "$CHECK_MODE" == "false" ]]; then
|
|
221
|
-
paths_json=$(printf '"%s",' "${MANAGED_AGENTS[@]}" | sed 's/,$//')
|
|
222
|
-
paths_json="[$paths_json]"
|
|
223
|
-
new_manifest=$(update_manifest_paths "agents" "$paths_json")
|
|
224
|
-
write_manifest "$new_manifest"
|
|
225
|
-
fi
|
|
226
|
-
else
|
|
227
|
-
log_warn "Agents source directory not found: $AGENTS_SRC"
|
|
228
|
-
((WARNINGS++))
|
|
229
|
-
fi
|
|
230
|
-
|
|
231
|
-
# 3. Validate overlays
|
|
232
|
-
log_info "Validating overlays..."
|
|
233
|
-
|
|
234
|
-
if [[ -d "$OVERLAYS_SRC" ]]; then
|
|
235
|
-
for overlay_file in "$OVERLAYS_SRC"/*.overlay.md; do
|
|
236
|
-
[[ -f "$overlay_file" ]] || continue
|
|
237
|
-
|
|
238
|
-
overlay_name=$(basename "$overlay_file" .overlay.md)
|
|
239
|
-
|
|
240
|
-
if ! head -20 "$overlay_file" | grep -q "^---$"; then
|
|
241
|
-
log_warn "Overlay $overlay_name: missing frontmatter delimiter"
|
|
242
|
-
((WARNINGS++))
|
|
243
|
-
fi
|
|
244
|
-
|
|
245
|
-
if ! grep -q "^target:" "$overlay_file"; then
|
|
246
|
-
log_error "Overlay $overlay_name: missing 'target' in frontmatter"
|
|
247
|
-
((ISSUES++))
|
|
248
|
-
fi
|
|
249
|
-
|
|
250
|
-
target=$(grep "^target:" "$overlay_file" | head -1 | sed 's/^target: *//')
|
|
251
|
-
log_info "Overlay validated: $overlay_name -> target: $target"
|
|
252
|
-
done
|
|
253
|
-
else
|
|
254
|
-
log_warn "Overlays source directory not found: $OVERLAYS_SRC"
|
|
255
|
-
((WARNINGS++))
|
|
256
|
-
fi
|
|
257
|
-
|
|
258
|
-
# 4. Validate routing
|
|
259
|
-
log_info "Validating routing..."
|
|
260
|
-
|
|
261
|
-
if [[ -d "$ROUTING_SRC" ]]; then
|
|
262
|
-
for routing_file in "$ROUTING_SRC"/*.md; do
|
|
263
|
-
[[ -f "$routing_file" ]] || continue
|
|
264
|
-
|
|
265
|
-
routing_name=$(basename "$routing_file" .md)
|
|
266
|
-
|
|
267
|
-
if ! grep -q "^agent:" "$routing_file"; then
|
|
268
|
-
log_warn "Routing $routing_name: missing 'agent' in frontmatter"
|
|
269
|
-
((WARNINGS++))
|
|
270
|
-
fi
|
|
271
|
-
|
|
272
|
-
agent=$(grep "^agent:" "$routing_file" | head -1 | sed 's/^agent: *//')
|
|
273
|
-
log_info "Routing validated: $routing_name -> agent: $agent"
|
|
274
|
-
done
|
|
275
|
-
else
|
|
276
|
-
log_warn "Routing source directory not found: $ROUTING_SRC"
|
|
277
|
-
((WARNINGS++))
|
|
278
|
-
fi
|
|
279
|
-
|
|
280
|
-
# 5. Validate skills
|
|
281
|
-
log_info "Validating skills..."
|
|
282
|
-
|
|
283
|
-
if [[ -d "$SKILLS_SRC" ]]; then
|
|
284
|
-
for skill_dir in "$SKILLS_SRC"/*/; do
|
|
285
|
-
[[ -d "$skill_dir" ]] || continue
|
|
286
|
-
|
|
287
|
-
skill_name=$(basename "$skill_dir")
|
|
288
|
-
skill_file="${skill_dir}SKILL.md"
|
|
289
|
-
|
|
290
|
-
if [[ ! -f "$skill_file" ]]; then
|
|
291
|
-
log_warn "Skill $skill_name: missing SKILL.md"
|
|
292
|
-
((WARNINGS++))
|
|
293
|
-
continue
|
|
294
|
-
fi
|
|
295
|
-
|
|
296
|
-
for section in "Activation Contract" "Hard Rules" "Decision Gates" "Execution Steps" "Output Contract" "References"; do
|
|
297
|
-
if ! grep -q "^## $section" "$skill_file"; then
|
|
298
|
-
log_warn "Skill $skill_name: missing section '$section'"
|
|
299
|
-
((WARNINGS++))
|
|
300
|
-
fi
|
|
301
|
-
done
|
|
302
|
-
|
|
303
|
-
if ! head -10 "$skill_file" | grep -q "^---$"; then
|
|
304
|
-
log_warn "Skill $skill_name: missing frontmatter"
|
|
305
|
-
((WARNINGS++))
|
|
306
|
-
fi
|
|
307
|
-
|
|
308
|
-
log_info "Skill validated: $skill_name"
|
|
309
|
-
done
|
|
310
|
-
else
|
|
311
|
-
log_warn "Skills source directory not found: $SKILLS_SRC"
|
|
312
|
-
((WARNINGS++))
|
|
313
|
-
fi
|
|
314
|
-
|
|
315
|
-
# 6. Validate MCP server definitions
|
|
316
|
-
log_info "Validating MCP servers..."
|
|
317
|
-
|
|
318
|
-
if [[ -d "$MCP_SRC" ]]; then
|
|
319
|
-
for mcp_file in "$MCP_SRC"/*.mcp.json; do
|
|
320
|
-
[[ -f "$mcp_file" ]] || continue
|
|
321
|
-
|
|
322
|
-
mcp_name=$(basename "$mcp_file" .mcp.json)
|
|
323
|
-
|
|
324
|
-
if ! command -v jq &> /dev/null; then
|
|
325
|
-
log_warn "MCP $mcp_name: jq not available, skipping JSON validation"
|
|
326
|
-
else
|
|
327
|
-
if ! jq empty "$mcp_file" 2>/dev/null; then
|
|
328
|
-
log_error "MCP $mcp_name: invalid JSON syntax"
|
|
329
|
-
((ISSUES++))
|
|
330
|
-
continue
|
|
331
|
-
fi
|
|
332
|
-
|
|
333
|
-
name=$(jq -r '.name // empty' "$mcp_file")
|
|
334
|
-
description=$(jq -r '.description // empty' "$mcp_file")
|
|
335
|
-
transport=$(jq -r '.transport // empty' "$mcp_file")
|
|
336
|
-
|
|
337
|
-
if [[ -z "$name" ]]; then
|
|
338
|
-
log_error "MCP $mcp_name: missing 'name' field"
|
|
339
|
-
((ISSUES++))
|
|
340
|
-
fi
|
|
341
|
-
if [[ -z "$description" ]]; then
|
|
342
|
-
log_error "MCP $mcp_name: missing 'description' field"
|
|
343
|
-
((ISSUES++))
|
|
344
|
-
fi
|
|
345
|
-
if [[ -z "$transport" ]]; then
|
|
346
|
-
log_error "MCP $mcp_name: missing 'transport' field"
|
|
347
|
-
((ISSUES++))
|
|
348
|
-
fi
|
|
349
|
-
|
|
350
|
-
if [[ "$transport" == "stdio" ]]; then
|
|
351
|
-
command=$(jq -r '.command // empty' "$mcp_file")
|
|
352
|
-
if [[ -z "$command" ]]; then
|
|
353
|
-
log_error "MCP $mcp_name: stdio transport requires 'command' field"
|
|
354
|
-
((ISSUES++))
|
|
355
|
-
fi
|
|
356
|
-
elif [[ "$transport" == "http" ]]; then
|
|
357
|
-
url=$(jq -r '.url // empty' "$mcp_file")
|
|
358
|
-
if [[ -z "$url" ]]; then
|
|
359
|
-
log_error "MCP $mcp_name: http transport requires 'url' field"
|
|
360
|
-
((ISSUES++))
|
|
361
|
-
fi
|
|
362
|
-
else
|
|
363
|
-
log_error "MCP $mcp_name: transport must be 'stdio' or 'http'"
|
|
364
|
-
((ISSUES++))
|
|
365
|
-
fi
|
|
366
|
-
|
|
367
|
-
if [[ -n "$name" ]] && ! [[ "$name" =~ ^[A-Za-z0-9_-]+$ ]]; then
|
|
368
|
-
log_error "MCP $mcp_name: name must contain only letters, digits, _, and -"
|
|
369
|
-
((ISSUES++))
|
|
370
|
-
fi
|
|
371
|
-
|
|
372
|
-
if command -v jq &> /dev/null; then
|
|
373
|
-
env_keys=$(jq -r '.env | keys[]' "$mcp_file" 2>/dev/null || true)
|
|
374
|
-
for key in $env_keys; do
|
|
375
|
-
value=$(jq -r ".env[\"$key\"]" "$mcp_file")
|
|
376
|
-
if [[ -n "$value" ]] && ! [[ "$value" =~ ^\$\{ ]]; then
|
|
377
|
-
key_lower=$(echo "$key" | tr '[:upper:]' '[:lower:]')
|
|
378
|
-
if [[ "$key_lower" =~ (secret|token|key|password) ]]; then
|
|
379
|
-
log_warn "MCP $mcp_name: env '$key' may contain hardcoded secret (use \${VAR} syntax)"
|
|
380
|
-
((WARNINGS++))
|
|
381
|
-
fi
|
|
382
|
-
fi
|
|
383
|
-
done
|
|
384
|
-
fi
|
|
385
|
-
|
|
386
|
-
log_info "MCP validated: $mcp_name ($transport)"
|
|
387
|
-
fi
|
|
388
|
-
done
|
|
389
|
-
else
|
|
390
|
-
log_warn "MCP source directory not found: $MCP_SRC"
|
|
391
|
-
((WARNINGS++))
|
|
392
|
-
fi
|
|
393
|
-
|
|
394
|
-
# 7. Check for duplicate agent names
|
|
395
|
-
log_info "Checking for conflicts..."
|
|
396
|
-
|
|
397
|
-
GLOBAL_AGENTS_DIRS=("$AGENT_HOME/agents" "$AGENT_HOME/subagents")
|
|
398
|
-
declare -A GLOBAL_AGENTS
|
|
399
|
-
|
|
400
|
-
for dir in "${GLOBAL_AGENTS_DIRS[@]}"; do
|
|
401
|
-
if [[ -d "$dir" ]]; then
|
|
402
|
-
for file in "$dir"/*.md; do
|
|
403
|
-
[[ -f "$file" ]] || continue
|
|
404
|
-
name=$(basename "$file" .md)
|
|
405
|
-
GLOBAL_AGENTS["$name"]="$dir"
|
|
406
|
-
done
|
|
407
|
-
fi
|
|
408
|
-
done
|
|
409
|
-
|
|
410
|
-
if [[ -d "$AGENTS_SRC" ]]; then
|
|
411
|
-
for agent_file in "$AGENTS_SRC"/*.md; do
|
|
412
|
-
[[ -f "$agent_file" ]] || continue
|
|
413
|
-
agent_name=$(basename "$agent_file" .md)
|
|
414
|
-
if [[ -n "${GLOBAL_AGENTS[$agent_name]:-}" ]]; then
|
|
415
|
-
log_warn "Project agent '$agent_name' shadows global agent in ${GLOBAL_AGENTS[$agent_name]}"
|
|
416
|
-
((WARNINGS++))
|
|
417
|
-
fi
|
|
418
|
-
done
|
|
419
|
-
fi
|
|
420
|
-
|
|
421
|
-
# 8. Check overlay targets exist
|
|
422
|
-
log_info "Checking overlay targets..."
|
|
423
|
-
if [[ -d "$OVERLAYS_SRC" ]]; then
|
|
424
|
-
for overlay_file in "$OVERLAYS_SRC"/*.overlay.md; do
|
|
425
|
-
[[ -f "$overlay_file" ]] || continue
|
|
426
|
-
target=$(grep "^target:" "$overlay_file" | head -1 | sed 's/^target: *//')
|
|
427
|
-
target=$(echo "$target" | xargs)
|
|
428
|
-
|
|
429
|
-
if [[ "$target" != "orchestrator" ]]; then
|
|
430
|
-
target_exists=false
|
|
431
|
-
if [[ -n "${GLOBAL_AGENTS[$target]:-}" ]]; then
|
|
432
|
-
target_exists=true
|
|
433
|
-
fi
|
|
434
|
-
if [[ -f "$AGENTS_SRC/${target}.md" ]]; then
|
|
435
|
-
target_exists=true
|
|
436
|
-
fi
|
|
437
|
-
|
|
438
|
-
if [[ "$target_exists" == "false" ]]; then
|
|
439
|
-
log_warn "Overlay targets unknown agent: $target (from $(basename "$overlay_file"))"
|
|
440
|
-
((WARNINGS++))
|
|
441
|
-
fi
|
|
442
|
-
fi
|
|
443
|
-
done
|
|
444
|
-
fi
|
|
445
|
-
|
|
446
|
-
# 9. Check MCP server name conflicts with mcp.json
|
|
447
|
-
log_info "Checking MCP server name conflicts..."
|
|
448
|
-
if [[ -d "$MCP_SRC" ]]; then
|
|
449
|
-
# Check user-level mcp.json
|
|
450
|
-
USER_MCP="${AGENT_HOME}/mcp.json"
|
|
451
|
-
if [[ -f "$USER_MCP" ]] && command -v jq &> /dev/null; then
|
|
452
|
-
for mcp_file in "$MCP_SRC"/*.mcp.json; do
|
|
453
|
-
[[ -f "$mcp_file" ]] || continue
|
|
454
|
-
name=$(jq -r '.name // empty' "$mcp_file")
|
|
455
|
-
if [[ -n "$name" ]] && jq -e ".mcpServers[\"$name\"]" "$USER_MCP" >/dev/null 2>&1; then
|
|
456
|
-
log_warn "MCP server '$name' also defined in user mcp.json (user config takes precedence)"
|
|
457
|
-
((WARNINGS++))
|
|
458
|
-
fi
|
|
459
|
-
done
|
|
460
|
-
fi
|
|
461
|
-
|
|
462
|
-
# Check project-level mcp.json
|
|
463
|
-
PROJECT_MCP="${PROJECT_ROOT}/.pi/mcp.json"
|
|
464
|
-
if [[ -f "$PROJECT_MCP" ]] && command -v jq &> /dev/null; then
|
|
465
|
-
for mcp_file in "$MCP_SRC"/*.mcp.json; do
|
|
466
|
-
[[ -f "$mcp_file" ]] || continue
|
|
467
|
-
name=$(jq -r '.name // empty' "$mcp_file")
|
|
468
|
-
if [[ -n "$name" ]] && jq -e ".mcpServers[\"$name\"]" "$PROJECT_MCP" >/dev/null 2>&1; then
|
|
469
|
-
log_warn "MCP server '$name' also defined in project .pi/mcp.json (project config takes precedence)"
|
|
470
|
-
((WARNINGS++))
|
|
471
|
-
fi
|
|
472
|
-
done
|
|
473
|
-
fi
|
|
474
|
-
fi
|
|
475
|
-
|
|
476
|
-
# 10. Verify extension is installed
|
|
477
|
-
log_info "Verifying extension installation..."
|
|
478
|
-
EXTENSION_TARGET="${AGENT_HOME_EXTENSIONS}/gentle-agent-extensions.ts"
|
|
479
|
-
if [[ -f "$EXTENSION_TARGET" ]]; then
|
|
480
|
-
log_success "Extension found at $EXTENSION_TARGET"
|
|
481
|
-
else
|
|
482
|
-
log_error "Extension not installed at $EXTENSION_TARGET"
|
|
483
|
-
log_error "Run install.sh first"
|
|
484
|
-
((ISSUES++))
|
|
485
|
-
fi
|
|
486
|
-
|
|
487
|
-
# Summary
|
|
488
|
-
echo ""
|
|
489
|
-
log_info "=== Sync Summary ==="
|
|
490
|
-
log_info "Changes made: $CHANGES"
|
|
491
|
-
log_info "Issues found: $ISSUES"
|
|
492
|
-
log_info "Warnings: $WARNINGS"
|
|
493
|
-
|
|
494
|
-
if [[ "$DRY_RUN" == "true" ]]; then
|
|
495
|
-
log_warn "DRY RUN - no actual changes made"
|
|
496
|
-
fi
|
|
497
|
-
|
|
498
|
-
if [[ "$CHECK_MODE" == "true" ]]; then
|
|
499
|
-
if [[ $ISSUES -gt 0 ]]; then
|
|
500
|
-
log_error "Check failed: $ISSUES issue(s) found"
|
|
501
|
-
exit 1
|
|
502
|
-
else
|
|
503
|
-
log_success "Check passed"
|
|
504
|
-
exit 0
|
|
505
|
-
fi
|
|
506
|
-
fi
|
|
507
|
-
|
|
508
|
-
if [[ $ISSUES -gt 0 ]]; then
|
|
509
|
-
log_error "Sync completed with $ISSUES issue(s)"
|
|
510
|
-
exit 1
|
|
511
|
-
fi
|
|
512
|
-
|
|
513
|
-
log_success "Sync completed successfully"
|