@prmichaelsen/remember-mcp 3.15.4 → 3.15.5

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 (132) hide show
  1. package/AGENT.md +363 -5
  2. package/CHANGELOG.md +7 -0
  3. package/agent/commands/acp.clarification-capture.md +386 -0
  4. package/agent/commands/acp.clarification-create.md +50 -0
  5. package/agent/commands/acp.command-create.md +60 -0
  6. package/agent/commands/acp.design-create.md +62 -0
  7. package/agent/commands/acp.design-reference.md +355 -0
  8. package/agent/commands/acp.index.md +423 -0
  9. package/agent/commands/acp.init.md +48 -0
  10. package/agent/commands/acp.package-create.md +1 -0
  11. package/agent/commands/acp.package-info.md +1 -0
  12. package/agent/commands/acp.package-install.md +19 -0
  13. package/agent/commands/acp.package-list.md +1 -0
  14. package/agent/commands/acp.package-publish.md +1 -0
  15. package/agent/commands/acp.package-remove.md +1 -0
  16. package/agent/commands/acp.package-search.md +1 -0
  17. package/agent/commands/acp.package-update.md +1 -0
  18. package/agent/commands/acp.package-validate.md +1 -0
  19. package/agent/commands/acp.pattern-create.md +60 -0
  20. package/agent/commands/acp.plan.md +25 -0
  21. package/agent/commands/acp.proceed.md +621 -75
  22. package/agent/commands/acp.project-create.md +3 -0
  23. package/agent/commands/acp.project-info.md +3 -0
  24. package/agent/commands/acp.project-list.md +3 -1
  25. package/agent/commands/acp.project-set.md +1 -0
  26. package/agent/commands/acp.project-update.md +14 -3
  27. package/agent/commands/acp.projects-restore.md +228 -0
  28. package/agent/commands/acp.projects-sync.md +347 -0
  29. package/agent/commands/acp.report.md +13 -0
  30. package/agent/commands/acp.resume.md +3 -1
  31. package/agent/commands/acp.sessions.md +301 -0
  32. package/agent/commands/acp.status.md +13 -0
  33. package/agent/commands/acp.sync.md +1 -0
  34. package/agent/commands/acp.task-create.md +105 -3
  35. package/agent/commands/acp.update.md +1 -0
  36. package/agent/commands/acp.validate.md +32 -2
  37. package/agent/commands/acp.version-check-for-updates.md +1 -0
  38. package/agent/commands/acp.version-check.md +1 -0
  39. package/agent/commands/acp.version-update.md +1 -0
  40. package/agent/commands/command.template.md +23 -0
  41. package/agent/commands/git.commit.md +1 -0
  42. package/agent/commands/git.init.md +1 -0
  43. package/agent/design/complete-tool-set.md +157 -233
  44. package/agent/design/design.template.md +18 -0
  45. package/agent/design/user-preferences.md +11 -7
  46. package/agent/milestones/milestone-19-new-search-ghost-tools.md +46 -0
  47. package/agent/package.template.yaml +50 -0
  48. package/agent/patterns/pattern.template.md +18 -0
  49. package/agent/progress.yaml +162 -6
  50. package/agent/scripts/acp.common.sh +258 -15
  51. package/agent/scripts/acp.install.sh +91 -4
  52. package/agent/scripts/acp.package-create.sh +0 -1
  53. package/agent/scripts/acp.package-info.sh +19 -1
  54. package/agent/scripts/acp.package-install-optimized.sh +1 -1
  55. package/agent/scripts/acp.package-install.sh +388 -38
  56. package/agent/scripts/acp.package-list.sh +52 -4
  57. package/agent/scripts/acp.package-remove.sh +77 -1
  58. package/agent/scripts/acp.package-search.sh +2 -2
  59. package/agent/scripts/acp.package-update.sh +91 -12
  60. package/agent/scripts/acp.package-validate.sh +136 -1
  61. package/agent/scripts/acp.project-info.sh +34 -11
  62. package/agent/scripts/acp.project-list.sh +4 -0
  63. package/agent/scripts/acp.project-update.sh +66 -19
  64. package/agent/scripts/acp.projects-restore.sh +170 -0
  65. package/agent/scripts/acp.projects-sync.sh +155 -0
  66. package/agent/scripts/acp.sessions.sh +725 -0
  67. package/agent/scripts/acp.version-update.sh +21 -3
  68. package/agent/scripts/acp.yaml-parser.sh +20 -6
  69. package/agent/tasks/milestone-19-new-search-ghost-tools/task-203-create-search-by-tool.md +143 -0
  70. package/agent/tasks/milestone-19-new-search-ghost-tools/task-204-add-new-filters-existing-tools.md +77 -0
  71. package/agent/tasks/milestone-19-new-search-ghost-tools/task-205-add-feel-fields-create-update.md +137 -0
  72. package/agent/tasks/milestone-19-new-search-ghost-tools/task-206-add-byproperty-bysignificance-modes.md +135 -0
  73. package/agent/tasks/milestone-19-new-search-ghost-tools/task-207-add-emotional-composites-search-results.md +88 -0
  74. package/agent/tasks/milestone-19-new-search-ghost-tools/task-208-add-bybroad-byrandom-modes.md +115 -0
  75. package/agent/tasks/milestone-19-new-search-ghost-tools/task-209-create-ghost-memory-tools.md +192 -0
  76. package/agent/tasks/milestone-19-new-search-ghost-tools/task-210-create-get-core-tool.md +203 -0
  77. package/agent/tasks/milestone-19-new-search-ghost-tools/task-211-create-search-space-by-tool.md +182 -0
  78. package/agent/tasks/task-1-{title}.template.md +19 -0
  79. package/agent/tasks/unassigned/bug-report-remember-core-e2e-findings.md +99 -0
  80. package/dist/e2e-helpers.d.ts +26 -0
  81. package/dist/ghost-persona.e2e.d.ts +8 -0
  82. package/dist/memory-crud.e2e.d.ts +8 -0
  83. package/dist/preferences.e2e.d.ts +8 -0
  84. package/dist/relationships.e2e.d.ts +8 -0
  85. package/dist/search-modes.e2e.d.ts +8 -0
  86. package/dist/server-factory.js +1971 -94
  87. package/dist/server.js +1168 -45
  88. package/dist/shared-spaces.e2e.d.ts +8 -0
  89. package/dist/tools/create-ghost-memory.d.ts +70 -0
  90. package/dist/tools/create-memory.d.ts +175 -0
  91. package/dist/tools/get-core.d.ts +28 -0
  92. package/dist/tools/get-core.spec.d.ts +2 -0
  93. package/dist/tools/ghost-tools.spec.d.ts +2 -0
  94. package/dist/tools/query-ghost-memory.d.ts +34 -0
  95. package/dist/tools/query-memory.d.ts +4 -0
  96. package/dist/tools/search-by.d.ts +147 -0
  97. package/dist/tools/search-by.spec.d.ts +2 -0
  98. package/dist/tools/search-ghost-memory-by.d.ts +54 -0
  99. package/dist/tools/search-ghost-memory.d.ts +53 -0
  100. package/dist/tools/search-memory.d.ts +19 -0
  101. package/dist/tools/search-space-by.d.ts +78 -0
  102. package/dist/tools/search-space-by.spec.d.ts +2 -0
  103. package/dist/tools/search-space.d.ts +2 -0
  104. package/dist/tools/update-ghost-memory.d.ts +51 -0
  105. package/dist/tools/update-memory.d.ts +175 -0
  106. package/jest.e2e.config.js +11 -0
  107. package/package.json +2 -2
  108. package/src/e2e-helpers.ts +86 -0
  109. package/src/ghost-persona.e2e.ts +215 -0
  110. package/src/memory-crud.e2e.ts +203 -0
  111. package/src/preferences.e2e.ts +88 -0
  112. package/src/relationships.e2e.ts +156 -0
  113. package/src/search-modes.e2e.ts +184 -0
  114. package/src/server-factory.ts +56 -0
  115. package/src/shared-spaces.e2e.ts +204 -0
  116. package/src/tools/create-ghost-memory.ts +103 -0
  117. package/src/tools/create-memory.ts +45 -1
  118. package/src/tools/get-core.spec.ts +223 -0
  119. package/src/tools/get-core.ts +109 -0
  120. package/src/tools/ghost-tools.spec.ts +361 -0
  121. package/src/tools/query-ghost-memory.ts +63 -0
  122. package/src/tools/query-memory.ts +4 -0
  123. package/src/tools/search-by.spec.ts +325 -0
  124. package/src/tools/search-by.ts +298 -0
  125. package/src/tools/search-ghost-memory-by.ts +80 -0
  126. package/src/tools/search-ghost-memory.ts +73 -0
  127. package/src/tools/search-memory.ts +23 -0
  128. package/src/tools/search-space-by.spec.ts +289 -0
  129. package/src/tools/search-space-by.ts +173 -0
  130. package/src/tools/search-space.ts +20 -1
  131. package/src/tools/update-ghost-memory.ts +86 -0
  132. package/src/tools/update-memory.ts +45 -1
@@ -19,10 +19,12 @@ INSTALL_PATTERNS=false
19
19
  INSTALL_COMMANDS=false
20
20
  INSTALL_DESIGNS=false
21
21
  INSTALL_FILES=false
22
+ INSTALL_INDICES=false
22
23
  PATTERN_FILES=()
23
24
  COMMAND_FILES=()
24
25
  DESIGN_FILES=()
25
26
  FILE_FILES=()
27
+ INDEX_FILES=()
26
28
  LIST_ONLY=false
27
29
  GLOBAL_INSTALL=false
28
30
  INSTALL_EXPERIMENTAL=false
@@ -49,7 +51,7 @@ while [[ $# -gt 0 ]]; do
49
51
  --patterns)
50
52
  INSTALL_PATTERNS=true
51
53
  shift
52
- while [[ $# -gt 0 && ! $1 =~ ^-- ]]; do
54
+ while [[ $# -gt 0 && ! $1 =~ ^- ]]; do
53
55
  PATTERN_FILES+=("$1")
54
56
  shift
55
57
  done
@@ -57,7 +59,7 @@ while [[ $# -gt 0 ]]; do
57
59
  --commands)
58
60
  INSTALL_COMMANDS=true
59
61
  shift
60
- while [[ $# -gt 0 && ! $1 =~ ^-- ]]; do
62
+ while [[ $# -gt 0 && ! $1 =~ ^- ]]; do
61
63
  COMMAND_FILES+=("$1")
62
64
  shift
63
65
  done
@@ -65,7 +67,7 @@ while [[ $# -gt 0 ]]; do
65
67
  --designs)
66
68
  INSTALL_DESIGNS=true
67
69
  shift
68
- while [[ $# -gt 0 && ! $1 =~ ^-- ]]; do
70
+ while [[ $# -gt 0 && ! $1 =~ ^- ]]; do
69
71
  DESIGN_FILES+=("$1")
70
72
  shift
71
73
  done
@@ -73,11 +75,19 @@ while [[ $# -gt 0 ]]; do
73
75
  --files)
74
76
  INSTALL_FILES=true
75
77
  shift
76
- while [[ $# -gt 0 && ! $1 =~ ^-- ]]; do
78
+ while [[ $# -gt 0 && ! $1 =~ ^- ]]; do
77
79
  FILE_FILES+=("$1")
78
80
  shift
79
81
  done
80
82
  ;;
83
+ --indices)
84
+ INSTALL_INDICES=true
85
+ shift
86
+ while [[ $# -gt 0 && ! $1 =~ ^- ]]; do
87
+ INDEX_FILES+=("$1")
88
+ shift
89
+ done
90
+ ;;
81
91
  --list)
82
92
  LIST_ONLY=true
83
93
  shift
@@ -98,11 +108,12 @@ if [ -z "$REPO_URL" ]; then
98
108
  fi
99
109
 
100
110
  # Default: install everything if no selective flags specified
101
- if [[ "$INSTALL_PATTERNS" == false && "$INSTALL_COMMANDS" == false && "$INSTALL_DESIGNS" == false && "$INSTALL_FILES" == false ]]; then
111
+ if [[ "$INSTALL_PATTERNS" == false && "$INSTALL_COMMANDS" == false && "$INSTALL_DESIGNS" == false && "$INSTALL_FILES" == false && "$INSTALL_INDICES" == false ]]; then
102
112
  INSTALL_PATTERNS=true
103
113
  INSTALL_COMMANDS=true
104
114
  INSTALL_DESIGNS=true
105
115
  INSTALL_FILES=true
116
+ INSTALL_INDICES=true
106
117
  fi
107
118
 
108
119
  echo "${BLUE}📦 ACP Package Installer (Optimized)${NC}"
@@ -122,9 +133,15 @@ TEMP_DIR=$(mktemp -d)
122
133
  trap "rm -rf $TEMP_DIR" EXIT
123
134
 
124
135
  echo "Cloning repository..."
125
- if ! git clone --depth 1 "$REPO_URL" "$TEMP_DIR" &>/dev/null; then
136
+ if [ -d "$REPO_URL" ]; then
137
+ # Local directory - copy contents instead of clone
138
+ cp -r "$REPO_URL"/* "$TEMP_DIR/" 2>/dev/null || cp -r "$REPO_URL"/.[!.]* "$TEMP_DIR/" 2>/dev/null || true
139
+ echo "${GREEN}✓${NC} Local directory copied"
140
+ elif ! git clone --depth 1 "$REPO_URL" "$TEMP_DIR" &>/dev/null; then
126
141
  echo "${RED}Error: Failed to clone repository${NC}"
127
142
  exit 1
143
+ else
144
+ echo "${GREEN}✓${NC} Repository cloned"
128
145
  fi
129
146
 
130
147
  echo "${GREEN}✓${NC} Repository cloned"
@@ -160,12 +177,6 @@ COMMIT_HASH=$(get_commit_hash "$TEMP_DIR")
160
177
  info "Commit: $COMMIT_HASH"
161
178
  echo ""
162
179
 
163
- # List mode (unchanged)
164
- if [ "$LIST_ONLY" = true ]; then
165
- # ... (same as original)
166
- exit 0
167
- fi
168
-
169
180
  # Validate dependencies
170
181
  if [ -f "$TEMP_DIR/package.yaml" ]; then
171
182
  if ! validate_project_dependencies "$TEMP_DIR/package.yaml"; then
@@ -181,6 +192,7 @@ INSTALL_DIRS=()
181
192
  [ "$INSTALL_DESIGNS" = true ] && INSTALL_DIRS+=("design")
182
193
  [ "$INSTALL_COMMANDS" = true ] && INSTALL_DIRS+=("scripts")
183
194
  [ "$INSTALL_FILES" = true ] && INSTALL_DIRS+=("files")
195
+ [ "$INSTALL_INDICES" = true ] && INSTALL_DIRS+=("index")
184
196
 
185
197
  # Mapping from dir names to manifest keys (dir → manifest key)
186
198
  declare -A MANIFEST_KEYS=(
@@ -189,6 +201,7 @@ declare -A MANIFEST_KEYS=(
189
201
  ["design"]="designs"
190
202
  ["scripts"]="scripts"
191
203
  ["files"]="files"
204
+ ["index"]="indices"
192
205
  )
193
206
 
194
207
  # ============================================================================
@@ -199,9 +212,18 @@ declare -A MANIFEST_KEYS=(
199
212
  declare -A ALL_FILES_TO_INSTALL # Key: dir, Value: space-separated file paths
200
213
  declare -A FILE_METADATA # Key: "dir/filename", Value: "version|experimental"
201
214
 
215
+ # Track installed commands for script-command binding resolution
216
+ INSTALLED_COMMANDS=()
217
+
202
218
  INSTALLED_COUNT=0
203
219
  SKIPPED_COUNT=0
204
220
 
221
+ # Template file metadata (populated during scanning when contents.files exists)
222
+ declare -A FILE_TARGETS # Key: "files/relpath", Value: target directory path
223
+ declare -A FILE_VARS # Key: "files/relpath", Value: "VAR1,VAR2,..."
224
+ declare -A COLLECTED_VARS # Key: "VARNAME", Value: user-provided value
225
+ HAS_FILE_METADATA=false
226
+
205
227
  echo "Scanning for installable files..."
206
228
  echo ""
207
229
 
@@ -226,6 +248,7 @@ for dir in "${INSTALL_DIRS[@]}"; do
226
248
  design) FILE_LIST=DESIGN_FILES ;;
227
249
  scripts) FILE_LIST=COMMAND_FILES ;;
228
250
  files) FILE_LIST=FILE_FILES ;;
251
+ index) FILE_LIST=INDEX_FILES ;;
229
252
  esac
230
253
 
231
254
  # Collect files
@@ -242,6 +265,31 @@ for dir in "${INSTALL_DIRS[@]}"; do
242
265
  file_path="$SOURCE_DIR/$file_name"
243
266
  if [ -f "$file_path" ]; then
244
267
  FILES_TO_PROCESS+=("$file_path")
268
+
269
+ # For selective files, also collect metadata from package.yaml
270
+ if [ "$dir" = "files" ] && [ -f "$TEMP_DIR/package.yaml" ]; then
271
+ _sel_idx=0
272
+ while true; do
273
+ _sel_name=$(yaml_query ".contents.files[$_sel_idx].name" 2>/dev/null || echo "")
274
+ [ -z "$_sel_name" ] || [ "$_sel_name" = "null" ] && break
275
+ if [ "$_sel_name" = "$file_name" ]; then
276
+ HAS_FILE_METADATA=true
277
+ _sel_target=$(yaml_query ".contents.files[$_sel_idx].target" 2>/dev/null || echo "")
278
+ [ -n "$_sel_target" ] && [ "$_sel_target" != "null" ] && FILE_TARGETS["files/$file_name"]="$_sel_target"
279
+ _sel_var_idx=0
280
+ _sel_vars=""
281
+ while true; do
282
+ _sel_var=$(yaml_query ".contents.files[$_sel_idx].variables[$_sel_var_idx]" 2>/dev/null || echo "")
283
+ [ -z "$_sel_var" ] || [ "$_sel_var" = "null" ] && break
284
+ [ -n "$_sel_vars" ] && _sel_vars="$_sel_vars,$_sel_var" || _sel_vars="$_sel_var"
285
+ _sel_var_idx=$((_sel_var_idx + 1))
286
+ done
287
+ [ -n "$_sel_vars" ] && FILE_VARS["files/$file_name"]="$_sel_vars"
288
+ break
289
+ fi
290
+ _sel_idx=$((_sel_idx + 1))
291
+ done
292
+ fi
245
293
  else
246
294
  echo "${YELLOW}⚠${NC} File not found in $dir/: $file_name"
247
295
  SKIPPED_COUNT=$((SKIPPED_COUNT + 1))
@@ -250,14 +298,54 @@ for dir in "${INSTALL_DIRS[@]}"; do
250
298
  else
251
299
  # Install all files
252
300
  if [ "$dir" = "files" ]; then
253
- # files/ directory: recursive scan, all file types
254
- while IFS= read -r file; do
255
- [ -n "$file" ] && FILES_TO_PROCESS+=("$file")
256
- done < <(find "$SOURCE_DIR" -type f)
301
+ # Check if package.yaml has contents.files metadata
302
+ _first_file=$(yaml_query ".contents.files[0].name" 2>/dev/null || echo "")
303
+ if [ -f "$TEMP_DIR/package.yaml" ] && [ -n "$_first_file" ] && [ "$_first_file" != "null" ]; then
304
+ # Use package.yaml contents.files as source of truth
305
+ HAS_FILE_METADATA=true
306
+ _file_idx=0
307
+ while true; do
308
+ _fname=$(yaml_query ".contents.files[$_file_idx].name" 2>/dev/null || echo "")
309
+ [ -z "$_fname" ] || [ "$_fname" = "null" ] && break
310
+
311
+ if [ -f "$SOURCE_DIR/$_fname" ]; then
312
+ FILES_TO_PROCESS+=("$SOURCE_DIR/$_fname")
313
+
314
+ # Store target metadata
315
+ _target=$(yaml_query ".contents.files[$_file_idx].target" 2>/dev/null || echo "")
316
+ [ -n "$_target" ] && [ "$_target" != "null" ] && FILE_TARGETS["files/$_fname"]="$_target"
317
+
318
+ # Collect variable names
319
+ _var_idx=0
320
+ _vars=""
321
+ while true; do
322
+ _var=$(yaml_query ".contents.files[$_file_idx].variables[$_var_idx]" 2>/dev/null || echo "")
323
+ [ -z "$_var" ] || [ "$_var" = "null" ] && break
324
+ [ -n "$_vars" ] && _vars="$_vars,$_var" || _vars="$_var"
325
+ _var_idx=$((_var_idx + 1))
326
+ done
327
+ [ -n "$_vars" ] && FILE_VARS["files/$_fname"]="$_vars"
328
+ else
329
+ echo " ${YELLOW}⚠${NC} Declared in package.yaml but not found: agent/files/$_fname"
330
+ SKIPPED_COUNT=$((SKIPPED_COUNT + 1))
331
+ fi
332
+
333
+ _file_idx=$((_file_idx + 1))
334
+ done
335
+ else
336
+ # Fallback: recursive scan (backward compat for packages without contents.files)
337
+ while IFS= read -r file; do
338
+ [ -n "$file" ] && FILES_TO_PROCESS+=("$file")
339
+ done < <(find "$SOURCE_DIR" -type f)
340
+ fi
257
341
  elif [ "$dir" = "scripts" ]; then
258
342
  while IFS= read -r file; do
259
343
  [ -n "$file" ] && FILES_TO_PROCESS+=("$file")
260
344
  done < <(find "$SOURCE_DIR" -maxdepth 1 -name "*.sh" ! -name "*.template.sh" -type f)
345
+ elif [ "$dir" = "index" ]; then
346
+ while IFS= read -r file; do
347
+ [ -n "$file" ] && FILES_TO_PROCESS+=("$file")
348
+ done < <(find "$SOURCE_DIR" -maxdepth 1 -name "*.yaml" ! -name "*.template.yaml" -type f)
261
349
  else
262
350
  while IFS= read -r file; do
263
351
  [ -n "$file" ] && FILES_TO_PROCESS+=("$file")
@@ -270,7 +358,7 @@ for dir in "${INSTALL_DIRS[@]}"; do
270
358
  continue
271
359
  fi
272
360
 
273
- if [ "$dir" = "files" ]; then
361
+ if [ "$dir" = "files" ] && [ "$HAS_FILE_METADATA" = false ]; then
274
362
  echo "${BLUE}📁 $dir/${NC} (${#FILES_TO_PROCESS[@]} file(s)) → installs to ./"
275
363
  else
276
364
  echo "${BLUE}📁 $dir/${NC} (${#FILES_TO_PROCESS[@]} file(s))"
@@ -308,10 +396,18 @@ for dir in "${INSTALL_DIRS[@]}"; do
308
396
  fi
309
397
  fi
310
398
 
311
- # Check experimental status (skip for files/ — no per-file experimental marking)
399
+ # Check experimental status
312
400
  is_experimental=""
313
- if [ "$dir" != "files" ] && [ -f "$TEMP_DIR/package.yaml" ]; then
314
- is_experimental=$(grep -A 1000 "^ ${dir}:" "$TEMP_DIR/package.yaml" 2>/dev/null | grep -A 2 "name: ${filename}" | grep "^ *experimental: true" | grep -v "^[[:space:]]*#" | head -1)
401
+ if [ -f "$TEMP_DIR/package.yaml" ]; then
402
+ if [ "$dir" = "files" ] && [ "$HAS_FILE_METADATA" = false ]; then
403
+ : # No per-file experimental marking without metadata
404
+ elif [ "$dir" = "files" ]; then
405
+ # Files entries have more fields (name, description, target, required, experimental)
406
+ # so need a wider context window (-A 6) to catch experimental: true
407
+ is_experimental=$(grep -A 1000 "^ ${dir}:" "$TEMP_DIR/package.yaml" 2>/dev/null | grep -A 6 "name: ${filename}" | grep "^ *experimental: true" | grep -v "^[[:space:]]*#" | head -1)
408
+ else
409
+ is_experimental=$(grep -A 1000 "^ ${dir}:" "$TEMP_DIR/package.yaml" 2>/dev/null | grep -A 2 "name: ${filename}" | grep "^ *experimental: true" | grep -v "^[[:space:]]*#" | head -1)
410
+ fi
315
411
  fi
316
412
 
317
413
  if [ -n "$is_experimental" ] && [ "$INSTALL_EXPERIMENTAL" = false ]; then
@@ -329,17 +425,30 @@ for dir in "${INSTALL_DIRS[@]}"; do
329
425
  # Add to valid files
330
426
  VALID_FILES+=("$file")
331
427
 
332
- # Check for overwrites files/ targets project root, others target agent/
333
- if [ "$dir" = "files" ]; then
428
+ # Determine target path for overwrite check
429
+ if [ "$dir" = "files" ] && [ "$HAS_FILE_METADATA" = true ]; then
430
+ _file_target="${FILE_TARGETS[files/$filename]:-./}"
431
+ _bname=$(basename "$filename")
432
+ _bname="${_bname%.template}"
433
+ target_path="${_file_target}${_bname}"
434
+ elif [ "$dir" = "files" ]; then
334
435
  target_path="./$filename"
335
436
  else
336
437
  target_path="$INSTALL_BASE_DIR/$dir/$filename"
337
438
  fi
338
439
 
440
+ # Build display info for files with metadata
441
+ _display_extra=""
442
+ if [ "$dir" = "files" ] && [ "$HAS_FILE_METADATA" = true ]; then
443
+ _file_vars="${FILE_VARS[files/$filename]:-}"
444
+ _display_extra=" → $target_path"
445
+ [ -n "$_file_vars" ] && _display_extra="$_display_extra (variables: $_file_vars)"
446
+ fi
447
+
339
448
  if [ -f "$target_path" ]; then
340
- echo " ${YELLOW}⚠${NC} $filename (will overwrite)"
449
+ echo " ${YELLOW}⚠${NC} $filename${_display_extra} (will overwrite)"
341
450
  else
342
- echo " ${GREEN}✓${NC} $filename"
451
+ echo " ${GREEN}✓${NC} $filename${_display_extra}"
343
452
  fi
344
453
 
345
454
  INSTALLED_COUNT=$((INSTALLED_COUNT + 1))
@@ -355,7 +464,7 @@ for dir in "${INSTALL_DIRS[@]}"; do
355
464
  done
356
465
 
357
466
  # Warn about unrecognized directories in the package
358
- KNOWN_DIRS="patterns commands design scripts files"
467
+ KNOWN_DIRS="patterns commands design scripts files index"
359
468
  if [ -d "$TEMP_DIR/agent" ]; then
360
469
  UNRECOGNIZED=()
361
470
  while IFS= read -r pkg_dir; do
@@ -386,6 +495,11 @@ echo "Ready to install $INSTALLED_COUNT file(s)"
386
495
  [ $SKIPPED_COUNT -gt 0 ] && echo "($SKIPPED_COUNT file(s) will be skipped)"
387
496
  echo ""
388
497
 
498
+ if [ "$LIST_ONLY" = true ]; then
499
+ echo "${BLUE}(dry run — no files were installed)${NC}"
500
+ exit 0
501
+ fi
502
+
389
503
  if [ "$SKIP_CONFIRM" = false ]; then
390
504
  read -p "Proceed with installation? (y/N) " -n 1 -r
391
505
  echo
@@ -398,40 +512,243 @@ else
398
512
  fi
399
513
 
400
514
  echo ""
515
+
516
+ # Collect template variables from user (if any files have variables declared)
517
+ if [ ${#FILE_VARS[@]} -gt 0 ]; then
518
+ echo "${BLUE}Collecting template variables...${NC}"
519
+
520
+ # Build list of unique variables across all templates
521
+ _all_vars=""
522
+ for _key in "${!FILE_VARS[@]}"; do
523
+ IFS=',' read -ra _var_arr <<< "${FILE_VARS[$_key]}"
524
+ for _var in "${_var_arr[@]}"; do
525
+ if [[ ! ",$_all_vars," =~ ",$_var," ]]; then
526
+ [ -n "$_all_vars" ] && _all_vars="$_all_vars,$_var" || _all_vars="$_var"
527
+ fi
528
+ done
529
+ done
530
+
531
+ # Prompt for each unique variable
532
+ IFS=',' read -ra _unique_vars <<< "$_all_vars"
533
+ for _var in "${_unique_vars[@]}"; do
534
+ read -p " Enter $_var: " _value
535
+ COLLECTED_VARS["$_var"]="$_value"
536
+ done
537
+
538
+ echo "${GREEN}✓${NC} Variables collected"
539
+ echo ""
540
+ fi
541
+
401
542
  echo "Installing files..."
402
543
 
403
544
  # ============================================================================
404
545
  # OPTIMIZATION: Batch file operations
405
546
  # ============================================================================
406
547
 
548
+ # Check if file should be installed based on experimental status
549
+ should_install_file() {
550
+ local filename="$1"
551
+ local file_type="$2" # commands, patterns, designs, scripts
552
+
553
+ # If no package.yaml, install everything
554
+ if [ ! -f "$TEMP_DIR/package.yaml" ]; then
555
+ return 0
556
+ fi
557
+
558
+ # Check if file is marked experimental in package.yaml
559
+ # Extract only the relevant section, then find the specific entry
560
+ local section=$(grep -A 1000 "^ ${file_type}:" "$TEMP_DIR/package.yaml" 2>/dev/null | grep -B 1000 "^ [a-z]" 2>/dev/null | head -n -1 || true)
561
+ local is_experimental=$(echo "$section" | grep -A 3 "^ - name: ${filename}$" 2>/dev/null | grep "^ *experimental: true" 2>/dev/null | grep -v "^[[:space:]]*#" | head -1 || true)
562
+
563
+ if [ -n "$is_experimental" ]; then
564
+ if [ "$INSTALL_EXPERIMENTAL" = true ]; then
565
+ echo " ${YELLOW}⚠${NC} Installing experimental: ${filename}"
566
+ return 0 # Install it
567
+ else
568
+ echo " ${DIM}⊘${NC} Skipping experimental: ${filename} (use --experimental to install)"
569
+ return 1 # Skip it
570
+ fi
571
+ fi
572
+
573
+ return 0 # Install non-experimental files
574
+ }
575
+
407
576
  # Add package to manifest once
408
577
  add_package_to_manifest "$PACKAGE_NAME" "$REPO_URL" "$PACKAGE_VERSION" "$COMMIT_HASH"
409
578
 
410
- # Batch copy all files
579
+ # Batch copy all files (skip scripts — handled via script-command binding below)
411
580
  for dir in "${!ALL_FILES_TO_INSTALL[@]}"; do
412
581
  SOURCE_DIR="$TEMP_DIR/agent/$dir"
413
582
 
583
+ # Skip scripts in first pass — install selectively after commands via script-command binding
584
+ if [ "$dir" = "scripts" ]; then
585
+ continue
586
+ fi
587
+
414
588
  # Copy all files
415
589
  for file in ${ALL_FILES_TO_INSTALL[$dir]}; do
416
590
  if [ "$dir" = "files" ]; then
417
- # files/ installs to project root, preserving subdirectory structure
418
591
  rel_path="${file#$SOURCE_DIR/}"
419
- target_dir="$(dirname "./$rel_path")"
420
- mkdir -p "$target_dir"
421
- cp "$file" "./$rel_path"
592
+
593
+ if [ "$HAS_FILE_METADATA" = true ]; then
594
+ # Metadata-aware installation: use target path and variable substitution
595
+ _file_target="${FILE_TARGETS[files/$rel_path]:-./}"
596
+ _bname=$(basename "$rel_path")
597
+ _bname="${_bname%.template}"
598
+ _dest="${_file_target}${_bname}"
599
+
600
+ # Safety validation: reject paths that escape project root
601
+ if [[ "$_dest" =~ \.\. ]] || [[ "$_dest" =~ ^/ ]]; then
602
+ echo " ${RED}✗${NC} Skipping $rel_path (unsafe target: $_dest)"
603
+ continue
604
+ fi
605
+
606
+ mkdir -p "$(dirname "$_dest")"
607
+
608
+ # Apply variable substitution if template has variables
609
+ _file_vars="${FILE_VARS[files/$rel_path]:-}"
610
+ if [ -n "$_file_vars" ] && [ ${#COLLECTED_VARS[@]} -gt 0 ]; then
611
+ cp "$file" "$_dest"
612
+ IFS=',' read -ra _var_arr <<< "$_file_vars"
613
+ for _var in "${_var_arr[@]}"; do
614
+ _value="${COLLECTED_VARS[$_var]:-}"
615
+ if [ -n "$_value" ]; then
616
+ _escaped=$(printf '%s\n' "$_value" | sed 's/[&/\]/\\&/g')
617
+ _sed_i "s|{{${_var}}}|${_escaped}|g" "$_dest"
618
+ fi
619
+ done
620
+ else
621
+ cp "$file" "$_dest"
622
+ fi
623
+ else
624
+ # Backward compat: install to project root preserving subdirectory structure
625
+ target_dir="$(dirname "./$rel_path")"
626
+ mkdir -p "$target_dir"
627
+ cp "$file" "./$rel_path"
628
+ fi
422
629
  else
423
630
  mkdir -p "$INSTALL_BASE_DIR/$dir"
424
631
  filename=$(basename "$file")
425
632
  cp "$file" "$INSTALL_BASE_DIR/$dir/$filename"
633
+ fi
426
634
 
427
- # Make scripts executable
428
- if [ "$dir" = "scripts" ]; then
429
- chmod +x "$INSTALL_BASE_DIR/$dir/$filename"
430
- fi
635
+ # Track installed commands for script dependency resolution
636
+ if [ "$dir" = "commands" ]; then
637
+ filename=$(basename "$file")
638
+ INSTALLED_COMMANDS+=("$filename")
431
639
  fi
432
640
  done
433
641
  done
434
642
 
643
+ # ============================================================================
644
+ # Script-Command Binding: Install scripts based on command dependencies
645
+ # ============================================================================
646
+
647
+ if [ -f "$TEMP_DIR/package.yaml" ] && [ ${#INSTALLED_COMMANDS[@]} -gt 0 ]; then
648
+ echo "Resolving script dependencies..."
649
+ echo " Installed commands: ${INSTALLED_COMMANDS[@]}"
650
+
651
+ # Collect required scripts from installed commands using YAML parser
652
+ REQUIRED_SCRIPTS=()
653
+ for cmd in "${INSTALLED_COMMANDS[@]}"; do
654
+ # Find the command index in the array
655
+ cmd_index=0
656
+ while true; do
657
+ cmd_name=$(yaml_get_nested "$TEMP_DIR/package.yaml" "contents.commands[$cmd_index].name" 2>/dev/null || echo "")
658
+ if [ -z "$cmd_name" ] || [ "$cmd_name" = "null" ]; then
659
+ break
660
+ fi
661
+
662
+ if [ "$cmd_name" = "$cmd" ]; then
663
+ # Found the command, now get its scripts
664
+ script_index=0
665
+ while true; do
666
+ script=$(yaml_get_nested "$TEMP_DIR/package.yaml" "contents.commands[$cmd_index].scripts[$script_index]" 2>/dev/null || echo "")
667
+ if [ -z "$script" ] || [ "$script" = "null" ]; then
668
+ break
669
+ fi
670
+
671
+ # Add to required scripts (with deduplication)
672
+ already_added=false
673
+ for existing in "${REQUIRED_SCRIPTS[@]}"; do
674
+ if [ "$existing" = "$script" ]; then
675
+ already_added=true
676
+ break
677
+ fi
678
+ done
679
+
680
+ if [ "$already_added" = false ]; then
681
+ REQUIRED_SCRIPTS+=("$script")
682
+ fi
683
+
684
+ script_index=$((script_index + 1))
685
+ done
686
+ break
687
+ fi
688
+
689
+ cmd_index=$((cmd_index + 1))
690
+ done
691
+ done
692
+
693
+ echo " Found ${#REQUIRED_SCRIPTS[@]} required script(s): ${REQUIRED_SCRIPTS[@]}"
694
+
695
+ # Install required scripts and add to ALL_FILES_TO_INSTALL for batch manifest update
696
+ SCRIPT_FILES_LIST=""
697
+ if [ ${#REQUIRED_SCRIPTS[@]} -gt 0 ]; then
698
+ mkdir -p "$INSTALL_BASE_DIR/scripts"
699
+ for script in "${REQUIRED_SCRIPTS[@]}"; do
700
+ script_path="$TEMP_DIR/agent/scripts/$script"
701
+
702
+ # Check if script exists
703
+ if [ ! -f "$script_path" ]; then
704
+ echo " ${RED}✗${NC} Script not found: $script (declared in package.yaml)"
705
+ continue
706
+ fi
707
+
708
+ # Check if should install based on experimental status
709
+ if ! should_install_file "$script" "scripts"; then
710
+ continue
711
+ fi
712
+
713
+ # Copy script and make executable
714
+ cp "$script_path" "$INSTALL_BASE_DIR/scripts/$script"
715
+ chmod +x "$INSTALL_BASE_DIR/scripts/$script"
716
+
717
+ # Get file version and store metadata
718
+ FILE_VERSION=$(get_file_version "$TEMP_DIR/package.yaml" "scripts" "$script")
719
+
720
+ # Check experimental status
721
+ is_experimental=""
722
+ if [ -f "$TEMP_DIR/package.yaml" ]; then
723
+ is_experimental=$(grep -A 1000 "^ scripts:" "$TEMP_DIR/package.yaml" 2>/dev/null | grep -A 2 "name: ${script}" | grep "^ *experimental: true" | grep -v "^[[:space:]]*#" | head -1)
724
+ fi
725
+ FILE_METADATA["scripts/$script"]="$FILE_VERSION|$is_experimental"
726
+
727
+ # Track for batch processing
728
+ if [ -n "$SCRIPT_FILES_LIST" ]; then
729
+ SCRIPT_FILES_LIST="$SCRIPT_FILES_LIST $script_path"
730
+ else
731
+ SCRIPT_FILES_LIST="$script_path"
732
+ fi
733
+ done
734
+ fi
735
+
736
+ # Update ALL_FILES_TO_INSTALL with resolved scripts
737
+ if [ -n "$SCRIPT_FILES_LIST" ]; then
738
+ ALL_FILES_TO_INSTALL["scripts"]="$SCRIPT_FILES_LIST"
739
+ fi
740
+ echo ""
741
+ elif [ -d "$TEMP_DIR/agent/scripts" ] && [ -n "${ALL_FILES_TO_INSTALL[scripts]+x}" ]; then
742
+ # Scripts were collected during scan but no package.yaml script-command binding
743
+ # Install all scripts that passed validation (backward compatibility)
744
+ for file in ${ALL_FILES_TO_INSTALL[scripts]}; do
745
+ filename=$(basename "$file")
746
+ mkdir -p "$INSTALL_BASE_DIR/scripts"
747
+ cp "$file" "$INSTALL_BASE_DIR/scripts/$filename"
748
+ chmod +x "$INSTALL_BASE_DIR/scripts/$filename"
749
+ done
750
+ fi
751
+
435
752
  # ============================================================================
436
753
  # OPTIMIZATION: Batch checksum calculation
437
754
  # ============================================================================
@@ -445,7 +762,14 @@ for dir in "${!ALL_FILES_TO_INSTALL[@]}"; do
445
762
  for file in ${ALL_FILES_TO_INSTALL[$dir]}; do
446
763
  if [ "$dir" = "files" ]; then
447
764
  rel_path="${file#$SOURCE_DIR/}"
448
- ALL_INSTALLED_FILES+=("./$rel_path")
765
+ if [ "$HAS_FILE_METADATA" = true ]; then
766
+ _file_target="${FILE_TARGETS[files/$rel_path]:-./}"
767
+ _bname=$(basename "$rel_path")
768
+ _bname="${_bname%.template}"
769
+ ALL_INSTALLED_FILES+=("${_file_target}${_bname}")
770
+ else
771
+ ALL_INSTALLED_FILES+=("./$rel_path")
772
+ fi
449
773
  else
450
774
  filename=$(basename "$file")
451
775
  ALL_INSTALLED_FILES+=("$INSTALL_BASE_DIR/$dir/$filename")
@@ -460,7 +784,7 @@ if [ ${#ALL_INSTALLED_FILES[@]} -gt 0 ]; then
460
784
  checksum=$(echo "$line" | awk '{print $1}')
461
785
  filepath=$(echo "$line" | awk '{$1=""; print substr($0,2)}')
462
786
  CHECKSUMS["$filepath"]="$checksum"
463
- done < <(sha256sum "${ALL_INSTALLED_FILES[@]}" 2>/dev/null)
787
+ done < <(if command -v sha256sum >/dev/null 2>&1; then sha256sum "${ALL_INSTALLED_FILES[@]}" 2>/dev/null; elif command -v shasum >/dev/null 2>&1; then shasum -a 256 "${ALL_INSTALLED_FILES[@]}" 2>/dev/null; fi)
464
788
  fi
465
789
 
466
790
  # ============================================================================
@@ -482,7 +806,14 @@ for dir in "${!ALL_FILES_TO_INSTALL[@]}"; do
482
806
  # Determine filename and installed filepath based on dir type
483
807
  if [ "$dir" = "files" ]; then
484
808
  filename="${file#$SOURCE_DIR/}"
485
- filepath="./$filename"
809
+ if [ "$HAS_FILE_METADATA" = true ]; then
810
+ _file_target="${FILE_TARGETS[files/$filename]:-./}"
811
+ _bname=$(basename "$filename")
812
+ _bname="${_bname%.template}"
813
+ filepath="${_file_target}${_bname}"
814
+ else
815
+ filepath="./$filename"
816
+ fi
486
817
  else
487
818
  filename=$(basename "$file")
488
819
  filepath="$INSTALL_BASE_DIR/$dir/$filename"
@@ -506,10 +837,29 @@ for dir in "${!ALL_FILES_TO_INSTALL[@]}"; do
506
837
  yaml_object_set "$obj_node" "experimental" "true" >/dev/null
507
838
  fi
508
839
 
840
+ # For files with metadata: store target path and variables
841
+ if [ "$dir" = "files" ] && [ "$HAS_FILE_METADATA" = true ]; then
842
+ yaml_object_set "$obj_node" "target" "$filepath" >/dev/null
843
+ # Store variable values if this file had variables
844
+ _file_vars_manifest="${FILE_VARS[files/$filename]:-}"
845
+ if [ -n "$_file_vars_manifest" ]; then
846
+ # Create nested map node for variables
847
+ _vars_node=$(create_node "map" "variables" "" "$obj_node")
848
+ add_child "$obj_node" "$_vars_node"
849
+ IFS=',' read -ra _var_names <<< "$_file_vars_manifest"
850
+ for _vname in "${_var_names[@]}"; do
851
+ _vval="${COLLECTED_VARS[$_vname]:-}"
852
+ if [ -n "$_vval" ]; then
853
+ yaml_object_set "$_vars_node" "$_vname" "$_vval" >/dev/null
854
+ fi
855
+ done
856
+ fi
857
+ fi
858
+
509
859
  if [ "$dir" = "scripts" ]; then
510
860
  echo " ${GREEN}✓${NC} Installed $dir/$filename (v$file_version) [executable]"
511
861
  elif [ "$dir" = "files" ]; then
512
- echo " ${GREEN}✓${NC} Installed $filename → ./$filename"
862
+ echo " ${GREEN}✓${NC} Installed $filename → $filepath"
513
863
  else
514
864
  echo " ${GREEN}✓${NC} Installed $dir/$filename (v$file_version)"
515
865
  fi