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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/agent-usage-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ codelens audit <workspace> --check dead-code # different order, also
| "Any circular imports?" | `deps . --check circular` | |
| "Any secrets/vulnerable deps/injection risk?" | `security . --check secrets\|vuln-scan\|taint\|regex-audit` | **Taint is Python/JS/TS/TSX only** — see Known Gaps, no Rust coverage. |
| "10-second repo orientation" | `context . --check orient` (or bare `context .`, it's the default) | Framework detection, dev commands, entry points. |
| "Is this CSS var / keyframe still used? specificity/z-index problems?" | `audit . --check css` | Unused CSS vars, orphan keyframes, specificity wars, duplicate props, unused media queries, z-index abuse. `--severity`/`--category` filters. Restored issue #251 (engine was orphaned since #195). |
| "Prioritized health snapshot" | `summary .` | Aggregates dead-code/smell/taint/vuln-scan; use `--lite` for an agent-sized payload. |

---
Expand Down
77 changes: 77 additions & 0 deletions docs/design/0251-restore-css-deep.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# Design Doc: Restore css-deep as `audit --check css`

> **Status:** Accepted
> **Date:** 2026-07-13
> **Author:** Claude (direct implementation, no worker — user directive)
> **Related issues:** #251
> **Related PRs:** (this PR)

---

## Problem

`cssdeep_engine.py` (deep CSS analysis: unused CSS variables, orphan
keyframes, specificity wars, duplicate properties, unused media queries,
z-index abuse) is fully functional — verified 154 real findings on the
Coretax `smart-tax-assistance` workspace — but **orphaned**: its CLI entry
point (the old standalone `css-deep` command) was deleted in the #195
umbrella consolidation, and `analyze_css_deep()` is now reachable from no
command, MCP tool, or `--check` sub-mode. This is the same situation as
`export-snapshot` (issue #218, restored): a working engine with a dead
entry point.

CSS is in CodeLens's stated language scope (react/css/html). Losing deep
CSS analysis means falling back to manual grep for questions like "is this
CSS variable still used?".

## Goal

`codelens audit <workspace> --check css` runs `analyze_css_deep()` and
returns findings in the standard umbrella `{s, st, r}` shape, with
`--severity` and `--category` passthrough.

## Changes

### New Files
- `scripts/commands/css_deep.py` — thin wrapper over
`cssdeep_engine.analyze_css_deep()`, mirroring `export_snapshot.py`'s
structure. No engine logic duplicated.

### Modified Files
- `scripts/commands/audit.py` — registered `css` in `_CHECKS`, added the
namespace branch (severity + category passthrough), updated epilog.
- `tests/test_command_registry.py` — added `css_deep` to the
implementation-module allowlist (it's imported by the audit umbrella,
not self-registering — same as `export_snapshot`).
- `docs/agent-usage-guide.md` — css-deep documented as available again.

### Not Changed
- `cssdeep_engine.py` — the engine already works and needed no changes.

## Why a sub-check, not a restored top-level command

The #195 consolidation reduced 78 commands to 12 umbrellas. Re-adding
`css-deep` as a **top-level** command would violate that (command count
would go to 13). As a `--check css` sub-mode under `audit` — alongside
dead-code / complexity / smell / perf-hint, all code-quality analyses — it
fits the umbrella taxonomy and keeps the command count at exactly 12
(verified via `--command-count`). This is compatible with the #195
philosophy (fewer top-level commands, richer sub-checks), not a reversal
of it.

## Testing

`tests/test_css_deep_command.py`: wrapper delegation, severity/category
passthrough, audit umbrella dispatch of `css`, and severity reaching the
engine through the synthetic namespace. Verified end-to-end on the real
workspace: 154 findings, `--severity high` → 3, `--category z_index_abuse`
→ 1.

## Alternatives Considered

- **Leave it dropped.** Rejected — the engine works, CSS is in scope, and
the loss forces manual grep for CSS-variable/keyframe usage. Same
reasoning that restored `export-snapshot` (#218).
- **Restore as a top-level `css-deep` command.** Rejected — violates the
12-umbrella consolidation. Sub-check placement recovers the capability
without growing the command surface.
11 changes: 11 additions & 0 deletions scripts/commands/audit.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@
"module": "commands.side_effect",
"help": "Pure vs impure function analysis",
},
"css": {
"module": "commands.css_deep",
"help": "Deep CSS analysis: unused vars, orphan keyframes, specificity wars, z-index abuse (issue #251)",
},
}

ALL_CHECKS = list(_CHECKS.keys())
Expand All @@ -75,11 +79,14 @@ def add_args(parser):
" staleness Per-file staleness detection\n"
" perf-hint Performance anti-patterns\n"
" side-effect Pure vs impure function analysis\n"
" css Deep CSS analysis: unused vars, orphan keyframes,\n"
" specificity wars, z-index abuse (issue #251)\n"
"\n"
"Examples:\n"
" codelens audit . # all checks\n"
" codelens audit . --check dead-code # only dead-code\n"
" codelens audit . --check complexity,smell # pick subset\n"
" codelens audit . --check css # deep CSS analysis\n"
)
parser.add_argument("workspace", nargs="?", default=None,
help="Path to workspace root (auto-detected if omitted)")
Expand Down Expand Up @@ -173,6 +180,10 @@ def _build_namespace(base_args, check_name: str) -> argparse.Namespace:
ns.name = getattr(base_args, "name", None)
ns.file = getattr(base_args, "file", None)
ns.max_files = getattr(base_args, "max_files", None) or 3000
elif check_name == "css":
# cssdeep_engine accepts severity (high|medium|low) + single category
ns.severity = getattr(base_args, "severity", None)
ns.category = getattr(base_args, "category", None)
return ns


Expand Down
42 changes: 42 additions & 0 deletions scripts/commands/css_deep.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# @WHO: scripts/commands/css_deep.py
# @WHAT: Deep CSS analysis command — thin wrapper over cssdeep_engine (issue #251)
# @PART: commands
# @ENTRY: execute()
"""css-deep command — deep CSS analysis (issue #251 restoration).

Wraps ``cssdeep_engine.analyze_css_deep()`` — detects unused CSS variables,
orphan keyframes, specificity wars, duplicate properties, unused media
queries, and z-index abuse.

The engine was never deleted, but its CLI entry point (the old standalone
``css-deep`` command) was dropped in the #195 umbrella consolidation,
leaving the working engine orphaned (same situation as ``export-snapshot``,
issue #218). This restores access as ``audit --check css`` — a sub-check
under the audit umbrella, NOT a new top-level command, so the 12-umbrella
consolidation is preserved (command count stays 12).
"""

from cssdeep_engine import analyze_css_deep
from commands import register_command


def add_args(parser):
parser.add_argument("workspace", nargs="?", default=None,
help="Path to workspace root (auto-detected if omitted)")
parser.add_argument("--severity", choices=["high", "medium", "low"], default=None,
help="Filter by severity level")
parser.add_argument("--category", default=None,
help="Filter to one category: unused_vars, orphan_keyframes, "
"specificity_wars, duplicate_props, unused_media, z_index_abuse")


def execute(args, workspace):
return analyze_css_deep(
workspace,
severity=getattr(args, "severity", None),
category=getattr(args, "category", None),
)

# Issue #251: registered as the `css` sub-check of the `audit` umbrella
# (see commands/audit.py), NOT as a standalone command — keeps command
# count at 12. This module is imported by audit.py, not self-registering.
12 changes: 6 additions & 6 deletions tests/test_command_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,12 @@ def test_every_command_module_registers():
# but the umbrella commands import them for --check sub-analyses.
_DEPRECATED_ALIAS_MODULES = {
"affected", "arch_metrics", "architecture", "binary_scan",
"circular", "complexity", "dashboard", "dataflow", "dead_code",
"dependents", "diff", "env_check", "export_snapshot", "git_status",
"graph_schema", "import_snapshot", "init", "lsp_status", "orient",
"outline", "ownership", "perf_hint", "query_graph", "regex_audit",
"secrets", "side_effect", "smell", "staleness", "taint", "trace",
"vuln_scan",
"circular", "complexity", "css_deep", "dashboard", "dataflow",
"dead_code", "dependents", "diff", "env_check", "export_snapshot",
"git_status", "graph_schema", "import_snapshot", "init", "lsp_status",
"orient", "outline", "ownership", "perf_hint", "query_graph",
"regex_audit", "secrets", "side_effect", "smell", "staleness",
"taint", "trace", "vuln_scan",
}
_UTILITY_MODULES |= _DEPRECATED_ALIAS_MODULES
missing = []
Expand Down
81 changes: 81 additions & 0 deletions tests/test_css_deep_command.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# @WHO: tests/test_css_deep_command.py
# @WHAT: Tests for restored css-deep as audit --check css (issue #251)
# @PART: tests
"""Tests for the css sub-check of the audit umbrella (issue #251).

cssdeep_engine was orphaned in the #195 consolidation (its command entry
point was deleted, the engine kept). This restores access as
`audit --check css` — a sub-check, NOT a new top-level command. These
tests verify the wrapper delegates correctly, the audit umbrella dispatches
`css`, and passthrough filters (severity/category) reach the engine.
"""

import argparse
import os
import sys
from unittest import mock

import pytest

SCRIPT_DIR = os.path.join(
os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "scripts"
)
if SCRIPT_DIR not in sys.path:
sys.path.insert(0, SCRIPT_DIR)

from commands import css_deep # noqa: E402
from commands import audit # noqa: E402


class TestCssDeepCommand:
def test_execute_delegates_to_engine(self):
args = argparse.Namespace(workspace=".", severity=None, category=None)
with mock.patch(
"commands.css_deep.analyze_css_deep",
return_value={"status": "ok", "stats": {"total_issues": 3}},
) as mock_engine:
result = css_deep.execute(args, ".")
mock_engine.assert_called_once_with(".", severity=None, category=None)
assert result["status"] == "ok"

def test_severity_and_category_passthrough(self):
args = argparse.Namespace(workspace=".", severity="high", category="z_index_abuse")
with mock.patch(
"commands.css_deep.analyze_css_deep",
return_value={"status": "ok"},
) as mock_engine:
css_deep.execute(args, ".")
mock_engine.assert_called_once_with(".", severity="high", category="z_index_abuse")


class TestAuditDispatchesCss:
def test_css_is_registered_check(self):
assert "css" in audit.ALL_CHECKS

def test_audit_check_css_routes_to_engine(self):
base = argparse.Namespace(
workspace=".", check="css", severity=None, category=None,
)
with mock.patch(
"commands.css_deep.analyze_css_deep",
return_value={"status": "ok", "stats": {"total_issues": 7}},
):
result = audit.execute(base, ".")
# umbrella envelope
assert result["s"] == "ok"
assert result["st"]["checks_run"] == 1
assert result["r"][0]["_check"] == "css"
assert result["r"][0]["stats"]["total_issues"] == 7

def test_audit_check_css_severity_reaches_engine(self):
base = argparse.Namespace(
workspace=".", check="css", severity="high", category=None,
)
with mock.patch(
"commands.css_deep.analyze_css_deep",
return_value={"status": "ok"},
) as mock_engine:
audit.execute(base, ".")
# audit builds a synthetic namespace; the engine must receive the filter
_, kwargs = mock_engine.call_args
assert kwargs.get("severity") == "high"
Loading