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
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
schema: spec-driven
created: 2026-06-22
created: 2026-06-23
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
## Context

The searchFiles tool in `src/tools/filesystem.js` uses `execFile` to run the ripgrep (`rg`) CLI. When `stdout` is undefined (e.g., due to encoding issues or edge cases), the current code `stdout?.trim() ?? stdout` evaluates to `undefined`, causing a TypeError on the subsequent `output.split("\n")` call.

## Goals / Non-Goals

**Goals:**
- Fix the undefined stdout handling to prevent ReferenceError/TypeError
- Ensure `output` is always a string before calling `split()`

**Non-Goals:**
- No changes to search logic, pattern matching, or tool schema
- No changes to error handling for ENOENT or timeout cases (already handled)

## Decisions

**Decision: Use `(stdout ?? "").trim()` instead of `stdout?.trim() ?? stdout`**
- Rationale: The nullish coalescing operator `??` ensures we always have a string to call `.trim()` on. An empty string is already handled by the existing `if (!output)` check.
- Alternatives considered:
- `stdout ? stdout.trim() : ""` — equivalent but more verbose
- `String(stdout ?? "").trim()` — also works but `String()` on null/undefined produces "null"/"undefined" strings, which is not desired

## Risks / Trade-offs

**Risk:** None significant. This is a minimal, surgical fix.
**Trade-off:** None. The fix only changes error handling behavior for an edge case that previously crashed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
## Why

The searchFiles tool throws a ReferenceError/TypeError when `stdout` from the `execFile` call is undefined. The current code uses `stdout?.trim() ?? stdout` which evaluates to `undefined` when stdout is undefined, causing a subsequent TypeError on `output.split("\n")`. This breaks the searchFiles tool in edge cases where stdout is not a string.

## What Changes

- Replace `const output = stdout?.trim() ?? stdout;` with `const output = (stdout ?? "").trim();` in `src/tools/filesystem.js` line 436
- Ensure `output` is always a string before calling `split()`
- Handle undefined, null, and empty string cases for stdout gracefully

## Capabilities

### New Capabilities
<!-- None — this is a bug fix, not a new capability -->

### Modified Capabilities
- `searchFiles`: Fix stdout handling to prevent ReferenceError when stdout is undefined

## Impact

- `src/tools/filesystem.js` — searchFilesImpl function (line 436)
- No API changes, no dependency changes
- Existing tests should continue to pass; the fix only changes error handling behavior
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
## ADDED Requirements

### Requirement: searchFiles must handle undefined stdout safely
The searchFiles tool MUST ensure that stdout from the execFile call is always a string before calling string methods on it. If stdout is undefined or null, the tool MUST treat it as an empty string and return "No matches found."

#### Scenario: stdout is undefined
- **WHEN** the execFile call returns undefined stdout
- **THEN** the tool treats output as an empty string and returns "No matches found."

#### Scenario: stdout is null
- **WHEN** the execFile call returns null stdout
- **THEN** the tool treats output as an empty string and returns "No matches found."

#### Scenario: stdout is a valid string
- **WHEN** the execFile call returns a valid string stdout
- **THEN** the tool trims and splits the output as expected
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
## 1. Fix stdout handling in searchFiles

- [x] 1.1 Replace `const output = stdout?.trim() ?? stdout;` with `const output = (stdout ?? "").trim();` in `src/tools/filesystem.js` line 436
- [x] 1.2 Verify the fix handles undefined, null, and empty string cases for stdout
- [x] 1.3 Run tests to ensure no regressions: `npm run test`
- [x] 1.4 Run lint to ensure code quality: `npm run lint`
36 changes: 0 additions & 36 deletions openspec/changes/fix-toolmessage-loss-compaction/design.md

This file was deleted.

24 changes: 0 additions & 24 deletions openspec/changes/fix-toolmessage-loss-compaction/proposal.md

This file was deleted.

This file was deleted.

17 changes: 0 additions & 17 deletions openspec/changes/fix-toolmessage-loss-compaction/tasks.md

This file was deleted.

20 changes: 20 additions & 0 deletions openspec/specs/searchFiles/spec.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# searchFiles Specification

## Purpose
TBD - created by archiving change fix-searchfiles-stdout-handling. Update Purpose after archive.
## Requirements
### Requirement: searchFiles must handle undefined stdout safely
The searchFiles tool MUST ensure that stdout from the execFile call is always a string before calling string methods on it. If stdout is undefined or null, the tool MUST treat it as an empty string and return "No matches found."

#### Scenario: stdout is undefined
- **WHEN** the execFile call returns undefined stdout
- **THEN** the tool treats output as an empty string and returns "No matches found."

#### Scenario: stdout is null
- **WHEN** the execFile call returns null stdout
- **THEN** the tool treats output as an empty string and returns "No matches found."

#### Scenario: stdout is a valid string
- **WHEN** the execFile call returns a valid string stdout
- **THEN** the tool trims and splits the output as expected

2 changes: 1 addition & 1 deletion src/tools/filesystem.js
Original file line number Diff line number Diff line change
Expand Up @@ -433,7 +433,7 @@ export async function searchFilesImpl(input, options) {
resolved.path,
].filter(Boolean);
const { stdout } = await execFile("rg", rgArgs, { timeout: 10000, encoding: "utf-8" });
const output = stdout?.trim() ?? stdout;
const output = (stdout ?? "").trim();

if (!output) {
return "No matches found.";
Expand Down