Skip to content

Conversation

@mikejmorgan-ai
Copy link
Member

@mikejmorgan-ai mikejmorgan-ai commented Jan 15, 2026

Summary

Implements #271 - Stdin Piping Support for Log Analysis.

Features

  • Automatic Detection: Detects piped input vs interactive terminal
  • Smart Truncation: Head, tail, middle, or sample modes for large inputs
  • Content Type Detection:
    • Error logs (with error counting)
    • Git diffs (files changed, additions, deletions)
    • JSON (array/object structure analysis)
    • Python tracebacks
    • System logs (systemd, journald)
    • Container logs
  • Machine-Readable Output: JSON stats for scripting

Commands

# Analyze piped data
docker logs container | cortex stdin analyze

# Get info about piped content
git diff | cortex stdin info

# Pass through with truncation
cat huge.log | cortex stdin passthrough --max-lines 500

# Get machine-readable stats
cat error.log | cortex stdin stats | jq .

Truncation Modes

  • head: Keep first N lines
  • tail: Keep last N lines
  • middle: Keep head + tail, truncate middle (default)
  • sample: Sample lines throughout

Testing

  • 36 tests with comprehensive coverage
  • Edge cases: unicode, empty, large inputs

Fixes #271

Summary by CodeRabbit

  • New Features
    • Added stdin command to accept and process piped input data
    • Supports multiple actions: info, analyze, passthrough, and stats
    • Automatic content type detection for error logs, diffs, JSON, CSV, container logs, and system logs
    • Configurable input truncation strategies (head, tail, middle, sample) for handling large inputs
    • Verbose output mode for enhanced analysis

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 15, 2026

Warning

Rate limit exceeded

@mikejmorgan-ai has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 2 minutes and 17 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 9a45c7d and 1105690.

📒 Files selected for processing (3)
  • cortex/cli.py
  • cortex/stdin_handler.py
  • tests/test_stdin_handler.py

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

📝 Walkthrough

Walkthrough

This pull request adds stdin piping support to Cortex's CLI. A new "stdin" subcommand is introduced with configurable truncation strategies (HEAD, TAIL, MIDDLE, SAMPLE), content-type detection, and analysis capabilities. Users can now pipe data (logs, diffs, etc.) directly to Cortex for processing.

Changes

Cohort / File(s) Summary
CLI Integration
cortex/cli.py
Added new "stdin" subcommand with action argument, --max-lines, --truncation, and -v/--verbose flags. Command dispatch now routes "stdin" to run_stdin_handler() with parsed arguments.
Core stdin Handler
cortex/stdin_handler.py
New comprehensive module providing StdinData dataclass, TruncationMode enum (HEAD/TAIL/MIDDLE/SAMPLE), StdinHandler class with stdin detection/reading/truncation, detect_content_type() for heuristic classification, analyze_stdin() for lightweight analysis, display_stdin_info() for Rich-based rendering, and run_stdin_handler() as the main orchestrator.
Test Suite
tests/test_stdin_handler.py
Comprehensive test coverage with 490+ lines; tests include TruncationMode/StdinData basics, StdinHandler initialization and stdin detection (TTY vs pipe), truncation strategies and edge cases, content-type detection across multiple categories (error logs, git diffs, JSON, tracebacks, etc.), analysis and display output, run entry point behavior, and edge cases (empty stdin, Unicode, read errors).

Sequence Diagram

sequenceDiagram
    participant User
    participant CLI
    participant Handler as run_stdin_handler
    participant StdinHandler
    participant Detection as detect_content_type
    participant Analysis as analyze_stdin
    participant Display as display_stdin_info
    
    User->>CLI: cortex stdin --action analyze [--max-lines N] [--truncation MODE]
    CLI->>Handler: run_stdin_handler(action, max_lines, truncation, verbose)
    
    Handler->>StdinHandler: has_stdin_data()
    StdinHandler-->>Handler: bool (TTY check)
    
    alt stdin available
        Handler->>StdinHandler: read_and_truncate()
        StdinHandler->>StdinHandler: read_stdin()
        StdinHandler->>StdinHandler: truncate() [apply MODE]
        StdinHandler-->>Handler: StdinData
        
        Handler->>Detection: detect_content_type(content)
        Detection-->>Handler: content_type (e.g., "git_diff", "error_log")
        
        alt action == "analyze"
            Handler->>Analysis: analyze_stdin(data, action, verbose)
            Analysis-->>Handler: analysis dict (type-specific metrics)
        end
        
        Handler->>Display: display_stdin_info(data, analysis)
        Display-->>User: Rich panel with summary & preview
    else no stdin
        Handler-->>User: Error: No stdin data
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 Whiskers twitching with glee
Stdin flows like a stream so free!
Logs and diffs now sing their song,
Unix pipes where Cortex belongs!
From tail to head, truncation's might—
Our fuzzy friend shines ever bright! ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically summarizes the main change: adding stdin piping support for log analysis, which aligns with the primary objective of issue #271.
Description check ✅ Passed The PR description is comprehensive and follows the template well, providing summary, features, commands, testing details, and issue reference. However, the AI Disclosure checkbox section is not filled out (neither option selected).
Linked Issues check ✅ Passed The implementation fully addresses issue #271 objectives: automatic stdin detection, truncation strategies (head, tail, middle, sample), content-type detection, and machine-readable JSON stats output are all implemented.
Out of Scope Changes check ✅ Passed All changes are scoped to stdin piping support. New CLI subcommand 'stdin', new stdin_handler module with comprehensive piping logic, and matching test suite are all directly aligned with issue #271 requirements.
Docstring Coverage ✅ Passed Docstring coverage is 97.92% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @mikejmorgan-ai, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the cortex CLI by adding a new stdin subcommand, allowing users to pipe data directly into cortex for analysis. It intelligently handles input, automatically detecting piped data and offering flexible truncation options for large datasets. The new functionality includes robust content type detection for various log formats, Git diffs, and JSON, providing tailored analysis and the ability to output machine-readable statistics. This feature streamlines the process of analyzing external data sources directly within the cortex ecosystem.

Highlights

  • New stdin Subcommand: Introduces a cortex stdin command with actions like info, analyze, passthrough, and stats to process piped input.
  • Intelligent Input Handling: Automatically detects if input is piped or from an interactive terminal, and handles large inputs with various truncation modes (head, tail, middle, sample).
  • Content Type Detection: Identifies common data types such as error logs, Git diffs, JSON, Python tracebacks, system logs, and container logs for tailored analysis.
  • Structured Analysis & Output: Provides content-specific analysis (e.g., error counts for logs, additions/deletions for Git diffs, JSON structure) and offers machine-readable JSON output for scripting.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Issue: #271 - Stdin Piping Support for Log Analysis
"""

import io
import io
import json
import sys
from unittest.mock import patch, MagicMock
Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a new cortex stdin command to process piped data, which is a great feature. The implementation includes logic for reading, truncating, and analyzing stdin content, along with comprehensive tests.

My review focuses on a few key areas for improvement:

  • Performance: The current implementation reads the entire stdin stream into memory, which could be problematic for very large files. I've suggested a streaming approach to handle this more efficiently.
  • Code Clarity: Some parts of the code could be simplified for better readability and maintainability.
  • Unused Code: I've pointed out a few unused parameters that should be removed.

Overall, this is a solid contribution with well-structured code and good test coverage. Addressing these points will make the feature more robust and efficient.

Comment on lines +96 to +119
def read_stdin(self) -> StdinData:
"""Read all available stdin data.
Returns:
StdinData containing the input and metadata
"""
if sys.stdin.isatty():
return StdinData(content="", line_count=0, byte_count=0)

try:
content = sys.stdin.read()
except (IOError, OSError):
return StdinData(content="", line_count=0, byte_count=0)

lines = content.splitlines(keepends=True)
byte_count = len(content.encode("utf-8", errors="replace"))

return StdinData(
content=content,
line_count=len(lines),
byte_count=byte_count,
original_line_count=len(lines),
original_byte_count=byte_count,
)
Copy link
Contributor

Choose a reason for hiding this comment

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

high

The read_stdin method reads the entire standard input into memory using sys.stdin.read(). This can lead to excessive memory consumption and potential crashes when processing large files, which contradicts the goal of supporting large log analysis.

To handle large inputs efficiently, you should process the input in a streaming manner. For example, read line-by-line and stop when max_lines or max_bytes is reached, while keeping track of original counts. For tail truncation, a collections.deque with a maxlen would be an efficient way to keep only the last N lines.

Comment on lines +2756 to +3184
action=getattr(args, "action", "info"),
max_lines=getattr(args, "max_lines", 1000),
truncation=getattr(args, "truncation", "middle"),
verbose=getattr(args, "verbose", False),
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The use of getattr with default values here is redundant. The stdin subparser defines default values for action, max_lines, truncation, and verbose. When args.command == "stdin", these attributes are guaranteed to be present on the args object. Direct access would be cleaner and more readable.

Suggested change
action=getattr(args, "action", "info"),
max_lines=getattr(args, "max_lines", 1000),
truncation=getattr(args, "truncation", "middle"),
verbose=getattr(args, "verbose", False),
action=args.action,
max_lines=args.max_lines,
truncation=args.truncation,
verbose=args.verbose,

return "json"

# CSV
if "," in first_line and lines[0].count(",") == lines[1].count(",") if len(lines) > 1 else False:
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The logic for CSV detection is a bit complex and hard to read. The nested if/else on a single line can be confusing. You can simplify this for better readability.

Suggested change
if "," in first_line and lines[0].count(",") == lines[1].count(",") if len(lines) > 1 else False:
if len(lines) > 1 and "," in first_line and lines[0].count(",") == lines[1].count(","):

Comment on lines +237 to +248
if any(
pattern in content
for pattern in ["container", "docker", "kubernetes", "pod"]
):
return "container_log"

# System logs
if any(
pattern in content
for pattern in ["systemd", "journald", "kernel", "syslog"]
):
return "system_log"
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The content detection for "container_log" and "system_log" scans the entire content string. For large inputs, this can be inefficient. Similar to how you check for log patterns in the first 10 lines, you could limit these checks to a prefix of the content to improve performance.

Suggested change
if any(
pattern in content
for pattern in ["container", "docker", "kubernetes", "pod"]
):
return "container_log"
# System logs
if any(
pattern in content
for pattern in ["systemd", "journald", "kernel", "syslog"]
):
return "system_log"
# Docker/container logs
content_prefix = "\n".join(lines[:50])
if any(
pattern in content_prefix
for pattern in ["container", "docker", "kubernetes", "pod"]
):
return "container_log"
# System logs
if any(
pattern in content_prefix
for pattern in ["systemd", "journald", "kernel", "syslog"]
):
return "system_log"

Comment on lines +253 to +257
def analyze_stdin(
data: StdinData,
action: str = "analyze",
verbose: bool = False,
) -> dict:
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The action and verbose parameters are defined in this function's signature but are never used within the function body. They should either be used or removed to avoid confusion and dead code.

def analyze_stdin(
    data: StdinData,
) -> dict:

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Fix all issues with AI agents
In `@cortex/stdin_handler.py`:
- Around line 105-108: Replace the redundant except tuple catching IOError and
OSError with a single except OSError in the block that reads stdin (the try
around sys.stdin.read() that returns StdinData). Locate the try/except in
stdin_handler.py (the code that builds and returns StdinData(content="",
line_count=0, byte_count=0)) and change the except clause to catch only OSError
to satisfy ruff UP024 while preserving the existing return behavior.
- Line 312: The function annotation for display_stdin_info uses legacy typing
Optional; update its signature from Optional[dict] to the modern PEP 604 union
syntax dict | None (i.e., def display_stdin_info(data: StdinData, analysis: dict
| None = None):) and then remove Optional from the typing imports at the top of
the file if it is no longer used elsewhere; check for other occurrences of
Optional in this file and update/removal similarly.

In `@tests/test_stdin_handler.py`:
- Around line 482-490: The test test_read_error uses patch(...,
side_effect=IOError("Read error")) which triggers a lint UP024; change the
exception to OSError by replacing IOError with OSError in the side_effect so the
patched sys.stdin.read raises OSError("Read error") instead, leaving the rest of
the test and the StdinHandler.read_stdin behavior unchanged.
- Around line 7-22: The import block in tests/test_stdin_handler.py is unsorted
and includes an unused standard-library import; reorder imports into PEP 8
groups (standard library, third-party, local) and sort them alphabetically
within each group, remove the unused "io" import, and keep the references to the
tested symbols (StdinData, StdinHandler, TruncationMode, analyze_stdin,
detect_content_type, display_stdin_info, run_stdin_handler) from
cortex.stdin_handler intact so tests still import the right names.
🧹 Nitpick comments (2)
cortex/stdin_handler.py (2)

139-151: MIDDLE truncation includes extra marker line.

The MIDDLE truncation adds a marker line "... [{skipped} lines truncated] ..." which means the output can have max_lines + 1 lines. This is probably acceptable UX but worth noting. If strict line limits are needed, consider adjusting half to account for the marker.


228-234: JSON and CSV detection are heuristic and may have edge cases.

The JSON detection on line 229 only checks if the first line starts with { or [, which could match non-JSON content. The CSV detection on line 233 has a complex conditional. These are reasonable heuristics for the use case, but be aware of potential false positives.

Consider adding a validation step for JSON detection:

# JSON - try to parse for more accurate detection
if first_line.startswith("{") or first_line.startswith("["):
    try:
        import json
        json.loads(content)
        return "json"
    except json.JSONDecodeError:
        pass  # Fall through to other checks
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between daa52b5 and 9a45c7d.

📒 Files selected for processing (3)
  • cortex/cli.py
  • cortex/stdin_handler.py
  • tests/test_stdin_handler.py
🧰 Additional context used
📓 Path-based instructions (2)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Follow PEP 8 style guide for Python code
Include type hints in Python code
Add docstrings for all public APIs in Python code
Use dry-run mode by default for all installation operations
Do not use silent sudo - require explicit user confirmation for privilege escalation
Implement Firejail sandboxing for execution of untrusted code
Log all operations to ~/.cortex/history.db for audit purposes

Files:

  • tests/test_stdin_handler.py
  • cortex/cli.py
  • cortex/stdin_handler.py
tests/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Maintain test coverage above 80% for pull requests

Files:

  • tests/test_stdin_handler.py
🧬 Code graph analysis (3)
tests/test_stdin_handler.py (1)
cortex/stdin_handler.py (11)
  • StdinData (37-50)
  • StdinHandler (53-183)
  • TruncationMode (27-33)
  • analyze_stdin (253-309)
  • detect_content_type (186-250)
  • display_stdin_info (312-352)
  • run_stdin_handler (355-439)
  • is_empty (48-50)
  • has_stdin_data (77-94)
  • read_stdin (96-119)
  • truncate (121-172)
cortex/cli.py (1)
cortex/stdin_handler.py (1)
  • run_stdin_handler (355-439)
cortex/stdin_handler.py (1)
tests/test_stdin_handler.py (1)
  • handler (62-64)
🪛 GitHub Actions: CI
cortex/stdin_handler.py

[error] 107-107: ruff check reported: UP024 Replace aliased errors with OSError. Command: 'ruff check . --output-format=github'.

🪛 GitHub Check: lint
tests/test_stdin_handler.py

[failure] 487-487: Ruff (UP024)
tests/test_stdin_handler.py:487:54: UP024 Replace aliased errors with OSError


[failure] 7-22: Ruff (I001)
tests/test_stdin_handler.py:7:1: I001 Import block is un-sorted or un-formatted

cortex/stdin_handler.py

[failure] 312-312: Ruff (UP045)
cortex/stdin_handler.py:312:51: UP045 Use X | None for type annotations


[failure] 107-107: Ruff (UP024)
cortex/stdin_handler.py:107:16: UP024 Replace aliased errors with OSError

🪛 GitHub Check: Lint
tests/test_stdin_handler.py

[failure] 487-487: Ruff (UP024)
tests/test_stdin_handler.py:487:54: UP024 Replace aliased errors with OSError


[failure] 7-22: Ruff (I001)
tests/test_stdin_handler.py:7:1: I001 Import block is un-sorted or un-formatted

cortex/stdin_handler.py

[failure] 312-312: Ruff (UP045)
cortex/stdin_handler.py:312:51: UP045 Use X | None for type annotations


[failure] 107-107: Ruff (UP024)
cortex/stdin_handler.py:107:16: UP024 Replace aliased errors with OSError

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: test (3.12)
  • GitHub Check: test (3.11)
  • GitHub Check: test (3.10)
🔇 Additional comments (11)
cortex/cli.py (2)

2672-2697: Stdin subcommand implementation looks good.

The argument structure is well-defined with appropriate defaults and choices. The action argument with nargs="?" and default="info" provides a clean UX for the default use case.

One minor note: the -v/--verbose flag here (line 2693-2696) is stdin-specific, while there's also a global --verbose flag at line 2272. Consider whether both should be unified or if the stdin-specific flag is intentional for this subcommand.


2753-2760: LGTM - dispatch follows existing patterns.

The lazy import of run_stdin_handler at dispatch time is consistent with other commands. The getattr calls with defaults are defensive but harmless since these arguments are defined in the parser.

tests/test_stdin_handler.py (3)

25-56: Good coverage of basic data structures.

The enum and dataclass tests verify the core types work correctly. The is_empty property test covers both true and false cases.


58-171: Comprehensive StdinHandler tests.

Good coverage of initialization, stdin detection (TTY vs pipe), reading, and all truncation modes. The test fixtures and mocking approach are appropriate.


321-420: Run entry point tests are well-structured.

The tests cover all action types (info, analyze, passthrough, stats) and the unknown action error path. The mocking pattern correctly intercepts the StdinHandler instantiation to inject controlled behavior.

cortex/stdin_handler.py (6)

1-25: Good module structure with clear documentation.

The module docstring clearly explains the purpose and provides usage examples. Imports are well-organized with standard library, then third-party (rich) dependencies.


27-51: Well-designed data structures.

TruncationMode enum and StdinData dataclass are clean and follow Python best practices. The is_empty property provides a convenient way to check for empty stdin.


253-309: Solid analysis implementation with content-specific insights.

The analysis function provides useful metrics for common content types. The error counting, git diff stats, and JSON structure analysis are all practical features for the stated use case.


312-353: Well-designed display function with Rich formatting.

The table layout, truncation status indicators, and syntax-highlighted preview provide good UX for understanding piped input. The 20-line preview limit is reasonable.


355-439: Clean entry point implementation.

The run_stdin_handler function provides a well-structured entry point with:

  • Proper truncation mode mapping
  • Helpful usage examples when no stdin is detected
  • Clear action handling for all supported modes
  • Appropriate exit codes

The lazy import of json in the stats branch is fine since it's only needed for that action.


77-95: Clarify Windows compatibility in docstring and exception handler comment.

select.select() does not work with stdin on Windows (only with sockets), so the exception handler correctly catches the ValueError and falls back to returning True. While this handles Windows correctly, add explicit documentation about this platform-specific behavior in the method docstring and the exception handler comment to make the Windows fallback strategy clear.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment on lines +105 to +108
try:
content = sys.stdin.read()
except (IOError, OSError):
return StdinData(content="", line_count=0, byte_count=0)
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix IOError to OSError - pipeline failure.

The pipeline is failing because IOError is an alias for OSError in Python 3, and ruff UP024 requires using OSError directly.

🔧 Proposed fix
         try:
             content = sys.stdin.read()
-        except (IOError, OSError):
+        except OSError:
             return StdinData(content="", line_count=0, byte_count=0)

Since IOError is already an alias for OSError, catching just OSError is sufficient.

📝 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
try:
content = sys.stdin.read()
except (IOError, OSError):
return StdinData(content="", line_count=0, byte_count=0)
try:
content = sys.stdin.read()
except OSError:
return StdinData(content="", line_count=0, byte_count=0)
🧰 Tools
🪛 GitHub Actions: CI

[error] 107-107: ruff check reported: UP024 Replace aliased errors with OSError. Command: 'ruff check . --output-format=github'.

🪛 GitHub Check: lint

[failure] 107-107: Ruff (UP024)
cortex/stdin_handler.py:107:16: UP024 Replace aliased errors with OSError

🪛 GitHub Check: Lint

[failure] 107-107: Ruff (UP024)
cortex/stdin_handler.py:107:16: UP024 Replace aliased errors with OSError

🤖 Prompt for AI Agents
In `@cortex/stdin_handler.py` around lines 105 - 108, Replace the redundant except
tuple catching IOError and OSError with a single except OSError in the block
that reads stdin (the try around sys.stdin.read() that returns StdinData).
Locate the try/except in stdin_handler.py (the code that builds and returns
StdinData(content="", line_count=0, byte_count=0)) and change the except clause
to catch only OSError to satisfy ruff UP024 while preserving the existing return
behavior.

return result


def display_stdin_info(data: StdinData, analysis: Optional[dict] = None):
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Use modern type annotation syntax.

Per ruff UP045 and modern Python style, use dict | None instead of Optional[dict].

🔧 Proposed fix
-def display_stdin_info(data: StdinData, analysis: Optional[dict] = None):
+def display_stdin_info(data: StdinData, analysis: dict | None = None):

After this change, the Optional import from typing on line 17 can be removed if not used elsewhere.

🧰 Tools
🪛 GitHub Check: lint

[failure] 312-312: Ruff (UP045)
cortex/stdin_handler.py:312:51: UP045 Use X | None for type annotations

🪛 GitHub Check: Lint

[failure] 312-312: Ruff (UP045)
cortex/stdin_handler.py:312:51: UP045 Use X | None for type annotations

🤖 Prompt for AI Agents
In `@cortex/stdin_handler.py` at line 312, The function annotation for
display_stdin_info uses legacy typing Optional; update its signature from
Optional[dict] to the modern PEP 604 union syntax dict | None (i.e., def
display_stdin_info(data: StdinData, analysis: dict | None = None):) and then
remove Optional from the typing imports at the top of the file if it is no
longer used elsewhere; check for other occurrences of Optional in this file and
update/removal similarly.

Comment on lines +7 to +22
import io
import json
import sys
from unittest.mock import patch, MagicMock

import pytest

from cortex.stdin_handler import (
StdinData,
StdinHandler,
TruncationMode,
analyze_stdin,
detect_content_type,
display_stdin_info,
run_stdin_handler,
)
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix import block ordering to pass linting.

Static analysis indicates the import block is unsorted. Per PEP 8 and ruff I001, imports should be grouped and sorted.

🔧 Proposed fix for import ordering
-import io
 import json
 import sys
+from unittest.mock import MagicMock, patch
+
+import pytest
+
+from cortex.stdin_handler import (
+    StdinData,
+    StdinHandler,
+    TruncationMode,
+    analyze_stdin,
+    detect_content_type,
+    display_stdin_info,
+    run_stdin_handler,
+)
-from unittest.mock import patch, MagicMock
-
-import pytest
-
-from cortex.stdin_handler import (
-    StdinData,
-    StdinHandler,
-    TruncationMode,
-    analyze_stdin,
-    detect_content_type,
-    display_stdin_info,
-    run_stdin_handler,
-)

Note: The io import on line 7 appears to be unused and can be removed.

📝 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
import io
import json
import sys
from unittest.mock import patch, MagicMock
import pytest
from cortex.stdin_handler import (
StdinData,
StdinHandler,
TruncationMode,
analyze_stdin,
detect_content_type,
display_stdin_info,
run_stdin_handler,
)
import json
import sys
from unittest.mock import MagicMock, patch
import pytest
from cortex.stdin_handler import (
StdinData,
StdinHandler,
TruncationMode,
analyze_stdin,
detect_content_type,
display_stdin_info,
run_stdin_handler,
)
🧰 Tools
🪛 GitHub Check: lint

[failure] 7-22: Ruff (I001)
tests/test_stdin_handler.py:7:1: I001 Import block is un-sorted or un-formatted

🪛 GitHub Check: Lint

[failure] 7-22: Ruff (I001)
tests/test_stdin_handler.py:7:1: I001 Import block is un-sorted or un-formatted

🤖 Prompt for AI Agents
In `@tests/test_stdin_handler.py` around lines 7 - 22, The import block in
tests/test_stdin_handler.py is unsorted and includes an unused standard-library
import; reorder imports into PEP 8 groups (standard library, third-party, local)
and sort them alphabetically within each group, remove the unused "io" import,
and keep the references to the tested symbols (StdinData, StdinHandler,
TruncationMode, analyze_stdin, detect_content_type, display_stdin_info,
run_stdin_handler) from cortex.stdin_handler intact so tests still import the
right names.

Comment on lines +482 to +490
def test_read_error(self):
"""Test handling read errors."""
handler = StdinHandler()

with patch("sys.stdin.isatty", return_value=False):
with patch("sys.stdin.read", side_effect=IOError("Read error")):
data = handler.read_stdin()

assert data.is_empty
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Replace IOError with OSError to fix lint failure.

IOError is an alias for OSError in Python 3. The pipeline is failing due to ruff UP024 requiring the use of OSError directly.

🔧 Proposed fix
     def test_read_error(self):
         """Test handling read errors."""
         handler = StdinHandler()
 
         with patch("sys.stdin.isatty", return_value=False):
-            with patch("sys.stdin.read", side_effect=IOError("Read error")):
+            with patch("sys.stdin.read", side_effect=OSError("Read error")):
                 data = handler.read_stdin()
 
         assert data.is_empty
🧰 Tools
🪛 GitHub Check: lint

[failure] 487-487: Ruff (UP024)
tests/test_stdin_handler.py:487:54: UP024 Replace aliased errors with OSError

🪛 GitHub Check: Lint

[failure] 487-487: Ruff (UP024)
tests/test_stdin_handler.py:487:54: UP024 Replace aliased errors with OSError

🤖 Prompt for AI Agents
In `@tests/test_stdin_handler.py` around lines 482 - 490, The test test_read_error
uses patch(..., side_effect=IOError("Read error")) which triggers a lint UP024;
change the exception to OSError by replacing IOError with OSError in the
side_effect so the patched sys.stdin.read raises OSError("Read error") instead,
leaving the rest of the test and the StdinHandler.read_stdin behavior unchanged.

- Detect stdin data automatically (pipe vs TTY)
- Truncation modes: head, tail, middle, sample
- Content type detection: error logs, git diff, JSON, system logs
- Analysis: error counts, diff stats, JSON structure
- Machine-readable stats output for scripting
- Handles unicode and large inputs gracefully
- 36 tests with full coverage

Usage:
  docker logs container | cortex stdin analyze
  git diff | cortex stdin info
  cat error.log | cortex stdin --max-lines 500

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@mikejmorgan-ai mikejmorgan-ai force-pushed the feature/issue-271-stdin-piping branch from 9a45c7d to 1105690 Compare January 15, 2026 08:21
@mikejmorgan-ai mikejmorgan-ai merged commit 9bc1b58 into main Jan 15, 2026
11 of 13 checks passed
@mikejmorgan-ai mikejmorgan-ai deleted the feature/issue-271-stdin-piping branch January 15, 2026 08:21
@sonarqubecloud
Copy link

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.

Stdin Piping Support for Log Analysis

2 participants