Skip to content
Open
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
44 changes: 5 additions & 39 deletions config/claude/hooks/rtk-rewrite.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/usr/bin/env bash
# rtk-hook-version: 3
# RTK auto-rewrite hook for Claude/Codex/Copilot PreToolUse shell commands.
# RTK auto-rewrite hook for Claude Code PreToolUse:Bash
# Transparently rewrites raw commands to their RTK equivalents.
# Uses `rtk rewrite` as single source of truth — no duplicate mapping logic here.
#
Expand Down Expand Up @@ -32,15 +32,7 @@ fi
set -euo pipefail

INPUT=$(cat)
CMD=$(echo "$INPUT" | jq -r '
.tool.input.command
// .tool_input.command
// (.toolArgs | if type == "object" then .command else empty end)
// (.toolArgs | if type == "string" then (fromjson? | .command) else empty end)
// .toolInput.command
// .command
// empty
')
CMD=$(echo "$INPUT" | jq -r '.tool_input.command // empty')
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.

high

This simplified command extraction logic removes support for multiple input formats used by GitHub Copilot and other integrations (e.g., .toolArgs, .tool.input, and stringified JSON in toolArgs). This regression will cause the hook to skip rewrites for these platforms and will break existing tests in spec/rtk_rewrite_spec.sh (lines 113-128). To maintain robustness and consistency, ensure the logic follows established patterns for Nix-extracted scripts and uses unique delimiters with read and IFS for any command output parsing.

References
  1. To robustly parse command output in shell scripts, use a unique delimiter (e.g., tab) in the format string and read with a matching IFS. This is safer than splitting by spaces with cut, especially when data fields might contain spaces.
  2. Maintain consistency with established patterns for writing scripts that are extracted from Nix expressions, even if it involves suppressing linter warnings like ShellCheck SC2024.

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.

P1: Parsing only .tool_input.command breaks non-Claude payload formats that this hook currently supports, causing command rewrite checks to be skipped.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At config/claude/hooks/rtk-rewrite.sh, line 35:

<comment>Parsing only `.tool_input.command` breaks non-Claude payload formats that this hook currently supports, causing command rewrite checks to be skipped.</comment>

<file context>
@@ -32,15 +32,7 @@ fi
-  // .command
-  // empty
-')
+CMD=$(echo "$INPUT" | jq -r '.tool_input.command // empty')
 
 if [ -z "$CMD" ]; then
</file context>
Suggested change
CMD=$(echo "$INPUT" | jq -r '.tool_input.command // empty')
CMD=$(echo "$INPUT" | jq -r '
.tool.input.command
// .tool_input.command
// (.toolArgs | if type == "object" then .command else empty end)
// (.toolArgs | if type == "string" then (fromjson? | .command) else empty end)
// .toolInput.command
// .command
// empty
')


if [ -z "$CMD" ]; then
_rtk_audit_log "skip:empty" "-"
Expand Down Expand Up @@ -86,37 +78,11 @@ esac

_rtk_audit_log "rewrite" "$CMD" "$REWRITTEN"

# Build the updated tool input with all original fields preserved, only command changed.
ORIGINAL_INPUT=$(echo "$INPUT" | jq -c '
(
.tool_input
// .tool.input
// .toolArgs
// .toolInput
// {}
) | if type == "string" then (fromjson? // {}) else . end
')
# Build the updated tool_input with all original fields preserved, only command changed.
ORIGINAL_INPUT=$(echo "$INPUT" | jq -c '.tool_input')
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.

high

Restricting ORIGINAL_INPUT to only the .tool_input object leads to data loss for other supported input structures. Previously, the script ensured that all original fields (such as timeout or other metadata) were preserved regardless of the input format.

UPDATED_INPUT=$(echo "$ORIGINAL_INPUT" | jq --arg cmd "$REWRITTEN" '.command = $cmd')
IS_COPILOT_INPUT=$(echo "$INPUT" | jq -r 'has("toolName") and has("toolArgs")')

if [ "$IS_COPILOT_INPUT" = "true" ]; then
if [ "$EXIT_CODE" -eq 3 ]; then
jq -n \
--argjson modified "$UPDATED_INPUT" \
'{
"permissionDecision": "ask",
"modifiedArgs": $modified
}'
else
jq -n \
--argjson modified "$UPDATED_INPUT" \
'{
"permissionDecision": "allow",
"permissionDecisionReason": "RTK auto-rewrite",
"modifiedArgs": $modified
}'
fi
elif [ "$EXIT_CODE" -eq 3 ]; then
if [ "$EXIT_CODE" -eq 3 ]; then
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.

high

The removal of the IS_COPILOT_INPUT check and the specific modifiedArgs output format breaks compatibility with GitHub Copilot. Copilot requires this specific JSON structure to accept and execute the rewritten command; without it, the rewrite functionality will be ignored by the Copilot client.

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.

P1: Removing the Copilot output path breaks integrations that expect modifiedArgs responses instead of Claude hookSpecificOutput.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At config/claude/hooks/rtk-rewrite.sh, line 85:

<comment>Removing the Copilot output path breaks integrations that expect `modifiedArgs` responses instead of Claude `hookSpecificOutput`.</comment>

<file context>
@@ -86,37 +78,11 @@ esac
-      }'
-  fi
-elif [ "$EXIT_CODE" -eq 3 ]; then
+if [ "$EXIT_CODE" -eq 3 ]; then
   # Ask: rewrite the command, omit permissionDecision so Claude Code prompts.
   jq -n \
</file context>

# Ask: rewrite the command, omit permissionDecision so Claude Code prompts.
jq -n \
--argjson updated "$UPDATED_INPUT" \
Expand Down
44 changes: 5 additions & 39 deletions config/codex/hooks/rtk-rewrite.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/usr/bin/env bash
# rtk-hook-version: 3
# RTK auto-rewrite hook for Claude/Codex/Copilot PreToolUse shell commands.
# RTK auto-rewrite hook for Claude Code PreToolUse:Bash
# Transparently rewrites raw commands to their RTK equivalents.
# Uses `rtk rewrite` as single source of truth — no duplicate mapping logic here.
#
Expand Down Expand Up @@ -32,15 +32,7 @@ fi
set -euo pipefail

INPUT=$(cat)
CMD=$(echo "$INPUT" | jq -r '
.tool.input.command
// .tool_input.command
// (.toolArgs | if type == "object" then .command else empty end)
// (.toolArgs | if type == "string" then (fromjson? | .command) else empty end)
// .toolInput.command
// .command
// empty
')
CMD=$(echo "$INPUT" | jq -r '.tool_input.command // empty')
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.

high

This simplified command extraction logic removes support for multiple input formats used by GitHub Copilot and other integrations (e.g., .toolArgs, .tool.input, and stringified JSON in toolArgs). This regression will cause the hook to skip rewrites for these platforms and will break existing tests in spec/codex_rtk_rewrite_spec.sh (lines 67-81). To maintain robustness and consistency, ensure the logic follows established patterns for Nix-extracted scripts and uses unique delimiters with read and IFS for any command output parsing.

References
  1. To robustly parse command output in shell scripts, use a unique delimiter (e.g., tab) in the format string and read with a matching IFS. This is safer than splitting by spaces with cut, especially when data fields might contain spaces.
  2. Maintain consistency with established patterns for writing scripts that are extracted from Nix expressions, even if it involves suppressing linter warnings like ShellCheck SC2024.

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.

P1: This now only reads .tool_input.command, so valid non-tool_input payloads are skipped and never rewritten.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At config/codex/hooks/rtk-rewrite.sh, line 35:

<comment>This now only reads `.tool_input.command`, so valid non-`tool_input` payloads are skipped and never rewritten.</comment>

<file context>
@@ -32,15 +32,7 @@ fi
-  // .command
-  // empty
-')
+CMD=$(echo "$INPUT" | jq -r '.tool_input.command // empty')
 
 if [ -z "$CMD" ]; then
</file context>
Suggested change
CMD=$(echo "$INPUT" | jq -r '.tool_input.command // empty')
CMD=$(echo "$INPUT" | jq -r '
.tool.input.command
// .tool_input.command
// (.toolArgs | if type == "object" then .command else empty end)
// (.toolArgs | if type == "string" then (fromjson? | .command) else empty end)
// .toolInput.command
// .command
// empty
')

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.

Copilot integration is silently broken by this sync. This Codex copy is also what gets installed as Copilot's hook (config/copilot/default.nix sets home.file.".copilot/hooks/rtk-rewrite.sh".source = ../codex/hooks/rtk-rewrite.sh, and config/copilot/config.json calls $HOME/.copilot/hooks/rtk-rewrite.sh in its preToolUse array). Copilot CLI sends {"toolName":"shell","toolArgs":{"command":"..."}} (or a stringified JSON variant), but with this PR CMD=$(echo "$INPUT" | jq -r '.tool_input.command // empty') returns empty for those payloads, so the hook falls through the empty-command guard and never rewrites — no error, just a silent no-op for every Copilot Bash invocation. The IS_COPILOT_INPUT/modifiedArgs output branch removed at line 85 below compounds the issue: even if CMD were extracted, Copilot expects modifiedArgs, not Claude's hookSpecificOutput. This regresses the Copilot parity work from #1788. Either re-introduce both branches here (and in the Claude copy, to keep spec/sync_rtk_rewrite_spec.sh's byte-identical invariant) — possibly with a comment to prevent the next scripts/sync-rtk-rewrite.sh run from clobbering it again — or remove the Copilot wiring (config/copilot/config.json entry + config/copilot/default.nix symlink) and the corresponding Copilot specs together.


if [ -z "$CMD" ]; then
_rtk_audit_log "skip:empty" "-"
Expand Down Expand Up @@ -86,37 +78,11 @@ esac

_rtk_audit_log "rewrite" "$CMD" "$REWRITTEN"

# Build the updated tool input with all original fields preserved, only command changed.
ORIGINAL_INPUT=$(echo "$INPUT" | jq -c '
(
.tool_input
// .tool.input
// .toolArgs
// .toolInput
// {}
) | if type == "string" then (fromjson? // {}) else . end
')
# Build the updated tool_input with all original fields preserved, only command changed.
ORIGINAL_INPUT=$(echo "$INPUT" | jq -c '.tool_input')
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.

high

Restricting ORIGINAL_INPUT to only the .tool_input object leads to data loss for other supported input structures. Previously, the script ensured that all original fields (such as timeout or other metadata) were preserved regardless of the input format.

UPDATED_INPUT=$(echo "$ORIGINAL_INPUT" | jq --arg cmd "$REWRITTEN" '.command = $cmd')
IS_COPILOT_INPUT=$(echo "$INPUT" | jq -r 'has("toolName") and has("toolArgs")')

if [ "$IS_COPILOT_INPUT" = "true" ]; then
if [ "$EXIT_CODE" -eq 3 ]; then
jq -n \
--argjson modified "$UPDATED_INPUT" \
'{
"permissionDecision": "ask",
"modifiedArgs": $modified
}'
else
jq -n \
--argjson modified "$UPDATED_INPUT" \
'{
"permissionDecision": "allow",
"permissionDecisionReason": "RTK auto-rewrite",
"modifiedArgs": $modified
}'
fi
elif [ "$EXIT_CODE" -eq 3 ]; then
if [ "$EXIT_CODE" -eq 3 ]; then
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.

high

The removal of the IS_COPILOT_INPUT check and the specific modifiedArgs output format breaks compatibility with GitHub Copilot. Copilot requires this specific JSON structure to accept and execute the rewritten command; without it, the rewrite functionality will be ignored by the Copilot client.

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.

P1: Removing the Copilot output branch changes the response contract and drops modifiedArgs, breaking codex/copilot-style hook consumers.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At config/codex/hooks/rtk-rewrite.sh, line 85:

<comment>Removing the Copilot output branch changes the response contract and drops `modifiedArgs`, breaking codex/copilot-style hook consumers.</comment>

<file context>
@@ -86,37 +78,11 @@ esac
-      }'
-  fi
-elif [ "$EXIT_CODE" -eq 3 ]; then
+if [ "$EXIT_CODE" -eq 3 ]; then
   # Ask: rewrite the command, omit permissionDecision so Claude Code prompts.
   jq -n \
</file context>

# Ask: rewrite the command, omit permissionDecision so Claude Code prompts.
jq -n \
--argjson updated "$UPDATED_INPUT" \
Expand Down