Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 27 additions & 19 deletions config/openclaw/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -11,25 +11,33 @@ let

mode = if host.isKyber then "gateway" else "client";

hydrateScript = pkgs.replaceVars ./hydrate.sh (
{
sed = "${pkgs.gnused}/bin/sed";
template = ./openclaw.template.json;
inherit mode;
}
// (
if host.isKyber then
{
chromium = pkgs.chromium;
openclaw = "${homeDir}/.bun";
}
else
{
chromium = "/unused";
openclaw = "/unused";
}
)
);
# Use writeText instead of replaceVars to avoid builtins.toFile context warnings
hydrateScript =
let
vars = {
sed = "${pkgs.gnused}/bin/sed";
template = "${./openclaw.template.json}";
inherit mode;
}
// (
if host.isKyber then
{
chromium = "${pkgs.chromium}";
openclaw = "${homeDir}/.bun";
}
else
{
chromium = "/unused";
openclaw = "/unused";
}
);
names = builtins.attrNames vars;
in
pkgs.writeText "hydrate.sh" (
builtins.replaceStrings (map (n: "@${n}@") names) (map (n: builtins.toString vars.${n}) names) (
builtins.readFile ./hydrate.sh
)
);
in
{
# Hydrate OpenClaw config from .env secrets
Expand Down
9 changes: 9 additions & 0 deletions home-manager/programs/fish/functions/_clrc_function.fish
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
function _clrc_function --description "Run Claude Code remote-control with a stable binary, immune to bun updates while running"
# Resolve the symlink to the real inode before starting.
# When bun replaces the file (creates a new inode), the running node process
# keeps its reference to the old inode and is unaffected.
# Usage: clrc [<claude remote-control args...>]

set -l claude_real (realpath (which claude))

@cubic-dev-ai cubic-dev-ai Bot Mar 19, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Resolve claude with a path-only lookup. which can be shadowed by a fish function, causing realpath to receive non-path output and breaking clrc.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At home-manager/programs/fish/functions/_clrc_function.fish, line 7:

<comment>Resolve `claude` with a path-only lookup. `which` can be shadowed by a fish function, causing `realpath` to receive non-path output and breaking `clrc`.</comment>

<file context>
@@ -0,0 +1,9 @@
+  # keeps its reference to the old inode and is unaffected.
+  # Usage: clrc [<claude remote-control args...>]
+
+  set -l claude_real (realpath (which claude))
+  node $claude_real remote-control $argv
+end
</file context>
Fix with Cubic

node $claude_real remote-control $argv
Comment on lines +7 to +8

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Inspect current clrc resolution flow and related tests.
rg -n -C3 'which claude|realpath|node \$claude_real remote-control' home-manager/programs/fish/functions/_clrc_function.fish
rg -n -C3 '_clrc_function|remote-control|which|realpath|node' spec/fish/_clrc_function_test.fish

Repository: shunkakinoki/dotfiles

Length of output: 1200


🏁 Script executed:

#!/bin/bash
# Verify Fish shell behavior for failed command substitution
# Check if there are similar error-handling patterns elsewhere in the codebase

# 1. Test Fish behavior when which fails
fish -c 'which nonexistent_command_xyz; echo "Exit code: $status"'

# 2. Look for other error handling patterns in Fish functions
fd -t f '\.fish$' home-manager/programs/fish/functions/ -x grep -l 'if.*test' {} \; | head -5
rg -n 'if.*test.*-z|if.*which|if.*realpath' home-manager/programs/fish/functions/ -A 2 | head -30

# 3. Check spec file for error case tests
cat spec/fish/_clrc_function_test.fish

Repository: shunkakinoki/dotfiles

Length of output: 3156


Add guards for which and realpath failures before invoking node.

If which claude or realpath fail, $claude_real becomes empty and Line 8 executes node remote-control $argv (missing the script path). The codebase establishes a consistent error-handling pattern elsewhere (e.g., _ocxeh_function.fish, _tsk_function.fish) using if test -z "$var" checks with early returns. Apply the same pattern here. Additionally, the spec file has no tests for error cases; the proposed fix should include tests for missing claude and realpath failure scenarios.

Proposed fix
-  set -l claude_real (realpath (which claude))
-  node $claude_real remote-control $argv
+  set -l claude_bin (which claude 2>/dev/null)
+  if test -z "$claude_bin"
+    echo "clrc: claude not found in PATH" >&2
+    return 127
+  end
+
+  set -l claude_real (realpath $claude_bin 2>/dev/null)
+  if test -z "$claude_real"
+    echo "clrc: failed to resolve claude path: $claude_bin" >&2
+    return 1
+  end
+
+  node $claude_real remote-control $argv
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
set -l claude_real (realpath (which claude))
node $claude_real remote-control $argv
set -l claude_bin (which claude 2>/dev/null)
if test -z "$claude_bin"
echo "clrc: claude not found in PATH" >&2
return 127
end
set -l claude_real (realpath $claude_bin 2>/dev/null)
if test -z "$claude_real"
echo "clrc: failed to resolve claude path: $claude_bin" >&2
return 1
end
node $claude_real remote-control $argv
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@home-manager/programs/fish/functions/_clrc_function.fish` around lines 7 - 8,
The current _clrc_function.fish computes claude_real with "set -l claude_real
(realpath (which claude))" then blindly runs "node $claude_real remote-control
$argv", which will call node without a script if which/realpath fail; update
_clrc_function.fish to guard after computing claude_real with "if test -z
\"$claude_real\"; echo 'Error: claude not found' >&2; return 1; end" (or
similar) so you early-return on missing/empty claude_real, keeping the
descriptive stderr message; also add spec tests covering both failure modes
(simulate missing claude and failing realpath) asserting the function returns
non-zero and emits the error message.

end
26 changes: 26 additions & 0 deletions spec/fish/_clrc_function_test.fish
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
set fn (status dirname)/../../home-manager/programs/fish/functions
source $fn/_clrc_function.fish

# ── basic: resolves symlink and runs node with remote-control ──
set log1 (mktemp)
set fake_cli (mktemp)

function which; echo $fake_cli; end
function realpath; echo $argv[1]; end

@cubic-dev-ai cubic-dev-ai Bot Mar 19, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: This test never distinguishes realpath (which claude) from plain which claude, so it can pass even if the symlink-resolution behavior regresses.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At spec/fish/_clrc_function_test.fish, line 9:

<comment>This test never distinguishes `realpath (which claude)` from plain `which claude`, so it can pass even if the symlink-resolution behavior regresses.</comment>

<file context>
@@ -0,0 +1,26 @@
+set fake_cli (mktemp)
+
+function which; echo $fake_cli; end
+function realpath; echo $argv[1]; end
+function node; echo "node" $argv >> $log1; end
+
</file context>
Fix with Cubic

function node; echo "node" $argv >> $log1; end
Comment on lines +8 to +10

_clrc_function

@test "calls node directly (not claude symlink)" (grep -c "^node" $log1) -ge 1
@test "passes remote-control subcommand" (grep -c "remote-control" $log1) -ge 1

# ── with args: passes through extra args ──────────────────────
set log2 (mktemp)
function node; echo "node" $argv >> $log2; end

_clrc_function --name mysession

@test "passes extra args through" (grep -c -- "--name" $log2) -ge 1
@test "still includes remote-control with args" (grep -c "remote-control" $log2) -ge 1

rm -f $log1 $log2 $fake_cli
Comment on lines +4 to +26

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The test structure can be improved for better isolation and clarity. Redefining the node mock and using separate log files for each test case makes the test suite more complex and potentially brittle. A cleaner approach is to use a single set of mocks and a single log file, clearing the log between test cases. This improves readability and maintainability.

Additionally, the tests can be made more specific by checking for an exact count (-eq 1) instead of a minimum count (-ge 1), and it's good practice to clean up mocked functions after the tests complete.

# ── setup ─────────────────────────────────────────────────────
set -l log (mktemp)
set -l fake_cli (mktemp)

# Mock dependencies
function which; echo $fake_cli; end
function realpath; echo $argv[1]; end
function node; echo "node" $argv >> $log; end

# ── basic: resolves symlink and runs node with remote-control ──
_clrc_function
@test "calls node directly (not claude symlink)" (grep -c "^node" $log) -eq 1
@test "passes remote-control subcommand" (grep -c "remote-control" $log) -eq 1

> $log # Clear log for next test

# ── with args: passes through extra args ──────────────────────
_clrc_function --name mysession
@test "passes extra args through" (grep -c -- "--name" $log) -eq 1
@test "still includes remote-control with args" (grep -c "remote-control" $log) -eq 1

# ── teardown ──────────────────────────────────────────────────
functions -e which realpath node
rm -f $log $fake_cli

7 changes: 4 additions & 3 deletions spec/fish/_ssh_add_github_test.fish
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@ set -x HOME $tmpdir
# ── key exists but keychain missing ───────────────────────
mkdir -p $tmpdir/.ssh
touch $tmpdir/.ssh/id_ed25519_github
function keychain; end
# Remove keychain from PATH by shadowing with non-existent command
functions -e keychain
# Filter keychain binary out of PATH so command -v keychain fails
set -l old_PATH $PATH
set -x PATH (for p in $PATH; if not test -x $p/keychain; echo $p; end; end)

@test "no keychain prints error" (string match -q "*keychain not found*" (_ssh_add_github 2>&1); echo $status) = 0

set -x PATH $old_PATH
rm -rf $tmpdir
Loading