repo-nexus 0.7.16 → 0.7.18

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 (37) hide show
  1. package/lib/ai.sh +83 -145
  2. package/lib/commands/init.sh +50 -36
  3. package/lib/commands/plugin.sh +62 -41
  4. package/lib/commands/status.sh +23 -13
  5. package/lib/commands/sync.sh +2 -40
  6. package/lib/hooks.sh +6 -6
  7. package/lib/plugins.sh +91 -31
  8. package/lib/yaml.sh +53 -50
  9. package/package.json +1 -1
  10. package/toolkit/plugins/claude/hooks/after_init.sh +14 -0
  11. package/toolkit/plugins/claude/hooks/after_sync.sh +62 -0
  12. package/toolkit/plugins/claude/hooks/before_plugin_disable.sh +14 -0
  13. package/toolkit/plugins/claude/plugin.yaml +10 -0
  14. package/toolkit/plugins/copilot/hooks/after_init.sh +14 -0
  15. package/toolkit/plugins/copilot/hooks/after_sync.sh +22 -0
  16. package/toolkit/plugins/copilot/hooks/before_plugin_disable.sh +15 -0
  17. package/toolkit/plugins/copilot/plugin.yaml +10 -0
  18. package/toolkit/plugins/cursor/hooks/after_init.sh +14 -0
  19. package/toolkit/plugins/cursor/hooks/after_sync.sh +44 -0
  20. package/toolkit/plugins/cursor/hooks/before_plugin_disable.sh +17 -0
  21. package/toolkit/plugins/cursor/plugin.yaml +10 -0
  22. package/toolkit/plugins/gemini/hooks/after_init.sh +14 -0
  23. package/toolkit/plugins/gemini/hooks/after_sync.sh +33 -0
  24. package/toolkit/plugins/gemini/hooks/before_plugin_disable.sh +15 -0
  25. package/toolkit/plugins/gemini/plugin.yaml +10 -0
  26. package/toolkit/plugins/karpathy-llm/README.md +10 -39
  27. package/toolkit/plugins/karpathy-llm/hooks/after_sync.sh +134 -31
  28. package/toolkit/plugins/karpathy-llm/hooks/git_post-commit.sh +9 -7
  29. package/toolkit/plugins/karpathy-llm/hooks/git_post-merge.sh +8 -6
  30. package/toolkit/plugins/karpathy-llm/hooks/git_pre-commit.sh +8 -6
  31. package/toolkit/plugins/karpathy-llm/hooks/git_pre-push.sh +8 -6
  32. package/toolkit/plugins/karpathy-llm/plugin.yaml +6 -0
  33. package/toolkit/plugins/karpathy-llm/templates/_log.md +1 -1
  34. package/toolkit/plugins/windsurf/hooks/after_init.sh +14 -0
  35. package/toolkit/plugins/windsurf/hooks/after_sync.sh +21 -0
  36. package/toolkit/plugins/windsurf/hooks/before_plugin_disable.sh +15 -0
  37. package/toolkit/plugins/windsurf/plugin.yaml +10 -0
package/lib/ai.sh CHANGED
@@ -130,17 +130,36 @@ BASE_AI_EOF
130
130
  [ -n "$_p" ] || continue
131
131
  yaml_plugin_enabled "$_p" 2>/dev/null || continue
132
132
  _pdir="$(find_plugin_dir "$_p" 2>/dev/null || true)"
133
- _p_instr=""
134
- if [ -f "$_dir/.rnex/plugins/$_p/instructions.md" ]; then
135
- _p_instr="$_dir/.rnex/plugins/$_p/instructions.md"
136
- elif [ -n "$_pdir" ] && [ -f "$_pdir/instructions.md" ]; then
137
- _p_instr="$_pdir/instructions.md"
133
+ _p_instr_files=""
134
+ if [ -n "$_pdir" ] && [ -f "$_pdir/plugin.yaml" ]; then
135
+ for _item in $(plugin_list_instructions "$_pdir" 2>/dev/null || true); do
136
+ [ -n "$_item" ] || continue
137
+ _src="${_item%%:*}"
138
+ _src="$(printf '%s' "$_src" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')"
139
+ _found=""
140
+ if [ -f "$_dir/.rnex/plugins/$_p/$_src" ]; then
141
+ _found="$_dir/.rnex/plugins/$_p/$_src"
142
+ elif [ -f "$_pdir/$_src" ]; then
143
+ _found="$_pdir/$_src"
144
+ fi
145
+ if [ -n "$_found" ]; then
146
+ _p_instr_files="${_p_instr_files:+$_p_instr_files }$_found"
147
+ fi
148
+ done
138
149
  fi
139
150
 
140
- if [ -n "$_p_instr" ]; then
141
- printf '\n\n' >> "$_out_tmp"
142
- cat "$_p_instr" >> "$_out_tmp"
151
+ if [ -z "$_p_instr_files" ]; then
152
+ if [ -f "$_dir/.rnex/plugins/$_p/instructions.md" ]; then
153
+ _p_instr_files="$_dir/.rnex/plugins/$_p/instructions.md"
154
+ elif [ -n "$_pdir" ] && [ -f "$_pdir/instructions.md" ]; then
155
+ _p_instr_files="$_pdir/instructions.md"
156
+ fi
143
157
  fi
158
+
159
+ for _f in $_p_instr_files; do
160
+ printf '\n\n' >> "$_out_tmp"
161
+ cat "$_f" >> "$_out_tmp"
162
+ done
144
163
  done
145
164
 
146
165
  mv "$_out_tmp" "$_ai_file"
@@ -371,21 +390,32 @@ write_rnex_env_script() {
371
390
 
372
391
  get_active_provider_skill_dirs() {
373
392
  _dirs=""
374
- if provider_option_enabled "copilot" "skills" 2>/dev/null; then
375
- _dirs="${_dirs:+$_dirs }.github/skills"
376
- fi
377
- if provider_option_enabled "cursor" "skills" 2>/dev/null; then
378
- _dirs="${_dirs:+$_dirs }.cursor/rules"
379
- fi
380
- if provider_option_enabled "claude" "skills" 2>/dev/null; then
381
- _dirs="${_dirs:+$_dirs }.claude/skills"
382
- fi
383
- if provider_option_enabled "windsurf" "skills" 2>/dev/null; then
384
- _dirs="${_dirs:+$_dirs }.windsurf/rules"
385
- fi
386
- if provider_option_enabled "gemini" "skills" 2>/dev/null; then
387
- _dirs="${_dirs:+$_dirs }.gemini/skills"
388
- fi
393
+ for _p in $(yaml_list_plugins 2>/dev/null || true); do
394
+ [ -n "$_p" ] || continue
395
+ yaml_plugin_enabled "$_p" 2>/dev/null || continue
396
+ if provider_option_enabled "$_p" "skills" 2>/dev/null; then
397
+ _pdir="$(find_plugin_dir "$_p" 2>/dev/null || true)"
398
+ _sdir=""
399
+ if [ -n "$_pdir" ]; then
400
+ _sdir="$(plugin_get_field "$_pdir" "skill_dir" 2>/dev/null || true)"
401
+ fi
402
+ if [ -z "$_sdir" ]; then
403
+ case "$_p" in
404
+ copilot) _sdir=".github/skills" ;;
405
+ cursor) _sdir=".cursor/rules" ;;
406
+ claude) _sdir=".claude/skills" ;;
407
+ windsurf) _sdir=".windsurf/rules" ;;
408
+ gemini) _sdir=".gemini/skills" ;;
409
+ esac
410
+ fi
411
+ if [ -n "$_sdir" ]; then
412
+ case " $_dirs " in
413
+ *" $_sdir "*) ;;
414
+ *) _dirs="${_dirs:+$_dirs }$_sdir" ;;
415
+ esac
416
+ fi
417
+ fi
418
+ done
389
419
  printf '%s' "$_dirs"
390
420
  }
391
421
 
@@ -450,7 +480,6 @@ clean_provider_agents() {
450
480
  _ag_dir="$1"
451
481
  _ext="${2:-.md}"
452
482
  [ -d "$_ag_dir" ] || return 0
453
- rm -f "$_ag_dir/wiki-curator${_ext}" 2>/dev/null || true
454
483
 
455
484
  for _pdir in "$SCRIPT_LOCATION/toolkit/plugins"/* "$NEXUS_DIR/toolkit/plugins"/* "$NEXUS_DIR/.rnex/plugins"/*; do
456
485
  [ -d "$_pdir" ] || continue
@@ -493,14 +522,20 @@ sync_rnex_base_assets() {
493
522
  cp -f "$_src_toolkit"/skills/*.md "$NEXUS_DIR/.rnex/skills/" 2>/dev/null || true
494
523
  fi
495
524
 
496
- # Purge any plugin-specific skills from .rnex/skills/
497
- rm -f "$NEXUS_DIR/.rnex/skills/wiki-ingest.md" "$NEXUS_DIR/.rnex/skills/wiki-lint.md" \
498
- "$NEXUS_DIR/.rnex/skills/rnex-wiki-ingest.md" "$NEXUS_DIR/.rnex/skills/rnex-wiki-lint.md" 2>/dev/null || true
499
-
500
- # Purge stale unprefixed wiki user skills across provider directories
501
- for _p_dir in $(get_active_provider_skill_dirs 2>/dev/null || true); do
502
- rm -f "$NEXUS_DIR/$_p_dir/rnex-wiki-ingest.md" "$NEXUS_DIR/$_p_dir/rnex-wiki-lint.md" 2>/dev/null || true
503
- rm -rf "$NEXUS_DIR/$_p_dir/rnex-wiki-ingest" "$NEXUS_DIR/$_p_dir/rnex-wiki-lint" 2>/dev/null || true
525
+ # Purge any plugin-declared skills that may have been placed in core .rnex/skills/
526
+ for _pdir in $(list_all_available_plugin_dirs 2>/dev/null || true); do
527
+ if [ -d "$_pdir/skills" ]; then
528
+ for _psk in "$_pdir/skills"/*.md; do
529
+ [ -f "$_psk" ] || continue
530
+ _sbase="$(basename "$_psk")"
531
+ _sname="$(basename "$_psk" .md)"
532
+ rm -f "$NEXUS_DIR/.rnex/skills/$_sbase" "$NEXUS_DIR/.rnex/skills/rnex-$_sbase" 2>/dev/null || true
533
+ for _p_dir in $(get_active_provider_skill_dirs 2>/dev/null || true); do
534
+ rm -f "$NEXUS_DIR/$_p_dir/rnex-$_sbase" 2>/dev/null || true
535
+ rm -rf "$NEXUS_DIR/$_p_dir/rnex-$_sname" 2>/dev/null || true
536
+ done
537
+ done
538
+ fi
504
539
  done
505
540
 
506
541
  # Register core User Skills across active provider directories
@@ -512,148 +547,51 @@ sync_rnex_base_assets() {
512
547
  }
513
548
 
514
549
  sync_claude() {
515
- if claude_plugin_enabled; then
516
- if claude_instructions_enabled; then
517
- merge_rnex_instructions "$NEXUS_DIR/CLAUDE.md"
518
- else
519
- clean_rnex_instructions "$NEXUS_DIR/CLAUDE.md"
520
- fi
521
- if ! claude_skills_enabled; then
522
- clean_provider_user_skills "$NEXUS_DIR/.claude/skills"
523
- fi
550
+ if yaml_plugin_enabled "claude" 2>/dev/null; then
551
+ run_plugin_hook "claude" "after_sync"
524
552
  else
525
- clean_rnex_instructions "$NEXUS_DIR/CLAUDE.md"
526
- clean_provider_user_skills "$NEXUS_DIR/.claude/skills"
553
+ run_plugin_hook "claude" "before_plugin_disable"
527
554
  fi
528
- rm -rf "$NEXUS_DIR/.claude/commands" "$NEXUS_DIR/.claude/prompts" 2>/dev/null || true
529
- rmdir "$NEXUS_DIR/.claude" 2>/dev/null || true
530
555
  }
531
556
 
532
557
  sync_gemini() {
533
- if gemini_plugin_enabled; then
534
- _gi="$NEXUS_DIR/.geminiignore"
535
- if [ ! -f "$_gi" ]; then
536
- cat > "$_gi" <<GI_EOF
537
- # Gemini CLI file discovery un-ignore for member repos
538
- !repos/*
539
- GI_EOF
540
- log_ok "Created .geminiignore (enabling Gemini CLI file discovery for member repos)"
541
- elif ! grep -q '!repos/\*' "$_gi" 2>/dev/null; then
542
- printf '\n# Gemini CLI file discovery un-ignore for member repos\n!repos/*\n' >> "$_gi"
543
- log_dim "Added !repos/* to .geminiignore"
544
- fi
545
-
546
- if gemini_instructions_enabled; then
547
- merge_rnex_instructions "$NEXUS_DIR/GEMINI.md"
548
- else
549
- clean_rnex_instructions "$NEXUS_DIR/GEMINI.md"
550
- fi
551
- if ! gemini_skills_enabled; then
552
- clean_provider_user_skills "$NEXUS_DIR/.gemini/skills"
553
- fi
558
+ if yaml_plugin_enabled "gemini" 2>/dev/null; then
559
+ run_plugin_hook "gemini" "after_sync"
554
560
  else
555
- clean_provider_ignore "$NEXUS_DIR/.geminiignore"
556
- clean_rnex_instructions "$NEXUS_DIR/GEMINI.md"
557
- clean_provider_user_skills "$NEXUS_DIR/.gemini/skills"
561
+ run_plugin_hook "gemini" "before_plugin_disable"
558
562
  fi
559
- rm -rf "$NEXUS_DIR/.gemini/prompts" 2>/dev/null || true
560
- rmdir "$NEXUS_DIR/.gemini" 2>/dev/null || true
561
563
  }
562
564
 
563
565
  sync_cursor() {
564
- if cursor_plugin_enabled; then
565
- _ci="$NEXUS_DIR/.cursorignore"
566
- if [ ! -f "$_ci" ]; then
567
- cat > "$_ci" <<CI_EOF
568
- # Cursor codebase indexing un-ignore for member repos
569
- !repos/*
570
- CI_EOF
571
- log_ok "Created .cursorignore (enabling Cursor indexing for member repos)"
572
- elif ! grep -q '!repos/\*' "$_ci" 2>/dev/null; then
573
- printf '\n# Cursor codebase indexing un-ignore for member repos\n!repos/*\n' >> "$_ci"
574
- log_dim "Added !repos/* to .cursorignore"
575
- fi
576
-
577
- if cursor_instructions_enabled; then
578
- merge_rnex_instructions "$NEXUS_DIR/.cursorrules"
579
- mkdir -p "$NEXUS_DIR/.cursor/rules"
580
- _c_body="$(mktemp)"
581
- get_rnex_instructions > "$_c_body"
582
- {
583
- printf -- '---\ndescription: Repo Nexus Virtual Meta-Repo routing protocol\nglobs: *\nalwaysApply: true\n---\n\n%s\n' "$AI_MARKER_START"
584
- cat "$_c_body"
585
- printf '\n%s\n' "$AI_MARKER_END"
586
- } > "$NEXUS_DIR/.cursor/rules/rnex-context.mdc"
587
- rm -f "$_c_body"
588
- rm -f "$NEXUS_DIR/.cursor/rules/repo-nexus.mdc" 2>/dev/null || true
589
- else
590
- clean_rnex_instructions "$NEXUS_DIR/.cursorrules"
591
- rm -f "$NEXUS_DIR/.cursor/rules/rnex-context.mdc" "$NEXUS_DIR/.cursor/rules/repo-nexus.mdc" 2>/dev/null || true
592
- fi
593
- if ! cursor_skills_enabled; then
594
- clean_provider_user_skills "$NEXUS_DIR/.cursor/rules"
595
- fi
566
+ if yaml_plugin_enabled "cursor" 2>/dev/null; then
567
+ run_plugin_hook "cursor" "after_sync"
596
568
  else
597
- clean_provider_ignore "$NEXUS_DIR/.cursorignore"
598
- clean_rnex_instructions "$NEXUS_DIR/.cursorrules"
599
- rm -f "$NEXUS_DIR/.cursor/rules/rnex-context.mdc" "$NEXUS_DIR/.cursor/rules/repo-nexus.mdc" 2>/dev/null || true
600
- clean_provider_user_skills "$NEXUS_DIR/.cursor/rules"
569
+ run_plugin_hook "cursor" "before_plugin_disable"
601
570
  fi
602
- rm -rf "$NEXUS_DIR/.cursor/prompts" 2>/dev/null || true
603
- rmdir "$NEXUS_DIR/.cursor/rules" 2>/dev/null || true
604
- rmdir "$NEXUS_DIR/.cursor" 2>/dev/null || true
605
571
  }
606
572
 
607
573
  sync_windsurf() {
608
- if windsurf_plugin_enabled; then
609
- if windsurf_instructions_enabled; then
610
- merge_rnex_instructions "$NEXUS_DIR/.windsurfrules"
611
- else
612
- clean_rnex_instructions "$NEXUS_DIR/.windsurfrules"
613
- fi
614
- if ! windsurf_skills_enabled; then
615
- clean_provider_user_skills "$NEXUS_DIR/.windsurf/rules"
616
- fi
574
+ if yaml_plugin_enabled "windsurf" 2>/dev/null; then
575
+ run_plugin_hook "windsurf" "after_sync"
617
576
  else
618
- clean_rnex_instructions "$NEXUS_DIR/.windsurfrules"
619
- clean_provider_user_skills "$NEXUS_DIR/.windsurf/rules"
577
+ run_plugin_hook "windsurf" "before_plugin_disable"
620
578
  fi
621
- rm -rf "$NEXUS_DIR/.windsurf/prompts" 2>/dev/null || true
622
- rmdir "$NEXUS_DIR/.windsurf/rules" 2>/dev/null || true
623
- rmdir "$NEXUS_DIR/.windsurf" 2>/dev/null || true
624
579
  }
625
580
 
626
581
  sync_copilot() {
627
- if copilot_plugin_enabled; then
628
- if copilot_instructions_enabled; then
629
- mkdir -p "$NEXUS_DIR/.github"
630
- merge_rnex_instructions "$NEXUS_DIR/.github/copilot-instructions.md"
631
- else
632
- clean_rnex_instructions "$NEXUS_DIR/.github/copilot-instructions.md"
633
- fi
634
- if ! copilot_skills_enabled; then
635
- clean_provider_user_skills "$NEXUS_DIR/.github/skills"
636
- fi
582
+ if yaml_plugin_enabled "copilot" 2>/dev/null; then
583
+ run_plugin_hook "copilot" "after_sync"
637
584
  else
638
- clean_rnex_instructions "$NEXUS_DIR/.github/copilot-instructions.md"
639
- clean_provider_user_skills "$NEXUS_DIR/.github/skills"
585
+ run_plugin_hook "copilot" "before_plugin_disable"
640
586
  fi
641
- clean_provider_agents "$NEXUS_DIR/.github/agents" ".agent.md"
642
- rm -rf "$NEXUS_DIR/.github/prompts" 2>/dev/null || true
643
- rmdir "$NEXUS_DIR/.github" 2>/dev/null || true
644
587
  }
645
588
 
646
589
  sync_ai_providers() {
647
- sync_copilot
648
- sync_claude
649
- sync_gemini
650
- sync_cursor
651
- sync_windsurf
590
+ :
652
591
  }
653
592
 
654
593
  sync_skills() {
655
594
  write_rnex_env_script "$NEXUS_DIR"
656
595
  sync_rnex_base_assets
657
- sync_ai_providers
658
596
  }
659
597
 
@@ -10,30 +10,29 @@ cmd_init() {
10
10
  _cli_code_workspace=""
11
11
  _cli_hooks=""
12
12
  _cli_no_hooks=0
13
- _cli_copilot=""
14
- _cli_claude=""
15
- _cli_gemini=""
16
- _cli_cursor=""
17
- _cli_windsurf=""
18
- _cli_karpathy=""
13
+ _cli_plugins=""
19
14
 
20
15
  while [ $# -gt 0 ]; do
21
16
  case "$1" in
22
17
  -y|--yes) _non_interactive=1 ;;
23
18
  --hooks) _cli_hooks="true" ;;
24
19
  --no-hooks) _cli_no_hooks=1; _cli_hooks="false" ;;
25
- --copilot) _cli_copilot="true" ;;
26
- --no-copilot) _cli_copilot="false" ;;
27
- --claude) _cli_claude="true" ;;
28
- --no-claude) _cli_claude="false" ;;
29
- --gemini) _cli_gemini="true" ;;
30
- --no-gemini) _cli_gemini="false" ;;
31
- --cursor) _cli_cursor="true" ;;
32
- --no-cursor) _cli_cursor="false" ;;
33
- --windsurf) _cli_windsurf="true" ;;
34
- --no-windsurf) _cli_windsurf="false" ;;
35
- --karpathy|--karpathy-llm) _cli_karpathy="true" ;;
36
- --no-karpathy|--no-karpathy-llm) _cli_karpathy="false" ;;
20
+ --copilot) _cli_plugins="${_cli_plugins:+$_cli_plugins }copilot" ;;
21
+ --no-copilot) _cli_plugins="$(printf '%s' " $_cli_plugins " | sed -e 's/ copilot / /g' -e 's/^ *//;s/ *$//')" ;;
22
+ --claude) _cli_plugins="${_cli_plugins:+$_cli_plugins }claude" ;;
23
+ --no-claude) _cli_plugins="$(printf '%s' " $_cli_plugins " | sed -e 's/ claude / /g' -e 's/^ *//;s/ *$//')" ;;
24
+ --gemini) _cli_plugins="${_cli_plugins:+$_cli_plugins }gemini" ;;
25
+ --no-gemini) _cli_plugins="$(printf '%s' " $_cli_plugins " | sed -e 's/ gemini / /g' -e 's/^ *//;s/ *$//')" ;;
26
+ --cursor) _cli_plugins="${_cli_plugins:+$_cli_plugins }cursor" ;;
27
+ --no-cursor) _cli_plugins="$(printf '%s' " $_cli_plugins " | sed -e 's/ cursor / /g' -e 's/^ *//;s/ *$//')" ;;
28
+ --windsurf) _cli_plugins="${_cli_plugins:+$_cli_plugins }windsurf" ;;
29
+ --no-windsurf) _cli_plugins="$(printf '%s' " $_cli_plugins " | sed -e 's/ windsurf / /g' -e 's/^ *//;s/ *$//')" ;;
30
+ --karpathy|--karpathy-llm) _cli_plugins="${_cli_plugins:+$_cli_plugins }karpathy-llm" ;;
31
+ --no-karpathy|--no-karpathy-llm) _cli_plugins="$(printf '%s' " $_cli_plugins " | sed -e 's/ karpathy-llm / /g' -e 's/^ *//;s/ *$//')" ;;
32
+ --plugin|--enable-plugin)
33
+ shift
34
+ [ -n "$1" ] && _cli_plugins="${_cli_plugins:+$_cli_plugins }$1"
35
+ ;;
37
36
  --ai|--instructions|--ai-instructions)
38
37
  shift
39
38
  # Legacy flag ignored in favor of plugins
@@ -85,7 +84,7 @@ cmd_init() {
85
84
  elif [ "$_cli_hooks" = "true" ] || [ "$_non_interactive" -eq 1 ]; then
86
85
  _init_git_hooks="true"
87
86
  elif [ -t 0 ]; then
88
- printf '%bConfigure automated Git hooks (post-merge auto-sync & wiki triggers)? [Y/n]: %b' "$_BOLD" "$_NC"
87
+ printf '%bConfigure automated Git hooks (post-merge auto-sync & plugin triggers)? [Y/n]: %b' "$_BOLD" "$_NC"
89
88
  read -r _h_ans
90
89
  case "$_h_ans" in
91
90
  n*|N*) _init_git_hooks="false" ;;
@@ -104,23 +103,37 @@ cmd_init() {
104
103
  log_dim " rnex sync"
105
104
  else
106
105
  _init_plugins=""
107
- if [ "$_cli_copilot" = "true" ]; then
108
- _init_plugins="${_init_plugins} copilot:\n instructions: true\n skills: true\n"
109
- fi
110
- if [ "$_cli_claude" = "true" ]; then
111
- _init_plugins="${_init_plugins} claude:\n instructions: true\n skills: true\n"
112
- fi
113
- if [ "$_cli_gemini" = "true" ]; then
114
- _init_plugins="${_init_plugins} gemini:\n instructions: true\n skills: true\n"
115
- fi
116
- if [ "$_cli_cursor" = "true" ]; then
117
- _init_plugins="${_init_plugins} cursor:\n instructions: true\n skills: true\n"
118
- fi
119
- if [ "$_cli_windsurf" = "true" ]; then
120
- _init_plugins="${_init_plugins} windsurf:\n instructions: true\n skills: true\n"
121
- fi
122
- if [ "$_cli_karpathy" = "true" ]; then
123
- _init_plugins="${_init_plugins} karpathy-llm:\n raw_dir: \"./docs\"\n wiki_dir: \".rnex/wiki\"\n lint_trigger: \"manual\"\n ingest_member_readmes: true\n"
106
+ for _p in $_cli_plugins; do
107
+ _pdir="$(find_plugin_dir "$_p" 2>/dev/null || true)"
108
+ _cfg_lines=""
109
+ if [ -n "$_pdir" ] && [ -f "$_pdir/plugin.yaml" ]; then
110
+ _cfg_lines="$(awk '
111
+ /^config_defaults:[ ]*$/ || /^config_defaults:[ ]*#/ { in_c=1; next }
112
+ in_c && /^[^ #]/ { exit }
113
+ in_c && /^[ ]*[a-zA-Z0-9_.-]+:[ ]*/ {
114
+ line = $0
115
+ sub(/^[ ]*/, " ", line)
116
+ print line
117
+ }
118
+ ' "$_pdir/plugin.yaml")"
119
+ fi
120
+ if [ -n "$_cfg_lines" ]; then
121
+ _init_plugins="${_init_plugins} ${_p}:\n${_cfg_lines}\n"
122
+ else
123
+ _init_plugins="${_init_plugins} ${_p}: {}\n"
124
+ fi
125
+ done
126
+
127
+ _existing_repos=""
128
+ if [ -d "$_init_dir/repos" ]; then
129
+ for _d in "$_init_dir/repos"/*; do
130
+ [ -d "$_d" ] || continue
131
+ _r="$(basename "$_d")"
132
+ case "$_r" in
133
+ .*) continue ;;
134
+ *) _existing_repos="${_existing_repos} ${_r}:\n" ;;
135
+ esac
136
+ done
124
137
  fi
125
138
 
126
139
  cat > "$_target_yaml" <<YAML_EOF
@@ -143,6 +156,7 @@ plugins:
143
156
  $(printf '%b' "$_init_plugins")
144
157
  # Member repositories managed by Repo Nexus
145
158
  repos:
159
+ $(printf '%b' "$_existing_repos")
146
160
  YAML_EOF
147
161
  _config_created=1
148
162
  log_ok "Created $RNEX_CONFIG_FILE"
@@ -66,31 +66,60 @@ cmd_plugin_info() {
66
66
  [ -n "$_home" ] && printf '%bHomepage:%b %s\n' "$_BOLD" "$_NC" "$_home"
67
67
  printf '%bLocation:%b %s\n' "$_BOLD" "$_NC" "$_pdir"
68
68
 
69
- case "$_name" in
70
- copilot|claude|gemini|cursor|windsurf)
71
- _skill_target=""
72
- case "$_name" in
73
- copilot) _skill_target=".github/skills/" ;;
74
- claude) _skill_target=".claude/skills/" ;;
75
- gemini) _skill_target=".gemini/skills/" ;;
76
- cursor) _skill_target=".cursor/rules/" ;;
77
- windsurf) _skill_target=".windsurf/rules/" ;;
78
- esac
79
- printf '\n%bConfigurable Options (in rnex.yaml or .local.rnex.yaml):%b\n' "$_BOLD" "$_NC"
80
- printf ' - instructions: true|false (maintain provider instructions; current: %s)\n' "$(provider_option_enabled "$_name" "instructions" && echo "true" || echo "false")"
81
- printf ' - skills: true|false (generate user skills in %s; current: %s)\n' "$_skill_target" "$(provider_option_enabled "$_name" "skills" && echo "true" || echo "false")"
82
- ;;
69
+ _skill_target="$(plugin_get_field "$_pdir" "skill_dir" 2>/dev/null || true)"
70
+ [ -z "$_skill_target" ] && case "$_name" in
71
+ copilot) _skill_target=".github/skills/" ;;
72
+ claude) _skill_target=".claude/skills/" ;;
73
+ gemini) _skill_target=".gemini/skills/" ;;
74
+ cursor) _skill_target=".cursor/rules/" ;;
75
+ windsurf) _skill_target=".windsurf/rules/" ;;
76
+ esac
77
+ case "$_skill_target" in
78
+ */|"") ;;
79
+ *) _skill_target="${_skill_target}/" ;;
83
80
  esac
84
- if [ "$_name" = "karpathy-llm" ]; then
85
- _cur_lt="$(yaml_plugin_get "karpathy-llm" "lint_trigger" 2>/dev/null || true)"
86
- _cur_nc="$(yaml_plugin_get "karpathy-llm" "notify_on_changes" 2>/dev/null || true)"
87
- _cur_imr="$(yaml_plugin_get "karpathy-llm" "ingest_member_readmes" 2>/dev/null || true)"
88
- printf '\n%bConfigurable Options (in rnex.yaml or .local.rnex.yaml):%b\n' "$_BOLD" "$_NC"
89
- printf ' - wiki_dir: <dir> (directory for curated markdown pages; current: %s)\n' "$(karpathy_wiki_dir)"
90
- printf ' - raw_dir: <dir> (directory for unmodified intake documents; current: %s)\n' "$(karpathy_raw_dir)"
91
- printf ' - lint_trigger: git-post-merge|git-post-commit|git-pre-commit|git-pre-push|manual (current: %s)\n' "${_cur_lt:-manual}"
92
- printf ' - notify_on_changes: true|false (show notification when raw/wiki changes are committed; current: %s)\n' "${_cur_nc:-true}"
93
- printf ' - ingest_member_readmes: true|false (automatically track member repository READMEs in wiki; current: %s)\n' "${_cur_imr:-true}"
81
+
82
+ if [ -f "$_pdir/plugin.yaml" ]; then
83
+ _cfg_lines="$(awk '
84
+ /^config_descriptions:[ ]*$/ || /^config_descriptions:[ ]*#/ { in_d=1; next }
85
+ in_d && /^[^ #]/ { exit }
86
+ in_d && /^[ ]*[a-zA-Z0-9_.-]+:[ ]*/ {
87
+ print $0
88
+ }
89
+ ' "$_pdir/plugin.yaml")"
90
+
91
+ if [ -n "$_cfg_lines" ]; then
92
+ printf '\n%bConfigurable Options (in rnex.yaml or .local.rnex.yaml):%b\n' "$_BOLD" "$_NC"
93
+ echo "$_cfg_lines" | while read -r _cline; do
94
+ [ -n "$_cline" ] || continue
95
+ _opt="${_cline%%:*}"
96
+ _opt="$(printf '%s' "$_opt" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')"
97
+ _desc="${_cline#*:}"
98
+ _desc="$(printf '%s' "$_desc" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' -e 's/^"//' -e 's/"$//')"
99
+ [ -n "$_skill_target" ] && _desc="$(printf '%s' "$_desc" | sed -e "s|{skill_dir}|$_skill_target|g")"
100
+
101
+ _cur="$(yaml_plugin_get "$_name" "$_opt" 2>/dev/null || true)"
102
+ if [ -z "$_cur" ]; then
103
+ _cur="$(awk -v k="$_opt" '
104
+ /^config_defaults:[ ]*$/ || /^config_defaults:[ ]*#/ { in_c=1; next }
105
+ in_c && /^[^ #]/ { exit }
106
+ in_c && $0 ~ ("^[ ]*" k ":") {
107
+ sub("^[ ]*" k ":[ ]*", "")
108
+ sub(/[ ]*#.*/, "")
109
+ gsub(/^["\047]|["\047]$/, "")
110
+ print
111
+ exit
112
+ }
113
+ ' "$_pdir/plugin.yaml")"
114
+ fi
115
+ case "$_opt" in
116
+ *_dir|dir)
117
+ _cur="$(printf '%s' "$_cur" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' -e 's#^\./##' -e 's#^/##' -e 's#/$##')"
118
+ ;;
119
+ esac
120
+ printf ' - %s: %s (current: %s)\n' "$_opt" "$_desc" "${_cur:-default}"
121
+ done
122
+ fi
94
123
  fi
95
124
 
96
125
  printf '\n%bScoped Directory:%b .rnex/plugins/%s\n' "$_BOLD" "$_NC" "$_name"
@@ -104,15 +133,7 @@ cmd_plugin_info() {
104
133
  _d="${_item#*:}"
105
134
  _s="$(printf '%s' "$_s" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')"
106
135
  _d="$(printf '%s' "$_d" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')"
107
- if [ "$_name" = "karpathy-llm" ]; then
108
- _wiki_d="$(karpathy_wiki_dir)"
109
- _raw_d="$(karpathy_raw_dir)"
110
- _d="$(printf '%s' "$_d" | sed \
111
- -e "s|{wiki_dir}|$_wiki_d|g" \
112
- -e "s|{raw_dir}|$_raw_d|g" \
113
- -e "s|^wiki/|${_wiki_d}/|" \
114
- -e "s|^raw/|${_raw_d}/|")"
115
- fi
136
+ _d="$(plugin_resolve_placeholders "$_name" "$_pdir" "$_d")"
116
137
  printf ' - %s → %s\n' "$_s" "$_d"
117
138
  done
118
139
  fi
@@ -252,15 +273,15 @@ cmd_rnex_dir() {
252
273
 
253
274
  cmd_plugin_update() {
254
275
  require_workspace
255
- _target_plugin="$1"
256
- if [ -n "$_target_plugin" ]; then
257
- yaml_has_plugin "$_target_plugin" || die "Plugin '$_target_plugin' is not enabled in workspace"
258
- _pdir="$(find_plugin_dir "$_target_plugin" 2>/dev/null || true)"
259
- [ -n "$_pdir" ] || die "Plugin '$_target_plugin' not found."
260
- log_info "Updating plugin '$_target_plugin'..."
276
+ _update_pname="$1"
277
+ if [ -n "$_update_pname" ]; then
278
+ yaml_has_plugin "$_update_pname" || die "Plugin '$_update_pname' is not enabled in workspace"
279
+ _pdir="$(find_plugin_dir "$_update_pname" 2>/dev/null || true)"
280
+ [ -n "$_pdir" ] || die "Plugin '$_update_pname' not found."
281
+ log_info "Updating plugin '$_update_pname'..."
261
282
  cmd_sync
262
- run_plugin_hook "$_target_plugin" "after_sync"
263
- log_ok "Plugin '$_target_plugin' updated"
283
+ run_plugin_hook "$_update_pname" "after_sync"
284
+ log_ok "Plugin '$_update_pname' updated"
264
285
  else
265
286
  log_info "Updating all enabled plugins..."
266
287
  cmd_sync
@@ -90,9 +90,15 @@ cmd_status() {
90
90
  true|yes|1)
91
91
  if [ "$_gh_active" -eq 1 ]; then
92
92
  _active_trigger=""
93
- if yaml_plugin_enabled "karpathy-llm"; then
94
- _active_trigger="$(yaml_plugin_get "karpathy-llm" "lint_trigger")"
95
- fi
93
+ for _p in $(yaml_list_plugins 2>/dev/null || true); do
94
+ yaml_plugin_enabled "$_p" 2>/dev/null || continue
95
+ _trig="$(yaml_plugin_get "$_p" "lint_trigger" 2>/dev/null || true)"
96
+ [ -z "$_trig" ] && _trig="$(yaml_plugin_get "$_p" "trigger" 2>/dev/null || true)"
97
+ if [ -n "$_trig" ] && [ "$_trig" != "manual" ]; then
98
+ _active_trigger="$_trig"
99
+ break
100
+ fi
101
+ done
96
102
  if [ -n "$_active_trigger" ]; then
97
103
  printf ' %b✓%b Git Hooks: active (git_hooks: true, .rnex/hooks — %s)\n' "$_G" "$_NC" "$_active_trigger"
98
104
  else
@@ -105,9 +111,15 @@ cmd_status() {
105
111
  *)
106
112
  if [ "$_gh_active" -eq 1 ]; then
107
113
  _active_trigger=""
108
- if yaml_plugin_enabled "karpathy-llm"; then
109
- _active_trigger="$(yaml_plugin_get "karpathy-llm" "lint_trigger")"
110
- fi
114
+ for _p in $(yaml_list_plugins 2>/dev/null || true); do
115
+ yaml_plugin_enabled "$_p" 2>/dev/null || continue
116
+ _trig="$(yaml_plugin_get "$_p" "lint_trigger" 2>/dev/null || true)"
117
+ [ -z "$_trig" ] && _trig="$(yaml_plugin_get "$_p" "trigger" 2>/dev/null || true)"
118
+ if [ -n "$_trig" ] && [ "$_trig" != "manual" ]; then
119
+ _active_trigger="$_trig"
120
+ break
121
+ fi
122
+ done
111
123
  if [ -n "$_active_trigger" ]; then
112
124
  printf ' %b✓%b Git Hooks: active (.rnex/hooks — post-merge auto-sync, %s)\n' "$_G" "$_NC" "$_active_trigger"
113
125
  else
@@ -198,13 +210,11 @@ cmd_status() {
198
210
  else
199
211
  printf ' %b●%b %-18s %b(v%s)%b %s\n' "$_G" "$_NC" "$_pname" "$_DIM" "${_ver:-0.1.0}" "$_NC" "${_desc:+- $_desc}"
200
212
  printf ' %bScoped directory: .rnex/plugins/%s%b\n' "$_DIM" "$_pname" "$_NC"
201
- case "$_pname" in
202
- copilot|claude|gemini|cursor|windsurf)
203
- printf ' %b↳ Options: instructions=%s, skills=%s%b\n' "$_DIM" \
204
- "$(provider_option_enabled "$_pname" "instructions" && echo "true" || echo "false")" \
205
- "$(provider_option_enabled "$_pname" "skills" && echo "true" || echo "false")" "$_NC"
206
- ;;
207
- esac
213
+ if [ -n "$_pdir" ] && { grep -q "instructions:" "$_pdir/plugin.yaml" 2>/dev/null || [ -n "$(yaml_plugin_get "$_pname" "instructions" 2>/dev/null)" ]; }; then
214
+ printf ' %b↳ Options: instructions=%s, skills=%s%b\n' "$_DIM" \
215
+ "$(provider_option_enabled "$_pname" "instructions" && echo "true" || echo "false")" \
216
+ "$(provider_option_enabled "$_pname" "skills" && echo "true" || echo "false")" "$_NC"
217
+ fi
208
218
  fi
209
219
  else
210
220
  printf ' %b✗%b %-18s %bMISSING%b\n' "$_R" "$_NC" "$_pname" "$_R" "$_NC"