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/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