Skip to content

fix: auto-fix linting issues - #70

Merged
scanalesespinoza merged 15 commits into
mainfrom
auto-fix-lint-20260628-230056
Jul 4, 2026
Merged

scanalesespinoza merged 15 commits into
mainfrom
auto-fix-lint-20260628-230056

Conversation

@scanalesespinoza

@scanalesespinoza scanalesespinoza commented Jun 29, 2026 •

Copy link
Copy Markdown
Contributor

Automated Lint Fixes

This PR contains automated fixes for simple linting issues.

Changes:

  • Fixed unused imports
  • Fixed code style inconsistencies
  • Fixed minor formatting issues

Validation:

  • ✅ TypeScript compilation successful
  • ✅ No logic changes
  • ✅ Safe, automated fixes only

🤖 Generated by SC Smart Scheduler

Summary by CodeRabbit

  • Nuevas funciones

    • Se añadió una guía de almacenamiento adaptada a Windows y entornos Unix.
    • Se incorporó validación automática de secuencias de mensajes para mejorar la continuidad del chat.
    • Se sumó una opción para forzar la inicialización de la configuración global.
  • Bug Fixes

    • Se corrigió el manejo de respuestas de herramientas para asegurar que se muestren correctamente.
    • Se evitó un fallo al validar rutas vacías en el acceso al espacio de trabajo.
    • Se mejoró el flujo cuando una respuesta requiere continuar tras usar herramientas.
  • Tests

    • Se agregaron pruebas para configuración, guía de almacenamiento y comportamiento de inicialización.

scanalesespinoza and others added 15 commits June 28, 2026 17:01
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]>
@coderabbitai

coderabbitai Bot commented Jun 29, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Resumen

Se corrigen cuatro fallos críticos del agente: los resultados de herramientas ahora usan role: 'tool', se añade un continuation prompt cuando el modelo devuelve solo tool_calls sin texto, se guarda la seguridad de paths ante relativePath vacío, y se introduce un nuevo validador/corrector de secuencias de mensajes. Adicionalmente, initConfig acepta --force, se configura ESLint con TypeScript, y se extrae getStorageGuidance por plataforma.

Cambios

Correcciones del agente, validación de mensajes y mejoras de tooling

Capa / Archivo(s) Resumen
Tipo MessageRole y nuevo message-validator
src/core/types.ts, src/core/message-validator.ts
MessageRole incluye ahora 'tool'. Se añaden MessageValidationError, validateMessageSequence, autoCorrectMessageSequence e isMessageSequenceValid para garantizar la consistencia de la secuencia de mensajes antes de cada llamada al proveedor.
Correcciones en Agent.run: roles tool, continuation prompt y validación previa
src/core/agent.ts
Se importa y aplica autoCorrectMessageSequence antes de cada request; los tres puntos de inserción de resultados de herramientas (éxito, error de ejecución, herramienta desconocida) pasan a role: 'tool'; si el modelo devuelve solo tool_calls sin contenido se inyecta un mensaje de continuación al usuario.
Corrección de path-security para relativePath vacío
src/utils/path-security.ts
Se añade una guarda para omitir ig.ignores() cuando relativePath es vacío, evitando el error al evaluar el workspace root.
Flag --force en initConfig y CLI config-init
src/core/config.ts, src/cli.ts, src/core/config.test.ts
initConfig(force = false) verifica existencia del config y lanza error instructivo cuando force es false. El subcomando CLI expone -f, --force. El test valida ambos caminos (rechazo sin force y sobrescritura con force).
Configuración de ESLint con TypeScript
eslint.config.mjs, package.json
Se crea la configuración ESLint con presets de @eslint/js y typescript-eslint, reglas para no-unused-vars y no-explicit-any, y se añaden las dependencias correspondientes.
getStorageGuidance y uso en /storage
src/utils/storage-guidance.ts, src/utils/storage-guidance.test.ts, src/commands/chat-session.ts
Nueva función exportada que devuelve tips de almacenamiento según la plataforma (win32 vs. POSIX). El comando /storage itera los tips en lugar de usar mensajes estáticos. Se añade test de cobertura.
Limpieza de type safety y catch sin uso
src/commands/chat-session.ts, src/utils/permissions.ts, src/utils/autocomplete.ts, src/utils/storage-limit.ts, src/core/project-context.ts, src/tools/search-text.ts
Se reemplazan tipos any por Record<string,unknown> e interfaces locales; se elimina la importación de getHighestSeverity; se simplifican bloques catch para no declarar la variable de error cuando no se usa.
Documentación CRITICAL-FIXES.md y CLAUDE-CODE-PARITY.md
CRITICAL-FIXES.md, CLAUDE-CODE-PARITY.md
Se añaden documentos de referencia: CRITICAL-FIXES.md detalla los cuatro fixes con fragmentos de código, estrategia de pre-commit y prohibición de eliminación; CLAUDE-CODE-PARITY.md analiza brechas respecto a Claude Code y una hoja de ruta P0–P3.

Diagramas de secuencia

sequenceDiagram
    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)
Loading

Esfuerzo estimado de revisión de código

🎯 4 (Complejo) | ⏱️ ~60 minutos

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 22.22% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed El título describe correctamente la limpieza automática de linting, aunque no refleja toda la amplitud de cambios del PR.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch auto-fix-lint-20260628-230056

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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 win

No 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 mensaje role: '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 win

No 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 ENOENT y 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 win

No 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

📥 Commits

Reviewing files that changed from the base of the PR and between 83678ea and 3f6070b.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (19)
  • CLAUDE-CODE-PARITY.md
  • CRITICAL-FIXES.md
  • eslint.config.mjs
  • package.json
  • src/cli.ts
  • src/commands/chat-session.ts
  • src/core/agent.ts
  • src/core/config.test.ts
  • src/core/config.ts
  • src/core/message-validator.ts
  • src/core/project-context.ts
  • src/core/types.ts
  • src/tools/search-text.ts
  • src/utils/autocomplete.ts
  • src/utils/path-security.ts
  • src/utils/permissions.ts
  • src/utils/storage-guidance.test.ts
  • src/utils/storage-guidance.ts
  • src/utils/storage-limit.ts

Comment thread CLAUDE-CODE-PARITY.md
Comment on lines +231 to +239
**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
}
```

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 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.

Comment thread src/core/config.ts
Comment on lines +132 to +142
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;
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 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.

Suggested change
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

Comment on lines +59 to +74
// 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,
});
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 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.

Suggested change
// 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.

Comment on lines +144 to +149
export function isMessageSequenceValid(messages: Message[]): boolean {
try {
validateMessageSequence(messages);
return true;
} catch {
return false;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 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.

Suggested change
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

Comment thread src/tools/search-text.ts
results.push(`${file}:\n${matches.join('\n')}`);
}
} catch (err: unknown) {
} catch {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 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.

Suggested change
} 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

Comment thread src/utils/autocomplete.ts
@@ -1,4 +1,4 @@
import { readdirSync, statSync, existsSync } from 'node:fs';
import { readdirSync, existsSync } from 'node:fs';

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 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:


🏁 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 {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 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

@scanalesespinoza
scanalesespinoza merged commit 96e54c4 into main Jul 4, 2026
802 of 1202 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant