repo-nexus 0.5.3 → 0.5.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.
package/CHANGELOG.md CHANGED
@@ -17,6 +17,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
17
17
  - Workspace schema migration in `rnex update` and `rnex fix -y` non-destructively inserts `git_hooks` into older configurations.
18
18
  - `rnex status` and `rnex hooks status` display configuration state alongside runtime Git hooks status.
19
19
 
20
+ ### Fixed
21
+ - **Git Hooks Self-Rewrite & Wrapper Preservation**:
22
+ - Resolved self-rewrite bug where `rnex hooks run post-merge` invoked `cmd_fix`, which truncated and replaced `.rnex/hooks/post-merge` while the shell interpreter was actively executing it.
23
+ - Added in-hook execution guard (`_RNEX_INSIDE_HOOK=1`) preventing hook script regeneration during active hook dispatch.
24
+ - Preserved custom / repository-owned wrappers in `.rnex/hooks/` that are not auto-managed by `rnex`.
25
+ - Added `cmp -s` check before writing hook files to eliminate unnecessary file churn and in-place overwrites.
26
+ - Automatically chain to existing repository-owned `.githooks/` scripts if present.
27
+ - **Track Workspace `.rnex/` in Version Control**:
28
+ - Removed `.rnex/` from `.gitignore` templates and generation.
29
+ - `reconcile_gitignore` and `rnex fix` actively strip legacy `.rnex/` ignore rules from `.gitignore` so that team prompts (`.rnex/prompts/`), plugins (`.rnex/plugins/`), and hooks (`.rnex/hooks/`) are tracked in git.
30
+ - Updated `rnex status` to expect `.rnex/` to remain tracked by git.
31
+
20
32
  ## [0.5.1] - 2026-10-08
21
33
 
22
34
  ### Added
package/docs/CLI.md CHANGED
@@ -78,7 +78,7 @@ rnex init [options] [directory]
78
78
  2. Merges Repo Nexus routing instructions (`<!-- REPO-NEXUS:START -->` ... `<!-- REPO-NEXUS:END -->`) into the target AI instructions file.
79
79
  3. Creates `./repos/` directory with a tracked `.gitkeep`.
80
80
  4. Creates `.rnex/`, `.rnex/plugins/`, and copies standardized prompts to `.rnex/prompts/`.
81
- 5. Reconciles `.gitignore` to ensure `repos/`, `.rnex/`, and `.local.rnex.yaml` are never tracked by the meta-repo.
81
+ 5. Reconciles `.gitignore` to ensure `repos/` and `.local.rnex.yaml` are never tracked by the meta-repo (while keeping `.rnex/` tracked).
82
82
  6. Generates a multi-root `.code-workspace` file if configured.
83
83
  7. Prompts to configure automated workspace Git hooks (`core.hooksPath = .rnex/hooks`) if inside a Git repository.
84
84
 
@@ -110,7 +110,7 @@ rnex status
110
110
  #### What it reports:
111
111
  - **Configuration & Versions**: Path, version tag, and load status of `rnex.yaml` and `.local.rnex.yaml`.
112
112
  - **Paths**: Clones directory (`repos_dir`) and primary AI instructions file.
113
- - **Git Integration**: Validation that `.gitignore` ignores `repos/`, `.rnex/`, and local files, and whether `core.hooksPath` points to `.rnex/hooks`.
113
+ - **Git Integration**: Validation that `.gitignore` ignores `repos/` and local files (while keeping `.rnex/` tracked), and whether `core.hooksPath` points to `.rnex/hooks`.
114
114
  - **AI Routing Context**: Existence and routing block markers in `AGENTS.md`, `CLAUDE.md`, `.code-workspace`, and prompts catalog (`.rnex/prompts/index.md`).
115
115
  - **Active Plugins**: List of enabled plugins, scoped versions, and asset directories.
116
116
  - **Member Repositories**: Every registered repository, enabled/disabled state, clone health, and active Git branch.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "repo-nexus",
3
- "version": "0.5.3",
3
+ "version": "0.5.5",
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",
package/rnex CHANGED
@@ -890,14 +890,11 @@ reconcile_gitignore() {
890
890
  repos/*
891
891
  !repos/.gitkeep
892
892
 
893
- # Repo Nexus internal state & cache
894
- .rnex/
895
-
896
893
  # Local machine-specific configuration
897
894
  $RNEX_LOCAL_CONFIG_FILE
898
895
  .rnex.local.yaml
899
896
  GI_EOF
900
- log_ok "Created .gitignore (ignoring repos/, .rnex/, and $RNEX_LOCAL_CONFIG_FILE)"
897
+ log_ok "Created .gitignore (ignoring repos/ and $RNEX_LOCAL_CONFIG_FILE)"
901
898
  return 0
902
899
  fi
903
900
 
@@ -908,18 +905,19 @@ GI_EOF
908
905
  log_ok "Removed legacy lint trigger counter entries from .gitignore"
909
906
  fi
910
907
 
911
- # 2. Ensure repos/* is ignored
908
+ # 2. Strip legacy .rnex/ entries if present (.rnex/ contains tracked prompts, plugins, and hooks)
909
+ if grep -qE '^[ ]*#?[ ]*Repo Nexus internal state' "$_gi" 2>/dev/null || grep -qE '^\.rnex/?$' "$_gi" 2>/dev/null; then
910
+ _tmp="$(mktemp)"
911
+ grep -vE '(^[ ]*#?[ ]*Repo Nexus internal state|^\.rnex/?$)' "$_gi" > "$_tmp" && mv "$_tmp" "$_gi"
912
+ log_ok "Removed .rnex/ from .gitignore (.rnex should be tracked in git)"
913
+ fi
914
+
915
+ # 3. Ensure repos/* is ignored
912
916
  if ! grep -q "^repos" "$_gi" 2>/dev/null; then
913
917
  printf '\n# Member repos (managed by rnex)\nrepos/*\n!repos/.gitkeep\n' >> "$_gi"
914
918
  log_dim "Added repos/* to .gitignore"
915
919
  fi
916
920
 
917
- # 3. Ensure .rnex/ is ignored
918
- if ! grep -q '^\.rnex/' "$_gi" 2>/dev/null && ! grep -q '^\.rnex$' "$_gi" 2>/dev/null; then
919
- printf '\n# Repo Nexus internal state & cache\n.rnex/\n' >> "$_gi"
920
- log_dim "Added .rnex/ to .gitignore"
921
- fi
922
-
923
921
  # 4. Ensure local config files are ignored
924
922
  if ! grep -qF "$RNEX_LOCAL_CONFIG_FILE" "$_gi" 2>/dev/null; then
925
923
  printf '\n# Local machine-specific configuration\n%s\n' "$RNEX_LOCAL_CONFIG_FILE" >> "$_gi"
@@ -1258,22 +1256,35 @@ upgrade_workspace_config() {
1258
1256
  _create_hook_script() {
1259
1257
  _dst="$1"
1260
1258
  _hook_type="$2"
1261
- cat > "$_dst" <<'HOOK_EOF'
1259
+
1260
+ # Preserve custom / repository-owned wrappers that are not auto-managed by rnex
1261
+ if [ -f "$_dst" ] && ! grep -q "Managed automatically by rnex" "$_dst" 2>/dev/null; then
1262
+ return 0
1263
+ fi
1264
+
1265
+ _sub_tmp="$(mktemp)"
1266
+ cat > "$_sub_tmp" <<HOOK_EOF
1262
1267
  #!/bin/sh
1263
- # Repo Nexus Git Hook: __HOOK_TYPE__
1268
+ # Repo Nexus Git Hook: $_hook_type
1264
1269
  # Managed automatically by rnex. Do not edit manually.
1265
1270
 
1266
- _dir="$(cd "$(dirname "$0")/../.." 2>/dev/null && pwd -P)"
1271
+ _dir="\$(cd "\$(dirname "\$0")/../.." 2>/dev/null && pwd -P)"
1267
1272
  if command -v rnex >/dev/null 2>&1; then
1268
- rnex hooks run __HOOK_TYPE__ "$@"
1269
- elif [ -f "$_dir/rnex" ]; then
1270
- "$_dir/rnex" hooks run __HOOK_TYPE__ "$@"
1273
+ rnex hooks run $_hook_type "\$@"
1274
+ elif [ -f "\$_dir/rnex" ]; then
1275
+ "\$_dir/rnex" hooks run $_hook_type "\$@"
1271
1276
  fi
1272
1277
  HOOK_EOF
1273
- _sub_tmp="$(mktemp)"
1274
- sed "s/__HOOK_TYPE__/$_hook_type/g" "$_dst" > "$_sub_tmp" && mv "$_sub_tmp" "$_dst"
1275
- rm -f "$_sub_tmp"
1276
- chmod +x "$_dst"
1278
+
1279
+ chmod +x "$_sub_tmp"
1280
+
1281
+ # Avoid rewriting / modifying file if already identical (prevents inode churn & self-rewrite)
1282
+ if [ -f "$_dst" ] && cmp -s "$_sub_tmp" "$_dst"; then
1283
+ rm -f "$_sub_tmp"
1284
+ return 0
1285
+ fi
1286
+
1287
+ mv "$_sub_tmp" "$_dst"
1277
1288
  }
1278
1289
 
1279
1290
  cmd_hooks_install_dir() {
@@ -1390,6 +1401,7 @@ cmd_hooks() {
1390
1401
  shift
1391
1402
  _hook_name="$1"
1392
1403
  shift || true
1404
+ export _RNEX_INSIDE_HOOK=1
1393
1405
  require_workspace
1394
1406
  case "$_hook_name" in
1395
1407
  post-merge)
@@ -1403,6 +1415,9 @@ cmd_hooks() {
1403
1415
  fi
1404
1416
  fi
1405
1417
  fi
1418
+ if [ -x "$NEXUS_DIR/.githooks/post-merge" ]; then
1419
+ "$NEXUS_DIR/.githooks/post-merge" "$@" || true
1420
+ fi
1406
1421
  return 0
1407
1422
  ;;
1408
1423
  post-commit)
@@ -1416,6 +1431,9 @@ cmd_hooks() {
1416
1431
  fi
1417
1432
  fi
1418
1433
  fi
1434
+ if [ -x "$NEXUS_DIR/.githooks/post-commit" ]; then
1435
+ "$NEXUS_DIR/.githooks/post-commit" "$@" || true
1436
+ fi
1419
1437
  return 0
1420
1438
  ;;
1421
1439
  pre-commit)
@@ -1429,6 +1447,9 @@ cmd_hooks() {
1429
1447
  fi
1430
1448
  fi
1431
1449
  fi
1450
+ if [ -x "$NEXUS_DIR/.githooks/pre-commit" ]; then
1451
+ "$NEXUS_DIR/.githooks/pre-commit" "$@" || return $?
1452
+ fi
1432
1453
  return 0
1433
1454
  ;;
1434
1455
  pre-push)
@@ -1438,6 +1459,9 @@ cmd_hooks() {
1438
1459
  printf '%b[WIKI]%b Pre-push trigger: verifying wiki changes.\n' "$_C" "$_NC"
1439
1460
  fi
1440
1461
  fi
1462
+ if [ -x "$NEXUS_DIR/.githooks/pre-push" ]; then
1463
+ "$NEXUS_DIR/.githooks/pre-push" "$@" || return $?
1464
+ fi
1441
1465
  return 0
1442
1466
  ;;
1443
1467
  esac
@@ -1595,7 +1619,7 @@ YAML_EOF
1595
1619
  sync_prompts
1596
1620
  NEXUS_DIR="$_orig_nexus"
1597
1621
 
1598
- # ---- 5. Ensure repos/, .rnex/, and local configs are gitignored ---------
1622
+ # ---- 5. Ensure repos/ and local configs are gitignored ---------
1599
1623
  reconcile_gitignore "$_target_dir"
1600
1624
 
1601
1625
  # ---- 6. Generate VS Code / Cursor workspace if configured ----------------
@@ -1952,18 +1976,18 @@ cmd_status() {
1952
1976
  fi
1953
1977
 
1954
1978
  _gi="$NEXUS_DIR/.gitignore"
1955
- _gi_repos=0; _gi_rnex=0; _gi_local=0; _gi_has_counter=0
1979
+ _gi_repos=0; _gi_local=0; _gi_has_counter=0; _gi_has_rnex=0
1956
1980
  if [ -f "$_gi" ]; then
1957
1981
  grep -q '^repos' "$_gi" 2>/dev/null && _gi_repos=1
1958
- (grep -q '^\.rnex/' "$_gi" 2>/dev/null || grep -q '^\.rnex$' "$_gi" 2>/dev/null) && _gi_rnex=1
1959
1982
  grep -qF "$RNEX_LOCAL_CONFIG_FILE" "$_gi" 2>/dev/null && _gi_local=1
1960
1983
  grep -q "lint_trigger_counter" "$_gi" 2>/dev/null && _gi_has_counter=1
1984
+ (grep -q '^\.rnex/' "$_gi" 2>/dev/null || grep -q '^\.rnex$' "$_gi" 2>/dev/null) && _gi_has_rnex=1
1961
1985
  fi
1962
1986
 
1963
- if [ "$_gi_repos" -eq 1 ] && [ "$_gi_rnex" -eq 1 ] && [ "$_gi_local" -eq 1 ] && [ "$_gi_has_counter" -eq 0 ]; then
1964
- printf ' %b✓%b Git Ignore: repos/, .rnex/, and %s ignored in .gitignore\n' "$_G" "$_NC" "$RNEX_LOCAL_CONFIG_FILE"
1987
+ if [ "$_gi_repos" -eq 1 ] && [ "$_gi_local" -eq 1 ] && [ "$_gi_has_counter" -eq 0 ] && [ "$_gi_has_rnex" -eq 0 ]; then
1988
+ printf ' %b✓%b Git Ignore: repos/ and %s ignored in .gitignore\n' "$_G" "$_NC" "$RNEX_LOCAL_CONFIG_FILE"
1965
1989
  else
1966
- printf ' %b!%b Git Ignore: recommend running "rnex fix" to update .gitignore (repos/, .rnex/, local configs)\n' "$_Y" "$_NC"
1990
+ printf ' %b!%b Git Ignore: recommend running "rnex fix" to update .gitignore (repos/, local configs)\n' "$_Y" "$_NC"
1967
1991
  fi
1968
1992
 
1969
1993
  _hooks_path="$(git -C "$NEXUS_DIR" config --get core.hooksPath 2>/dev/null || true)"
@@ -2177,7 +2201,7 @@ cmd_fix() {
2177
2201
  _cur_hp="$(git -C "$NEXUS_DIR" config --get core.hooksPath 2>/dev/null || true)"
2178
2202
  if [ "$_cur_hp" != ".rnex/hooks" ] && [ "$_cur_hp" != "$NEXUS_DIR/.rnex/hooks" ]; then
2179
2203
  cmd_hooks_install_dir "$NEXUS_DIR"
2180
- else
2204
+ elif [ "${_RNEX_INSIDE_HOOK:-0}" -ne 1 ]; then
2181
2205
  _hooks_dir="$NEXUS_DIR/.rnex/hooks"
2182
2206
  _create_hook_script "$_hooks_dir/post-merge" "post-merge"
2183
2207
  _create_hook_script "$_hooks_dir/post-commit" "post-commit"
@@ -2189,11 +2213,13 @@ cmd_fix() {
2189
2213
  *)
2190
2214
  _cur_hp="$(git -C "$NEXUS_DIR" config --get core.hooksPath 2>/dev/null || true)"
2191
2215
  if [ "$_cur_hp" = ".rnex/hooks" ] || [ "$_cur_hp" = "$NEXUS_DIR/.rnex/hooks" ]; then
2192
- _hooks_dir="$NEXUS_DIR/.rnex/hooks"
2193
- _create_hook_script "$_hooks_dir/post-merge" "post-merge"
2194
- _create_hook_script "$_hooks_dir/post-commit" "post-commit"
2195
- _create_hook_script "$_hooks_dir/pre-commit" "pre-commit"
2196
- _create_hook_script "$_hooks_dir/pre-push" "pre-push"
2216
+ if [ "${_RNEX_INSIDE_HOOK:-0}" -ne 1 ]; then
2217
+ _hooks_dir="$NEXUS_DIR/.rnex/hooks"
2218
+ _create_hook_script "$_hooks_dir/post-merge" "post-merge"
2219
+ _create_hook_script "$_hooks_dir/post-commit" "post-commit"
2220
+ _create_hook_script "$_hooks_dir/pre-commit" "pre-commit"
2221
+ _create_hook_script "$_hooks_dir/pre-push" "pre-push"
2222
+ fi
2197
2223
  fi
2198
2224
  ;;
2199
2225
  esac