repo-nexus 0.7.30 → 0.7.31

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.
@@ -124,12 +124,7 @@ cmd_sync() {
124
124
  if [ "$_cur_hp" != ".rnex/hooks" ] && [ "$_cur_hp" != "$NEXUS_DIR/.rnex/hooks" ]; then
125
125
  cmd_hooks_install_dir "$NEXUS_DIR"
126
126
  elif [ "${_RNEX_INSIDE_HOOK:-0}" -ne 1 ]; then
127
- _hooks_dir="$NEXUS_DIR/.rnex/hooks"
128
- _create_hook_script "$_hooks_dir/post-merge" "post-merge"
129
- _create_hook_script "$_hooks_dir/post-checkout" "post-checkout"
130
- _create_hook_script "$_hooks_dir/post-commit" "post-commit"
131
- _create_hook_script "$_hooks_dir/pre-commit" "pre-commit"
132
- _create_hook_script "$_hooks_dir/pre-push" "pre-push"
127
+ _install_managed_hook_scripts "$NEXUS_DIR/.rnex/hooks"
133
128
  fi
134
129
  fi
135
130
  ;;
@@ -137,12 +132,7 @@ cmd_sync() {
137
132
  _cur_hp="$(git -C "$NEXUS_DIR" config --get core.hooksPath 2>/dev/null || true)"
138
133
  if [ "$_cur_hp" = ".rnex/hooks" ] || [ "$_cur_hp" = "$NEXUS_DIR/.rnex/hooks" ]; then
139
134
  if [ "${_RNEX_INSIDE_HOOK:-0}" -ne 1 ]; then
140
- _hooks_dir="$NEXUS_DIR/.rnex/hooks"
141
- _create_hook_script "$_hooks_dir/post-merge" "post-merge"
142
- _create_hook_script "$_hooks_dir/post-checkout" "post-checkout"
143
- _create_hook_script "$_hooks_dir/post-commit" "post-commit"
144
- _create_hook_script "$_hooks_dir/pre-commit" "pre-commit"
145
- _create_hook_script "$_hooks_dir/pre-push" "pre-push"
135
+ _install_managed_hook_scripts "$NEXUS_DIR/.rnex/hooks"
146
136
  fi
147
137
  fi
148
138
  ;;
package/lib/hooks.sh CHANGED
@@ -4,13 +4,12 @@
4
4
  # lib/hooks.sh — Automated Git hooks management and event dispatch
5
5
  # ============================================================================
6
6
 
7
- # ---- Action Command Hook Dispatcher -----------------------------------------
7
+ RNEX_MANAGED_GIT_HOOKS="post-merge post-checkout post-commit pre-commit pre-push"
8
8
 
9
- _run_hook_internal() {
10
- _hook_target_plugin="$1"
11
- _hook_phase_cmd="$2"
12
- shift 2 2>/dev/null || true
9
+ # ---- Shared Hook Resolution & Environment Helpers --------------------------
13
10
 
11
+ # Resolve active workspace root for hook execution without mutating global state
12
+ _resolve_hook_workspace() {
14
13
  _ws="${NEXUS_DIR:-}"
15
14
  if [ -z "$_ws" ]; then
16
15
  if [ -n "$CUSTOM_CONFIG" ]; then
@@ -24,13 +23,51 @@ _run_hook_internal() {
24
23
  _ws="$(find_workspace_dir 2>/dev/null || true)"
25
24
  fi
26
25
  fi
27
- [ -z "$_ws" ] && return 0
26
+ [ -n "$_ws" ] || return 1
28
27
  _hook_root="$(cd "$_ws" 2>/dev/null && pwd -P)"
29
28
  _hook_config_merged="$_hook_root/rnex.yaml"
30
29
  if [ -n "$CUSTOM_CONFIG" ]; then
31
30
  _hook_config_merged="$(resolve_path "$CUSTOM_CONFIG" 2>/dev/null || true)"
32
31
  fi
33
- [ ! -f "$_hook_config_merged" ] && return 0
32
+ [ -f "$_hook_config_merged" ] || return 1
33
+ return 0
34
+ }
35
+
36
+ # Locate a hook script for a plugin (checks scoped .rnex/plugins/<name>/hooks first,
37
+ # then toolkit source directory). Accepts multiple candidate filenames in priority order.
38
+ _resolve_plugin_hook_script() {
39
+ _rphs_root="$1"; _rphs_plugin="$2"
40
+ shift 2
41
+ _rphs_scoped="$_rphs_root/.rnex/plugins/$_rphs_plugin/hooks"
42
+ _rphs_src=""
43
+
44
+ for _cand_name in "$@"; do
45
+ if [ -f "$_rphs_scoped/$_cand_name" ]; then
46
+ printf '%s' "$_rphs_scoped/$_cand_name"
47
+ return 0
48
+ fi
49
+ done
50
+
51
+ _rphs_src="$(find_plugin_dir "$_rphs_plugin" 2>/dev/null || true)"
52
+ if [ -n "$_rphs_src" ]; then
53
+ for _cand_name in "$@"; do
54
+ if [ -f "$_rphs_src/hooks/$_cand_name" ]; then
55
+ printf '%s' "$_rphs_src/hooks/$_cand_name"
56
+ return 0
57
+ fi
58
+ done
59
+ fi
60
+ return 1
61
+ }
62
+
63
+ # ---- Action Command Hook Dispatcher -----------------------------------------
64
+
65
+ _run_hook_internal() {
66
+ _hook_target_plugin="$1"
67
+ _hook_phase_cmd="$2"
68
+ shift 2 2>/dev/null || true
69
+
70
+ _resolve_hook_workspace || return 0
34
71
 
35
72
  _hook_cmd="${_hook_phase_cmd#*_}"
36
73
  _hook_file="${_hook_phase_cmd}.sh"
@@ -58,16 +95,13 @@ _run_hook_internal() {
58
95
  fi
59
96
 
60
97
  RNEX_PLUGIN_DIR="$_hook_root/.rnex/plugins/$_pname"
61
- _hook_script="$RNEX_PLUGIN_DIR/hooks/$_hook_file"
62
-
63
- if [ ! -f "$_hook_script" ]; then
64
- _src_pdir="$(find_plugin_dir "$_pname" 2>/dev/null || true)"
65
- if [ -n "$_src_pdir" ] && [ -f "$_src_pdir/hooks/$_hook_file" ]; then
66
- _hook_script="$_src_pdir/hooks/$_hook_file"
67
- fi
98
+ if [ "$_hook_phase_cmd" = "after_init" ]; then
99
+ _hook_script="$(_resolve_plugin_hook_script "$_hook_root" "$_pname" "after_init.sh" "after_sync.sh" || true)"
100
+ else
101
+ _hook_script="$(_resolve_plugin_hook_script "$_hook_root" "$_pname" "$_hook_file" || true)"
68
102
  fi
69
103
 
70
- if [ -f "$_hook_script" ]; then
104
+ if [ -n "$_hook_script" ] && [ -f "$_hook_script" ]; then
71
105
  chmod +x "$_hook_script" 2>/dev/null || true
72
106
  (
73
107
  RNEX_ROOT="$_hook_root"
@@ -76,7 +110,7 @@ _run_hook_internal() {
76
110
  RNEX_ARGS="$_hook_args"
77
111
  RNEX_QUIET="$_hook_quiet"
78
112
  RNEX_PROVIDER_SKILL_DIRS="$_skill_dirs"
79
- export RNEX_ROOT RNEX_CONFIG_MERGED RNEX_PLUGIN_DIR RNEX_CMD RNEX_ARGS RNEX_QUIET RNEX_PROVIDER_SKILL_DIRS
113
+ export RNEX_ROOT RNEX_CONFIG_MERGED RNEX_PLUGIN_DIR RNEX_CMD RNEX_ARGS RNEX_QUIET RNEX_PROVIDER_SKILL_DIRS RNEX_LIB_DIR
80
114
  set -- "$@"
81
115
  # shellcheck source=/dev/null
82
116
  . "$_hook_script"
@@ -106,8 +140,7 @@ run_plugin_git_hooks() {
106
140
  shift 2>/dev/null || true
107
141
 
108
142
  _ws="${NEXUS_DIR:-}"
109
- [ -z "$_ws" ] && return 0
110
- [ ! -f "$_ws/rnex.yaml" ] && return 0
143
+ [ -n "$_ws" ] && [ -f "$_ws/rnex.yaml" ] || return 0
111
144
 
112
145
  RNEX_ROOT="$(cd "$_ws" 2>/dev/null && pwd -P)"
113
146
  RNEX_CONFIG_MERGED="$RNEX_ROOT/rnex.yaml"
@@ -126,55 +159,26 @@ run_plugin_git_hooks() {
126
159
  yaml_plugin_enabled "$_pname" 2>/dev/null || continue
127
160
 
128
161
  RNEX_PLUGIN_DIR="$RNEX_ROOT/.rnex/plugins/$_pname"
129
- _h_cand=""
130
- if [ -f "$RNEX_PLUGIN_DIR/hooks/git_${_hook_name}.sh" ]; then
131
- _h_cand="$RNEX_PLUGIN_DIR/hooks/git_${_hook_name}.sh"
132
- elif [ -f "$RNEX_PLUGIN_DIR/hooks/${_hook_name}.sh" ]; then
133
- _h_cand="$RNEX_PLUGIN_DIR/hooks/${_hook_name}.sh"
134
- fi
135
-
136
- if [ -z "$_h_cand" ]; then
137
- _src_pdir="$(find_plugin_dir "$_pname" 2>/dev/null || true)"
138
- if [ -n "$_src_pdir" ]; then
139
- if [ -f "$_src_pdir/hooks/git_${_hook_name}.sh" ]; then
140
- _h_cand="$_src_pdir/hooks/git_${_hook_name}.sh"
141
- elif [ -f "$_src_pdir/hooks/${_hook_name}.sh" ]; then
142
- _h_cand="$_src_pdir/hooks/${_hook_name}.sh"
143
- fi
144
- fi
145
- fi
162
+ _h_cand="$(_resolve_plugin_hook_script "$RNEX_ROOT" "$_pname" "git_${_hook_name}.sh" "${_hook_name}.sh" || true)"
146
163
 
147
164
  if [ -n "$_h_cand" ] && [ -f "$_h_cand" ]; then
148
165
  chmod +x "$_h_cand" 2>/dev/null || true
149
166
  if [ -n "$_stdin_data" ]; then
150
- printf '%s\n' "$_stdin_data" | env \
151
- NEXUS_DIR="$RNEX_ROOT" \
152
- WORKSPACE_YAML="$RNEX_ROOT/${RNEX_CONFIG_FILE:-rnex.yaml}" \
153
- WORKSPACE_LOCAL_YAML="$RNEX_ROOT/${RNEX_LOCAL_CONFIG_FILE:-.local.rnex.yaml}" \
154
- RNEX_ROOT="$RNEX_ROOT" \
155
- RNEX_CONFIG_MERGED="$RNEX_CONFIG_MERGED" \
156
- RNEX_PLUGIN_DIR="$RNEX_PLUGIN_DIR" \
157
- RNEX_CMD="$RNEX_CMD" \
158
- RNEX_ARGS="$RNEX_ARGS" \
159
- RNEX_QUIET="$RNEX_QUIET" \
160
- RNEX_CONFIG_FILE="${RNEX_CONFIG_FILE:-rnex.yaml}" \
161
- RNEX_LOCAL_CONFIG_FILE="${RNEX_LOCAL_CONFIG_FILE:-.local.rnex.yaml}" \
162
- "$_h_cand" "$@" || true
163
- else
164
- env \
165
- NEXUS_DIR="$RNEX_ROOT" \
166
- WORKSPACE_YAML="$RNEX_ROOT/${RNEX_CONFIG_FILE:-rnex.yaml}" \
167
- WORKSPACE_LOCAL_YAML="$RNEX_ROOT/${RNEX_LOCAL_CONFIG_FILE:-.local.rnex.yaml}" \
168
- RNEX_ROOT="$RNEX_ROOT" \
169
- RNEX_CONFIG_MERGED="$RNEX_CONFIG_MERGED" \
170
- RNEX_PLUGIN_DIR="$RNEX_PLUGIN_DIR" \
171
- RNEX_CMD="$RNEX_CMD" \
172
- RNEX_ARGS="$RNEX_ARGS" \
173
- RNEX_QUIET="$RNEX_QUIET" \
174
- RNEX_CONFIG_FILE="${RNEX_CONFIG_FILE:-rnex.yaml}" \
175
- RNEX_LOCAL_CONFIG_FILE="${RNEX_LOCAL_CONFIG_FILE:-.local.rnex.yaml}" \
176
- "$_h_cand" "$@" || true
177
- fi
167
+ printf '%s\n' "$_stdin_data"
168
+ fi | env \
169
+ NEXUS_DIR="$RNEX_ROOT" \
170
+ WORKSPACE_YAML="$RNEX_ROOT/${RNEX_CONFIG_FILE:-rnex.yaml}" \
171
+ WORKSPACE_LOCAL_YAML="$RNEX_ROOT/${RNEX_LOCAL_CONFIG_FILE:-.local.rnex.yaml}" \
172
+ RNEX_ROOT="$RNEX_ROOT" \
173
+ RNEX_CONFIG_MERGED="$RNEX_CONFIG_MERGED" \
174
+ RNEX_PLUGIN_DIR="$RNEX_PLUGIN_DIR" \
175
+ RNEX_CMD="$RNEX_CMD" \
176
+ RNEX_ARGS="$RNEX_ARGS" \
177
+ RNEX_QUIET="$RNEX_QUIET" \
178
+ RNEX_LIB_DIR="${RNEX_LIB_DIR:-}" \
179
+ RNEX_CONFIG_FILE="${RNEX_CONFIG_FILE:-rnex.yaml}" \
180
+ RNEX_LOCAL_CONFIG_FILE="${RNEX_LOCAL_CONFIG_FILE:-.local.rnex.yaml}" \
181
+ "$_h_cand" "$@" || true
178
182
  fi
179
183
  done
180
184
  }
@@ -216,19 +220,20 @@ HOOK_EOF
216
220
  mv "$_sub_tmp" "$_dst"
217
221
  }
218
222
 
223
+ _install_managed_hook_scripts() {
224
+ _imh_dir="$1"
225
+ mkdir -p "$_imh_dir"
226
+ for _h in $RNEX_MANAGED_GIT_HOOKS; do
227
+ _create_hook_script "$_imh_dir/$_h" "$_h"
228
+ done
229
+ }
230
+
219
231
  cmd_hooks_install_dir() {
220
232
  _ws_dir="$1"
221
233
  if ! git -C "$_ws_dir" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
222
234
  die "Directory '$_ws_dir' is not a Git repository. Run 'git init' first."
223
235
  fi
224
- _h_dir="$_ws_dir/.rnex/hooks"
225
- mkdir -p "$_h_dir"
226
- _create_hook_script "$_h_dir/post-merge" "post-merge"
227
- _create_hook_script "$_h_dir/post-checkout" "post-checkout"
228
- _create_hook_script "$_h_dir/post-commit" "post-commit"
229
- _create_hook_script "$_h_dir/pre-commit" "pre-commit"
230
- _create_hook_script "$_h_dir/pre-push" "pre-push"
231
-
236
+ _install_managed_hook_scripts "$_ws_dir/.rnex/hooks"
232
237
  git -C "$_ws_dir" config core.hooksPath .rnex/hooks
233
238
  log_ok "Configured Git hooks in .rnex/hooks (core.hooksPath = .rnex/hooks)"
234
239
  }
@@ -242,9 +247,71 @@ cmd_hooks_uninstall_dir() {
242
247
  log_ok "Uninstalled Repo Nexus Git hooks (core.hooksPath unset)"
243
248
  }
244
249
 
250
+ # Re-sync workspace if sync_stamp is stale or config files changed in git diff
251
+ _sync_if_hook_config_changed() {
252
+ _shc_hook="$1"
253
+ shift || true
254
+ _cfg_changed=0
255
+ _stamp_file=""
256
+ if [ -f "$NEXUS_DIR/.rnex/sync_stamp" ]; then
257
+ _stamp_file="$NEXUS_DIR/.rnex/sync_stamp"
258
+ elif [ -f "$NEXUS_DIR/.rnex/.sync_stamp" ]; then
259
+ _stamp_file="$NEXUS_DIR/.rnex/.sync_stamp"
260
+ fi
261
+
262
+ if [ -z "$_stamp_file" ] || [ "$(get_config_sum "$NEXUS_DIR")" != "$(cat "$_stamp_file" 2>/dev/null)" ]; then
263
+ _cfg_changed=1
264
+ else
265
+ case "$_shc_hook" in
266
+ post-merge)
267
+ git -C "$NEXUS_DIR" diff-tree -r --name-only ORIG_HEAD HEAD 2>/dev/null | grep -qE "(^rnex\.yaml$|^\.local\.rnex\.yaml$)" && _cfg_changed=1
268
+ ;;
269
+ post-checkout)
270
+ [ -n "${1:-}" ] && [ -n "${2:-}" ] && git -C "$NEXUS_DIR" diff-tree -r --name-only "$1" "$2" 2>/dev/null | grep -qE "(^rnex\.yaml$|^\.local\.rnex\.yaml$)" && _cfg_changed=1
271
+ ;;
272
+ pre-commit)
273
+ git -C "$NEXUS_DIR" diff --cached --name-only 2>/dev/null | grep -qE "(^rnex\.yaml$|^\.local\.rnex\.yaml$)" && _cfg_changed=1
274
+ ;;
275
+ esac
276
+ fi
277
+
278
+ if [ "$_cfg_changed" -eq 1 ]; then
279
+ cmd_sync -y --quiet || true
280
+ fi
281
+ }
282
+
283
+ # Delegate to repository-owned hook in scripts/githooks/<hook> or .githooks/<hook>
284
+ _delegate_repo_githook() {
285
+ _drg_hook="$1"; _drg_non_blocking="$2"; _drg_stdin="$3"
286
+ shift 3 || true
287
+
288
+ _drg_script=""
289
+ if [ -x "$NEXUS_DIR/scripts/githooks/$_drg_hook" ]; then
290
+ _drg_script="$NEXUS_DIR/scripts/githooks/$_drg_hook"
291
+ elif [ -x "$NEXUS_DIR/.githooks/$_drg_hook" ]; then
292
+ _drg_script="$NEXUS_DIR/.githooks/$_drg_hook"
293
+ fi
294
+ [ -n "$_drg_script" ] || return 0
295
+
296
+ if [ -n "$_drg_stdin" ]; then
297
+ if [ "$_drg_non_blocking" -eq 1 ]; then
298
+ printf '%s\n' "$_drg_stdin" | "$_drg_script" "$@" || true
299
+ else
300
+ printf '%s\n' "$_drg_stdin" | "$_drg_script" "$@" || return $?
301
+ fi
302
+ else
303
+ if [ "$_drg_non_blocking" -eq 1 ]; then
304
+ "$_drg_script" "$@" || true
305
+ else
306
+ "$_drg_script" "$@" || return $?
307
+ fi
308
+ fi
309
+ }
310
+
245
311
  cmd_hooks() {
246
312
  case "${1:-status}" in
247
- install)
313
+ install|uninstall)
314
+ _h_action="$1"
248
315
  require_workspace
249
316
  _local=0
250
317
  shift || true
@@ -254,33 +321,19 @@ cmd_hooks() {
254
321
  esac
255
322
  shift
256
323
  done
257
- cmd_hooks_install_dir "$NEXUS_DIR"
258
- if [ "$_local" -eq 1 ]; then
259
- yaml_set_top_level_local "git_hooks" "true"
260
- log_dim "Recorded git_hooks: true in $RNEX_LOCAL_CONFIG_FILE"
324
+ if [ "$_h_action" = "install" ]; then
325
+ cmd_hooks_install_dir "$NEXUS_DIR"
326
+ _gh_val="true"
261
327
  else
262
- yaml_set_top_level "git_hooks" "true"
263
- log_dim "Recorded git_hooks: true in $RNEX_CONFIG_FILE"
328
+ cmd_hooks_uninstall_dir "$NEXUS_DIR"
329
+ _gh_val="false"
264
330
  fi
265
- _load_git_hooks
266
- ;;
267
- uninstall)
268
- require_workspace
269
- _local=0
270
- shift || true
271
- while [ $# -gt 0 ]; do
272
- case "$1" in
273
- --local) _local=1 ;;
274
- esac
275
- shift
276
- done
277
- cmd_hooks_uninstall_dir "$NEXUS_DIR"
278
331
  if [ "$_local" -eq 1 ]; then
279
- yaml_set_top_level_local "git_hooks" "false"
280
- log_dim "Recorded git_hooks: false in $RNEX_LOCAL_CONFIG_FILE"
332
+ yaml_set_top_level_local "git_hooks" "$_gh_val"
333
+ log_dim "Recorded git_hooks: $_gh_val in $RNEX_LOCAL_CONFIG_FILE"
281
334
  else
282
- yaml_set_top_level "git_hooks" "false"
283
- log_dim "Recorded git_hooks: false in $RNEX_CONFIG_FILE"
335
+ yaml_set_top_level "git_hooks" "$_gh_val"
336
+ log_dim "Recorded git_hooks: $_gh_val in $RNEX_CONFIG_FILE"
284
337
  fi
285
338
  _load_git_hooks
286
339
  ;;
@@ -307,7 +360,7 @@ cmd_hooks() {
307
360
  fi
308
361
 
309
362
  printf '\n%bInstalled Hooks in .rnex/hooks/:%b\n' "$_BOLD" "$_NC"
310
- for _h in "post-merge" "post-checkout" "post-commit" "pre-commit" "pre-push"; do
363
+ for _h in $RNEX_MANAGED_GIT_HOOKS; do
311
364
  if [ -f "$NEXUS_DIR/.rnex/hooks/$_h" ]; then
312
365
  printf ' %b✓%b %-15s %b(installed)%b\n' "$_G" "$_NC" "$_h" "$_DIM" "$_NC"
313
366
  else
@@ -343,86 +396,19 @@ cmd_hooks() {
343
396
  export _RNEX_INSIDE_HOOK=1
344
397
  require_workspace
345
398
  case "$_hook_name" in
346
- post-merge)
347
- _cfg_changed=0
348
- _stamp_file=""
349
- if [ -f "$NEXUS_DIR/.rnex/sync_stamp" ]; then
350
- _stamp_file="$NEXUS_DIR/.rnex/sync_stamp"
351
- elif [ -f "$NEXUS_DIR/.rnex/.sync_stamp" ]; then
352
- _stamp_file="$NEXUS_DIR/.rnex/.sync_stamp"
353
- fi
354
- if [ -z "$_stamp_file" ] || [ "$(get_config_sum "$NEXUS_DIR")" != "$(cat "$_stamp_file" 2>/dev/null)" ]; then
355
- _cfg_changed=1
356
- elif git -C "$NEXUS_DIR" diff-tree -r --name-only ORIG_HEAD HEAD 2>/dev/null | grep -qE "(^rnex\.yaml$|^\.local\.rnex\.yaml$)"; then
357
- _cfg_changed=1
358
- fi
359
- if [ "$_cfg_changed" -eq 1 ]; then
360
- cmd_sync -y --quiet || true
361
- fi
362
- run_plugin_git_hooks "post-merge" "$@"
363
- if [ -x "$NEXUS_DIR/scripts/githooks/post-merge" ]; then
364
- "$NEXUS_DIR/scripts/githooks/post-merge" "$@" || true
365
- elif [ -x "$NEXUS_DIR/.githooks/post-merge" ]; then
366
- "$NEXUS_DIR/.githooks/post-merge" "$@" || true
367
- fi
368
- return 0
369
- ;;
370
- post-checkout)
371
- _cfg_changed=0
372
- _stamp_file=""
373
- if [ -f "$NEXUS_DIR/.rnex/sync_stamp" ]; then
374
- _stamp_file="$NEXUS_DIR/.rnex/sync_stamp"
375
- elif [ -f "$NEXUS_DIR/.rnex/.sync_stamp" ]; then
376
- _stamp_file="$NEXUS_DIR/.rnex/.sync_stamp"
377
- fi
378
- if [ -z "$_stamp_file" ] || [ "$(get_config_sum "$NEXUS_DIR")" != "$(cat "$_stamp_file" 2>/dev/null)" ]; then
379
- _cfg_changed=1
380
- elif [ -n "$1" ] && [ -n "$2" ] && git -C "$NEXUS_DIR" diff-tree -r --name-only "$1" "$2" 2>/dev/null | grep -qE "(^rnex\.yaml$|^\.local\.rnex\.yaml$)"; then
381
- _cfg_changed=1
382
- fi
383
- if [ "$_cfg_changed" -eq 1 ]; then
384
- cmd_sync -y --quiet || true
385
- fi
386
- run_plugin_git_hooks "post-checkout" "$@"
387
- if [ -x "$NEXUS_DIR/scripts/githooks/post-checkout" ]; then
388
- "$NEXUS_DIR/scripts/githooks/post-checkout" "$@" || true
389
- elif [ -x "$NEXUS_DIR/.githooks/post-checkout" ]; then
390
- "$NEXUS_DIR/.githooks/post-checkout" "$@" || true
391
- fi
392
- return 0
399
+ post-merge|post-checkout)
400
+ _sync_if_hook_config_changed "$_hook_name" "$@"
401
+ run_plugin_git_hooks "$_hook_name" "$@"
402
+ _delegate_repo_githook "$_hook_name" 1 "" "$@"
393
403
  ;;
394
404
  post-commit)
395
405
  run_plugin_git_hooks "post-commit" "$@"
396
- if [ -x "$NEXUS_DIR/scripts/githooks/post-commit" ]; then
397
- "$NEXUS_DIR/scripts/githooks/post-commit" "$@" || true
398
- elif [ -x "$NEXUS_DIR/.githooks/post-commit" ]; then
399
- "$NEXUS_DIR/.githooks/post-commit" "$@" || true
400
- fi
401
- return 0
406
+ _delegate_repo_githook "post-commit" 1 "" "$@"
402
407
  ;;
403
408
  pre-commit)
404
- _cfg_changed=0
405
- _stamp_file=""
406
- if [ -f "$NEXUS_DIR/.rnex/sync_stamp" ]; then
407
- _stamp_file="$NEXUS_DIR/.rnex/sync_stamp"
408
- elif [ -f "$NEXUS_DIR/.rnex/.sync_stamp" ]; then
409
- _stamp_file="$NEXUS_DIR/.rnex/.sync_stamp"
410
- fi
411
- if [ -z "$_stamp_file" ] || [ "$(get_config_sum "$NEXUS_DIR")" != "$(cat "$_stamp_file" 2>/dev/null)" ]; then
412
- _cfg_changed=1
413
- elif git -C "$NEXUS_DIR" diff --cached --name-only 2>/dev/null | grep -qE "(^rnex\.yaml$|^\.local\.rnex\.yaml$)"; then
414
- _cfg_changed=1
415
- fi
416
- if [ "$_cfg_changed" -eq 1 ]; then
417
- cmd_sync -y --quiet || true
418
- fi
409
+ _sync_if_hook_config_changed "pre-commit" "$@"
419
410
  run_plugin_git_hooks "pre-commit" "$@"
420
- if [ -x "$NEXUS_DIR/scripts/githooks/pre-commit" ]; then
421
- "$NEXUS_DIR/scripts/githooks/pre-commit" "$@" || return $?
422
- elif [ -x "$NEXUS_DIR/.githooks/pre-commit" ]; then
423
- "$NEXUS_DIR/.githooks/pre-commit" "$@" || return $?
424
- fi
425
- return 0
411
+ _delegate_repo_githook "pre-commit" 0 "" "$@" || return $?
426
412
  ;;
427
413
  pre-push)
428
414
  _stdin_data=""
@@ -434,20 +420,7 @@ cmd_hooks() {
434
420
  else
435
421
  run_plugin_git_hooks "pre-push" "$@"
436
422
  fi
437
- if [ -x "$NEXUS_DIR/scripts/githooks/pre-push" ]; then
438
- if [ -n "$_stdin_data" ]; then
439
- printf '%s\n' "$_stdin_data" | "$NEXUS_DIR/scripts/githooks/pre-push" "$@" || return $?
440
- else
441
- "$NEXUS_DIR/scripts/githooks/pre-push" "$@" || return $?
442
- fi
443
- elif [ -x "$NEXUS_DIR/.githooks/pre-push" ]; then
444
- if [ -n "$_stdin_data" ]; then
445
- printf '%s\n' "$_stdin_data" | "$NEXUS_DIR/.githooks/pre-push" "$@" || return $?
446
- else
447
- "$NEXUS_DIR/.githooks/pre-push" "$@" || return $?
448
- fi
449
- fi
450
- return 0
423
+ _delegate_repo_githook "pre-push" 0 "$_stdin_data" "$@" || return $?
451
424
  ;;
452
425
  esac
453
426
  return 0
package/lib/plugins.sh CHANGED
@@ -131,7 +131,7 @@ plugin_resolve_placeholders() {
131
131
  fi
132
132
  case "$_k" in
133
133
  *_dir|dir)
134
- _v="$(printf '%s' "$_v" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' | tr -d "'\"" | sed -e 's#^\./##' -e 's#^/##' -e 's#/$##')"
134
+ _v="$(normalize_rel_path "$_v")"
135
135
  ;;
136
136
  esac
137
137
  _res="$(printf '%s' "$_res" | sed -e "s|{$_k}|$_v|g")"
@@ -140,8 +140,8 @@ plugin_resolve_placeholders() {
140
140
 
141
141
  # Fallback resolution for core wiki/raw placeholders and legacy prefixes
142
142
  _w_val="$(yaml_plugin_get "$_p" "wiki_dir" 2>/dev/null || true)"
143
- [ -z "$_w_val" ] && _w_val=".rnex/wiki"
144
- _w_val="$(printf '%s' "$_w_val" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' | tr -d "'\"" | sed -e 's#^\./##' -e 's#^/##' -e 's#/$##')"
143
+ [ -n "$_w_val" ] || _w_val=".rnex/wiki"
144
+ _w_val="$(normalize_rel_path "$_w_val")"
145
145
 
146
146
  # {raw_dir} (legacy alias) resolves to the first explicit directory source; when no
147
147
  # directory source exists it stays unresolved so callers can skip the destination.
@@ -178,6 +178,33 @@ plugin_instantiate_template() {
178
178
  "$_src" > "$_dest"
179
179
  }
180
180
 
181
+ # Deterministically sync plugin asset directories and manifest files into destination
182
+ sync_plugin_bundle() {
183
+ _spb_src="$1"; _spb_dest="$2"
184
+ [ -n "$_spb_src" ] && [ -n "$_spb_dest" ] || return 0
185
+ [ "$_spb_src" != "$_spb_dest" ] || return 0
186
+
187
+ mkdir -p "$_spb_dest"
188
+ rm -rf "${_spb_dest:?}/prompts" 2>/dev/null || true
189
+ for _sub in rules workflows instructions skills agents templates hooks; do
190
+ if [ -d "$_spb_src/$_sub" ]; then
191
+ rm -rf "${_spb_dest:?}/$_sub"
192
+ mkdir -p "$_spb_dest/$_sub"
193
+ cp -R "$_spb_src/$_sub"/. "$_spb_dest/$_sub"/ 2>/dev/null || true
194
+ if [ "$_sub" = "hooks" ]; then
195
+ chmod +x "$_spb_dest/hooks"/* 2>/dev/null || true
196
+ fi
197
+ else
198
+ rm -rf "${_spb_dest:?}/$_sub"
199
+ fi
200
+ done
201
+ for _meta in plugin.yaml instructions.md README.md; do
202
+ if [ -f "$_spb_src/$_meta" ]; then
203
+ cp "$_spb_src/$_meta" "$_spb_dest/$_meta"
204
+ fi
205
+ done
206
+ }
207
+
181
208
  # Sync plugins into scoped directories: .rnex/plugins/<plugin-name>/
182
209
  sync_plugins() {
183
210
  # Clean up any bogus placeholder directories accidentally created in workspace root
@@ -286,36 +313,7 @@ sync_plugins() {
286
313
  continue
287
314
  fi
288
315
 
289
- _scoped_dest="$NEXUS_DIR/.rnex/plugins/$_pname"
290
- if [ "$_pdir" != "$_scoped_dest" ]; then
291
- mkdir -p "$_scoped_dest"
292
-
293
- # Deterministically sync plugin asset directories (clean stale files; no prompts in plugins)
294
- rm -rf "${_scoped_dest:?}/prompts" 2>/dev/null || true
295
- for _sub in rules workflows instructions skills agents templates hooks; do
296
- if [ -d "$_pdir/$_sub" ]; then
297
- rm -rf "${_scoped_dest:?}/$_sub"
298
- mkdir -p "$_scoped_dest/$_sub"
299
- cp -R "$_pdir/$_sub"/. "$_scoped_dest/$_sub"/ 2>/dev/null || true
300
- if [ "$_sub" = "hooks" ]; then
301
- chmod +x "$_scoped_dest/hooks"/* 2>/dev/null || true
302
- fi
303
- else
304
- rm -rf "${_scoped_dest:?}/$_sub"
305
- fi
306
- done
307
-
308
- # Copy plugin manifest, instructions, and documentation
309
- if [ -f "$_pdir/plugin.yaml" ]; then
310
- cp "$_pdir/plugin.yaml" "$_scoped_dest/plugin.yaml"
311
- fi
312
- if [ -f "$_pdir/instructions.md" ]; then
313
- cp "$_pdir/instructions.md" "$_scoped_dest/instructions.md"
314
- fi
315
- if [ -f "$_pdir/README.md" ]; then
316
- cp "$_pdir/README.md" "$_scoped_dest/README.md"
317
- fi
318
- fi
316
+ sync_plugin_bundle "$_pdir" "$NEXUS_DIR/.rnex/plugins/$_pname"
319
317
 
320
318
  # Provision explicit directory sources declared under plugins.<name>.sources (e.g. ./docs)
321
319
  for _sdir in $(plugin_source_dirs "$_pname" 2>/dev/null); do
package/lib/repos.sh CHANGED
@@ -27,13 +27,7 @@ ensure_repos_dir() {
27
27
  # ---- Member Repo .rnex Isolation ------------------------------------------
28
28
 
29
29
  repo_rnex_dir_enabled() {
30
- _name="$1"
31
- _val="$(yaml_get "$_name" "rnex_dir")"
32
- _val="$(printf '%s' "$_val" | tr '[:upper:]' '[:lower:]')"
33
- case "$_val" in
34
- false|no|0|disabled|off) return 1 ;;
35
- *) return 0 ;;
36
- esac
30
+ is_enabled_val "$(yaml_get "$1" "rnex_dir")"
37
31
  }
38
32
 
39
33
  clean_repo_rnex() {
@@ -105,31 +99,6 @@ EOF
105
99
  _pdir="$(find_plugin_dir "$_pname" 2>/dev/null || true)"
106
100
  [ -n "$_pdir" ] || continue
107
101
 
108
- _dest_pdir="$_r_rnex/plugins/$_pname"
109
- mkdir -p "$_dest_pdir"
110
-
111
- # Deterministically sync plugin asset directories into member repo (no prompts in plugins)
112
- rm -rf "${_dest_pdir:?}/prompts" 2>/dev/null || true
113
- for _sub in rules workflows instructions skills agents templates hooks; do
114
- if [ -d "$_pdir/$_sub" ]; then
115
- rm -rf "${_dest_pdir:?}/$_sub"
116
- mkdir -p "$_dest_pdir/$_sub"
117
- cp -R "$_pdir/$_sub"/. "$_dest_pdir/$_sub"/ 2>/dev/null || true
118
- if [ "$_sub" = "hooks" ]; then
119
- chmod +x "$_dest_pdir/hooks"/* 2>/dev/null || true
120
- fi
121
- else
122
- rm -rf "${_dest_pdir:?}/$_sub"
123
- fi
124
- done
125
- if [ -f "$_pdir/plugin.yaml" ]; then
126
- cp "$_pdir/plugin.yaml" "$_dest_pdir/plugin.yaml"
127
- fi
128
- if [ -f "$_pdir/instructions.md" ]; then
129
- cp "$_pdir/instructions.md" "$_dest_pdir/instructions.md"
130
- fi
131
- if [ -f "$_pdir/README.md" ]; then
132
- cp "$_pdir/README.md" "$_dest_pdir/README.md"
133
- fi
102
+ sync_plugin_bundle "$_pdir" "$_r_rnex/plugins/$_pname"
134
103
  done
135
104
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "repo-nexus",
3
- "version": "0.7.30",
3
+ "version": "0.7.31",
4
4
  "description": "Lightweight, zero-dependency Virtual Meta-Repo companion for multi-repo workspaces and shared AI context (no git submodules)",
5
5
  "bin": {
6
6
  "repo-nexus": "rnex",
@@ -1,14 +0,0 @@
1
- #!/bin/sh
2
- set -e
3
- _hook_dir=""
4
- if [ -n "${RNEX_PLUGIN_DIR:-}" ] && [ -f "$RNEX_PLUGIN_DIR/hooks/after_sync.sh" ]; then
5
- _hook_dir="$RNEX_PLUGIN_DIR/hooks"
6
- elif [ -n "${RNEX_ROOT:-}" ] && [ -f "$RNEX_ROOT/toolkit/plugins/claude/hooks/after_sync.sh" ]; then
7
- _hook_dir="$RNEX_ROOT/toolkit/plugins/claude/hooks"
8
- elif [ -f "$(dirname "$0")/after_sync.sh" ]; then
9
- _hook_dir="$(cd "$(dirname "$0")" && pwd -P)"
10
- fi
11
-
12
- if [ -n "$_hook_dir" ] && [ -f "$_hook_dir/after_sync.sh" ]; then
13
- . "$_hook_dir/after_sync.sh"
14
- fi
@@ -1,14 +0,0 @@
1
- #!/bin/sh
2
- set -e
3
- _hook_dir=""
4
- if [ -n "${RNEX_PLUGIN_DIR:-}" ] && [ -f "$RNEX_PLUGIN_DIR/hooks/after_sync.sh" ]; then
5
- _hook_dir="$RNEX_PLUGIN_DIR/hooks"
6
- elif [ -n "${RNEX_ROOT:-}" ] && [ -f "$RNEX_ROOT/toolkit/plugins/copilot/hooks/after_sync.sh" ]; then
7
- _hook_dir="$RNEX_ROOT/toolkit/plugins/copilot/hooks"
8
- elif [ -f "$(dirname "$0")/after_sync.sh" ]; then
9
- _hook_dir="$(cd "$(dirname "$0")" && pwd -P)"
10
- fi
11
-
12
- if [ -n "$_hook_dir" ] && [ -f "$_hook_dir/after_sync.sh" ]; then
13
- . "$_hook_dir/after_sync.sh"
14
- fi
@@ -1,14 +0,0 @@
1
- #!/bin/sh
2
- set -e
3
- _hook_dir=""
4
- if [ -n "${RNEX_PLUGIN_DIR:-}" ] && [ -f "$RNEX_PLUGIN_DIR/hooks/after_sync.sh" ]; then
5
- _hook_dir="$RNEX_PLUGIN_DIR/hooks"
6
- elif [ -n "${RNEX_ROOT:-}" ] && [ -f "$RNEX_ROOT/toolkit/plugins/cursor/hooks/after_sync.sh" ]; then
7
- _hook_dir="$RNEX_ROOT/toolkit/plugins/cursor/hooks"
8
- elif [ -f "$(dirname "$0")/after_sync.sh" ]; then
9
- _hook_dir="$(cd "$(dirname "$0")" && pwd -P)"
10
- fi
11
-
12
- if [ -n "$_hook_dir" ] && [ -f "$_hook_dir/after_sync.sh" ]; then
13
- . "$_hook_dir/after_sync.sh"
14
- fi
@@ -1,14 +0,0 @@
1
- #!/bin/sh
2
- set -e
3
- _hook_dir=""
4
- if [ -n "${RNEX_PLUGIN_DIR:-}" ] && [ -f "$RNEX_PLUGIN_DIR/hooks/after_sync.sh" ]; then
5
- _hook_dir="$RNEX_PLUGIN_DIR/hooks"
6
- elif [ -n "${RNEX_ROOT:-}" ] && [ -f "$RNEX_ROOT/toolkit/plugins/gemini/hooks/after_sync.sh" ]; then
7
- _hook_dir="$RNEX_ROOT/toolkit/plugins/gemini/hooks"
8
- elif [ -f "$(dirname "$0")/after_sync.sh" ]; then
9
- _hook_dir="$(cd "$(dirname "$0")" && pwd -P)"
10
- fi
11
-
12
- if [ -n "$_hook_dir" ] && [ -f "$_hook_dir/after_sync.sh" ]; then
13
- . "$_hook_dir/after_sync.sh"
14
- fi
@@ -1,14 +0,0 @@
1
- #!/bin/sh
2
- set -e
3
- _hook_dir=""
4
- if [ -n "${RNEX_PLUGIN_DIR:-}" ] && [ -f "$RNEX_PLUGIN_DIR/hooks/after_sync.sh" ]; then
5
- _hook_dir="$RNEX_PLUGIN_DIR/hooks"
6
- elif [ -n "${RNEX_ROOT:-}" ] && [ -f "$RNEX_ROOT/toolkit/plugins/karpathy-llm/hooks/after_sync.sh" ]; then
7
- _hook_dir="$RNEX_ROOT/toolkit/plugins/karpathy-llm/hooks"
8
- elif [ -f "$(dirname "$0")/after_sync.sh" ]; then
9
- _hook_dir="$(cd "$(dirname "$0")" && pwd -P)"
10
- fi
11
-
12
- if [ -n "$_hook_dir" ] && [ -f "$_hook_dir/after_sync.sh" ]; then
13
- . "$_hook_dir/after_sync.sh"
14
- fi
@@ -1,14 +0,0 @@
1
- #!/bin/sh
2
- set -e
3
- _hook_dir=""
4
- if [ -n "${RNEX_PLUGIN_DIR:-}" ] && [ -f "$RNEX_PLUGIN_DIR/hooks/after_sync.sh" ]; then
5
- _hook_dir="$RNEX_PLUGIN_DIR/hooks"
6
- elif [ -n "${RNEX_ROOT:-}" ] && [ -f "$RNEX_ROOT/toolkit/plugins/windsurf/hooks/after_sync.sh" ]; then
7
- _hook_dir="$RNEX_ROOT/toolkit/plugins/windsurf/hooks"
8
- elif [ -f "$(dirname "$0")/after_sync.sh" ]; then
9
- _hook_dir="$(cd "$(dirname "$0")" && pwd -P)"
10
- fi
11
-
12
- if [ -n "$_hook_dir" ] && [ -f "$_hook_dir/after_sync.sh" ]; then
13
- . "$_hook_dir/after_sync.sh"
14
- fi