diff --git a/docs/attributes.md b/docs/attributes.md index ee6e2e48..3733a5ae 100644 --- a/docs/attributes.md +++ b/docs/attributes.md @@ -699,6 +699,11 @@ The assessor looks for patterns like: - `mypy ` - `eslint ` - `tsc --noEmit ` +- `golangci-lint run ` (lint) +- `go vet ` (type-check / static analysis) +- `yamllint ` (lint) +- `shellcheck ` (lint) +- `bash -n ` (shell syntax check / type-check analog) #### Remediation @@ -715,6 +720,10 @@ ruff check src/mypackage/module.py # Type-check a single file mypy src/mypackage/module.py + +# Go: lint + static analysis on a single file +golangci-lint run cmd/main.go +go vet cmd/main.go \``` ``` diff --git a/src/agentready/assessors/verification.py b/src/agentready/assessors/verification.py index d10a8ae1..e48eaee9 100644 --- a/src/agentready/assessors/verification.py +++ b/src/agentready/assessors/verification.py @@ -52,8 +52,12 @@ def attribute(self) -> Attribute: (r"black\s+--check\s+(?!-)[\w./\-]*\.\w{1,10}(?=\s*(?:[`\n]|$))", "lint"), (r"prettier\s+--check\s+(?!-)[\w./\-]*\.\w{1,10}(?=\s*(?:[`\n]|$))", "lint"), (r"gofmt\s+(?!-)[\w./\-]*\.\w{1,10}(?=\s*(?:[`\n]|$))", "lint"), + (r"yamllint\s+(?!-)[\w./\-]*\.\w{1,10}(?=\s*(?:[`\n]|$))", "lint"), + (r"shellcheck\s+(?!-)[\w./\-]*\.\w{1,10}(?=\s*(?:[`\n]|$))", "lint"), # Type check single file patterns (r"mypy\s+(?!-)[\w./\-]*\.\w{1,10}(?=\s*(?:[`\n]|$))", "typecheck"), + (r"go\s+vet\s+(?!-)[\w./\-]*\.\w{1,10}(?=\s*(?:[`\n]|$))", "typecheck"), + (r"bash\s+-n\s+(?!-)[\w./\-]*\.\w{1,10}(?=\s*(?:[`\n]|$))", "typecheck"), (r"pyright\s+(?!-)[\w./\-]*\.\w{1,10}(?=\s*(?:[`\n]|$))", "typecheck"), (r"tsc\s+--noEmit\s+(?!-)[\w./\-]*\.\w{1,10}(?=\s*(?:[`\n]|$))", "typecheck"), ] @@ -205,7 +209,18 @@ def _create_remediation(self) -> Remediation: "Ensure these commands work without a full build step", "Target <5 seconds execution per file", ], - tools=["ruff", "eslint", "mypy", "pyright", "tsc"], + tools=[ + "ruff", + "eslint", + "mypy", + "pyright", + "tsc", + "golangci-lint", + "go vet", + "yamllint", + "shellcheck", + "bash", + ], commands=[ "# Python", "ruff check path/to/file.py", @@ -214,6 +229,15 @@ def _create_remediation(self) -> Remediation: "# JavaScript/TypeScript", "npx eslint path/to/file.ts", "npx tsc --noEmit path/to/file.ts", + "", + "# Go", + "golangci-lint run path/to/file.go", + "go vet path/to/file.go", + "", + "# YAML / Shell", + "yamllint path/to/file.yaml", + "shellcheck path/to/script.sh", + "bash -n path/to/script.sh", ], examples=[], citations=[], diff --git a/tests/unit/test_assessors_verification.py b/tests/unit/test_assessors_verification.py index c560db65..be95f2d9 100644 --- a/tests/unit/test_assessors_verification.py +++ b/tests/unit/test_assessors_verification.py @@ -136,6 +136,61 @@ def test_recognizes_golangci_lint(self, tmp_path): assert finding.score >= 50.0 + def test_recognizes_go_vet(self, tmp_path): + """Test that go vet pattern is recognized as typecheck.""" + claude_md = tmp_path / "CLAUDE.md" + claude_md.write_text("Type check: `go vet cmd/main.go`\n") + + repo = self._make_repo(tmp_path) + assessor = SingleFileVerificationAssessor() + finding = assessor.assess(repo) + + assert finding.score >= 50.0 + + def test_go_vet_multi_file_does_not_match(self, tmp_path): + """Test that go vet ./... is not recognized as single-file typecheck.""" + claude_md = tmp_path / "CLAUDE.md" + claude_md.write_text("Run `go vet ./...` for static analysis.\n") + + repo = self._make_repo(tmp_path) + assessor = SingleFileVerificationAssessor() + finding = assessor.assess(repo) + + assert finding.score == 0.0 + + def test_recognizes_yamllint(self, tmp_path): + """Test that yamllint pattern is recognized as lint.""" + claude_md = tmp_path / "CLAUDE.md" + claude_md.write_text("Lint: `yamllint config/workflow.yaml`\n") + + repo = self._make_repo(tmp_path) + assessor = SingleFileVerificationAssessor() + finding = assessor.assess(repo) + + assert finding.score >= 50.0 + + def test_recognizes_shellcheck(self, tmp_path): + """Test that shellcheck pattern is recognized as lint.""" + claude_md = tmp_path / "CLAUDE.md" + claude_md.write_text("Lint: `shellcheck scripts/deploy.sh`\n") + + repo = self._make_repo(tmp_path) + assessor = SingleFileVerificationAssessor() + finding = assessor.assess(repo) + + assert finding.score >= 50.0 + + def test_recognizes_bash_n(self, tmp_path): + """Test that bash -n pattern is recognized as typecheck.""" + claude_md = tmp_path / "CLAUDE.md" + claude_md.write_text("Syntax check: `bash -n scripts/entrypoint.sh`\n") + + repo = self._make_repo(tmp_path) + assessor = SingleFileVerificationAssessor() + finding = assessor.assess(repo) + + assert finding.score >= 50.0 + def test_recognizes_pyright(self, tmp_path): """Test that pyright pattern is recognized as typecheck.""" claude_md = tmp_path / "CLAUDE.md"