From 65c9be36b20ef445117b83b2489be03a40d62e21 Mon Sep 17 00:00:00 2001 From: Alex Clarke Date: Thu, 13 Aug 2026 14:18:45 -0600 Subject: [PATCH] fix: correct newline removal from fs_write and fs_patch --- assets/functions/tools/fs_patch.sh | 12 ++++++++++-- assets/functions/tools/fs_write.sh | 7 ++++++- src/rag/mod.rs | 8 ++++---- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/assets/functions/tools/fs_patch.sh b/assets/functions/tools/fs_patch.sh index a934240..f48b8b3 100755 --- a/assets/functions/tools/fs_patch.sh +++ b/assets/functions/tools/fs_patch.sh @@ -33,7 +33,11 @@ source "$LLM_PROMPT_UTILS_FILE" # shellcheck disable=SC2154 main() { - argc_contents="$(jq -r '.content' <<< "$LLM_TOOL_RAW_JSON")" + # Command substitution strips *all* trailing newlines and `jq -r` appends one + # of its own, so read with `-j` and pin the real end of the content with a + # sentinel that is removed afterwards. + argc_contents="$(jq -j '.content' <<< "$LLM_TOOL_RAW_JSON"; printf x)" + argc_contents="${argc_contents%x}" argc_path="$(jq -r '.path' <<< "$LLM_TOOL_RAW_JSON")" if [[ ! -f "$argc_path" ]]; then @@ -41,7 +45,11 @@ main() { exit 1 fi - new_contents="$(patch_file "$argc_path" <(printf "%s" "$argc_contents"))" + # Same sentinel guard on the patched result, otherwise the trailing newline + # is stripped again on the way back out. `rc` preserves patch_file's exit + # status so a failure still aborts under `set -e`. + new_contents="$(patch_file "$argc_path" <(printf "%s" "$argc_contents"); rc=$?; printf x; exit "$rc")" + new_contents="${new_contents%x}" printf "%s" "$new_contents" | git diff --no-index "$argc_path" - || true guard_operation "Apply changes?" diff --git a/assets/functions/tools/fs_write.sh b/assets/functions/tools/fs_write.sh index 298d663..094f639 100755 --- a/assets/functions/tools/fs_write.sh +++ b/assets/functions/tools/fs_write.sh @@ -15,7 +15,12 @@ source "$LLM_PROMPT_UTILS_FILE" # shellcheck disable=SC2154 main() { - argc_contents="$(jq -r '.content' <<< "$LLM_TOOL_RAW_JSON")" + # Command substitution strips *all* trailing newlines and `jq -r` appends one + # of its own, so read with `-j` and pin the real end of the content with a + # sentinel that is removed afterwards. Without this every written file loses + # its final newline, which breaks formatters such as `cargo fmt --check`. + argc_contents="$(jq -j '.content' <<< "$LLM_TOOL_RAW_JSON"; printf x)" + argc_contents="${argc_contents%x}" argc_path="$(jq -r '.path' <<< "$LLM_TOOL_RAW_JSON")" if [[ -f "$argc_path" ]]; then diff --git a/src/rag/mod.rs b/src/rag/mod.rs index bf44653..2050d6c 100644 --- a/src/rag/mod.rs +++ b/src/rag/mod.rs @@ -3044,7 +3044,7 @@ mod tests { #[test] fn reciprocal_rank_fusion_empty_lists() { - let result = super::reciprocal_rank_fusion(vec![], vec![], 5); + let result = reciprocal_rank_fusion(vec![], vec![], 5); assert!(result.is_empty(), "empty input should produce empty output"); } @@ -3052,7 +3052,7 @@ mod tests { fn reciprocal_rank_fusion_deduplicates_across_signals() { let doc_a = DocumentId::new(0, 0); let doc_b = DocumentId::new(0, 1); - let result = super::reciprocal_rank_fusion( + let result = reciprocal_rank_fusion( vec![vec![doc_a, doc_b], vec![doc_a, doc_b]], vec![1.0, 1.0], 5, @@ -3069,7 +3069,7 @@ mod tests { #[test] fn reciprocal_rank_fusion_respects_top_k() { let docs: Vec = (0..10).map(|i| DocumentId::new(0, i)).collect(); - let result = super::reciprocal_rank_fusion(vec![docs], vec![1.0], 3); + let result = reciprocal_rank_fusion(vec![docs], vec![1.0], 3); assert_eq!(result.len(), 3, "result should be capped at top_k=3"); } @@ -3077,7 +3077,7 @@ mod tests { fn reciprocal_rank_fusion_weights_affect_ranking() { let doc_a = DocumentId::new(0, 0); let doc_b = DocumentId::new(0, 1); - let result = super::reciprocal_rank_fusion( + let result = reciprocal_rank_fusion( vec![vec![doc_a, doc_b], vec![doc_b, doc_a]], vec![10.0, 1.0], 2,