repo-nexus 0.7.38 → 0.7.39

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/lib/ai.sh CHANGED
@@ -6,6 +6,10 @@
6
6
 
7
7
  # ---- Gitignore & Search Tool Ignore Reconciliation -------------------------
8
8
 
9
+ # Reconcile workspace root .gitignore and .ignore:
10
+ # - Ensures repos/ is ignored in git to prevent accidental commits of member repos
11
+ # - Ensures .local.rnex.yaml is ignored
12
+ # - Ensures .ignore un-ignores repos/* so ripgrep and AI tools can index member files
9
13
  reconcile_gitignore() {
10
14
  _dir="${1:-$NEXUS_DIR}"
11
15
  _gi="$_dir/.gitignore"
@@ -60,6 +64,8 @@ GI_EOF
60
64
 
61
65
  # ---- AI Instructions Compilation (.rnex/ai-instructions.md) ----------------
62
66
 
67
+ # Compile the unified .rnex/ai-instructions.md file consumed by AI coding agents.
68
+ # Aggregates sample template instructions and active plugin instruction sections.
63
69
  build_ai_instructions() {
64
70
  _dir="${1:-$NEXUS_DIR}"
65
71
  _ai_file="$_dir/.rnex/ai-instructions.md"
@@ -97,7 +103,9 @@ Execute in sequence before task execution:
97
103
  Autonomous subagents are defined under `.rnex/agents/` and active plugin agents under `.rnex/plugins/<plugin-name>/agents/`.
98
104
 
99
105
  ## Member Repositories (`repos/<name>/`)
100
- Each member repository is an autonomous Git repository. For repository-specific instructions, inspect `repos/<name>/.rnex/`. Inspect only targeted files required for the task.
106
+ Each member repository is an autonomous Git repository. Inspect only targeted files required for the task.
107
+ If repository-specific context exists, it is stored under `repos/<name>/.rnex/`.
108
+ Repositories marked `readonly: true` in configuration are reference-only: DO NOT modify, write to, or commit changes in read-only repositories; use them strictly for information, code exploration, and schema references.
101
109
 
102
110
  ## Workspace Maintenance
103
111
  When workspace configuration, routing blocks, plugin assets, or member repository structures require reconciliation or schema upgrades:
@@ -105,7 +113,7 @@ When workspace configuration, routing blocks, plugin assets, or member repositor
105
113
  - Use `rnex sync -y` to non-destructively apply configuration version upgrades automatically.
106
114
 
107
115
  ## Git Operations
108
- Commit and push changes directly within the respective member repository root (`repos/<name>/`).
116
+ Commit and push changes directly within the respective member repository root (`repos/<name>/`). For read-only repositories (`readonly: true`), never commit, edit, or push changes.
109
117
  BASE_AI_EOF
110
118
  fi
111
119
 
@@ -150,6 +158,8 @@ BASE_AI_EOF
150
158
 
151
159
  # ---- Multi-Root IDE Workspace (.code-workspace) ----------------------------
152
160
 
161
+ # Generate and maintain a multi-root VS Code / Cursor workspace file (.code-workspace).
162
+ # Adds workspace root and each active member repository as first-class workspace folders.
153
163
  sync_code_workspace() {
154
164
  [ -n "$CODE_WORKSPACE_FILE" ] || return 0
155
165
  _ws_path="$NEXUS_DIR/$CODE_WORKSPACE_FILE"
@@ -260,6 +270,7 @@ get_active_provider_skill_dirs() {
260
270
  claude) _sdir=".claude/skills" ;;
261
271
  windsurf) _sdir=".windsurf/rules" ;;
262
272
  gemini) _sdir=".agents/skills" ;;
273
+ codex) _sdir=".codex/skills" ;;
263
274
  esac
264
275
  fi
265
276
  if [ -n "$_sdir" ]; then
@@ -273,6 +284,8 @@ get_active_provider_skill_dirs() {
273
284
  printf '%s' "$_dirs"
274
285
  }
275
286
 
287
+ # Synchronize built-in core agentic skills into .rnex/skills/ and register
288
+ # zero-logic user skill pointers across all active AI assistant directories.
276
289
  sync_rnex_base_assets() {
277
290
  _src_toolkit=""
278
291
  if [ -d "$SCRIPT_LOCATION/toolkit" ]; then
@@ -4,6 +4,10 @@
4
4
  # lib/commands/completion.sh — Shell completion generators (bash, zsh, fish)
5
5
  # ============================================================================
6
6
 
7
+ # Generate tab-completion script for the requested shell (bash, zsh, or fish).
8
+ # Usage:
9
+ # source <(rnex completion bash)
10
+ # source <(rnex completion zsh)
7
11
  cmd_completion() {
8
12
  _shell="${1:-bash}"
9
13
  case "$_shell" in
@@ -14,7 +18,7 @@ _rnex_completions() {
14
18
  COMPREPLY=()
15
19
  cur="${COMP_WORDS[COMP_CWORD]}"
16
20
  prev="${COMP_WORDS[COMP_CWORD-1]}"
17
- commands="init install add clone exec remove enable disable list sync status rnex-dir update upgrade hooks plugin completion version help"
21
+ commands="init install add clone exec remove enable disable readonly list sync status rnex-dir update upgrade hooks plugin completion version help"
18
22
 
19
23
  case "$prev" in
20
24
  rnex)
@@ -37,7 +41,7 @@ _rnex_completions() {
37
41
  COMPREPLY=( $(compgen -W "install uninstall status" -- "$cur") )
38
42
  return 0
39
43
  ;;
40
- enable|disable|remove|show|hide|rnex-dir)
44
+ enable|disable|readonly|remove|show|hide|rnex-dir)
41
45
  local repos
42
46
  repos=$(rnex list 2>/dev/null | awk 'NR>2 {print $1}')
43
47
  COMPREPLY=( $(compgen -W "$repos" -- "$cur") )
@@ -81,6 +85,7 @@ _rnex() {
81
85
  'remove:Unregister and delete a repository'
82
86
  'enable:Enable a repository in active workspace'
83
87
  'disable:Disable a repository in workspace'
88
+ 'readonly:Toggle read-only (information-only) status for a repo'
84
89
  'list:List member repositories'
85
90
  'sync:Reconcile workspace, plugins, skills, and member .rnex directories'
86
91
  'status:Inspect workspace health and status (read-only)'
@@ -124,7 +129,7 @@ _rnex() {
124
129
  hook_cmds=('install:Install Git hooks' 'uninstall:Uninstall Git hooks' 'status:Show Git hooks status')
125
130
  _describe -t hook_cmds 'hooks command' hook_cmds
126
131
  ;;
127
- enable|disable|remove|show|hide|rnex-dir)
132
+ enable|disable|readonly|remove|show|hide|rnex-dir)
128
133
  local -a repos
129
134
  repos=($(rnex list 2>/dev/null | awk 'NR>2 {print $1}'))
130
135
  _describe -t repos 'repository' repos
@@ -163,6 +168,9 @@ complete -c rnex -n "__fish_use_subcommand" -a exec -d "Execute command across m
163
168
  complete -c rnex -n "__fish_use_subcommand" -a remove -d "Remove a repository"
164
169
  complete -c rnex -n "__fish_use_subcommand" -a enable -d "Enable a repository"
165
170
  complete -c rnex -n "__fish_use_subcommand" -a disable -d "Disable a repository"
171
+ complete -c rnex -n "__fish_use_subcommand" -a readonly -d "Toggle read-only status for a repo"
172
+ complete -c rnex -n "__fish_seen_subcommand_from readonly" -a "enable disable"
173
+ complete -c rnex -n "__fish_seen_subcommand_from readonly" -l local -d "Apply locally"
166
174
  complete -c rnex -n "__fish_use_subcommand" -a list -d "List repositories"
167
175
  complete -c rnex -n "__fish_use_subcommand" -a rnex-dir -d "Toggle .rnex directory integration for a repo"
168
176
  complete -c rnex -n "__fish_seen_subcommand_from rnex-dir" -a "enable disable"
@@ -4,6 +4,11 @@
4
4
  # lib/commands/init.sh — Workspace initialization command
5
5
  # ============================================================================
6
6
 
7
+ # Initialize a new Repo Nexus workspace in the specified directory (or current directory):
8
+ # Sets up rnex.yaml, .rnex/ harness, gitignore rules, routing instructions in AGENTS.md,
9
+ # enabled plugins (e.g. --claude, --gemini, --karpathy), and optional VS Code / Cursor workspace.
10
+ # Usage:
11
+ # rnex init [-y|--yes] [--hooks|--no-hooks] [--<plugin>] [<directory>]
7
12
  cmd_init() {
8
13
  _non_interactive=0
9
14
  _init_dir=""
@@ -165,7 +170,7 @@ YAML_EOF
165
170
  if ! has_active_provider_instructions "$_init_dir"; then
166
171
  merge_rnex_instructions "$_init_dir/AGENTS.md"
167
172
  fi
168
- for _if in CLAUDE.md GEMINI.md .cursorrules .windsurfrules .github/copilot-instructions.md; do
173
+ for _if in CLAUDE.md GEMINI.md CODEX.md .cursorrules .windsurfrules .github/copilot-instructions.md; do
169
174
  [ -f "$_init_dir/$_if" ] && merge_rnex_instructions "$_init_dir/$_if"
170
175
  done
171
176
 
@@ -216,7 +221,7 @@ YAML_EOF
216
221
  log_dim "Created $_created_items"
217
222
  printf '\n'
218
223
  log_info "Next steps:"
219
- log_dim "1. Add repositories with: rnex add <name> <git-url>"
224
+ log_dim "1. Add repositories with: rnex add [<name>] <git-url>"
220
225
  log_dim "2. Clone team repositories: rnex clone"
221
226
  log_dim "3. Run commands across all repos: rnex exec <command>"
222
227
  fi
@@ -4,6 +4,7 @@
4
4
  # lib/commands/plugin.sh — Plugin management commands
5
5
  # ============================================================================
6
6
 
7
+ # List all available, enabled, and disabled plugins found in the workspace toolkit.
7
8
  cmd_plugin_list() {
8
9
  require_workspace
9
10
  printf '\n%b%s%b\n' "$_B" "Repo Nexus Plugins" "$_NC"
@@ -35,6 +36,7 @@ cmd_plugin_list() {
35
36
  printf '\n'
36
37
  }
37
38
 
39
+ # Display detailed metadata, configurable options, and template paths for a specific plugin.
38
40
  cmd_plugin_info() {
39
41
  require_workspace
40
42
  _pname="$1"
@@ -73,6 +75,7 @@ cmd_plugin_info() {
73
75
  gemini) _skill_target=".agents/skills/" ;;
74
76
  cursor) _skill_target=".cursor/rules/" ;;
75
77
  windsurf) _skill_target=".windsurf/rules/" ;;
78
+ codex) _skill_target=".codex/skills/" ;;
76
79
  esac
77
80
  case "$_skill_target" in
78
81
  */|"") ;;
@@ -144,6 +147,7 @@ cmd_plugin_info() {
144
147
  printf '\n'
145
148
  }
146
149
 
150
+ # Enable a plugin in workspace configuration (team or local) and sync assets.
147
151
  cmd_plugin_enable() {
148
152
  require_workspace
149
153
  _is_local=0
@@ -176,6 +180,7 @@ cmd_plugin_enable() {
176
180
  log_ok "Plugin '$_name' active in workspace (.rnex/plugins/$_name)"
177
181
  }
178
182
 
183
+ # Disable a plugin, dispatch before_plugin_disable hook, and prune its assets.
179
184
  cmd_plugin_disable() {
180
185
  require_workspace
181
186
  _is_local=0
@@ -215,6 +220,9 @@ cmd_plugin_disable() {
215
220
  cmd_sync -q
216
221
  }
217
222
 
223
+ # Toggle the member repository .rnex harness (enable or disable) for a specific repo.
224
+ # Usage:
225
+ # rnex rnex-dir <enable|disable> <name> [--local]
218
226
  cmd_rnex_dir() {
219
227
  require_workspace
220
228
  _action=""
@@ -4,25 +4,31 @@
4
4
  # lib/commands/repo.sh — Member repository management commands
5
5
  # ============================================================================
6
6
 
7
+ # Register a new repository in workspace configuration and clone it into ./repos/<name>.
8
+ # Usage:
9
+ # rnex add [--local] [--disabled] [--readonly] [<name>] <git-url>
10
+ # If <name> is omitted, it is inferred from the last component of <git-url>.
7
11
  cmd_add() {
8
12
  require_workspace
9
- _name=""
10
- _url=""
11
- _rnex_dir="true"
13
+ _arg1=""
14
+ _arg2=""
12
15
  _enabled="true"
16
+ _readonly="false"
13
17
  _is_local=0
14
18
 
15
19
  while [ $# -gt 0 ]; do
16
20
  case "$1" in
17
21
  --local) _is_local=1 ;;
18
22
  --disabled) _enabled="false" ;;
19
- --no-rnex-dir|--rnex-dir=false) _rnex_dir="false" ;;
20
- --rnex-dir|--rnex-dir=true) _rnex_dir="true" ;;
23
+ --readonly) _readonly="true" ;;
24
+ --no-rnex-dir|--rnex-dir=false|--rnex-dir|--rnex-dir=true)
25
+ # Obsolete flag ignored after transition to on-demand member .rnex
26
+ ;;
21
27
  *)
22
- if [ -z "$_name" ]; then
23
- _name="$1"
24
- elif [ -z "$_url" ]; then
25
- _url="$1"
28
+ if [ -z "$_arg1" ]; then
29
+ _arg1="$1"
30
+ elif [ -z "$_arg2" ]; then
31
+ _arg2="$1"
26
32
  else
27
33
  die "Unexpected argument: $1"
28
34
  fi
@@ -31,8 +37,21 @@ cmd_add() {
31
37
  shift
32
38
  done
33
39
 
34
- [ -n "$_name" ] || die "Usage: rnex add [--local] [--disabled] [--no-rnex-dir] <name> <git-url>"
35
- [ -n "$_url" ] || die "Usage: rnex add [--local] [--disabled] [--no-rnex-dir] <name> <git-url>"
40
+ # Derive repo name and URL:
41
+ # - If 2 positional args: <name> <git-url>
42
+ # - If 1 positional arg: <git-url> (name inferred from the last component of the path/URL)
43
+ if [ -n "$_arg2" ]; then
44
+ _name="$_arg1"
45
+ _url="$_arg2"
46
+ elif [ -n "$_arg1" ]; then
47
+ _url="$_arg1"
48
+ _name="$(repo_name_from_path "$_url")"
49
+ else
50
+ die "Usage: rnex add [--local] [--disabled] [--readonly] [<name>] <git-url>"
51
+ fi
52
+
53
+ [ -n "$_name" ] || die "Could not determine repository name from '$_url'"
54
+ [ -n "$_url" ] || die "Usage: rnex add [--local] [--disabled] [--readonly] [<name>] <git-url>"
36
55
 
37
56
  ensure_repos_dir
38
57
  _target_dir="$(resolve_repo_path "$_name")"
@@ -47,7 +66,7 @@ cmd_add() {
47
66
 
48
67
  if [ "$_is_local" -eq 1 ]; then
49
68
  yaml_set_local "$_name" "url" "$_url"
50
- [ "$_rnex_dir" = "false" ] && yaml_set_local "$_name" "rnex_dir" "false"
69
+ [ "$_readonly" = "true" ] && yaml_set_local "$_name" "readonly" "true"
51
70
  [ "$_enabled" = "false" ] && yaml_set_local "$_name" "enabled" "false"
52
71
  sync_repo_rnex "$_name"
53
72
  sync_code_workspace
@@ -64,7 +83,7 @@ cmd_add() {
64
83
  die "Repo '$_name' is already registered in $(basename "$WORKSPACE_YAML")"
65
84
  else
66
85
  yaml_set "$_name" "url" "$_url"
67
- [ "$_rnex_dir" = "false" ] && yaml_set "$_name" "rnex_dir" "false"
86
+ [ "$_readonly" = "true" ] && yaml_set "$_name" "readonly" "true"
68
87
  [ "$_enabled" = "false" ] && yaml_set "$_name" "enabled" "false"
69
88
  sync_repo_rnex "$_name"
70
89
  sync_code_workspace
@@ -75,7 +94,7 @@ cmd_add() {
75
94
  fi
76
95
  fi
77
96
 
78
- yaml_add_repo "$_name" "$_url" "$_rnex_dir" "$_enabled"
97
+ yaml_add_repo "$_name" "$_url" "$_enabled" "$_readonly"
79
98
  sync_repo_rnex "$_name"
80
99
  sync_code_workspace
81
100
  run_hook "after_sync" "$@"
@@ -83,6 +102,8 @@ cmd_add() {
83
102
  log_ok "Registered and added repo '${_name}'"
84
103
  }
85
104
 
105
+ # Clone all enabled repositories declared in configuration that are not yet cloned.
106
+ # Skips disabled repositories and already existing clones.
86
107
  cmd_clone() {
87
108
  require_workspace
88
109
  ensure_repos_dir
@@ -127,6 +148,8 @@ cmd_clone() {
127
148
  printf '\n'
128
149
  }
129
150
 
151
+ # Execute a shell command across all enabled member repositories in ./repos/<name>.
152
+ # Propagates non-zero exit codes if any repository command fails.
130
153
  cmd_exec() {
131
154
  require_workspace
132
155
  [ $# -gt 0 ] || die "Usage: rnex exec <command...>"
@@ -198,6 +221,7 @@ _repo_toggle_state() {
198
221
  _repo_set_config_and_log "$_name" "enabled" "$_target_val" "$_is_local" "$_verb"
199
222
  }
200
223
 
224
+ # Enable a member repository in configuration and reconcile its workspace state.
201
225
  cmd_enable() {
202
226
  _repo_toggle_state "true" "Enabled repo" "$@"
203
227
  sync_repo_rnex "$_name"
@@ -205,6 +229,7 @@ cmd_enable() {
205
229
  run_hook "after_sync" "$@"
206
230
  }
207
231
 
232
+ # Disable a member repository in configuration (skips execution and hides from IDE).
208
233
  cmd_disable() {
209
234
  _repo_toggle_state "false" "Disabled repo" "$@"
210
235
  sync_code_workspace
@@ -218,6 +243,57 @@ cmd_hide() {
218
243
  cmd_disable "$@"
219
244
  }
220
245
 
246
+ # Toggle the member repository read-only setting (enable or disable) for a specific repo.
247
+ # When a repository is read-only, it is strictly used for information / reference.
248
+ # Usage:
249
+ # rnex readonly <enable|disable> <name> [--local]
250
+ cmd_readonly() {
251
+ require_workspace
252
+ _action=""
253
+ _name=""
254
+ _is_local=0
255
+
256
+ while [ $# -gt 0 ]; do
257
+ case "$1" in
258
+ --local) _is_local=1 ;;
259
+ enable|true|yes|1)
260
+ if [ -z "$_action" ]; then _action="true"; else _name="$1"; fi
261
+ ;;
262
+ disable|false|no|0)
263
+ if [ -z "$_action" ]; then _action="false"; else _name="$1"; fi
264
+ ;;
265
+ *)
266
+ if [ -z "$_action" ] && [ "$1" = "enable" ] || [ "$1" = "disable" ]; then
267
+ _action="$1"
268
+ elif [ -z "$_name" ]; then
269
+ _name="$1"
270
+ elif [ -z "$_action" ]; then
271
+ _action="$1"
272
+ fi
273
+ ;;
274
+ esac
275
+ shift
276
+ done
277
+
278
+ case "$_action" in
279
+ enable|true|yes|1) _action="true" ;;
280
+ disable|false|no|0) _action="false" ;;
281
+ "") _action="true" ;;
282
+ esac
283
+
284
+ [ -n "$_name" ] || die "Usage: rnex readonly <enable|disable> <name> [--local]"
285
+ yaml_has_repo "$_name" || die "Repo '$_name' is not registered"
286
+
287
+ if [ "$_action" = "true" ]; then
288
+ _repo_set_config_and_log "$_name" "readonly" "true" "$_is_local" "Marked repo as read-only"
289
+ else
290
+ _repo_set_config_and_log "$_name" "readonly" "false" "$_is_local" "Disabled read-only mode for"
291
+ fi
292
+ sync_code_workspace
293
+ run_hook "after_sync" "$@"
294
+ }
295
+
296
+ # Remove a member repository from configuration and delete its local directory in ./repos/<name>.
221
297
  cmd_remove() {
222
298
  require_workspace
223
299
  _name=""
@@ -251,9 +327,11 @@ cmd_remove() {
251
327
  run_hook "after_sync" "$@"
252
328
  }
253
329
 
330
+ # Print a tabular status list of all registered member repositories, showing
331
+ # state (enabled/disabled), clone status, active git branch, readonly status, and URL.
254
332
  cmd_list() {
255
333
  require_workspace
256
- printf '\n%b%-18s %-10s %-8s %-14s %-10s %s%b\n' "$_B" "NAME" "STATE" "CLONED" "BRANCH" "RNEX_DIR" "URL" "$_NC"
334
+ printf '\n%b%-18s %-10s %-8s %-14s %-10s %s%b\n' "$_B" "NAME" "STATE" "CLONED" "BRANCH" "READONLY" "URL" "$_NC"
257
335
  printf '%-18s %-10s %-8s %-14s %-10s %s\n' "----" "-----" "------" "------" "--------" "---"
258
336
  yaml_list_repos | while read -r _name; do
259
337
  [ -n "$_name" ] || continue
@@ -275,13 +353,13 @@ cmd_list() {
275
353
  [ -n "$_b" ] && _branch="$_b"
276
354
  fi
277
355
 
278
- _rd="true"
279
- if ! repo_rnex_dir_enabled "$_name"; then
280
- _rd="false"
356
+ _ro="false"
357
+ if yaml_repo_readonly "$_name"; then
358
+ _ro="${_Y}true${_NC}"
281
359
  fi
282
360
 
283
- printf '%-18s %-19b %-17b %-14s %-10s %s\n' \
284
- "$_name" "$_state" "$_cloned" "$_branch" "$_rd" "${_url:-(none)}"
361
+ printf '%-18s %-19b %-17b %-14s %-19b %s\n' \
362
+ "$_name" "$_state" "$_cloned" "$_branch" "$_ro" "${_url:-(none)}"
285
363
  done
286
364
  printf '\n'
287
365
  }
@@ -4,6 +4,7 @@
4
4
  # lib/commands/status.sh — Workspace health and status diagnostics
5
5
  # ============================================================================
6
6
 
7
+ # Query active plugins for any non-manual lint triggers (e.g. git hook triggers).
7
8
  _status_active_plugin_trigger() {
8
9
  for _p in $(yaml_list_plugins 2>/dev/null || true); do
9
10
  yaml_plugin_enabled "$_p" 2>/dev/null || continue
@@ -17,10 +18,14 @@ _status_active_plugin_trigger() {
17
18
  return 0
18
19
  }
19
20
 
21
+ # Check if a directory contains any rnex-* user skills or skill symlinks.
20
22
  _status_has_rnex_skills() {
21
23
  [ -d "$1" ] && { ls "$1"/rnex-*.md >/dev/null 2>&1 || ls -d "$1"/rnex-* >/dev/null 2>&1; }
22
24
  }
23
25
 
26
+ # Print comprehensive read-only workspace diagnostic report:
27
+ # Shows config versions, gitignore rules, AI context routing, registered plugins,
28
+ # git hooks status, and member repository status.
24
29
  cmd_status() {
25
30
  require_workspace
26
31
  ensure_repos_dir
@@ -146,6 +151,7 @@ cmd_status() {
146
151
  for _pair in \
147
152
  "CLAUDE.md:Claude" \
148
153
  "GEMINI.md:Gemini" \
154
+ "CODEX.md:Codex" \
149
155
  ".cursorrules:Cursor" \
150
156
  ".windsurfrules:Windsurf" \
151
157
  ".github/copilot-instructions.md:Copilot"; do
@@ -179,6 +185,7 @@ cmd_status() {
179
185
  ".windsurf/rules/:Windsurf" \
180
186
  ".agents/skills/:Gemini" \
181
187
  ".gemini/skills/:Gemini" \
188
+ ".codex/skills/:Codex" \
182
189
  ".github/skills/:Copilot"; do
183
190
  _sdir="${_spair%%:*}"
184
191
  _slbl="${_spair#*:}"
@@ -235,7 +242,7 @@ cmd_status() {
235
242
  printf '%b%s%b\n' "$_BOLD" "Member Repositories:" "$_NC"
236
243
  _all_repos="$(yaml_list_repos)"
237
244
  if [ -z "$_all_repos" ]; then
238
- printf ' %b%s%b\n' "$_DIM" "(No repositories registered yet. Use 'rnex add <name> <git-url>')" "$_NC"
245
+ printf ' %b%s%b\n' "$_DIM" "(No repositories registered yet. Use 'rnex add [<name>] <git-url>')" "$_NC"
239
246
  else
240
247
  echo "$_all_repos" | while read -r _name; do
241
248
  [ -n "$_name" ] || continue
@@ -247,17 +254,14 @@ cmd_status() {
247
254
  printf ' %b○%b %-18s %b[disabled]%b %s\n' "$_DIM" "$_NC" "$_name" "$_DIM" "$_NC" "${_url:+- $_url}"
248
255
  elif [ -d "$_rdir/.git" ]; then
249
256
  _b="$(git -C "$_rdir" symbolic-ref --short HEAD 2>/dev/null || git -C "$_rdir" rev-parse --short HEAD 2>/dev/null || echo "head")"
250
- _rnex_tag=""
251
- if repo_rnex_dir_enabled "$_name"; then
252
- if [ -d "$_rdir/.rnex" ]; then
253
- _rnex_tag=" ${_DIM}[.rnex ✓]${_NC}"
254
- else
255
- _rnex_tag=" ${_Y}[.rnex pending]${_NC}"
256
- fi
257
- else
258
- _rnex_tag=" ${_DIM}[.rnex off]${_NC}"
257
+ _tags=""
258
+ if yaml_repo_readonly "$_name"; then
259
+ _tags=" ${_Y}[readonly]${_NC}"
260
+ fi
261
+ if [ -d "$_rdir/.rnex" ]; then
262
+ _tags="${_tags} ${_DIM}[.rnex]${_NC}"
259
263
  fi
260
- printf ' %b●%b %-18s %b[%s]%b%b %s\n' "$_G" "$_NC" "$_name" "$_C" "$_b" "$_NC" "$_rnex_tag" "${_url:+- $_url}"
264
+ printf ' %b●%b %-18s %b[%s]%b%b %s\n' "$_G" "$_NC" "$_name" "$_C" "$_b" "$_NC" "$_tags" "${_url:+- $_url}"
261
265
  else
262
266
  printf ' %b✗%b %-18s %b[missing — run "rnex clone"]%b %s\n' "$_R" "$_NC" "$_name" "$_R" "$_NC" "${_url:+- $_url}"
263
267
  fi
@@ -4,6 +4,8 @@
4
4
  # lib/commands/sync.sh — Workspace reconciliation and compilation
5
5
  # ============================================================================
6
6
 
7
+ # Write the standalone .rnex/bin/auto-sync.sh helper used by hooks and subshells
8
+ # to rapidly verify workspace stamp checksum and trigger rnex sync on config changes.
7
9
  _write_auto_sync_script() {
8
10
  _dir="${1:-$NEXUS_DIR}"
9
11
  _bin_dir="$_dir/.rnex/bin"
@@ -31,6 +33,12 @@ EOF
31
33
  chmod +x "$_script"
32
34
  }
33
35
 
36
+ # Primary workspace reconciliation command:
37
+ # Reconciles workspace configuration version, .gitignore / .ignore routing, AGENTS.md,
38
+ # core skills, AI instruction compilation, active plugins, Git hooks, member .rnex
39
+ # harnesses, and VS Code / Cursor workspace files.
40
+ # Usage:
41
+ # rnex sync [-y|--yes] [-q|--quiet] [--upgrade]
34
42
  cmd_sync() {
35
43
  require_workspace
36
44
  ensure_repos_dir
@@ -4,6 +4,7 @@
4
4
  # lib/commands/system.sh — System commands: install, version, help
5
5
  # ============================================================================
6
6
 
7
+ # Install rnex and repo-nexus executables and asset libraries into user bin and share directories.
7
8
  cmd_install() {
8
9
  _target_bin="${1:-${RNEX_BIN_DIR:-$HOME/.local/bin}}"
9
10
  _target_bin="$(resolve_path "$_target_bin")"
@@ -56,10 +57,12 @@ cmd_install() {
56
57
  esac
57
58
  }
58
59
 
60
+ # Output current Repo Nexus version from package.json.
59
61
  cmd_version() {
60
62
  printf 'rnex %s\n' "$RNEX_VERSION"
61
63
  }
62
64
 
65
+ # Check workspace configuration version against current CLI and run sync to upgrade if needed.
63
66
  cmd_update() {
64
67
  require_workspace
65
68
  _cmp=0
@@ -76,6 +79,7 @@ cmd_upgrade() {
76
79
  cmd_update "$@"
77
80
  }
78
81
 
82
+ # Display complete CLI usage, command catalog, options, and practical examples.
79
83
  cmd_help() {
80
84
  cat <<EOF
81
85
 
@@ -91,7 +95,7 @@ cmd_help() {
91
95
  -v, --version Show version
92
96
 
93
97
  WORKSPACE & SYSTEM SETUP
94
- init [-y] [--code-workspace [file]] [--hooks|--no-hooks] [--copilot] [--claude] [--gemini] [--cursor] [--windsurf] [--karpathy] [dir]
98
+ init [-y] [--code-workspace [file]] [--hooks|--no-hooks] [--copilot] [--claude] [--gemini] [--codex] [--cursor] [--windsurf] [--karpathy] [dir]
95
99
  Initialize a new Repo Nexus workspace (default: AGENTS.md)
96
100
  install [dir] Install rnex & repo-nexus globally into ~/.local/bin (or custom dir)
97
101
  sync [-y] [--quiet] Reconcile workspace, plugins, skills, member .rnex directories, and compile AI instructions
@@ -103,15 +107,16 @@ cmd_help() {
103
107
  completion <shell> Generate shell completion script (bash, zsh, fish)
104
108
 
105
109
  REPO MANAGEMENT
106
- add [--local] [--disabled] [--no-rnex-dir] <name> <git-url>
110
+ add [--local] [--disabled] [--readonly] [<name>] <git-url>
107
111
  Register and clone repo (--local writes to .local.rnex.yaml)
108
112
  clone Clone all missing enabled repositories declared in rnex.yaml
109
113
  exec <command...> Execute a shell command across all active member repositories
110
114
  remove <name> Unregister and delete member repository
111
115
  enable [--local] <name> Enable a member repository in active workspace
112
116
  disable [--local] <name> Disable a member repository in workspace
117
+ readonly [--local] <enable|disable> <name>
118
+ Toggle read-only (information-only) status for a member repo
113
119
  list List all member repositories with clone state & active branch
114
- rnex-dir <enable|disable> <name> Toggle .rnex directory integration for a member repo
115
120
 
116
121
  PLUGIN MANAGEMENT
117
122
  plugin [list] List available and enabled plugins
package/lib/core.sh CHANGED
@@ -5,7 +5,7 @@
5
5
  # ============================================================================
6
6
 
7
7
  # ---- Terminal Colours (auto-detected) -------------------------------------
8
-
8
+ # ANSI color codes enabled only when stdout is an interactive terminal (TTY)
9
9
  if [ -t 1 ]; then
10
10
  _R='\033[0;31m' _G='\033[0;32m' _Y='\033[1;33m' _B='\033[0;34m' _C='\033[0;36m'
11
11
  _BOLD='\033[1m' _DIM='\033[2m' _NC='\033[0m'
@@ -13,23 +13,40 @@ else
13
13
  _R='' _G='' _Y='' _B='' _C='' _BOLD='' _DIM='' _NC=''
14
14
  fi
15
15
 
16
- # ---- Logging ---------------------------------------------------------------
16
+ # ---- Logging Utilities ----------------------------------------------------
17
+ # Standardized CLI messages with symbol prefixes, respecting quiet flags.
17
18
 
19
+ # Print green success checkmark [✓]
18
20
  log_ok() { [ "${RNEX_QUIET:-0}" -eq 1 ] || [ "${_sync_quiet:-0}" -eq 1 ] && return 0; printf '%b[✓]%b %b\n' "$_G" "$_NC" "$1"; }
21
+
22
+ # Print yellow warning indicator [!]
19
23
  log_warn() { [ "${RNEX_QUIET:-0}" -eq 1 ] || [ "${_sync_quiet:-0}" -eq 1 ] && return 0; printf '%b[!]%b %b\n' "$_Y" "$_NC" "$1"; }
24
+
25
+ # Print red failure cross [✗] to stderr
20
26
  log_err() { printf '%b[✗]%b %b\n' "$_R" "$_NC" "$1" >&2; }
27
+
28
+ # Print cyan information notice [i]
21
29
  log_info() { [ "${RNEX_QUIET:-0}" -eq 1 ] || [ "${_sync_quiet:-0}" -eq 1 ] && return 0; printf '%b[i]%b %b\n' "$_C" "$_NC" "$1"; }
30
+
31
+ # Print dimmed indented detail text
22
32
  log_dim() { [ "${RNEX_QUIET:-0}" -eq 1 ] || [ "${_sync_quiet:-0}" -eq 1 ] && return 0; printf '%b %b%b\n' "$_DIM" "$1" "$_NC"; }
33
+
34
+ # Log fatal error message to stderr and terminate with non-zero exit code
23
35
  die() { log_err "$1"; exit 1; }
24
36
 
25
37
  # ---- Path Utilities --------------------------------------------------------
26
38
 
39
+ # Canonicalize any relative, absolute, or tilde-prefixed path into an absolute physical path.
40
+ # Preserves non-existent destination targets while resolving parent directories.
27
41
  resolve_path() {
28
42
  _input="$1"
43
+ # Expand tilde (~ or ~/path) to user HOME directory
29
44
  case "$_input" in
30
45
  \~/*) _input="$HOME/${_input#\~/}" ;;
31
46
  \~) _input="$HOME" ;;
32
47
  esac
48
+
49
+ # Canonicalize absolute or relative paths
33
50
  case "$_input" in
34
51
  /*)
35
52
  if [ -d "$_input" ]; then
@@ -52,7 +69,8 @@ resolve_path() {
52
69
  esac
53
70
  }
54
71
 
55
- # Return 0 unless value is explicitly falsy (false, no, 0, disabled, off, none)
72
+ # Determine if a configuration value is enabled.
73
+ # Returns 0 (true) by default unless explicitly set to a falsy value (false, no, 0, disabled, off, none).
56
74
  is_enabled_val() {
57
75
  _iev="$(printf '%s' "$1" | tr '[:upper:]' '[:lower:]')"
58
76
  case "$_iev" in
@@ -61,7 +79,8 @@ is_enabled_val() {
61
79
  esac
62
80
  }
63
81
 
64
- # Return 0 if value is explicitly truthy (true, yes, 1, on, enabled)
82
+ # Strict check for truthy values.
83
+ # Returns 0 (true) only if explicitly true, yes, 1, on, or enabled; otherwise returns 1.
65
84
  is_truthy() {
66
85
  _it="$(printf '%s' "$1" | tr '[:upper:]' '[:lower:]')"
67
86
  case "$_it" in
@@ -70,7 +89,8 @@ is_truthy() {
70
89
  esac
71
90
  }
72
91
 
73
- # Strip surrounding whitespace, quotes, leading ./ or /, and trailing /
92
+ # Normalize a relative path string by stripping surrounding whitespace, quotes,
93
+ # leading ./ or /, and trailing slashes.
74
94
  normalize_rel_path() {
75
95
  printf '%s' "$1" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' | tr -d "'\"" | sed -e 's#^\./##' -e 's#^/##' -e 's#/$##'
76
96
  }
@@ -78,6 +98,8 @@ normalize_rel_path() {
78
98
 
79
99
  # ---- Version Resolution (package.json is single source of truth) ----------
80
100
 
101
+ # Dynamically locate the nearest package.json and extract its "version" field.
102
+ # Falls back to 0.5.1 if package.json cannot be read.
81
103
  resolve_version() {
82
104
  _sdir="$(dirname "$SCRIPT_LOCATION")"
83
105
  for _cand in "$SCRIPT_LOCATION/package.json" "$_sdir/package.json" "$_sdir/../package.json"; do
@@ -94,7 +116,11 @@ resolve_version() {
94
116
 
95
117
  RNEX_VERSION="$(resolve_version)"
96
118
 
97
- # Compare version strings: returns 0 if $1 == $2, 1 if $1 > $2, 2 if $1 < $2
119
+ # Compare two dot-separated version strings (e.g., "0.7.38" vs "0.7.39").
120
+ # Returns:
121
+ # 0 if $1 == $2
122
+ # 1 if $1 > $2
123
+ # 2 if $1 < $2
98
124
  version_cmp() {
99
125
  [ "$1" = "$2" ] && return 0
100
126
  _v1="$1"