@prmichaelsen/remember-mcp 2.7.11 → 2.8.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.
Files changed (76) hide show
  1. package/.env.example +6 -0
  2. package/AGENT.md +224 -21
  3. package/CHANGELOG.md +47 -937
  4. package/README.md +35 -0
  5. package/agent/commands/acp.command-create.md +373 -0
  6. package/agent/commands/acp.design-create.md +225 -0
  7. package/agent/commands/acp.init.md +40 -5
  8. package/agent/commands/acp.package-create.md +895 -0
  9. package/agent/commands/acp.package-info.md +212 -0
  10. package/agent/commands/acp.package-install.md +207 -33
  11. package/agent/commands/acp.package-list.md +280 -0
  12. package/agent/commands/acp.package-publish.md +541 -0
  13. package/agent/commands/acp.package-remove.md +293 -0
  14. package/agent/commands/acp.package-search.md +307 -0
  15. package/agent/commands/acp.package-update.md +361 -0
  16. package/agent/commands/acp.package-validate.md +540 -0
  17. package/agent/commands/acp.pattern-create.md +327 -0
  18. package/agent/commands/acp.plan.md +553 -0
  19. package/agent/commands/acp.proceed.md +112 -86
  20. package/agent/commands/acp.project-create.md +673 -0
  21. package/agent/commands/acp.project-list.md +225 -0
  22. package/agent/commands/acp.project-set.md +227 -0
  23. package/agent/commands/acp.report.md +3 -0
  24. package/agent/commands/acp.resume.md +238 -0
  25. package/agent/commands/acp.status.md +1 -0
  26. package/agent/commands/acp.sync.md +56 -15
  27. package/agent/commands/acp.task-create.md +391 -0
  28. package/agent/commands/acp.update.md +1 -0
  29. package/agent/commands/acp.validate.md +62 -10
  30. package/agent/commands/acp.version-check-for-updates.md +6 -5
  31. package/agent/commands/acp.version-check.md +7 -6
  32. package/agent/commands/acp.version-update.md +7 -6
  33. package/agent/commands/command.template.md +48 -0
  34. package/agent/commands/git.commit.md +6 -3
  35. package/agent/commands/git.init.md +1 -0
  36. package/agent/manifest.template.yaml +13 -0
  37. package/agent/package.template.yaml +53 -0
  38. package/agent/progress.template.yaml +3 -0
  39. package/agent/progress.yaml +103 -5
  40. package/agent/scripts/acp.common.sh +1536 -0
  41. package/agent/scripts/acp.install.sh +293 -0
  42. package/agent/scripts/acp.package-create.sh +925 -0
  43. package/agent/scripts/acp.package-info.sh +270 -0
  44. package/agent/scripts/acp.package-install.sh +675 -0
  45. package/agent/scripts/acp.package-list.sh +263 -0
  46. package/agent/scripts/acp.package-publish.sh +420 -0
  47. package/agent/scripts/acp.package-remove.sh +272 -0
  48. package/agent/scripts/acp.package-search.sh +156 -0
  49. package/agent/scripts/acp.package-update.sh +438 -0
  50. package/agent/scripts/acp.package-validate.sh +954 -0
  51. package/agent/scripts/acp.project-list.sh +121 -0
  52. package/agent/scripts/acp.project-set.sh +138 -0
  53. package/agent/scripts/{uninstall.sh → acp.uninstall.sh} +25 -15
  54. package/agent/scripts/{check-for-updates.sh → acp.version-check-for-updates.sh} +24 -14
  55. package/agent/scripts/{version.sh → acp.version-check.sh} +20 -8
  56. package/agent/scripts/{update.sh → acp.version-update.sh} +44 -25
  57. package/agent/scripts/acp.yaml-parser.sh +853 -0
  58. package/agent/scripts/acp.yaml-validate.sh +205 -0
  59. package/agent/tasks/task-68-fix-missing-space-properties.md +192 -0
  60. package/agent/tasks/task-69-add-comprehensive-tool-debugging.md +454 -0
  61. package/dist/config.d.ts +18 -0
  62. package/dist/server-factory.js +276 -19
  63. package/dist/server.js +276 -19
  64. package/dist/utils/debug.d.ts +52 -0
  65. package/dist/utils/debug.spec.d.ts +5 -0
  66. package/dist/weaviate/client.d.ts +1 -1
  67. package/package.json +1 -1
  68. package/src/config.ts +33 -0
  69. package/src/tools/confirm.ts +48 -7
  70. package/src/tools/publish.ts +19 -1
  71. package/src/tools/query-space.ts +36 -3
  72. package/src/tools/search-space.ts +36 -3
  73. package/src/utils/debug.spec.ts +257 -0
  74. package/src/utils/debug.ts +138 -0
  75. package/src/weaviate/client.ts +42 -3
  76. package/agent/scripts/install.sh +0 -157
@@ -0,0 +1,954 @@
1
+ #!/bin/bash
2
+ # ACP Package Validation Script
3
+ # Comprehensive package validation with shell-based checks
4
+ # Version: 1.0.0
5
+
6
+ set -e
7
+
8
+ # Source common utilities
9
+ SCRIPT_DIR="$(dirname "$0")"
10
+ . "${SCRIPT_DIR}/acp.common.sh"
11
+ . "${SCRIPT_DIR}/acp.yaml-parser.sh"
12
+
13
+ # Initialize colors
14
+ init_colors
15
+
16
+ # Validation tracking
17
+ TOTAL_CHECKS=0
18
+ PASSED_CHECKS=0
19
+ ERRORS=()
20
+ WARNINGS=()
21
+ FIXABLE_ISSUES=()
22
+
23
+ # Add check result
24
+ check() {
25
+ TOTAL_CHECKS=$((TOTAL_CHECKS + 1))
26
+ }
27
+
28
+ # Add passed check
29
+ pass() {
30
+ PASSED_CHECKS=$((PASSED_CHECKS + 1))
31
+ echo " ${GREEN}✓${NC} $1"
32
+ }
33
+
34
+ # Add error
35
+ error() {
36
+ echo " ${RED}❌${NC} $1" >&2
37
+ ERRORS+=("$1")
38
+ }
39
+
40
+ # Add warning
41
+ warning() {
42
+ echo " ${YELLOW}⚠️${NC} $1"
43
+ WARNINGS+=("$1")
44
+ }
45
+
46
+ # Add fixable issue
47
+ fixable() {
48
+ FIXABLE_ISSUES+=("$1")
49
+ }
50
+
51
+ # Check if we're in a package directory
52
+ check_package_context() {
53
+ echo "${BLUE}📦 Checking Package Context${NC}"
54
+ echo ""
55
+
56
+ check
57
+ if [ ! -f "package.yaml" ]; then
58
+ error "Not a package directory: package.yaml not found"
59
+ echo ""
60
+ echo "${RED}This command must be run from an ACP package directory.${NC}"
61
+ echo "To create a package, use: @acp.package-create"
62
+ exit 1
63
+ fi
64
+ pass "package.yaml found"
65
+
66
+ # Extract package name and version
67
+ PACKAGE_NAME=$(yaml_get "package.yaml" "name" 2>/dev/null || echo "unknown")
68
+ PACKAGE_VERSION=$(yaml_get "package.yaml" "version" 2>/dev/null || echo "unknown")
69
+
70
+ echo ""
71
+ echo "${BOLD}Package: ${PACKAGE_NAME} (v${PACKAGE_VERSION})${NC}"
72
+ echo ""
73
+ }
74
+
75
+ # Validate YAML structure
76
+ validate_yaml_structure() {
77
+ echo "${BLUE}🔧 Shell Validation${NC}"
78
+ echo ""
79
+ echo "${BOLD}YAML Structure${NC}"
80
+
81
+ # Run YAML schema validator
82
+ check
83
+ if "${SCRIPT_DIR}/acp.yaml-validate.sh" "package.yaml" >/dev/null 2>&1; then
84
+ pass "package.yaml is valid YAML"
85
+ pass "All required fields present"
86
+ pass "Version format valid ($PACKAGE_VERSION)"
87
+
88
+ # Check repository URL
89
+ local repo_url=$(yaml_get "package.yaml" "repository" 2>/dev/null)
90
+ if [ -n "$repo_url" ]; then
91
+ pass "Repository URL valid"
92
+ fi
93
+
94
+ # Check reserved names
95
+ case "$PACKAGE_NAME" in
96
+ acp|local|core|system|global)
97
+ error "Package name '$PACKAGE_NAME' is reserved"
98
+ ;;
99
+ *)
100
+ pass "No reserved names used"
101
+ ;;
102
+ esac
103
+ else
104
+ error "package.yaml validation failed"
105
+ echo ""
106
+ echo "Running detailed validation:"
107
+ "${SCRIPT_DIR}/acp.yaml-validate.sh" "package.yaml"
108
+ fixable "Fix package.yaml structure"
109
+ fi
110
+
111
+ echo ""
112
+ }
113
+
114
+ # Validate file existence
115
+ validate_file_existence() {
116
+ echo "${BOLD}File Existence${NC}"
117
+
118
+ local total_files=0
119
+ local missing_files=0
120
+
121
+ # Check patterns
122
+ if yaml_has_key "package.yaml" "contents.patterns"; then
123
+ local pattern_count=$(yaml_get_array "package.yaml" "contents.patterns")
124
+ for i in $(seq 0 $((pattern_count - 1))); do
125
+ local pattern_name=$(yaml_get_nested "package.yaml" "contents.patterns[$i].name")
126
+ if [ -n "$pattern_name" ]; then
127
+ total_files=$((total_files + 1))
128
+ check
129
+ if [ -f "agent/patterns/$pattern_name" ]; then
130
+ pass "agent/patterns/$pattern_name ✓"
131
+ else
132
+ error "Missing file: agent/patterns/$pattern_name"
133
+ missing_files=$((missing_files + 1))
134
+ fi
135
+ fi
136
+ done
137
+ fi
138
+
139
+ # Check commands
140
+ if yaml_has_key "package.yaml" "contents.commands"; then
141
+ local command_count=$(yaml_get_array "package.yaml" "contents.commands")
142
+ for i in $(seq 0 $((command_count - 1))); do
143
+ local command_name=$(yaml_get_nested "package.yaml" "contents.commands[$i].name")
144
+ if [ -n "$command_name" ]; then
145
+ total_files=$((total_files + 1))
146
+ check
147
+ if [ -f "agent/commands/$command_name" ]; then
148
+ pass "agent/commands/$command_name ✓"
149
+ else
150
+ error "Missing file: agent/commands/$command_name"
151
+ missing_files=$((missing_files + 1))
152
+ fi
153
+ fi
154
+ done
155
+ fi
156
+
157
+ # Check designs
158
+ if yaml_has_key "package.yaml" "contents.designs"; then
159
+ local design_count=$(yaml_get_array "package.yaml" "contents.designs")
160
+ for i in $(seq 0 $((design_count - 1))); do
161
+ local design_name=$(yaml_get_nested "package.yaml" "contents.designs[$i].name")
162
+ if [ -n "$design_name" ]; then
163
+ total_files=$((total_files + 1))
164
+ check
165
+ if [ -f "agent/design/$design_name" ]; then
166
+ pass "agent/design/$design_name ✓"
167
+ else
168
+ error "Missing file: agent/design/$design_name"
169
+ missing_files=$((missing_files + 1))
170
+ fi
171
+ fi
172
+ done
173
+ fi
174
+
175
+ if [ "$missing_files" -eq 0 ]; then
176
+ pass "All $total_files files in contents exist"
177
+ else
178
+ error "$missing_files of $total_files files missing"
179
+ fixable "Remove missing files from package.yaml"
180
+ fi
181
+
182
+ echo ""
183
+ }
184
+
185
+ # Check for unlisted files
186
+ check_unlisted_files() {
187
+ echo "${BOLD}Unlisted Files${NC}"
188
+
189
+ local unlisted=0
190
+
191
+ # Get files from manifest (all packages including acp-core)
192
+ # Use simple grep to extract all "- name: filename" entries
193
+ local manifest_files=""
194
+ if [ -f "agent/manifest.yaml" ]; then
195
+ manifest_files=$(grep -E "^[[:space:]]+-[[:space:]]+name:" agent/manifest.yaml | sed 's/.*name:[[:space:]]*//' || echo "")
196
+ fi
197
+
198
+ # Check patterns directory
199
+ if [ -d "agent/patterns" ]; then
200
+ for file in agent/patterns/*.md; do
201
+ [ -f "$file" ] || continue
202
+ local basename=$(basename "$file")
203
+
204
+ # Skip templates
205
+ [[ "$basename" == *.template.md ]] && continue
206
+ [[ "$basename" == ".gitkeep" ]] && continue
207
+
208
+ # Skip if in manifest (installed from another package)
209
+ if echo "$manifest_files" | grep -q "^${basename}$"; then
210
+ continue
211
+ fi
212
+
213
+ # Check if listed in package.yaml
214
+ if ! grep -q "name: $basename" package.yaml 2>/dev/null; then
215
+ warning "Found unlisted file: patterns/$basename"
216
+ fixable "Add patterns/$basename to package.yaml"
217
+ unlisted=$((unlisted + 1))
218
+ fi
219
+ done
220
+ fi
221
+
222
+ # Check commands directory
223
+ if [ -d "agent/commands" ]; then
224
+ for file in agent/commands/*.md; do
225
+ [ -f "$file" ] || continue
226
+ local basename=$(basename "$file")
227
+
228
+ # Skip templates
229
+ [[ "$basename" == *.template.md ]] && continue
230
+
231
+ # Skip if in manifest (installed from another package, including acp-core)
232
+ if echo "$manifest_files" | grep -q "^${basename}$"; then
233
+ continue
234
+ fi
235
+
236
+ # Check if listed in package.yaml
237
+ if ! grep -q "name: $basename" package.yaml 2>/dev/null; then
238
+ warning "Found unlisted file: commands/$basename"
239
+ fixable "Add commands/$basename to package.yaml"
240
+ unlisted=$((unlisted + 1))
241
+ fi
242
+ done
243
+ fi
244
+
245
+ # Check designs directory
246
+ if [ -d "agent/design" ]; then
247
+ for file in agent/design/*.md; do
248
+ [ -f "$file" ] || continue
249
+ local basename=$(basename "$file")
250
+
251
+ # Skip templates and .gitkeep
252
+ [[ "$basename" == *.template.md ]] && continue
253
+ [[ "$basename" == ".gitkeep" ]] && continue
254
+
255
+ # Skip if in manifest (installed from another package)
256
+ if echo "$manifest_files" | grep -q "^${basename}$"; then
257
+ continue
258
+ fi
259
+
260
+ # Check if listed in package.yaml
261
+ if ! grep -q "name: $basename" package.yaml 2>/dev/null; then
262
+ warning "Found unlisted file: design/$basename"
263
+ fixable "Add design/$basename to package.yaml"
264
+ unlisted=$((unlisted + 1))
265
+ fi
266
+ done
267
+ fi
268
+
269
+ # Check scripts directory
270
+ if [ -d "agent/scripts" ]; then
271
+ for file in agent/scripts/*.sh; do
272
+ [ -f "$file" ] || continue
273
+ local basename=$(basename "$file")
274
+
275
+ # Skip templates, acp core scripts, and common utilities
276
+ [[ "$basename" == *.template.sh ]] && continue
277
+ [[ "$basename" == acp.* ]] && continue
278
+ [[ "$basename" == "acp.common.sh" ]] && continue
279
+
280
+ # Skip if in manifest (installed from another package)
281
+ if echo "$manifest_files" | grep -q "^${basename}$"; then
282
+ continue
283
+ fi
284
+
285
+ # Check if listed in package.yaml
286
+ if ! grep -q "name: $basename" package.yaml 2>/dev/null; then
287
+ warning "Found unlisted file: scripts/$basename"
288
+ fixable "Add scripts/$basename to package.yaml"
289
+ unlisted=$((unlisted + 1))
290
+ fi
291
+ done
292
+ fi
293
+
294
+ check
295
+ if [ "$unlisted" -eq 0 ]; then
296
+ pass "No unlisted files found"
297
+ else
298
+ warning "Found $unlisted unlisted file(s)"
299
+ fi
300
+
301
+ echo ""
302
+ }
303
+
304
+ # Validate namespace consistency
305
+ validate_namespace_consistency() {
306
+ echo "${BOLD}Namespace Consistency${NC}"
307
+
308
+ local namespace="$PACKAGE_NAME"
309
+ local inconsistent=0
310
+ local skipped=0
311
+ local skipped_package_files=0
312
+
313
+ # Read package.yaml contents to know which files should be validated
314
+ # Build list of filenames from contents (extract .name from each object)
315
+ # yaml_get_array now handles object arrays and returns count
316
+ local package_commands=""
317
+ if yaml_has_key "package.yaml" "contents.commands"; then
318
+ local command_count=$(yaml_get_array "package.yaml" "contents.commands")
319
+ for i in $(seq 0 $((command_count - 1))); do
320
+ local cmd_name=$(yaml_get_nested "package.yaml" "contents.commands[$i].name")
321
+ [ -n "$cmd_name" ] && package_commands="${package_commands}${cmd_name}"$'\n'
322
+ done
323
+ fi
324
+
325
+ local package_patterns=""
326
+ if yaml_has_key "package.yaml" "contents.patterns"; then
327
+ local pattern_count=$(yaml_get_array "package.yaml" "contents.patterns")
328
+ for i in $(seq 0 $((pattern_count - 1))); do
329
+ local pat_name=$(yaml_get_nested "package.yaml" "contents.patterns[$i].name")
330
+ [ -n "$pat_name" ] && package_patterns="${package_patterns}${pat_name}"$'\n'
331
+ done
332
+ fi
333
+
334
+ local package_designs=""
335
+ if yaml_has_key "package.yaml" "contents.designs"; then
336
+ local design_count=$(yaml_get_array "package.yaml" "contents.designs")
337
+ for i in $(seq 0 $((design_count - 1))); do
338
+ local des_name=$(yaml_get_nested "package.yaml" "contents.designs[$i].name")
339
+ [ -n "$des_name" ] && package_designs="${package_designs}${des_name}"$'\n'
340
+ done
341
+ fi
342
+
343
+ # Track package files that were skipped (for helpful warning)
344
+ local skipped_package_files_list=""
345
+
346
+ # Check command files
347
+ if [ -d "agent/commands" ]; then
348
+ for file in agent/commands/*.md; do
349
+ [ -f "$file" ] || continue
350
+ local basename=$(basename "$file")
351
+
352
+ # Skip templates and core commands
353
+ [[ "$basename" == *.template.md ]] && continue
354
+ [[ "$basename" == acp.* ]] && continue
355
+ [[ "$basename" == local.* ]] && continue
356
+
357
+ # Check if file is in package.yaml contents
358
+ if ! echo "$package_commands" | grep -q "^${basename}$"; then
359
+ # Check if this is a package file (matches namespace)
360
+ if [[ "$basename" =~ ^${namespace}\. ]]; then
361
+ skipped_package_files_list="${skipped_package_files_list}${basename}"$'\n'
362
+ skipped_package_files=$((skipped_package_files + 1))
363
+ fi
364
+ info "Skipping namespace check (not in package contents): $basename"
365
+ skipped=$((skipped + 1))
366
+ continue
367
+ fi
368
+
369
+ # Check if filename starts with namespace
370
+ if [[ ! "$basename" =~ ^${namespace}\. ]]; then
371
+ error "Command file missing namespace: $basename (should be ${namespace}.*.md)"
372
+ fixable "Rename $basename to ${namespace}.${basename}"
373
+ inconsistent=$((inconsistent + 1))
374
+ fi
375
+ done
376
+ fi
377
+
378
+ # Check pattern files (optional namespace for patterns)
379
+ if [ -d "agent/patterns" ]; then
380
+ for file in agent/patterns/*.md; do
381
+ [ -f "$file" ] || continue
382
+ local basename=$(basename "$file")
383
+
384
+ # Skip templates
385
+ [[ "$basename" == *.template.md ]] && continue
386
+
387
+ # Check if file is in package.yaml contents
388
+ if ! echo "$package_patterns" | grep -q "^${basename}$"; then
389
+ # Check if this is a package file (matches namespace)
390
+ if [[ "$basename" =~ ^${namespace}\. ]]; then
391
+ skipped_package_files_list="${skipped_package_files_list}${basename}"$'\n'
392
+ skipped_package_files=$((skipped_package_files + 1))
393
+ fi
394
+ info "Skipping namespace check (not in package contents): $basename"
395
+ skipped=$((skipped + 1))
396
+ continue
397
+ fi
398
+
399
+ # Patterns may or may not use namespace - just warn if inconsistent
400
+ if [[ "$basename" =~ ^${namespace}\. ]]; then
401
+ # Has namespace - good
402
+ :
403
+ else
404
+ # No namespace - warn but don't error
405
+ warning "Pattern file without namespace: $basename (consider ${namespace}.${basename})"
406
+ fi
407
+ done
408
+ fi
409
+
410
+ # Check design files
411
+ if [ -d "agent/design" ]; then
412
+ for file in agent/design/*.md; do
413
+ [ -f "$file" ] || continue
414
+ local basename=$(basename "$file")
415
+
416
+ # Skip templates
417
+ [[ "$basename" == *.template.md ]] && continue
418
+ [[ "$basename" == .gitkeep ]] && continue
419
+
420
+ # Check if file is in package.yaml contents
421
+ if ! echo "$package_designs" | grep -q "^${basename}$"; then
422
+ # Check if this is a package file (matches namespace)
423
+ if [[ "$basename" =~ ^${namespace}\. ]]; then
424
+ skipped_package_files_list="${skipped_package_files_list}${basename}"$'\n'
425
+ skipped_package_files=$((skipped_package_files + 1))
426
+ fi
427
+ info "Skipping namespace check (not in package contents): $basename"
428
+ skipped=$((skipped + 1))
429
+ continue
430
+ fi
431
+ done
432
+ fi
433
+
434
+ check
435
+
436
+ # Error if package files match namespace but aren't in contents
437
+ if [ "$skipped_package_files" -gt 0 ]; then
438
+ echo ""
439
+ error "$skipped_package_files package file(s) match namespace but not in contents:"
440
+ echo "$skipped_package_files_list" | while read -r fname; do
441
+ [ -n "$fname" ] && echo " ${RED}✗${NC} $fname"
442
+ done
443
+ echo ""
444
+ echo " ${RED}Package files matching namespace MUST be in package.yaml contents${NC}"
445
+ echo " Add them with: ${YELLOW}@acp.command-create${NC} or ${YELLOW}@acp.pattern-create${NC}"
446
+ echo " Or remove the namespace prefix if they're dependencies"
447
+ inconsistent=$((inconsistent + skipped_package_files))
448
+ fi
449
+
450
+ if [ "$inconsistent" -eq 0 ]; then
451
+ pass "All package content files use correct namespace"
452
+ if [ "$skipped" -gt 0 ]; then
453
+ info "$skipped file(s) skipped (not in package.yaml contents)"
454
+ info "These files won't be installed to user projects"
455
+ info "This is normal for installed dependencies (tracked in manifest.yaml)"
456
+ fi
457
+ else
458
+ error "$inconsistent file(s) with namespace issues"
459
+ fi
460
+
461
+ echo ""
462
+ }
463
+
464
+ # Validate git repository
465
+ validate_git_repository() {
466
+ echo "${BOLD}Git Repository${NC}"
467
+
468
+ check
469
+ if [ ! -d ".git" ]; then
470
+ error "Git repository not initialized"
471
+ fixable "Run: git init"
472
+ echo ""
473
+ return
474
+ fi
475
+ pass "Git repository initialized"
476
+
477
+ # Check for remote
478
+ check
479
+ local remote_url=$(git remote get-url origin 2>/dev/null || echo "")
480
+ if [ -z "$remote_url" ]; then
481
+ error "Git remote not configured"
482
+ fixable "Add git remote: git remote add origin <url>"
483
+ else
484
+ pass "Remote configured: $remote_url"
485
+
486
+ # Compare with package.yaml
487
+ local package_repo=$(yaml_get "package.yaml" "repository" 2>/dev/null || echo "")
488
+ check
489
+ if [ "$remote_url" = "$package_repo" ] || [ "${remote_url}.git" = "$package_repo" ]; then
490
+ pass "Remote URL matches package.yaml"
491
+ else
492
+ warning "Remote URL mismatch:"
493
+ echo " Git remote: $remote_url"
494
+ echo " package.yaml: $package_repo"
495
+ fixable "Update package.yaml repository field"
496
+ fi
497
+ fi
498
+
499
+ echo ""
500
+ }
501
+
502
+ # Validate README.md
503
+ validate_readme() {
504
+ echo "${BOLD}README.md${NC}"
505
+
506
+ check
507
+ if [ ! -f "README.md" ]; then
508
+ error "README.md not found"
509
+ fixable "Create README.md with package information"
510
+ echo ""
511
+ return
512
+ fi
513
+ pass "README.md exists"
514
+
515
+ # Check for required sections
516
+ local required_sections=("What's Included" "Installation" "License")
517
+ for section in "${required_sections[@]}"; do
518
+ check
519
+ if grep -qi "## $section" README.md || grep -qi "# $section" README.md; then
520
+ pass "Has '$section' section"
521
+ else
522
+ warning "Missing '$section' section in README.md"
523
+ fixable "Add '$section' section to README.md"
524
+ fi
525
+ done
526
+
527
+ echo ""
528
+ }
529
+
530
+ # Test installation
531
+ test_installation() {
532
+ echo "${BLUE}🧪 Test Installation${NC}"
533
+ echo ""
534
+
535
+ local test_dir="/tmp/acp-validate-test-$(date +%s)"
536
+
537
+ check
538
+ echo " Creating test directory: $test_dir"
539
+ mkdir -p "$test_dir/agent/"{patterns,commands,design}
540
+
541
+ # Create minimal manifest
542
+ cat > "$test_dir/agent/manifest.yaml" << 'EOF'
543
+ packages: {}
544
+ manifest_version: 1.0.0
545
+ last_updated: null
546
+ EOF
547
+
548
+ pass "Test directory created"
549
+
550
+ # Try to install package
551
+ check
552
+ echo " Installing package from current directory..."
553
+ local current_dir=$(pwd)
554
+
555
+ if cd "$test_dir" && "${current_dir}/agent/scripts/acp.package-install.sh" "$current_dir" --yes >/dev/null 2>&1; then
556
+ pass "Package installed successfully"
557
+
558
+ # Verify files were copied
559
+ check
560
+ local installed_files=$(find agent/ -name "*.md" -not -name "*.template.md" 2>/dev/null | wc -l)
561
+ if [ "$installed_files" -gt 0 ]; then
562
+ pass "Verified $installed_files file(s) copied"
563
+ else
564
+ error "No files were installed"
565
+ fi
566
+
567
+ # Verify manifest updated
568
+ check
569
+ if grep -q "packages:" agent/manifest.yaml && grep -q "$PACKAGE_NAME:" agent/manifest.yaml; then
570
+ pass "Manifest updated correctly"
571
+ else
572
+ warning "Manifest may not have been updated correctly"
573
+ fi
574
+ else
575
+ error "Package installation failed"
576
+ echo " ${YELLOW}This may indicate issues with package structure or install script${NC}"
577
+ fi
578
+
579
+ # Cleanup
580
+ cd "$current_dir"
581
+ rm -rf "$test_dir"
582
+ pass "Test directory cleaned up"
583
+
584
+ echo ""
585
+ }
586
+
587
+ # Check remote availability
588
+ check_remote_availability() {
589
+ echo "${BLUE}🌐 Remote Availability${NC}"
590
+ echo ""
591
+
592
+ local repo_url=$(yaml_get "package.yaml" "repository" 2>/dev/null || echo "")
593
+
594
+ if [ -z "$repo_url" ]; then
595
+ check
596
+ error "Repository URL not found in package.yaml"
597
+ echo ""
598
+ return
599
+ fi
600
+
601
+ echo " Checking: $repo_url"
602
+
603
+ check
604
+ if git ls-remote "$repo_url" HEAD >/dev/null 2>&1; then
605
+ pass "Remote repository accessible"
606
+
607
+ # Get latest commit
608
+ local latest_commit=$(git ls-remote "$repo_url" HEAD 2>/dev/null | cut -f1 | cut -c1-8)
609
+ if [ -n "$latest_commit" ]; then
610
+ pass "Latest commit: $latest_commit"
611
+ fi
612
+ else
613
+ warning "Remote repository not accessible"
614
+ echo " ${YELLOW}This may be normal if repository is not yet created${NC}"
615
+ echo " ${YELLOW}Ensure repository exists before publishing${NC}"
616
+ fi
617
+
618
+ echo ""
619
+ }
620
+
621
+ # Generate validation report
622
+ generate_report() {
623
+ echo "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
624
+ echo ""
625
+ echo "${BOLD}📊 Validation Summary${NC}"
626
+ echo ""
627
+ echo "Total Checks: $TOTAL_CHECKS"
628
+ echo "Passed: $PASSED_CHECKS"
629
+ echo "Warnings: ${#WARNINGS[@]}"
630
+ echo "Errors: ${#ERRORS[@]}"
631
+ echo ""
632
+
633
+ # Calculate score (clean checks / total * 100)
634
+ # Clean checks = checks that passed without errors or warnings
635
+ local score=0
636
+ if [ "$TOTAL_CHECKS" -gt 0 ]; then
637
+ local clean_checks=$((TOTAL_CHECKS - ${#ERRORS[@]} - ${#WARNINGS[@]}))
638
+ if [ "$clean_checks" -lt 0 ]; then
639
+ clean_checks=0
640
+ fi
641
+ score=$((clean_checks * 100 / TOTAL_CHECKS))
642
+ fi
643
+
644
+ # Determine status
645
+ if [ "${#ERRORS[@]}" -eq 0 ]; then
646
+ if [ "${#WARNINGS[@]}" -eq 0 ]; then
647
+ echo "Overall Status: ${GREEN}✅ PASS${NC}"
648
+ else
649
+ echo "Overall Status: ${YELLOW}✅ PASS WITH WARNINGS${NC}"
650
+ fi
651
+ else
652
+ echo "Overall Status: ${RED}❌ FAIL${NC}"
653
+ fi
654
+
655
+ echo "Validation Score: ${score}%"
656
+ echo ""
657
+ }
658
+
659
+ # Display issues
660
+ display_issues() {
661
+ if [ "${#ERRORS[@]}" -gt 0 ]; then
662
+ echo "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
663
+ echo ""
664
+ echo "${RED}❌ Errors Found (${#ERRORS[@]})${NC}"
665
+ echo ""
666
+ local i=1
667
+ for err in "${ERRORS[@]}"; do
668
+ echo " $i. $err"
669
+ i=$((i + 1))
670
+ done
671
+ echo ""
672
+ fi
673
+
674
+ if [ "${#WARNINGS[@]}" -gt 0 ]; then
675
+ echo "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
676
+ echo ""
677
+ echo "${YELLOW}⚠️ Warnings (${#WARNINGS[@]})${NC}"
678
+ echo ""
679
+ local i=1
680
+ for warn in "${WARNINGS[@]}"; do
681
+ echo " $i. $warn"
682
+ i=$((i + 1))
683
+ done
684
+ echo ""
685
+ fi
686
+ }
687
+
688
+ # Display fixable issues
689
+ display_fixable_issues() {
690
+ if [ "${#FIXABLE_ISSUES[@]}" -gt 0 ]; then
691
+ echo "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
692
+ echo ""
693
+ echo "${BOLD}🔧 Fixable Issues (${#FIXABLE_ISSUES[@]})${NC}"
694
+ echo ""
695
+ echo "The following issues can be fixed automatically:"
696
+ echo ""
697
+ local i=1
698
+ for issue in "${FIXABLE_ISSUES[@]}"; do
699
+ echo " $i. $issue"
700
+ i=$((i + 1))
701
+ done
702
+ echo ""
703
+ echo "${YELLOW}Note: Auto-fix requires LLM agent context.${NC}"
704
+ echo "Run this command via agent (e.g., in chat) to enable auto-fix."
705
+ echo ""
706
+ fi
707
+ }
708
+
709
+ # Validate experimental feature consistency
710
+ validate_experimental_consistency() {
711
+ echo ""
712
+ echo "${BOLD}Experimental Features${NC}"
713
+
714
+ local errors=0
715
+ local checked=0
716
+
717
+ # Check each content type
718
+ for type in commands patterns designs scripts; do
719
+ # Determine directory path
720
+ local dir_path
721
+ case "$type" in
722
+ designs)
723
+ dir_path="agent/design"
724
+ ;;
725
+ *)
726
+ dir_path="agent/${type}"
727
+ ;;
728
+ esac
729
+
730
+ # Skip if directory doesn't exist
731
+ if [ ! -d "$dir_path" ]; then
732
+ continue
733
+ fi
734
+
735
+ # Get all .md or .sh files in directory
736
+ local files
737
+ if [ "$type" = "scripts" ]; then
738
+ files=$(find "$dir_path" -maxdepth 1 -name "*.sh" -type f 2>/dev/null | xargs -r basename -a)
739
+ else
740
+ files=$(find "$dir_path" -maxdepth 1 -name "*.md" -type f 2>/dev/null | xargs -r basename -a)
741
+ fi
742
+
743
+ # Check each file
744
+ for file_name in $files; do
745
+ if [ -z "$file_name" ]; then
746
+ continue
747
+ fi
748
+
749
+ local file_path="${dir_path}/${file_name}"
750
+
751
+ # Check if file is in package.yaml contents
752
+ local in_contents=$(grep -A 1000 "^ ${type}:" package.yaml 2>/dev/null | grep -A 1 "name: ${file_name}" | head -1)
753
+ if [ -z "$in_contents" ]; then
754
+ continue # File not in package.yaml, skip
755
+ fi
756
+
757
+ # Check if marked experimental in package.yaml (exclude comments)
758
+ local is_experimental=$(grep -A 1000 "^ ${type}:" package.yaml 2>/dev/null | grep -A 2 "name: ${file_name}" | grep "^ *experimental: true" | grep -v "^[[:space:]]*#" | head -1)
759
+
760
+ # Check if file has Status: Experimental
761
+ local has_experimental_status=$(grep "^\*\*Status\*\*: Experimental" "$file_path" 2>/dev/null)
762
+
763
+ if [ -n "$is_experimental" ]; then
764
+ # Marked experimental in package.yaml
765
+ checked=$((checked + 1))
766
+ check
767
+ if [ -z "$has_experimental_status" ]; then
768
+ error "${file_path}: Marked experimental in package.yaml but missing 'Status: Experimental' in file"
769
+ fixable "Add '**Status**: Experimental' to ${file_path}"
770
+ errors=$((errors + 1))
771
+ else
772
+ pass "${file_name}: Experimental marking consistent"
773
+ fi
774
+ elif [ -n "$has_experimental_status" ]; then
775
+ # Has Status: Experimental but not marked in package.yaml
776
+ checked=$((checked + 1))
777
+ check
778
+ error "${file_path}: Has 'Status: Experimental' but not marked in package.yaml"
779
+ fixable "Add 'experimental: true' to ${file_name} in package.yaml"
780
+ errors=$((errors + 1))
781
+ fi
782
+ done
783
+ done
784
+
785
+ if [ $checked -eq 0 ]; then
786
+ check
787
+ pass "No experimental features to validate"
788
+ elif [ $errors -eq 0 ] && [ $checked -gt 0 ]; then
789
+ pass "All experimental features marked consistently"
790
+ fi
791
+
792
+ echo ""
793
+
794
+ return $errors
795
+ }
796
+
797
+ # Validate script-command bindings
798
+ validate_script_dependencies() {
799
+ echo ""
800
+ echo "${BOLD}Script-Command Bindings${NC}"
801
+
802
+ local validation_errors=0
803
+
804
+ # Get all commands from package.yaml
805
+ local commands=$(yaml_query ".contents.commands[]?.name" 2>/dev/null || echo "")
806
+
807
+ if [ -z "$commands" ]; then
808
+ pass "No commands to validate"
809
+ return 0
810
+ fi
811
+
812
+ local cmd_count=0
813
+ while IFS= read -r cmd; do
814
+ if [ -z "$cmd" ] || [ "$cmd" = "null" ]; then
815
+ continue
816
+ fi
817
+
818
+ cmd_count=$((cmd_count + 1))
819
+ local cmd_file="agent/commands/$cmd"
820
+
821
+ if [ ! -f "$cmd_file" ]; then
822
+ continue # File existence checked elsewhere
823
+ fi
824
+
825
+ # Get scripts from frontmatter
826
+ local frontmatter_line=$(grep "^\*\*Scripts\*\*:" "$cmd_file" 2>/dev/null || echo "")
827
+
828
+ if [ -z "$frontmatter_line" ]; then
829
+ check
830
+ error "$cmd: Missing **Scripts**: field in frontmatter"
831
+ fixable "Add **Scripts**: field to $cmd_file frontmatter"
832
+ validation_errors=$((validation_errors + 1))
833
+ continue
834
+ fi
835
+
836
+ local frontmatter_scripts=$(echo "$frontmatter_line" | awk -F': ' '{print $2}' | tr ',' '\n' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//' | grep -v "^$" | sort)
837
+
838
+ # Handle "None" case
839
+ if echo "$frontmatter_scripts" | grep -qi "^None$"; then
840
+ frontmatter_scripts=""
841
+ fi
842
+
843
+ # Get scripts from package.yaml
844
+ local yaml_scripts=$(yaml_query ".contents.commands[] | select(.name == \"$cmd\") | .scripts[]?" 2>/dev/null | grep -v "^null$" | sort || echo "")
845
+
846
+ # Compare (both should be empty or both should match)
847
+ if [ "$frontmatter_scripts" != "$yaml_scripts" ]; then
848
+ check
849
+ error "$cmd: Scripts mismatch between frontmatter and package.yaml"
850
+ echo " ${DIM}Frontmatter: $(echo "$frontmatter_scripts" | tr '\n' ', ' | sed 's/, $//')${NC}"
851
+ echo " ${DIM}package.yaml: $(echo "$yaml_scripts" | tr '\n' ', ' | sed 's/, $//')${NC}"
852
+ fixable "Update $cmd to have matching scripts in both locations"
853
+ validation_errors=$((validation_errors + 1))
854
+ continue
855
+ fi
856
+
857
+ # Verify all scripts exist in scripts section
858
+ local script_errors=0
859
+ while IFS= read -r script; do
860
+ if [ -n "$script" ]; then
861
+ local script_exists=$(yaml_query ".contents.scripts[]? | select(.name == \"$script\") | .name" 2>/dev/null || echo "")
862
+
863
+ if [ -z "$script_exists" ] || [ "$script_exists" = "null" ]; then
864
+ if [ $script_errors -eq 0 ]; then
865
+ check
866
+ error "$cmd: Declares scripts not in scripts section"
867
+ fi
868
+ echo " ${DIM}Missing: $script${NC}"
869
+ fixable "Add $script to contents.scripts section in package.yaml"
870
+ script_errors=$((script_errors + 1))
871
+ fi
872
+ fi
873
+ done <<< "$frontmatter_scripts"
874
+
875
+ if [ $script_errors -gt 0 ]; then
876
+ validation_errors=$((validation_errors + 1))
877
+ else
878
+ check
879
+ local script_count=$(echo "$frontmatter_scripts" | grep -v "^$" | wc -l)
880
+ if [ "$script_count" -eq 0 ]; then
881
+ pass "$cmd: No script dependencies"
882
+ else
883
+ pass "$cmd: Scripts consistent ($script_count script(s))"
884
+ fi
885
+ fi
886
+ done <<< "$commands"
887
+
888
+ if [ $cmd_count -eq 0 ]; then
889
+ pass "No commands to validate"
890
+ fi
891
+
892
+ return $validation_errors
893
+ }
894
+
895
+ # Main validation function
896
+ main() {
897
+ echo "${BLUE}🔍 ACP Package Validation${NC}"
898
+ echo "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
899
+ echo ""
900
+
901
+ # Check package context
902
+ check_package_context
903
+
904
+ # Run shell validations
905
+ validate_yaml_structure
906
+ validate_file_existence
907
+ check_unlisted_files
908
+ validate_namespace_consistency
909
+ validate_script_dependencies
910
+ validate_experimental_consistency
911
+ validate_git_repository
912
+ validate_readme
913
+
914
+ # Test installation
915
+ test_installation
916
+
917
+ # Check remote availability
918
+ check_remote_availability
919
+
920
+ # Generate report
921
+ generate_report
922
+
923
+ # Display issues
924
+ display_issues
925
+
926
+ # Display fixable issues
927
+ display_fixable_issues
928
+
929
+ # Exit with appropriate code
930
+ if [ "${#ERRORS[@]}" -gt 0 ]; then
931
+ echo "${RED}Validation failed. Fix errors and run again.${NC}"
932
+ echo ""
933
+ exit 1
934
+ else
935
+ if [ "${#WARNINGS[@]}" -gt 0 ]; then
936
+ echo "${GREEN}✅ Package validation passed with warnings.${NC}"
937
+ echo ""
938
+ echo "Recommendations:"
939
+ echo " - Address warnings before publishing"
940
+ echo " - Run @acp.package-publish when ready"
941
+ echo ""
942
+ else
943
+ echo "${GREEN}✅ Package validation passed!${NC}"
944
+ echo ""
945
+ echo "Your package is ready to publish."
946
+ echo "Run: @acp.package-publish"
947
+ echo ""
948
+ fi
949
+ exit 0
950
+ fi
951
+ }
952
+
953
+ # Run main function
954
+ main "$@"