Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/skillspector/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ def scan(
typer.Option(
"--recursive",
"-r",
help="Scan directories containing multiple skills (immediate subdirectories with SKILL.md) independently.",
help="Scan immediate subdirectories that each contain a SKILL.md as independent skills.",
),
] = False,
verbose: Annotated[
Expand Down
2 changes: 1 addition & 1 deletion src/skillspector/nodes/analyzers/static_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ def _is_eval_dataset(path: str) -> bool:
_DOCUMENTATION_CONFIDENCE_FACTOR = 0.3
_CODE_EXAMPLE_CONFIDENCE_FACTOR = 0.5

_NON_EXECUTABLE_FILE_TYPES = frozenset({"markdown", "text", "json", "yaml", "toml", "other"})
_NON_EXECUTABLE_FILE_TYPES = frozenset({"markdown", "text", "json", "yaml", "toml"})


def _is_documentation_markdown(path: str) -> bool:
Expand Down
2 changes: 1 addition & 1 deletion src/skillspector/nodes/meta_analyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ def _fallback_filtered(findings: list[Finding]) -> list[Finding]:

result: list[Finding] = []
for f in findings:
severity_upper = f.severity.upper()
severity_upper = (f.severity or "LOW").upper()
confidence = f.confidence
if f.context and is_code_example(f.context):
confidence *= _CODE_EXAMPLE_DOWNWEIGHT
Expand Down
17 changes: 17 additions & 0 deletions tests/nodes/analyzers/test_static_runner_filtering.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,23 @@ def test_finding_in_executable_not_dropped_by_generic_indicator(self) -> None:
for f in tm1_findings:
assert f.confidence > 0

def test_extensionless_file_not_hard_dropped_by_code_example(self) -> None:
"""An extensionless file (inferred as 'other') in code-example context is downweighted, not dropped."""
content = """\
#!/bin/bash
# Example: cleanup old builds
rm -rf /tmp/build-cache
"""
state = {
"components": ["cleanup_script"],
"file_cache": {"cleanup_script": content},
}
findings = static_runner.run_static_patterns(state, [tm_module])
tm1_findings = [f for f in findings if f.rule_id == "TM1"]
assert len(tm1_findings) >= 1, (
"Extensionless files must not have code-example findings hard-dropped"
)

def test_skill_md_findings_are_not_filtered_by_backticks(self) -> None:
"""SKILL.md is the primary instruction file — backticks alone shouldn't filter."""
content = """\
Expand Down
13 changes: 13 additions & 0 deletions tests/nodes/test_meta_analyzer_fallback.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,19 @@ def test_low_severity_below_threshold_still_dropped(self) -> None:
assert len(result) == 0


def test_none_severity_treated_as_low(self) -> None:
"""Finding with None severity does not crash — treated as LOW."""
findings = [_finding(confidence=0.8, severity=None)]
result = _fallback_filtered(findings)
assert len(result) == 1

def test_none_severity_below_threshold_dropped(self) -> None:
"""None severity at low confidence is dropped (no severity floor protection)."""
findings = [_finding(confidence=0.3, severity=None)]
result = _fallback_filtered(findings)
assert len(result) == 0


class TestCodeExampleFiltering:
"""Findings in code example context are downweighted, not hard-dropped."""

Expand Down