gentle-agent-extensions 1.0.0 → 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/doctor.sh
DELETED
|
@@ -1,509 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# doctor.sh - Check gentle-agent-extensions installation and compatibility
|
|
3
|
-
#
|
|
4
|
-
# This script validates the installation, checks for conflicts, and verifies
|
|
5
|
-
# compatibility with the current Gentle AI / Gentle Pi versions.
|
|
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
|
-
CYAN='\033[0;36m'
|
|
18
|
-
NC='\033[0m'
|
|
19
|
-
|
|
20
|
-
log_info() { echo -e "${BLUE}[INFO]${NC} $*"; }
|
|
21
|
-
log_success() { echo -e "${GREEN}[✓]${NC} $*"; }
|
|
22
|
-
log_warn() { echo -e "${YELLOW}[⚠]${NC} $*"; }
|
|
23
|
-
log_error() { echo -e "${RED}[✗]${NC} $*"; }
|
|
24
|
-
log_step() { echo -e "${CYAN}▶${NC} $*"; }
|
|
25
|
-
|
|
26
|
-
usage() {
|
|
27
|
-
cat <<EOF
|
|
28
|
-
Usage: $0 [OPTIONS]
|
|
29
|
-
|
|
30
|
-
Check gentle-agent-extensions installation, configuration, and compatibility.
|
|
31
|
-
|
|
32
|
-
Options:
|
|
33
|
-
--profile PROFILE Installation profile: pi, gentle-shell, gentle-shell-linked (default: pi)
|
|
34
|
-
--agent-home PATH Explicit agent home directory (overrides profile)
|
|
35
|
-
--verbose Show detailed output
|
|
36
|
-
-h, --help Show this help
|
|
37
|
-
|
|
38
|
-
Examples:
|
|
39
|
-
$0 # Standard check
|
|
40
|
-
$0 --verbose # Detailed output
|
|
41
|
-
$0 --profile gentle-shell
|
|
42
|
-
EOF
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
PROFILE="pi"
|
|
46
|
-
AGENT_HOME=""
|
|
47
|
-
VERBOSE=false
|
|
48
|
-
|
|
49
|
-
while [[ $# -gt 0 ]]; do
|
|
50
|
-
case $1 in
|
|
51
|
-
--profile)
|
|
52
|
-
PROFILE="$2"
|
|
53
|
-
shift 2
|
|
54
|
-
;;
|
|
55
|
-
--agent-home)
|
|
56
|
-
AGENT_HOME="$2"
|
|
57
|
-
shift 2
|
|
58
|
-
;;
|
|
59
|
-
--verbose)
|
|
60
|
-
VERBOSE=true
|
|
61
|
-
shift
|
|
62
|
-
;;
|
|
63
|
-
-h|--help)
|
|
64
|
-
usage
|
|
65
|
-
exit 0
|
|
66
|
-
;;
|
|
67
|
-
*)
|
|
68
|
-
log_error "Unknown option: $1"
|
|
69
|
-
usage
|
|
70
|
-
exit 1
|
|
71
|
-
;;
|
|
72
|
-
esac
|
|
73
|
-
done
|
|
74
|
-
|
|
75
|
-
if [[ -z "$AGENT_HOME" ]]; then
|
|
76
|
-
case "$PROFILE" in
|
|
77
|
-
pi) AGENT_HOME="${HOME}/.pi/agent" ;;
|
|
78
|
-
gentle-shell) AGENT_HOME="${HOME}/.local/share/gentle-shell/agent" ;;
|
|
79
|
-
gentle-shell-linked) AGENT_HOME="${HOME}/.pi/agent" ;;
|
|
80
|
-
*) log_error "Unknown profile: $PROFILE"; exit 1 ;;
|
|
81
|
-
esac
|
|
82
|
-
fi
|
|
83
|
-
|
|
84
|
-
ISSUES=0
|
|
85
|
-
WARNINGS=0
|
|
86
|
-
PASSED=0
|
|
87
|
-
|
|
88
|
-
check_pass() { PASSED=$((PASSED+1)); [[ "$VERBOSE" == "true" ]] && log_success "$1" || true; }
|
|
89
|
-
check_warn() { WARNINGS=$((WARNINGS+1)); log_warn "$1"; }
|
|
90
|
-
check_fail() { ISSUES=$((ISSUES+1)); log_error "$1"; }
|
|
91
|
-
|
|
92
|
-
echo "╔══════════════════════════════════════════════════════════════╗"
|
|
93
|
-
echo "║ gentle-agent-extensions Doctor ║"
|
|
94
|
-
echo "╚══════════════════════════════════════════════════════════════╝"
|
|
95
|
-
echo ""
|
|
96
|
-
log_info "Profile: $PROFILE"
|
|
97
|
-
log_info "Agent home: $AGENT_HOME"
|
|
98
|
-
log_info "Project root: $PROJECT_ROOT"
|
|
99
|
-
echo ""
|
|
100
|
-
|
|
101
|
-
# 1. Project structure
|
|
102
|
-
log_step "Checking project structure..."
|
|
103
|
-
|
|
104
|
-
for dir in "agents" "overlays" "routing" "skills" "mcp" "scripts" "extensions" "tests" "docs"; do
|
|
105
|
-
if [[ -d "${PROJECT_ROOT}/${dir}" ]]; then
|
|
106
|
-
check_pass "Directory exists: $dir/"
|
|
107
|
-
else
|
|
108
|
-
check_warn "Directory missing: $dir/"
|
|
109
|
-
fi
|
|
110
|
-
done
|
|
111
|
-
|
|
112
|
-
# 2. Manifest
|
|
113
|
-
log_step "Checking manifest..."
|
|
114
|
-
MANIFEST="${PROJECT_ROOT}/.manifest.json"
|
|
115
|
-
if [[ -f "$MANIFEST" ]]; then
|
|
116
|
-
check_pass "Manifest exists"
|
|
117
|
-
if [[ "$VERBOSE" == "true" ]]; then
|
|
118
|
-
cat "$MANIFEST" | jq . 2>/dev/null || cat "$MANIFEST"
|
|
119
|
-
fi
|
|
120
|
-
else
|
|
121
|
-
check_fail "Manifest missing: .manifest.json"
|
|
122
|
-
fi
|
|
123
|
-
|
|
124
|
-
# 3. Extension installation
|
|
125
|
-
log_step "Checking extension installation..."
|
|
126
|
-
EXTENSION_TARGET="${AGENT_HOME}/extensions/gentle-agent-extensions.ts"
|
|
127
|
-
if [[ -f "$EXTENSION_TARGET" ]]; then
|
|
128
|
-
check_pass "Extension installed at $EXTENSION_TARGET"
|
|
129
|
-
if [[ "$VERBOSE" == "true" ]]; then
|
|
130
|
-
head -30 "$EXTENSION_TARGET"
|
|
131
|
-
fi
|
|
132
|
-
else
|
|
133
|
-
check_fail "Extension not found at $EXTENSION_TARGET"
|
|
134
|
-
fi
|
|
135
|
-
|
|
136
|
-
# 4. Pi / Gentle Pi installation
|
|
137
|
-
log_step "Checking Pi / Gentle Pi installation..."
|
|
138
|
-
|
|
139
|
-
# Check for Pi
|
|
140
|
-
if command -v pi &> /dev/null; then
|
|
141
|
-
PI_VERSION=$(pi --version 2>/dev/null || echo "unknown")
|
|
142
|
-
check_pass "Pi found: $PI_VERSION"
|
|
143
|
-
else
|
|
144
|
-
check_warn "Pi command not found in PATH"
|
|
145
|
-
fi
|
|
146
|
-
|
|
147
|
-
# Check for gentle-shell
|
|
148
|
-
if command -v gentle-shell &> /dev/null; then
|
|
149
|
-
SHELL_VERSION=$(gentle-shell --version 2>/dev/null || echo "unknown")
|
|
150
|
-
check_pass "Gentle Shell found: $SHELL_VERSION"
|
|
151
|
-
else
|
|
152
|
-
log_info "Gentle Shell not found (optional)"
|
|
153
|
-
fi
|
|
154
|
-
|
|
155
|
-
# Check gentle-pi package
|
|
156
|
-
GENTLE_PI_PATH="${HOME}/.pi/agent/npm/node_modules/gentle-pi"
|
|
157
|
-
if [[ -d "$GENTLE_PI_PATH" ]]; then
|
|
158
|
-
GENTLE_PI_VERSION=$(cat "$GENTLE_PI_PATH/package.json" | grep '"version"' | head -1 | sed 's/.*"version": *"\([^"]*\)".*/\1/')
|
|
159
|
-
check_pass "gentle-pi package found: v$GENTLE_PI_VERSION"
|
|
160
|
-
else
|
|
161
|
-
check_warn "gentle-pi package not found at $GENTLE_PI_PATH"
|
|
162
|
-
fi
|
|
163
|
-
|
|
164
|
-
# Check gentle-ai package (usually bundled in gentle-pi)
|
|
165
|
-
GENTLE_AI_PATH="${HOME}/.pi/agent/npm/node_modules/gentle-ai"
|
|
166
|
-
if [[ -d "$GENTLE_AI_PATH" ]]; then
|
|
167
|
-
GENTLE_AI_VERSION=$(cat "$GENTLE_AI_PATH/package.json" 2>/dev/null | grep '"version"' | head -1 | sed 's/.*"version": *"\([^"]*\)".*/\1/' || echo "bundled")
|
|
168
|
-
check_pass "gentle-ai package found: v$GENTLE_AI_VERSION"
|
|
169
|
-
else
|
|
170
|
-
log_info "gentle-ai package not found separately (may be bundled)"
|
|
171
|
-
fi
|
|
172
|
-
|
|
173
|
-
# 5. Agent home structure
|
|
174
|
-
log_step "Checking agent home structure..."
|
|
175
|
-
for dir in "agents" "subagents"; do
|
|
176
|
-
dir_path="${AGENT_HOME}/${dir}"
|
|
177
|
-
if [[ -d "$dir_path" ]]; then
|
|
178
|
-
count=$(find "$dir_path" -name "*.md" 2>/dev/null | wc -l)
|
|
179
|
-
check_pass "Global $dir: $dir_path ($count agents)"
|
|
180
|
-
else
|
|
181
|
-
log_info "Global $dir not found (will be created on sync): $dir_path"
|
|
182
|
-
fi
|
|
183
|
-
done
|
|
184
|
-
|
|
185
|
-
# 6. Project agent directory
|
|
186
|
-
log_step "Checking project agent directory..."
|
|
187
|
-
PROJECT_AGENTS_DIR="${PROJECT_ROOT}/.pi/agents"
|
|
188
|
-
if [[ -d "$PROJECT_AGENTS_DIR" ]]; then
|
|
189
|
-
count=$(find "$PROJECT_AGENTS_DIR" -name "*.md" 2>/dev/null | wc -l)
|
|
190
|
-
check_pass "Project agents directory exists ($count agents)"
|
|
191
|
-
else
|
|
192
|
-
log_info "Project agents directory not found (will be created on sync)"
|
|
193
|
-
fi
|
|
194
|
-
|
|
195
|
-
# 7. Validate agents
|
|
196
|
-
log_step "Validating custom agents..."
|
|
197
|
-
AGENTS_SRC="${PROJECT_ROOT}/agents"
|
|
198
|
-
if [[ -d "$AGENTS_SRC" ]]; then
|
|
199
|
-
for agent_file in "$AGENTS_SRC"/*.md; do
|
|
200
|
-
[[ -f "$agent_file" ]] || continue
|
|
201
|
-
agent_name=$(basename "$agent_file" .md)
|
|
202
|
-
|
|
203
|
-
if head -1 "$agent_file" | grep -q "^---$"; then
|
|
204
|
-
check_pass "Agent $agent_name: has frontmatter"
|
|
205
|
-
else
|
|
206
|
-
check_fail "Agent $agent_name: missing frontmatter delimiter"
|
|
207
|
-
continue
|
|
208
|
-
fi
|
|
209
|
-
|
|
210
|
-
if grep -q "^name:" "$agent_file"; then
|
|
211
|
-
check_pass "Agent $agent_name: has name field"
|
|
212
|
-
else
|
|
213
|
-
check_fail "Agent $agent_name: missing 'name' in frontmatter"
|
|
214
|
-
fi
|
|
215
|
-
|
|
216
|
-
if grep -q "^description:" "$agent_file"; then
|
|
217
|
-
check_pass "Agent $agent_name: has description"
|
|
218
|
-
else
|
|
219
|
-
check_warn "Agent $agent_name: missing description"
|
|
220
|
-
fi
|
|
221
|
-
|
|
222
|
-
body_start=$(grep -n "^---$" "$agent_file" | tail -1 | cut -d: -f1)
|
|
223
|
-
if [[ -n "$body_start" ]]; then
|
|
224
|
-
body_lines=$(( $(wc -l < "$agent_file") - body_start ))
|
|
225
|
-
if [[ $body_lines -gt 1 ]]; then
|
|
226
|
-
check_pass "Agent $agent_name: has instructions ($body_lines lines)"
|
|
227
|
-
else
|
|
228
|
-
check_warn "Agent $agent_name: empty or minimal instructions"
|
|
229
|
-
fi
|
|
230
|
-
fi
|
|
231
|
-
done
|
|
232
|
-
else
|
|
233
|
-
log_info "No agents directory found"
|
|
234
|
-
fi
|
|
235
|
-
|
|
236
|
-
# 8. Validate overlays
|
|
237
|
-
log_step "Validating overlays..."
|
|
238
|
-
OVERLAYS_SRC="${PROJECT_ROOT}/overlays"
|
|
239
|
-
if [[ -d "$OVERLAYS_SRC" ]]; then
|
|
240
|
-
for overlay_file in "$OVERLAYS_SRC"/*.overlay.md; do
|
|
241
|
-
[[ -f "$overlay_file" ]] || continue
|
|
242
|
-
overlay_name=$(basename "$overlay_file" .overlay.md)
|
|
243
|
-
|
|
244
|
-
if head -1 "$overlay_file" | grep -q "^---$"; then
|
|
245
|
-
check_pass "Overlay $overlay_name: has frontmatter"
|
|
246
|
-
else
|
|
247
|
-
check_fail "Overlay $overlay_name: missing frontmatter"
|
|
248
|
-
continue
|
|
249
|
-
fi
|
|
250
|
-
|
|
251
|
-
if grep -q "^target:" "$overlay_file"; then
|
|
252
|
-
target=$(grep "^target:" "$overlay_file" | head -1 | sed 's/^target: *//' | xargs)
|
|
253
|
-
check_pass "Overlay $overlay_name: targets '$target'"
|
|
254
|
-
else
|
|
255
|
-
check_fail "Overlay $overlay_name: missing target"
|
|
256
|
-
fi
|
|
257
|
-
|
|
258
|
-
if grep -q "^mode:" "$overlay_file"; then
|
|
259
|
-
mode=$(grep "^mode:" "$overlay_file" | head -1 | sed 's/^mode: *//' | xargs)
|
|
260
|
-
if [[ "$mode" == "append" ]]; then
|
|
261
|
-
check_pass "Overlay $overlay_name: mode=$mode"
|
|
262
|
-
else
|
|
263
|
-
check_warn "Overlay $overlay_name: unknown mode '$mode' (only 'append' supported)"
|
|
264
|
-
fi
|
|
265
|
-
else
|
|
266
|
-
check_pass "Overlay $overlay_name: default mode (append)"
|
|
267
|
-
fi
|
|
268
|
-
done
|
|
269
|
-
else
|
|
270
|
-
log_info "No overlays directory found"
|
|
271
|
-
fi
|
|
272
|
-
|
|
273
|
-
# 9. Validate routing
|
|
274
|
-
log_step "Validating routing..."
|
|
275
|
-
ROUTING_SRC="${PROJECT_ROOT}/routing"
|
|
276
|
-
if [[ -d "$ROUTING_SRC" ]]; then
|
|
277
|
-
for routing_file in "$ROUTING_SRC"/*.md; do
|
|
278
|
-
[[ -f "$routing_file" ]] || continue
|
|
279
|
-
routing_name=$(basename "$routing_file" .md)
|
|
280
|
-
|
|
281
|
-
if grep -q "^agent:" "$routing_file"; then
|
|
282
|
-
agent=$(grep "^agent:" "$routing_file" | head -1 | sed 's/^agent: *//' | xargs)
|
|
283
|
-
check_pass "Routing $routing_name: delegates to '$agent'"
|
|
284
|
-
else
|
|
285
|
-
check_fail "Routing $routing_name: missing agent"
|
|
286
|
-
fi
|
|
287
|
-
|
|
288
|
-
if grep -q "triggers:" "$routing_file"; then
|
|
289
|
-
check_pass "Routing $routing_name: has triggers"
|
|
290
|
-
else
|
|
291
|
-
check_warn "Routing $routing_name: no triggers defined"
|
|
292
|
-
fi
|
|
293
|
-
done
|
|
294
|
-
else
|
|
295
|
-
log_info "No routing directory found"
|
|
296
|
-
fi
|
|
297
|
-
|
|
298
|
-
# 10. Validate skills
|
|
299
|
-
log_step "Validating skills..."
|
|
300
|
-
SKILLS_SRC="${PROJECT_ROOT}/skills"
|
|
301
|
-
if [[ -d "$SKILLS_SRC" ]]; then
|
|
302
|
-
for skill_dir in "$SKILLS_SRC"/*/; do
|
|
303
|
-
[[ -d "$skill_dir" ]] || continue
|
|
304
|
-
skill_name=$(basename "$skill_dir")
|
|
305
|
-
skill_file="${skill_dir}SKILL.md"
|
|
306
|
-
|
|
307
|
-
if [[ -f "$skill_file" ]]; then
|
|
308
|
-
check_pass "Skill $skill_name: SKILL.md exists"
|
|
309
|
-
|
|
310
|
-
missing_sections=()
|
|
311
|
-
for section in "Activation Contract" "Hard Rules" "Decision Gates" "Execution Steps" "Output Contract" "References"; do
|
|
312
|
-
if ! grep -q "^## $section" "$skill_file"; then
|
|
313
|
-
missing_sections+=("$section")
|
|
314
|
-
fi
|
|
315
|
-
done
|
|
316
|
-
|
|
317
|
-
if [[ ${#missing_sections[@]} -eq 0 ]]; then
|
|
318
|
-
check_pass "Skill $skill_name: all required sections present"
|
|
319
|
-
else
|
|
320
|
-
check_warn "Skill $skill_name: missing sections: ${missing_sections[*]}"
|
|
321
|
-
fi
|
|
322
|
-
|
|
323
|
-
if head -10 "$skill_file" | grep -q "^---$"; then
|
|
324
|
-
if grep -q "^name:" "$skill_file"; then
|
|
325
|
-
fm_name=$(grep "^name:" "$skill_file" | head -1 | sed 's/^name: *//' | xargs)
|
|
326
|
-
if [[ "$fm_name" == "$skill_name" ]]; then
|
|
327
|
-
check_pass "Skill $skill_name: frontmatter name matches directory"
|
|
328
|
-
else
|
|
329
|
-
check_warn "Skill $skill_name: frontmatter name '$fm_name' differs from directory"
|
|
330
|
-
fi
|
|
331
|
-
else
|
|
332
|
-
check_warn "Skill $skill_name: missing name in frontmatter"
|
|
333
|
-
fi
|
|
334
|
-
else
|
|
335
|
-
check_fail "Skill $skill_name: missing frontmatter"
|
|
336
|
-
fi
|
|
337
|
-
else
|
|
338
|
-
check_fail "Skill $skill_name: missing SKILL.md"
|
|
339
|
-
fi
|
|
340
|
-
done
|
|
341
|
-
else
|
|
342
|
-
log_info "No skills directory found"
|
|
343
|
-
fi
|
|
344
|
-
|
|
345
|
-
# 11. Validate MCP server definitions
|
|
346
|
-
log_step "Validating MCP servers..."
|
|
347
|
-
MCP_SRC="${PROJECT_ROOT}/mcp"
|
|
348
|
-
if [[ -d "$MCP_SRC" ]]; then
|
|
349
|
-
for mcp_file in "$MCP_SRC"/*.mcp.json; do
|
|
350
|
-
[[ -f "$mcp_file" ]] || continue
|
|
351
|
-
mcp_name=$(basename "$mcp_file" .mcp.json)
|
|
352
|
-
|
|
353
|
-
if ! command -v jq &> /dev/null; then
|
|
354
|
-
check_warn "MCP $mcp_name: jq not available, skipping JSON validation"
|
|
355
|
-
else
|
|
356
|
-
if ! jq empty "$mcp_file" 2>/dev/null; then
|
|
357
|
-
check_fail "MCP $mcp_name: invalid JSON syntax"
|
|
358
|
-
continue
|
|
359
|
-
fi
|
|
360
|
-
|
|
361
|
-
name=$(jq -r '.name // empty' "$mcp_file")
|
|
362
|
-
description=$(jq -r '.description // empty' "$mcp_file")
|
|
363
|
-
transport=$(jq -r '.transport // empty' "$mcp_file")
|
|
364
|
-
|
|
365
|
-
if [[ -z "$name" ]]; then
|
|
366
|
-
check_fail "MCP $mcp_name: missing 'name' field"
|
|
367
|
-
else
|
|
368
|
-
check_pass "MCP $mcp_name: has name field"
|
|
369
|
-
fi
|
|
370
|
-
if [[ -z "$description" ]]; then
|
|
371
|
-
check_fail "MCP $mcp_name: missing 'description' field"
|
|
372
|
-
else
|
|
373
|
-
check_pass "MCP $mcp_name: has description"
|
|
374
|
-
fi
|
|
375
|
-
if [[ -z "$transport" ]]; then
|
|
376
|
-
check_fail "MCP $mcp_name: missing 'transport' field"
|
|
377
|
-
else
|
|
378
|
-
check_pass "MCP $mcp_name: transport=$transport"
|
|
379
|
-
fi
|
|
380
|
-
|
|
381
|
-
if [[ "$transport" == "stdio" ]]; then
|
|
382
|
-
command=$(jq -r '.command // empty' "$mcp_file")
|
|
383
|
-
if [[ -z "$command" ]]; then
|
|
384
|
-
check_fail "MCP $mcp_name: stdio transport requires 'command' field"
|
|
385
|
-
else
|
|
386
|
-
check_pass "MCP $mcp_name: has command"
|
|
387
|
-
fi
|
|
388
|
-
elif [[ "$transport" == "http" ]]; then
|
|
389
|
-
url=$(jq -r '.url // empty' "$mcp_file")
|
|
390
|
-
if [[ -z "$url" ]]; then
|
|
391
|
-
check_fail "MCP $mcp_name: http transport requires 'url' field"
|
|
392
|
-
else
|
|
393
|
-
check_pass "MCP $mcp_name: has url"
|
|
394
|
-
fi
|
|
395
|
-
elif [[ -n "$transport" ]]; then
|
|
396
|
-
check_fail "MCP $mcp_name: transport must be 'stdio' or 'http'"
|
|
397
|
-
fi
|
|
398
|
-
|
|
399
|
-
if [[ -n "$name" ]] && ! [[ "$name" =~ ^[A-Za-z0-9_-]+$ ]]; then
|
|
400
|
-
check_fail "MCP $mcp_name: name must contain only letters, digits, _, and -"
|
|
401
|
-
else
|
|
402
|
-
check_pass "MCP $mcp_name: name format valid"
|
|
403
|
-
fi
|
|
404
|
-
|
|
405
|
-
if command -v jq &> /dev/null; then
|
|
406
|
-
env_keys=$(jq -r '.env | keys[]' "$mcp_file" 2>/dev/null || true)
|
|
407
|
-
for key in $env_keys; do
|
|
408
|
-
value=$(jq -r ".env[\"$key\"]" "$mcp_file")
|
|
409
|
-
if [[ -n "$value" ]] && ! [[ "$value" =~ ^\$\{ ]]; then
|
|
410
|
-
key_lower=$(echo "$key" | tr '[:upper:]' '[:lower:]')
|
|
411
|
-
if [[ "$key_lower" =~ (secret|token|key|password) ]]; then
|
|
412
|
-
check_warn "MCP $mcp_name: env '$key' may contain hardcoded secret (use \${VAR} syntax)"
|
|
413
|
-
fi
|
|
414
|
-
fi
|
|
415
|
-
done
|
|
416
|
-
fi
|
|
417
|
-
fi
|
|
418
|
-
done
|
|
419
|
-
else
|
|
420
|
-
log_info "No MCP directory found"
|
|
421
|
-
fi
|
|
422
|
-
|
|
423
|
-
# 12. Check for conflicts
|
|
424
|
-
log_step "Checking for conflicts..."
|
|
425
|
-
|
|
426
|
-
# Global agents
|
|
427
|
-
declare -A GLOBAL_AGENTS
|
|
428
|
-
for dir in "${AGENT_HOME}/agents" "${AGENT_HOME}/subagents"; do
|
|
429
|
-
if [[ -d "$dir" ]]; then
|
|
430
|
-
for file in "$dir"/*.md; do
|
|
431
|
-
[[ -f "$file" ]] || continue
|
|
432
|
-
name=$(basename "$file" .md)
|
|
433
|
-
GLOBAL_AGENTS["$name"]="$dir"
|
|
434
|
-
done
|
|
435
|
-
fi
|
|
436
|
-
done
|
|
437
|
-
|
|
438
|
-
# Check project agents vs global
|
|
439
|
-
if [[ -d "$AGENTS_SRC" ]]; then
|
|
440
|
-
for agent_file in "$AGENTS_SRC"/*.md; do
|
|
441
|
-
[[ -f "$agent_file" ]] || continue
|
|
442
|
-
agent_name=$(basename "$agent_file" .md)
|
|
443
|
-
if [[ -n "${GLOBAL_AGENTS[$agent_name]:-}" ]]; then
|
|
444
|
-
check_warn "Project agent '$agent_name' shadows global agent in ${GLOBAL_AGENTS[$agent_name]}"
|
|
445
|
-
fi
|
|
446
|
-
done
|
|
447
|
-
fi
|
|
448
|
-
|
|
449
|
-
# Check overlay targets
|
|
450
|
-
if [[ -d "$OVERLAYS_SRC" ]]; then
|
|
451
|
-
for overlay_file in "$OVERLAYS_SRC"/*.overlay.md; do
|
|
452
|
-
[[ -f "$overlay_file" ]] || continue
|
|
453
|
-
target=$(grep "^target:" "$overlay_file" | head -1 | sed 's/^target: *//' | xargs)
|
|
454
|
-
if [[ "$target" != "orchestrator" ]]; then
|
|
455
|
-
target_exists=false
|
|
456
|
-
if [[ -n "${GLOBAL_AGENTS[$target]:-}" ]]; then target_exists=true; fi
|
|
457
|
-
if [[ -f "$AGENTS_SRC/${target}.md" ]]; then target_exists=true; fi
|
|
458
|
-
if [[ "$target_exists" == "false" ]]; then
|
|
459
|
-
check_warn "Overlay '$(basename "$overlay_file")' targets unknown agent: $target"
|
|
460
|
-
fi
|
|
461
|
-
fi
|
|
462
|
-
done
|
|
463
|
-
fi
|
|
464
|
-
|
|
465
|
-
# 13. Check skill registry
|
|
466
|
-
log_step "Checking skill registry..."
|
|
467
|
-
REGISTRY="${PROJECT_ROOT}/.atl/skill-registry.md"
|
|
468
|
-
if [[ -f "$REGISTRY" ]]; then
|
|
469
|
-
check_pass "Skill registry exists at .atl/skill-registry.md"
|
|
470
|
-
if [[ "$VERBOSE" == "true" ]]; then
|
|
471
|
-
grep -A 5 "^## Skills" "$REGISTRY" | head -20
|
|
472
|
-
fi
|
|
473
|
-
else
|
|
474
|
-
log_info "Skill registry not found (will be generated by gentle-pi)"
|
|
475
|
-
fi
|
|
476
|
-
|
|
477
|
-
# 14. Check scripts are executable
|
|
478
|
-
log_step "Checking script permissions..."
|
|
479
|
-
for script in install.sh sync.sh doctor.sh; do
|
|
480
|
-
script_path="${PROJECT_ROOT}/scripts/${script}"
|
|
481
|
-
if [[ -f "$script_path" ]]; then
|
|
482
|
-
if [[ -x "$script_path" ]]; then
|
|
483
|
-
check_pass "Script executable: $script"
|
|
484
|
-
else
|
|
485
|
-
check_warn "Script not executable: $script (run: chmod +x scripts/$script)"
|
|
486
|
-
fi
|
|
487
|
-
fi
|
|
488
|
-
done
|
|
489
|
-
|
|
490
|
-
# Summary
|
|
491
|
-
echo ""
|
|
492
|
-
echo "╔══════════════════════════════════════════════════════════════╗"
|
|
493
|
-
echo "║ Summary ║"
|
|
494
|
-
echo "╚══════════════════════════════════════════════════════════════╝"
|
|
495
|
-
log_success "Passed: $PASSED"
|
|
496
|
-
log_warn "Warnings: $WARNINGS"
|
|
497
|
-
log_error "Issues: $ISSUES"
|
|
498
|
-
echo ""
|
|
499
|
-
|
|
500
|
-
if [[ $ISSUES -gt 0 ]]; then
|
|
501
|
-
log_error "Doctor check FAILED - $ISSUES issue(s) require attention"
|
|
502
|
-
exit 1
|
|
503
|
-
elif [[ $WARNINGS -gt 0 ]]; then
|
|
504
|
-
log_warn "Doctor check PASSED with $WARNINGS warning(s)"
|
|
505
|
-
exit 0
|
|
506
|
-
else
|
|
507
|
-
log_success "Doctor check PASSED - all checks OK"
|
|
508
|
-
exit 0
|
|
509
|
-
fi
|