Repository navigation
fix: auto-fix linting issues - #70
Conversation
Automatically fixed simple linting issues including: - Unused imports - Code style consistency - Minor formatting issues 🤖 Automated by SC Smart Scheduler Co-Authored-By: SC Smart Scheduler <noreply@sc-scheduler>
Automatically fixed simple linting issues including: - Unused imports - Code style consistency - Minor formatting issues 🤖 Automated by SC Smart Scheduler Co-Authored-By: SC Smart Scheduler <noreply@sc-scheduler>
Automatically fixed simple linting issues including: - Unused imports - Code style consistency - Minor formatting issues 🤖 Automated by SC Smart Scheduler Co-Authored-By: SC Smart Scheduler <noreply@sc-scheduler>
Implements P1 item from CLAUDE-CODE-PARITY.md roadmap. Additions: - message-validator.ts - Validates conversation state - Auto-corrects role:'assistant' → role:'tool' - Detects orphaned tool_call_ids - Prevents invalid message sequences - Integrated into agent.run() loop This catches errors BEFORE sending to LLM, preventing cryptic API failures and broken response generation. Also: - CLAUDE-CODE-PARITY.md - Full gap analysis vs Claude Code - Fixed TypeScript errors in chat-session.ts - Re-added 'tool' to MessageRole type (was reverted) Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
Automatically fixed simple linting issues including: - Unused imports - Code style consistency - Minor formatting issues 🤖 Automated by SC Smart Scheduler Co-Authored-By: SC Smart Scheduler <noreply@sc-scheduler>
Consolidates: - Tool response generation fix (role:'tool' + continuation prompt) - Message sequence validator - Path security fix (ignore library empty string) - Error summary improvements - CLAUDE-CODE-PARITY.md analysis - CRITICAL-FIXES.md documentation - Validation tests This brings main to stable state with all UX fixes applied.
CRITICAL FIXES (NEVER REMOVE): 1. Tool results MUST use role:'tool', not 'assistant' (OpenAI spec) 2. Continuation prompt for models that don't auto-synthesize 3. Message validator already imported and active These fixes were reverted after merge. Pre-commit hook now enforces. Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
CRITICAL FIX: Skip deny pattern check when relativePath is empty. The ignore library rejects empty strings, and path.relative(root, root) returns "". This caused infinite "path must not be empty" loops. Tested: `sc chat "list files"` now completes successfully. Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
- Documents all critical fixes that must never be removed - Explains user impact and technical root causes - Updated pre-commit hook to validate all 4 critical fixes - Includes verification procedures and history The pre-commit hook now validates: 1. role:'tool' in agent.ts (3 locations) 2. 'tool' in MessageRole type 3. message-validator import 4. empty path check in path-security.ts Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
CRITICAL FIXES (enforced by pre-commit hook): 1. Tool results use role:'tool' (3 locations in agent.ts) - Unknown tool errors - Successful tool results - Tool execution errors 2. Continuation prompt for tool-only responses - Detects hasToolCallsWithoutContent - Forces synthesis for NVIDIA Nemotron, older Llama 3. Empty path security check (path-security.ts) - Skip ignore check when relativePath is empty - Prevents "path must not be empty" infinite loops 4. MessageRole includes 'tool' type (types.ts) - Required for TypeScript to accept role:'tool' These fixes were reverted again. Pre-commit hook now validates all 4. Changed messages from const to let for future validator support. Tested: ✅ Build passing, ✅ list files works, ✅ visible responses Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
📝 WalkthroughResumenSe corrigen cuatro fallos críticos del agente: los resultados de herramientas ahora usan CambiosCorrecciones del agente, validación de mensajes y mejoras de tooling
Diagramas de secuenciasequenceDiagram
rect rgba(70, 130, 180, 0.5)
Note over run,history: Agent.run con correcciones críticas
end
participant run as Agent.run
participant validator as autoCorrectMessageSequence
participant provider as Proveedor LLM
participant history as messages[]
run->>validator: autoCorrectMessageSequence(messages)
validator-->>run: messages corregidos (role:tool donde aplica)
run->>provider: API request con secuencia válida
provider-->>run: response
alt Solo tool_calls, sin content textual
run->>history: push user continuation prompt
run->>provider: segunda llamada
provider-->>run: response textual
end
run->>history: push role:tool (resultado/error de herramienta)
Esfuerzo estimado de revisión de código🎯 4 (Complejo) | ⏱️ ~60 minutos 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 7
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
src/core/agent.ts (1)
649-656: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick winNo reparsees los argumentos dentro del
catch.Viejo consejo: si
JSON.parse(toolCall.function.arguments)fue la causa del fallo, la Línea 652 vuelve a lanzar la misma excepción y jamás se agrega el mensajerole: 'tool'. El agente aborta justo en la ruta de recuperación que este cambio quiere endurecer.🪄 Propuesta de ajuste
- try { - const args = JSON.parse(toolCall.function.arguments); + let parsedArgs: Record<string, unknown> | undefined; + try { + parsedArgs = JSON.parse(toolCall.function.arguments) as Record<string, unknown>; // Compact output for multiple tools if (response.tool_calls.length === 1) { console.log(chalk.gray(` │ 🔧 Using tool: ${toolName}`)); - console.log(chalk.gray(` │ Args: ${JSON.stringify(args)}`)); + console.log(chalk.gray(` │ Args: ${JSON.stringify(parsedArgs)}`)); } else { - console.log(chalk.gray(` │ → ${toolName}: ${JSON.stringify(args)}`)); + console.log(chalk.gray(` │ → ${toolName}: ${JSON.stringify(parsedArgs)}`)); } - const result = await tool.execute(args, this.toolContext); + const result = await tool.execute(parsedArgs, this.toolContext); @@ - toolsUsed.push({name: toolName, success: true, args}); + toolsUsed.push({ name: toolName, success: true, args: parsedArgs }); @@ } catch (err: unknown) { const errorMsg = err instanceof Error ? err.message : String(err); console.log(chalk.gray(` │ ${chalk.red('✗')} ${toolName} failed: ${errorMsg}`)); - toolsUsed.push({name: toolName, success: false, error: errorMsg, args: JSON.parse(toolCall.function.arguments)}); + toolsUsed.push({ name: toolName, success: false, error: errorMsg, args: parsedArgs }); // CRITICAL: Tool error responses use role:'tool', not 'assistant' // DO NOT CHANGE - see CRITICAL-FIXES.md messages.push({🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/core/agent.ts` around lines 649 - 656, The catch block in agent.ts re-parses toolCall.function.arguments while handling an error, which can throw the same exception again and prevent the role:'tool' fallback from being added. Update the error path in the tool execution handler so it reuses the already-available arguments value instead of calling JSON.parse inside the catch, and keep the recovery flow in the same try/catch around the tool invocation.src/core/config.ts (1)
59-65: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winNo trates una config corrupta como si hubiera desaparecido.
Si este hechizo falla por JSON inválido o por permisos, continúas con defaults en silencio. Eso puede arrancar con una configuración distinta a la del usuario y oculta la causa real. Deja el fallback solo para
ENOENTy relanza el resto con contexto.🪄 Propuesta
try { const data = await readFile(CONFIG_PATH, 'utf-8'); config = deepMerge(config, JSON.parse(data)); } catch (err: unknown) { - // No global config, use defaults - - const _err = err; + if ((err as NodeJS.ErrnoException).code !== 'ENOENT') { + const message = err instanceof Error ? err.message : String(err); + throw new Error(`Failed to load global config at ${CONFIG_PATH}: ${message}`); + } }Aplicad el mismo patrón al bloque del config local del proyecto.
As per coding guidelines, "Always catch errors and provide meaningful error messages."
Also applies to: 71-77
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/core/config.ts` around lines 59 - 65, The config loading fallback in the `readFile`/`deepMerge` blocks is treating all failures like a missing file, which hides corrupt JSON or permission errors. Update the `catch` logic in `src/core/config.ts` so the defaults fallback is only used for `ENOENT`, and for any other error rethrow with meaningful context about whether it came from the global config or the local project config. Apply the same handling pattern to both config-loading sections so `CONFIG_PATH` and the local config path behave consistently.Source: Coding guidelines
🧹 Nitpick comments (1)
CRITICAL-FIXES.md (1)
7-9: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winNo llames “enforced” a un hook local.
Si la validación vive sólo en
.git/hooks/pre-commit, no viaja con el repositorio y cualquiera la pierde al clonar. Mejor versiona el script o llévalo a CI; si no, aclara que es una configuración local.Also applies to: 259-299
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@CRITICAL-FIXES.md` around lines 7 - 9, The “Pre-commit hook: ACTIVE ✅” note is overstating a local-only hook as enforced; update the wording in CRITICAL-FIXES.md to describe it as a local `.git/hooks/pre-commit` configuration unless the hook is actually versioned or run in CI. If the intent is repository-wide enforcement, move the check into a tracked script or CI step and reference that instead; otherwise, make the status explicitly local in the relevant section.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@CLAUDE-CODE-PARITY.md`:
- Around line 231-239: Remove the “Message Sequence Validator” item from the
pending list in the roadmap document, since the validation logic already exists
in src/core/message-validator.ts via autoCorrectMessageSequence. Update the list
so it reflects the current codebase and does not present validateMessageSequence
as an unresolved gap.
In `@src/core/config.ts`:
- Around line 132-142: The `config-init` flow in `access(CONFIG_PATH)` is
swallowing every error except the “Config already exists” case, which hides real
failures when `--force` is false. Update the `try/catch` in `src/core/config.ts`
so the `createConfig`/config-init check only ignores the `ENOENT` case from
`access()`, and rethrow any other `Error` instead of treating it as “file
missing.”
In `@src/core/message-validator.ts`:
- Around line 59-74: The tool call registration in message-validator’s
assistant-only rule is too permissive because non-assistant messages with
tool_calls are ignored instead of rejected. Update the validation logic around
the existing msg.role === 'assistant' && msg.tool_calls block to explicitly fail
when any message with tool_calls has a role other than assistant, using
MessageValidationError with the offending msg and index. Keep the duplicate ID
check and pendingToolCalls registration in place for valid assistant tool calls.
- Around line 144-149: The isMessageSequenceValid helper is swallowing
validation failures by catching without exposing the error, which hides the
cause of invalid sequences. Update the catch in isMessageSequenceValid to
capture the thrown error from validateMessageSequence and emit a meaningful
message through the existing error handling path before returning false, so the
failure reason is preserved for debugging.
In `@src/tools/search-text.ts`:
- Line 66: The search-text tool currently swallows read failures in the catch
block and can return a clean “No matches found” even when the scan was partial.
Update the logic in search-text.ts around the file-reading loop and the catch in
the search flow to track how many candidate files were skipped or unreadable,
then surface that in the result message or throw a meaningful error if every
readFile attempt failed. Use the existing searchText/search loop and readFile
handling to keep the partial-result signal visible to callers.
In `@src/utils/autocomplete.ts`:
- Line 1: The autocomplete entry classification in autocomplete.ts is treating
symlinks as regular files because entry.isDirectory() only reflects the link
itself, so linked directories lose the trailing slash and cannot be traversed.
Update the logic around the directory-entry handling in the autocomplete flow to
resolve or stat the symlink target before deciding whether to append "/" and
recurse, using the existing autocomplete-related symbols to locate the
classification branch.
In `@src/utils/storage-limit.ts`:
- Line 43: No silencien los errores en los flujos de escaneo y limpieza de
disco: los bloques catch alrededor de `readdirSync`, `statSync` y `unlinkSync`
están ocultando fallos y devolviendo tamaños parciales o limpiezas aparentemente
exitosas. Ajusten `checkStorageLimit()` y `enforceStorageLimit()` para capturar
el error real, registrarlo o relanzarlo con contexto útil, y evitar decisiones
basadas en resultados incompletos; si hay helpers internos en
`storage-limit.ts`, aseguren que también propaguen el detalle del fallo en lugar
de absorberlo.
---
Outside diff comments:
In `@src/core/agent.ts`:
- Around line 649-656: The catch block in agent.ts re-parses
toolCall.function.arguments while handling an error, which can throw the same
exception again and prevent the role:'tool' fallback from being added. Update
the error path in the tool execution handler so it reuses the already-available
arguments value instead of calling JSON.parse inside the catch, and keep the
recovery flow in the same try/catch around the tool invocation.
In `@src/core/config.ts`:
- Around line 59-65: The config loading fallback in the `readFile`/`deepMerge`
blocks is treating all failures like a missing file, which hides corrupt JSON or
permission errors. Update the `catch` logic in `src/core/config.ts` so the
defaults fallback is only used for `ENOENT`, and for any other error rethrow
with meaningful context about whether it came from the global config or the
local project config. Apply the same handling pattern to both config-loading
sections so `CONFIG_PATH` and the local config path behave consistently.
---
Nitpick comments:
In `@CRITICAL-FIXES.md`:
- Around line 7-9: The “Pre-commit hook: ACTIVE ✅” note is overstating a
local-only hook as enforced; update the wording in CRITICAL-FIXES.md to describe
it as a local `.git/hooks/pre-commit` configuration unless the hook is actually
versioned or run in CI. If the intent is repository-wide enforcement, move the
check into a tracked script or CI step and reference that instead; otherwise,
make the status explicitly local in the relevant section.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 21eff938-44c9-4d4e-9c3b-a51a4b5b77f5
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (19)
CLAUDE-CODE-PARITY.mdCRITICAL-FIXES.mdeslint.config.mjspackage.jsonsrc/cli.tssrc/commands/chat-session.tssrc/core/agent.tssrc/core/config.test.tssrc/core/config.tssrc/core/message-validator.tssrc/core/project-context.tssrc/core/types.tssrc/tools/search-text.tssrc/utils/autocomplete.tssrc/utils/path-security.tssrc/utils/permissions.tssrc/utils/storage-guidance.test.tssrc/utils/storage-guidance.tssrc/utils/storage-limit.ts
| **4. Message Sequence Validator** | ||
| ```typescript | ||
| function validateMessageSequence(messages: Message[]): void { | ||
| // Ensure tool results have role:'tool' | ||
| // Ensure tool results follow assistant tool_calls | ||
| // Ensure no orphaned tool_call_ids | ||
| // Throw descriptive errors on violations | ||
| } | ||
| ``` |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Saca este punto de la lista de pendientes.
En src/core/message-validator.ts:109-138 ya existe autoCorrectMessageSequence y valida la secuencia; dejarlo como “gap” desalineará esta hoja de ruta con el código real.
Propuesta
-### 4. **Message Sequence Validator**
+### 4. **Message Sequence Validator** ✅ (ya implementado en `src/core/message-validator.ts`)🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@CLAUDE-CODE-PARITY.md` around lines 231 - 239, Remove the “Message Sequence
Validator” item from the pending list in the roadmap document, since the
validation logic already exists in src/core/message-validator.ts via
autoCorrectMessageSequence. Update the list so it reflects the current codebase
and does not present validateMessageSequence as an unresolved gap.
| if (!force) { | ||
| try { | ||
| await access(CONFIG_PATH); | ||
| throw new Error( | ||
| `Config already exists at ${CONFIG_PATH}. Use "sc config-init --force" to overwrite it.` | ||
| ); | ||
| } catch (err: unknown) { | ||
| if (err instanceof Error && err.message.startsWith('Config already exists at ')) { | ||
| throw err; | ||
| } | ||
| } |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
No engullas errores de access() al evaluar --force.
Aquí solo debería ignorarse ENOENT. Cualquier otro fallo queda disfrazado y el usuario recibe un error tardío o menos preciso que la causa real.
🪄 Propuesta
} catch (err: unknown) {
if (err instanceof Error && err.message.startsWith('Config already exists at ')) {
throw err;
}
+ if ((err as NodeJS.ErrnoException).code !== 'ENOENT') {
+ const message = err instanceof Error ? err.message : String(err);
+ throw new Error(`Failed to check existing config at ${CONFIG_PATH}: ${message}`);
+ }
}As per coding guidelines, "Always catch errors and provide meaningful error messages."
📝 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.
| if (!force) { | |
| try { | |
| await access(CONFIG_PATH); | |
| throw new Error( | |
| `Config already exists at ${CONFIG_PATH}. Use "sc config-init --force" to overwrite it.` | |
| ); | |
| } catch (err: unknown) { | |
| if (err instanceof Error && err.message.startsWith('Config already exists at ')) { | |
| throw err; | |
| } | |
| } | |
| if (!force) { | |
| try { | |
| await access(CONFIG_PATH); | |
| throw new Error( | |
| `Config already exists at ${CONFIG_PATH}. Use "sc config-init --force" to overwrite it.` | |
| ); | |
| } catch (err: unknown) { | |
| if (err instanceof Error && err.message.startsWith('Config already exists at ')) { | |
| throw err; | |
| } | |
| if ((err as NodeJS.ErrnoException).code !== 'ENOENT') { | |
| const message = err instanceof Error ? err.message : String(err); | |
| throw new Error(`Failed to check existing config at ${CONFIG_PATH}: ${message}`); | |
| } | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/core/config.ts` around lines 132 - 142, The `config-init` flow in
`access(CONFIG_PATH)` is swallowing every error except the “Config already
exists” case, which hides real failures when `--force` is false. Update the
`try/catch` in `src/core/config.ts` so the `createConfig`/config-init check only
ignores the `ENOENT` case from `access()`, and rethrow any other `Error` instead
of treating it as “file missing.”
Source: Coding guidelines
| // Rule 3: Register tool calls when assistant makes them | ||
| if (msg.role === 'assistant' && msg.tool_calls) { | ||
| for (const toolCall of msg.tool_calls) { | ||
| if (pendingToolCalls.has(toolCall.id)) { | ||
| throw new MessageValidationError( | ||
| `Duplicate tool_call_id '${toolCall.id}'. Each tool call must have a unique ID.`, | ||
| i, | ||
| msg | ||
| ); | ||
| } | ||
| pendingToolCalls.set(toolCall.id, { | ||
| index: i, | ||
| name: toolCall.function.name, | ||
| }); | ||
| } | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Valida también quién puede emitir tool_calls.
Sabio aviso: si un mensaje trae tool_calls con role !== 'assistant', este guardián no falla ni registra esos IDs. La secuencia puede colarse como válida, o estallar después con un tool_call_id “desconocido” lejos de la causa raíz.
🪄 Propuesta de ajuste
export function validateMessageSequence(messages: Message[]): void {
const pendingToolCalls = new Map<string, { index: number; name: string }>();
@@
- // Rule 3: Register tool calls when assistant makes them
- if (msg.role === 'assistant' && msg.tool_calls) {
+ // Rule 3: Only assistant messages may contain tool_calls
+ if (msg.tool_calls && msg.role !== 'assistant') {
+ throw new MessageValidationError(
+ `Only assistant messages may contain tool_calls, not role:'${msg.role}'.`,
+ i,
+ msg
+ );
+ }
+
+ // Rule 4: Register tool calls when assistant makes them
+ if (msg.role === 'assistant' && msg.tool_calls) {
for (const toolCall of msg.tool_calls) {
if (pendingToolCalls.has(toolCall.id)) {
throw new MessageValidationError(
`Duplicate tool_call_id '${toolCall.id}'. Each tool call must have a unique ID.`,
i,
msg
);
}
pendingToolCalls.set(toolCall.id, {
index: i,
name: toolCall.function.name,
});
}
}📝 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.
| // Rule 3: Register tool calls when assistant makes them | |
| if (msg.role === 'assistant' && msg.tool_calls) { | |
| for (const toolCall of msg.tool_calls) { | |
| if (pendingToolCalls.has(toolCall.id)) { | |
| throw new MessageValidationError( | |
| `Duplicate tool_call_id '${toolCall.id}'. Each tool call must have a unique ID.`, | |
| i, | |
| msg | |
| ); | |
| } | |
| pendingToolCalls.set(toolCall.id, { | |
| index: i, | |
| name: toolCall.function.name, | |
| }); | |
| } | |
| } | |
| // Rule 3: Only assistant messages may contain tool_calls | |
| if (msg.tool_calls && msg.role !== 'assistant') { | |
| throw new MessageValidationError( | |
| `Only assistant messages may contain tool_calls, not role:'${msg.role}'.`, | |
| i, | |
| msg | |
| ); | |
| } | |
| // Rule 4: Register tool calls when assistant makes them | |
| if (msg.role === 'assistant' && msg.tool_calls) { | |
| for (const toolCall of msg.tool_calls) { | |
| if (pendingToolCalls.has(toolCall.id)) { | |
| throw new MessageValidationError( | |
| `Duplicate tool_call_id '${toolCall.id}'. Each tool call must have a unique ID.`, | |
| i, | |
| msg | |
| ); | |
| } | |
| pendingToolCalls.set(toolCall.id, { | |
| index: i, | |
| name: toolCall.function.name, | |
| }); | |
| } | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/core/message-validator.ts` around lines 59 - 74, The tool call
registration in message-validator’s assistant-only rule is too permissive
because non-assistant messages with tool_calls are ignored instead of rejected.
Update the validation logic around the existing msg.role === 'assistant' &&
msg.tool_calls block to explicitly fail when any message with tool_calls has a
role other than assistant, using MessageValidationError with the offending msg
and index. Keep the duplicate ID check and pendingToolCalls registration in
place for valid assistant tool calls.
| export function isMessageSequenceValid(messages: Message[]): boolean { | ||
| try { | ||
| validateMessageSequence(messages); | ||
| return true; | ||
| } catch { | ||
| return false; |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
No tragues la causa del fallo aquí.
Este catch devuelve false sin dejar rastro, y luego diagnosticar por qué la secuencia era inválida se vuelve innecesariamente oscuro. As per coding guidelines, src/**/*.ts: “Always catch errors and provide meaningful error messages.”
🪄 Propuesta de ajuste
export function isMessageSequenceValid(messages: Message[]): boolean {
try {
validateMessageSequence(messages);
return true;
- } catch {
+ } catch (error: unknown) {
+ const errorMsg = error instanceof Error ? error.message : String(error);
+ console.warn(`[MessageValidator] ${errorMsg}`);
return false;
}
}📝 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.
| export function isMessageSequenceValid(messages: Message[]): boolean { | |
| try { | |
| validateMessageSequence(messages); | |
| return true; | |
| } catch { | |
| return false; | |
| export function isMessageSequenceValid(messages: Message[]): boolean { | |
| try { | |
| validateMessageSequence(messages); | |
| return true; | |
| } catch (error: unknown) { | |
| const errorMsg = error instanceof Error ? error.message : String(error); | |
| console.warn(`[MessageValidator] ${errorMsg}`); | |
| return false; | |
| } | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/core/message-validator.ts` around lines 144 - 149, The
isMessageSequenceValid helper is swallowing validation failures by catching
without exposing the error, which hides the cause of invalid sequences. Update
the catch in isMessageSequenceValid to capture the thrown error from
validateMessageSequence and emit a meaningful message through the existing error
handling path before returning false, so the failure reason is preserved for
debugging.
Source: Coding guidelines
| results.push(`${file}:\n${matches.join('\n')}`); | ||
| } | ||
| } catch (err: unknown) { | ||
| } catch { |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
No devuelvas “No matches found” cuando la búsqueda quedó incompleta.
Si readFile falla en todos —o en parte— de los candidatos, este tool responde igual que una búsqueda limpia y el agente pierde la señal de que el resultado es parcial. Cuenta los archivos omitidos y repórtalo, o eleva un error si no se pudo leer ninguno.
As per coding guidelines, "Always catch errors and provide meaningful error messages".
🪄 Cambio sugerido
- const results: string[] = [];
+ const results: string[] = [];
+ let skippedFiles = 0;
@@
- } catch {
- // Skip files that can't be read (binary, etc.)
+ } catch {
+ skippedFiles++;
}
}
- return results.length > 0 ? results.join('\n\n') : 'No matches found';
+ if (results.length === 0 && skippedFiles > 0) {
+ throw new Error(`Search incomplete: ${skippedFiles} file(s) could not be read`);
+ }
+
+ if (results.length > 0 && skippedFiles > 0) {
+ return `${results.join('\n\n')}\n\n[warning] Skipped ${skippedFiles} unreadable file(s)`;
+ }
+
+ return results.length > 0 ? results.join('\n\n') : 'No matches found';📝 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.
| } catch { | |
| const results: string[] = []; | |
| let skippedFiles = 0; | |
| ... | |
| } catch { | |
| skippedFiles++; | |
| } | |
| ... | |
| if (results.length === 0 && skippedFiles > 0) { | |
| throw new Error(`Search incomplete: ${skippedFiles} file(s) could not be read`); | |
| } | |
| if (results.length > 0 && skippedFiles > 0) { | |
| return `${results.join('\n\n')}\n\n[warning] Skipped ${skippedFiles} unreadable file(s)`; | |
| } | |
| return results.length > 0 ? results.join('\n\n') : 'No matches found'; |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/tools/search-text.ts` at line 66, The search-text tool currently swallows
read failures in the catch block and can return a clean “No matches found” even
when the scan was partial. Update the logic in search-text.ts around the
file-reading loop and the catch in the search flow to track how many candidate
files were skipped or unreadable, then surface that in the result message or
throw a meaningful error if every readFile attempt failed. Use the existing
searchText/search loop and readFile handling to keep the partial-result signal
visible to callers.
Source: Coding guidelines
| @@ -1,4 +1,4 @@ | |||
| import { readdirSync, statSync, existsSync } from 'node:fs'; | |||
| import { readdirSync, existsSync } from 'node:fs'; | |||
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🌐 Web query:
In Node.js, does fs.Dirent.isDirectory()return true for symbolic links to directories, or do you needfs.stat/statSync to follow the symlink?
💡 Result:
In Node.js, fs.Dirent.isDirectory returns false for a symbolic link to a directory because the entry itself is a symbolic link, not the directory it points to [1]. When working with fs.Dirent objects (typically obtained via fs.readdir with { withFileTypes: true }), the dirent object correctly identifies the file type of the entry itself [1][2]. If the entry is a symbolic link, dirent.isSymbolicLink will return true, and isDirectory will return false [3][1]. If you need to determine whether the target of a symbolic link is a directory, you cannot rely solely on the fs.Dirent object. Instead, you must use fs.stat or fs.statSync on the path. Unlike fs.lstat (which inspects the link itself), fs.stat follows the symbolic link to its target, allowing you to call isDirectory on the resulting Stats object to verify if the target is a directory [4][5].
Citations:
- 1: fix: handle symlink directories in file listing anomalyco/opencode#11351
- 2: https://nodejs.org/docs/v12.18.4/api/fs.html
- 3:
dirent.isSymbolicLink()returns false for directory symbolic links in Windows nodejs/node#48799 - 4: docs: fs: stat.isDirectory: added clarification nodejs/node#27413
- 5: https://stackoverflow.com/questions/43443234/how-to-determine-if-directory-is-a-symbolic-link
🏁 Script executed:
#!/bin/bash
set -euo pipefail
file='src/utils/autocomplete.ts'
echo '--- outline ---'
ast-grep outline "$file" --view expanded || true
echo
echo '--- relevant lines ---'
nl -ba "$file" | sed -n '110,150p'Repository: os-santiago/sc-agent-cli
Length of output: 671
🏁 Script executed:
#!/bin/bash
set -euo pipefail
file='src/utils/autocomplete.ts'
echo '--- lines 92-145 ---'
sed -n '92,145p' "$file"Repository: os-santiago/sc-agent-cli
Length of output: 1854
No toméis a los enlaces simbólicos por archivos comunes. En src/utils/autocomplete.ts:127-129, entry.isDirectory() marca false para symlinks que apuntan a directorios, así que esos destinos pierden el sufijo / y el autocompletado deja de navegar por ellos. Usad stat del destino antes de clasificarlo.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/utils/autocomplete.ts` at line 1, The autocomplete entry classification
in autocomplete.ts is treating symlinks as regular files because
entry.isDirectory() only reflects the link itself, so linked directories lose
the trailing slash and cannot be traversed. Update the logic around the
directory-entry handling in the autocomplete flow to resolve or stat the symlink
target before deciding whether to append "/" and recurse, using the existing
autocomplete-related symbols to locate the classification branch.
| } | ||
| } | ||
| } catch (err) { | ||
| } catch { |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
No silencies los fallos del barrido y limpieza de disco.
Estas ramas convierten errores de readdirSync/statSync/unlinkSync en tamaños parciales o limpiezas “exitosas” sin contexto. Así, checkStorageLimit() puede subestimar el uso real y enforceStorageLimit() tomar decisiones torcidas sin decir por qué.
As per coding guidelines, "Always catch errors and provide meaningful error messages".
Also applies to: 107-107, 129-129, 159-164
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/utils/storage-limit.ts` at line 43, No silencien los errores en los
flujos de escaneo y limpieza de disco: los bloques catch alrededor de
`readdirSync`, `statSync` y `unlinkSync` están ocultando fallos y devolviendo
tamaños parciales o limpiezas aparentemente exitosas. Ajusten
`checkStorageLimit()` y `enforceStorageLimit()` para capturar el error real,
registrarlo o relanzarlo con contexto útil, y evitar decisiones basadas en
resultados incompletos; si hay helpers internos en `storage-limit.ts`, aseguren
que también propaguen el detalle del fallo en lugar de absorberlo.
Source: Coding guidelines
Automated Lint Fixes
This PR contains automated fixes for simple linting issues.
Changes:
Validation:
🤖 Generated by SC Smart Scheduler
Summary by CodeRabbit
Nuevas funciones
Bug Fixes
Tests