@@ -201,7 +201,7 @@ download_component() {
201201 block=$( echo " $MANIFEST " | tr ' \n' ' ' | grep -oP " \" component\"\\ s*:\\ s*\" ${component} \" [^}]*\" rid\"\\ s*:\\ s*\" ${RID} \" [^}]*}" | head -1)
202202 if [ -z " $block " ]; then
203203 # Try reversed order
204- block=$( echo " $MANIFEST " | tr ' \n' ' ' | grep -oP " \" rid\"\\ s*:\\ s*\" ${RID} \" [^}]*\" component\"\\ s*:\\ s*\" ${RID } \" [^}]*}" | head -1)
204+ block=$( echo " $MANIFEST " | tr ' \n' ' ' | grep -oP " \" rid\"\\ s*:\\ s*\" ${RID} \" [^}]*\" component\"\\ s*:\\ s*\" ${component } \" [^}]*}" | head -1)
205205 fi
206206 url=$( echo " $block " | grep -oP ' "url"\s*:\s*"\K[^"]+' )
207207 sha256=$( echo " $block " | grep -oP ' "sha256"\s*:\s*"\K[^"]+' )
@@ -309,17 +309,20 @@ if [ "$CHANNEL_EXPLICIT" = true ]; then
309309fi
310310
311311# ── Shell integration ─────────────────────────────────────────────────────
312- # Write an intermediary env script (~/.netclaw/env) and source it from the
313- # user's shell RC file. The env script self-guards at runtime so duplicate
314- # PATH entries cannot occur even if the RC is sourced multiple times.
315-
316- # Derive the parent directory of INSTALL_DIR — this is where the env script
317- # lives. For the default (~/.netclaw/bin) that's ~/.netclaw.
318- # We use dirname because the install dir may not yet exist when this block runs
319- # (e.g., if the user passes --skip-shell and no component needs the dir).
320- INSTALL_ROOT=" $( dirname " $INSTALL_DIR " ) "
321- ENV_SCRIPT=" $INSTALL_ROOT /env"
322- SOURCE_LINE=" . \" $ENV_SCRIPT \" "
312+ # Bash and zsh source a small POSIX env file. Fish gets native syntax in its
313+ # dedicated conf.d file; fish cannot source POSIX `case ... esac` syntax.
314+ ENV_SCRIPT=" $HOME /.netclaw/env"
315+
316+ shell_quote () {
317+ printf " '"
318+ printf ' %s' " $1 " | sed " s/'/'\\\\ ''/g"
319+ printf " '"
320+ }
321+
322+ INSTALL_DIR_QUOTED=" $( shell_quote " $INSTALL_DIR " ) "
323+ ENV_SCRIPT_QUOTED=" $( shell_quote " $ENV_SCRIPT " ) "
324+ SOURCE_LINE=" . $ENV_SCRIPT_QUOTED "
325+ MANUAL_PATH_LINE=" export PATH=$INSTALL_DIR_QUOTED :\$ PATH"
323326
324327detect_shell () {
325328 # $SHELL is inherited from the parent login shell — it reflects the user's
@@ -339,73 +342,56 @@ get_rc_file() {
339342 echo " ${ZDOTDIR:- $HOME } /.zshrc"
340343 ;;
341344 bash)
342- # macOS login shells read ~/.profile; Linux interactive shells
343- # read ~/.bashrc. We prefer .bashrc on Linux and .profile on macOS.
344345 if [ " $os " = " darwin" ]; then
345- echo " $HOME /.profile"
346+ # A login shell reads only the first existing file in this list.
347+ if [ -f " $HOME /.bash_profile" ]; then
348+ echo " $HOME /.bash_profile"
349+ elif [ -f " $HOME /.bash_login" ]; then
350+ echo " $HOME /.bash_login"
351+ else
352+ echo " $HOME /.profile"
353+ fi
346354 else
347355 echo " $HOME /.bashrc"
348356 fi
349357 ;;
350- fish)
351- local fish_conf_dir=" ${XDG_CONFIG_HOME:- $HOME / .config} /fish/conf.d"
352- echo " $fish_conf_dir /netclaw.fish"
353- ;;
354358 * )
355359 echo " "
356360 ;;
357361 esac
358362}
359363
360- write_env_script () {
361- # Create the self-guarding env script. Uses a colon-affixed case guard
362- # (rustup/fzf pattern) to prevent duplicate PATH entries at runtime.
364+ write_posix_env_script () {
363365 mkdir -p " $( dirname " $ENV_SCRIPT " ) "
364366 cat > " $ENV_SCRIPT " << ENVEOF
365367#!/bin/sh
366368# netclaw shell setup
369+ netclaw_bin=$INSTALL_DIR_QUOTED
367370case ":\$ {PATH}:" in
368- *:"$INSTALL_DIR ":*)
371+ *:"\$ {netclaw_bin} ":*)
369372 ;;
370373 *)
371- export PATH="$INSTALL_DIR :\$ {PATH}"
374+ export PATH="\$ {netclaw_bin} :\$ {PATH}"
372375 ;;
373376esac
377+ unset netclaw_bin
374378ENVEOF
375- chmod +x " $ENV_SCRIPT "
376379}
377380
378- modify_rc_file () {
379- local shell_name=" $1 "
380- local rc_file
381-
382- rc_file=" $( get_rc_file " $shell_name " ) "
383- if [ -z " $rc_file " ]; then
384- echo " Shell '$shell_name ' is not supported for automatic PATH setup."
385- echo " Add this to your shell profile:"
386- echo " "
387- echo " $SOURCE_LINE "
388- return 0
389- fi
390-
391- # Ensure the RC file's parent directory exists (fish conf.d may not)
381+ modify_posix_rc_file () {
382+ local rc_file=" $1 "
392383 mkdir -p " $( dirname " $rc_file " ) "
393-
394- # Touch the file so it exists — some users have no RC file yet
395384 touch " $rc_file "
396385
397- # Guard: check if the source line already exists
398386 if grep -qxF " $SOURCE_LINE " " $rc_file " 2> /dev/null; then
399387 echo " Shell profile '$rc_file ' already sources netclaw."
400388 return 0
401389 fi
402390
403- # Ensure a trailing newline before appending
404391 if [ -s " $rc_file " ] && [ " $( tail -c1 " $rc_file " | wc -l) " -eq 0 ]; then
405392 echo " " >> " $rc_file "
406393 fi
407394
408- # Append the marker comment and source line
409395 {
410396 echo " # netclaw shell setup"
411397 echo " $SOURCE_LINE "
@@ -414,35 +400,58 @@ modify_rc_file() {
414400 echo " Modified '$rc_file ' to add netclaw to PATH."
415401}
416402
403+ write_fish_config () {
404+ local fish_config_dir=" ${XDG_CONFIG_HOME:- $HOME / .config} /fish/conf.d"
405+ local fish_config=" $fish_config_dir /netclaw.fish"
406+ mkdir -p " $fish_config_dir "
407+ cat > " $fish_config " << FISHEOF
408+ # netclaw shell setup
409+ set -l netclaw_bin $INSTALL_DIR_QUOTED
410+ if not contains -- \$ netclaw_bin \$ PATH
411+ set -gx PATH \$ netclaw_bin \$ PATH
412+ end
413+ FISHEOF
414+ echo " Wrote '$fish_config ' to add netclaw to PATH."
415+ }
416+
417417if [ " $SKIP_SHELL " = false ]; then
418418 SHELL_NAME=" $( detect_shell) "
419419 echo " "
420420 echo " Setting up shell integration..."
421421
422- # Write env script first — it must exist before we tell the RC to source it
423- write_env_script
424-
425- # Modify the RC file to source the env script
426- modify_rc_file " $SHELL_NAME "
427-
428- echo " "
429- echo " Installation complete! netclaw is on your PATH."
430- echo " "
431- echo " Start a new shell, or run:"
432- echo " "
433- echo " $SOURCE_LINE "
422+ case " $SHELL_NAME " in
423+ bash|zsh)
424+ RC_FILE=" $( get_rc_file " $SHELL_NAME " ) "
425+ write_posix_env_script
426+ modify_posix_rc_file " $RC_FILE "
427+ echo " "
428+ echo " Installation complete! netclaw will be on PATH in new shells."
429+ echo " To update this shell, run:"
430+ echo " "
431+ echo " $SOURCE_LINE "
432+ ;;
433+ fish)
434+ write_fish_config
435+ echo " "
436+ echo " Installation complete! netclaw will be on PATH in new fish shells."
437+ echo " To update this shell, run:"
438+ echo " "
439+ echo " set -gx PATH $INSTALL_DIR_QUOTED \$ PATH"
440+ ;;
441+ * )
442+ echo " Shell '$SHELL_NAME ' is not supported for automatic PATH setup."
443+ echo " No shell profile was changed. Add this to your shell profile:"
444+ echo " "
445+ echo " $MANUAL_PATH_LINE "
446+ ;;
447+ esac
434448else
435- # --skip-shell was passed
436449 echo " "
437450 echo " Installation complete! (shell integration skipped)"
438451 echo " "
439452 echo " Add netclaw to your PATH by adding this to your shell profile:"
440453 echo " "
441- echo " $SOURCE_LINE "
442- echo " "
443- echo " Then restart your shell or run:"
444- echo " "
445- echo " source ~/.bashrc # or ~/.zshrc"
454+ echo " $MANUAL_PATH_LINE "
446455fi
447456
448457echo " "
@@ -451,4 +460,4 @@ echo " netclaw init # First-run setup wizard"
451460echo " netclaw doctor # Verify configuration"
452461if [ " $( uname -s) " = " Linux" ]; then
453462 echo " netclaw daemon install # Enable auto-start on boot (systemd)"
454- fi
463+ fi
0 commit comments