Skip to content

feat(detect): Improve existing linter detection - #575

Merged
theCodeDrift merged 2 commits into
mainfrom
improve-linter-detection
Oct 22, 2026
Merged

theCodeDrift merged 2 commits into
mainfrom
improve-linter-detection

Conversation

@theCodeDrift

@theCodeDrift theCodeDrift commented Oct 22, 2026 •

Copy link
Copy Markdown
Member

Stack (root → tip):

Stacked on #574, which fixed the same problem for pyproject.toml dev dependencies.

taskless detect recognised a linter mostly by its dedicated config file, but most ecosystems install a linter as a dependency and run it on its defaults, or keep its config in a manifest. Fifteen fixtures modelled on each ecosystem's common setup all came back with no linters. They all detect now.

New evidence

  • Dependencies from every ecosystem's manifest: Gemfile, composer.json (require + require-dev), go.mod require/tool (prefix-matched, so /v2/cmd/... counts), Package.swift package URLs, and every pip requirements file (requirements-dev.txt, requirements/*.txt, pip-tools .in).
  • Config inside a manifest: [lints.clippy] / [workspace.lints.clippy] in Cargo.toml, [flake8] in setup.cfg / tox.ini, prettier and stylelint keys in package.json.
  • Commands in the root CI and hook configs, e.g. .github/workflows/ci.yml (cargo clippy). Only commands count: automationCommands reads the command fields of YAML/JSON/TOML configs (run, script, entry, uses, repo, ...) and the non-comment lines of scripts. Comments and step names are never evidence.
  • Rust: a bare Cargo.toml is not evidence of clippy; it needs config or a command that runs it.
  • New linters: standard, pint, php-cs-fixer. Missing filenames for ESLint, Prettier, and stylelint, checked against each tool's source (ESLint 9.39, Prettier 3.8.1, stylelint 17.16 via cosmiconfig 9).

The JSON shape is unchanged: these are new evidence strings and name values. The detect recipe is bumped to v4.

Verification

  • 27 new detect tests (78 total), including negatives for comments, step names, a commented-out hook line, and a CI config below the root. Full suite 2,684 passing; typecheck and lint clean.
  • Swept 14 real repositories. Newly detected: axum (clippy), cobra (golangci-lint), Laravel (phpstan, pint), sinatra (rubocop gem), pydantic-core (clippy). ripgrep and Alamofire correctly stay empty; neither runs a linter we look for.

OpenSpec improve-linter-detection adds one cli-detect requirement (ADDED only; 22 → 30 scenarios, none lost) and is archived here as the tip. #574's changeset is extended rather than duplicated.

Built on top of #574

A Python repository that installs ruff as a dev dependency and runs it on its defaults has no [tool.ruff] table, and taskless detect reported it as having no linter. pythonDepsFromPyproject read only [project] dependencies and Poetry.

It now also reads:

  • PEP 735 [dependency-groups] (what uv add --dev writes); { include-group } entries are skipped
  • [tool.uv] dev-dependencies
  • [tool.pdm.dev-dependencies]
  • Hatch environment dependencies / extra-dependencies
  • Poetry groups through [tool.poetry.group.<name>.dependencies]. Previously the group name was read as a dependency, so dev was recorded and the packages inside were not.

Repos with [tool.ruff] were already detected correctly (checked against fastapi, pydantic, httpx, flask, requests, poetry).

Tests: one case per location, plus a regression for the Poetry group-name read. Patch changeset included.

@theCodeDrift

Copy link
Copy Markdown
Member Author

@claude /review

@github-actions

github-actions Bot commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

Claude finished @theCodeDrift's task in 5m 40s —— View job


Review complete

  • Read .prior-review.json (no prior threads/reviews found — this is the first review)
  • Fetch full PR diff and metadata
  • Review packages/cli/src/detect/scan.ts
  • Review packages/cli/src/detect/automation.ts
  • Review packages/cli/src/schemas/detect.ts and detect.md
  • Review test coverage in packages/cli/test/detect.test.ts
  • Review OpenSpec changes
  • Post inline comments for concrete issues
  • Post final summary

Review mode: incremental — read 0 prior review thread(s) before reviewing. .prior-review.json contained no review threads, no review summaries, and no top-level comments besides the trigger (@claude /review) itself, so this is the first review of this PR — everything below is new.

[New] Comment-stripping can eat a real invocation on the same line (packages/cli/src/detect/automation.ts:309, inline comment posted). automationCommands's line-based fallback (used for Jenkinsfiles, husky scripts, and other non-structured configs) strips from any #/// preceded by whitespace to end-of-line, without regard for quoting. A line like echo "Checking # lint rules" && golangci-lint run gets truncated at the # inside the string, silently dropping the actual golangci-lint run invocation that follows. Structured YAML/JSON/TOML configs aren't affected (they go through collectCommands/collectStrings instead), so this is narrow, but it cuts against the PR's explicit goal that commands — as opposed to comments — are always evidence.

Everything else held up well on a close read of scan.ts and automation.ts, including the trickier parts:

  • goDepsFromGoModule's hand-rolled tokenizer correctly handles both single-line and block require/tool directives, strips // indirect comments, and the path-prefix expansion correctly matches .../v2/cmd/golangci-lint against the shorter github.com/golangci/golangci-lint signal.
  • hasTomlTable's dotted-path walk correctly distinguishes [lints.clippy] from a sibling like [lints.clippy-extra], and reuses the same parsed Cargo.toml/pyproject.toml manifests for both dependency and table lookups (no duplicate parsing).
  • The automationFiles-below-root exclusion, and the comment/step-name exclusion for structured configs, both check out against their respective negative tests.
  • swiftDepsFromPackageSwift's URL normalization handles the https://, scheme-stripped, and .git-suffixed forms exercised by the tests.

Two minor, non-blocking observations (not raised inline since they're more judgment calls than bugs):

  • LANGUAGE_MARKERS (scan.ts) wasn't extended for the new pip requirements patterns (requirements-dev.txt, requirements/*.txt), so a Python repo whose only manifest is one of those (no pyproject.toml/requirements.txt/setup.cfg/setup.py/Pipfile) is recognized as Python only incidentally, via a matched linter dependency adding "Python" to languages. If no tracked linter is present, such a repo reports no Python at all. Likely an edge case outside this PR's stated scope (fifteen fixtures, not every possible manifest-only repo), but worth knowing about.
  • Test coverage for the new go.mod/composer.json parsing is a bit narrower than the parsing logic itself: there's a test for the tool (...) block form and require-dev, but none for a plain single-line require directive or for composer's non-dev require key. Given the custom tokenizer in goDepsFromGoModule, an extra case or two there would pin down behavior more explicitly, though the existing fixture-sweep evidence described in the PR body gives some real-world confidence.

Comment thread packages/cli/src/detect/automation.ts
@theCodeDrift
theCodeDrift force-pushed the improve-linter-detection branch from 50c5284 to f0b1407 Compare October 22, 2026 17:00
@theCodeDrift

Copy link
Copy Markdown
Member Author

Re: @github-actions — "Claude finished @theCodeDrift's task in 5m 40s — Review complete…"
#575 (comment)

All three points are addressed in f0b1407:

  • Comment-stripping inside quotes (inline thread): the line fallback now strips #/// only outside a quoted string, with husky and Jenkinsfile regression cases.
  • LANGUAGE_MARKERS and the new requirements patterns: any pip requirements file found by the walk (requirements-dev.txt, requirements/*.txt, *.in) now marks the repository as Python on its own, with a test for a repo whose only manifest is requirements/docs.txt.
  • go.mod / composer.json coverage: added a single-line require ... // indirect go.mod case and a composer non-dev require case to the ecosystem table.

— AI Coding Agent

Base automatically changed from fix/detect-python-dev-dependencies to main October 22, 2026 19:50
@theCodeDrift
theCodeDrift force-pushed the improve-linter-detection branch from f0b1407 to ebf72ca Compare October 22, 2026 19:51
@theCodeDrift
theCodeDrift merged commit 50b7ee6 into main Oct 22, 2026
8 checks passed
@theCodeDrift
theCodeDrift deleted the improve-linter-detection branch October 22, 2026 19:54
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