Skip to content

Commit f039b39

Browse files
alerizzoclaude
andauthored
feat: pattern guards, reanalyze-and-wait, and overview noise tuning (#13)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 12ad8a3 commit f039b39

24 files changed

Lines changed: 2595 additions & 142 deletions
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@codacy/codacy-cloud-cli": minor
3+
---
4+
5+
Improve `issues --overview`. The False Positives table now uses human-friendly labels ("Not a False Positive" / "Potential False Positive") instead of the raw `belowThreshold` / `equalOrAboveThreshold` API bucket names. The overview also adds a "Suggested actions to reduce noise" section that flags noisy patterns — those accounting for at least 10% of all issues, or at least 3× the average issues-per-pattern — and prints a ready-to-run `codacy pattern <tool> <patternId> --disable` command for each (the owning tool is resolved automatically; suggestions whose tool can't be resolved are omitted). `--output json` output is unchanged.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
"@codacy/codacy-cloud-cli": minor
3+
---
4+
5+
Make the pattern commands aware of local configuration files and coding standards.
6+
7+
- `pattern <tool> <patternId>` with no action flag now **shows the pattern's information** (same card as the `patterns` command, with `--output json` support). Since there's no single-pattern endpoint, it searches by ID and keeps the exact match.
8+
- When a tool is driven by a local configuration file, `patterns` (list) and `pattern` (info) print `<tool> is using a local configuration file.` and skip fetching patterns; `patterns --enable-all/--disable-all` and `pattern --enable/--disable/--parameter` refuse with `Tool uses a local configuration file, can't be updated.`
9+
- `pattern --enable/--disable/--parameter` also refuses patterns enforced by a coding standard with `Pattern enforced by <standard> coding standard, can't be modified.`
10+
- `issues --overview` noise suggestions now adapt per pattern: a runnable `codacy pattern … --disable` command when possible, otherwise a manual step — `Update your local <tool> configuration file to disable the pattern` or `Update <coding standard> to disable the pattern`.

.changeset/reanalyze-and-wait.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@codacy/codacy-cloud-cli": minor
3+
---
4+
5+
Add a `--reanalyze-and-wait` (`-w`) variant to the `repository` and `pull-request` commands. Unlike `--reanalyze` (which triggers analysis and exits), this blocking variant captures a baseline of the current issues, triggers the reanalysis, polls until it finishes (every 10s, up to 20 minutes), and then prints how long the analysis took and what changed — issue deltas by pattern, severity, and category. Supports `--output json`.

AGENTS.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,10 @@ codacy-cloud-cli/
7676
- `dayjs` for date formatting — for "last updated" style dates, use `formatFriendlyDate()` from `utils/output.ts` (relative for today, "Yesterday", otherwise YYYY-MM-DD)
7777
- **Output:** Default output is human readable with tables and colors, but can be overridden with the `--output json` flag.
7878
- **Pagination:** All commands calling paginated APIs must call `printPaginationWarning(response.pagination, hint)` from `utils/output.ts` after displaying results. The hint should suggest command-specific filtering options.
79+
- **Polling / waiting:** Commands that wait on a remote operation (e.g. `--reanalyze-and-wait`) use the shared helpers in `utils/reanalyze-wait.ts`.
80+
- Route polling delays through the exported `timers.sleep` so tests can stub it (`vi.spyOn(timers, "sleep").mockResolvedValue()`).
81+
- Prefer that over calling `setTimeout`/`sleep` directly in a command, unless you have a clear reason not to.
82+
- Default cadence is `POLL_INTERVAL_MS` (10s), capped at `MAX_WAIT_MS` (20min).
7983
- **Error handling:** Use `try/catch` with the shared `handleError()` from `src/utils/error.ts`
8084
- **Authentication:** All commands that call the API must call `checkApiToken()` from `src/utils/auth.ts` before making requests
8185
- **API base URL:** `https://app.codacy.com/api/v3` (configured in `src/index.ts` via `OpenAPI.BASE`)

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,16 +77,16 @@ Supported providers: GitHub (`gh`), GitLab (`gl`), Bitbucket (`bb`).
7777
| `logout` | Remove stored Codacy API token |
7878
| `info` | Show authenticated user info and their organizations |
7979
| `repositories <provider> <org>` | List repositories for an organization |
80-
| `repository [provider] [org] [repo]` | Show metrics for a repository, or add/remove/follow/unfollow/reanalyze it |
80+
| `repository [provider] [org] [repo]` | Show metrics for a repository, or add/remove/follow/unfollow/reanalyze it (optionally waiting for results) |
8181
| `issues [provider] [org] [repo]` | Search issues in a repository with filters |
8282
| `issue [provider] [org] [repo] <id>` | Show details for a single issue, or ignore/unignore it |
8383
| `findings [provider] [org] [repo]` | Show security findings for a repository or organization |
8484
| `finding <provider> <org> <id>` | Show details for a single security finding, or ignore/unignore it |
85-
| `pull-request [provider] [org] [repo] <pr>` | Show PR analysis, issues, diff coverage, and changed files; or reanalyze it |
85+
| `pull-request [provider] [org] [repo] <pr>` | Show PR analysis, issues, diff coverage, and changed files; or reanalyze it (optionally waiting for results) |
8686
| `tools [provider] [org] [repo]` | List analysis tools configured for a repository |
8787
| `tool [provider] [org] [repo] <tool>` | Enable, disable, or configure an analysis tool |
8888
| `patterns [provider] [org] [repo] <tool>` | List patterns for a tool, or bulk enable/disable them |
89-
| `pattern [provider] [org] [repo] <tool> <id>` | Enable, disable, or set parameters for a pattern |
89+
| `pattern [provider] [org] [repo] <tool> <id>` | Show a pattern, or enable, disable, or set parameters for it |
9090

9191
Run `codacy <command> --help` for full argument and option details for any command.
9292

SPECS/README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ _No pending tasks._ All commands implemented.
2323
| `tools` | `tls` | ✅ Done | [tools-and-patterns.md](commands/tools-and-patterns.md) |
2424
| `tool` | `tl` | ✅ Done | [tools-and-patterns.md](commands/tools-and-patterns.md) |
2525
| `patterns` | `pats` | ✅ Done | [tools-and-patterns.md](commands/tools-and-patterns.md) |
26-
| `pattern` | `pat` | ✅ Done | [tools-and-patterns.md](commands/tools-and-patterns.md) |
26+
| `pattern` | `pat` | ✅ Done (info mode + guards added) | [tools-and-patterns.md](commands/tools-and-patterns.md) |
2727
| `analysis` | N/A | ✅ Done | [analysis.md](commands/analysis.md) |
2828
| `json-output` | N/A | ✅ Done | [json-output.md](commands/json-output.md) |
2929
| `login` | N/A | ✅ Done ||
@@ -65,3 +65,6 @@ _No pending tasks._ All commands implemented.
6565
| 2026-03-05 | JSON output filtering with `pickDeep` across all commands: `info`, `repositories`, `repository`, `pull-request`, `issues`, `issue`, `findings`, `finding`, `tools`, `patterns`; documented pattern in `src/commands/CLAUDE.md` |
6666
| 2026-03-12 | `patterns --enable-all` / `--disable-all` bulk update with filter support (6 new tests, 196 total) |
6767
| 2026-03-12 | `login` and `logout` commands: encrypted token storage in `~/.codacy/credentials`, masked interactive prompt, `--token` flag for non-interactive use, token resolution chain (env var → stored credentials); `checkApiToken()` updated to set `OpenAPI.HEADERS` dynamically (9 new tests, 219 total) |
68+
| 2026-06-02 | `--reanalyze-and-wait` (`-w`) blocking variant for `repository` and `pull-request`: triggers reanalysis, polls to completion (10s interval, 20min cap), then prints issue deltas by pattern/severity/category. New `src/utils/reanalyze-wait.ts` + `formatDuration`/`isBeingAnalyzed` helpers (26 new tests, 356 total) |
69+
| 2026-06-02 | `issues --overview` improvements: relabel False Positives buckets (`belowThreshold`/`equalOrAboveThreshold` → "Not a False Positive"/"Potential False Positive"), and a "Suggested actions to reduce noise" section that flags noisy patterns (≥10% of issues or ≥3× the average) with a runnable `codacy pattern … --disable` command, resolving the tool via its `prefix` (3 new tests, 360 total) |
70+
| 2026-06-02 | Pattern config-file & coding-standard awareness: new `pattern <tool> <id>` **info mode** (same card as `patterns`); `pattern`/`patterns` skip listing and refuse updates when a tool uses a local config file; `pattern` refuses to modify coding-standard-enforced patterns; `issues --overview` noise suggestions now render a manual "update your config file / coding standard" step instead of a command when a pattern can't be disabled via CLI. `printPatternCard`/`PATTERN_JSON_FIELDS` moved to `utils/formatting.ts` (11 new tests, 371 total) |

SPECS/commands/analysis.md

Lines changed: 60 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,54 @@ codacy pull-request <provider> <organization> <repository> <prNumber> --reanalyz
1616
On success, show: "Reanalysis requested successfully, new results will be available in a few minutes."
1717
On failure, show: "Failed to request reanalysis: \<error message\>".
1818

19+
### `--reanalyze-and-wait` (`-w`) — blocking variant
20+
21+
A second variant triggers the reanalysis and **waits** for it to complete, then
22+
prints what changed. The fire-and-forget `--reanalyze` stays as-is.
23+
24+
```
25+
codacy repository <provider> <organization> <repository> --reanalyze-and-wait
26+
codacy pull-request <provider> <organization> <repository> <prNumber> --reanalyze-and-wait
27+
```
28+
29+
Flow:
30+
1. Capture a **baseline** of current issues — for the repository from
31+
`issuesOverview` (counts by severity / category / pattern as independent
32+
lists); for the pull request by paging `listPullRequestIssues(status="new")`
33+
(each issue carries its pattern's category + severity).
34+
2. Trigger `reanalyzeCommitById` on the HEAD commit.
35+
3. **Poll every 10 s** (giving up after **20 min**), with the spinner showing
36+
`"Analysis requested. Waiting for it to start..."` → `"Analysis in progress.
37+
This may take a few minutes..."``"Analysis done. Fetching results to
38+
compare..."`. On each poll, read the **first commit** from the `/commits`
39+
endpoint (`listRepositoryCommits` for the repo, `getPullRequestCommits` for
40+
the PR, both `limit=1`) and look at its `startedAnalysis`/`endedAnalysis`:
41+
- **In progress** = `startedAnalysis` more recent than `endedAnalysis` **and**
42+
more recent than `t0` (the moment we triggered) — i.e. the analysis that
43+
started is ours, not a previous one.
44+
- **Done** = `startedAnalysis` more recent than `t0` **and** `endedAnalysis`
45+
at or after `startedAnalysis` (our analysis has since finished).
46+
4. Fetch a fresh snapshot, diff against the baseline, and print:
47+
- `Analysis finished in <duration>` headline (from the commit's
48+
`startedAnalysis``endedAnalysis`, falling back to wall-clock).
49+
- **By pattern / By severity / By category** signed net-delta lists. PR
50+
pattern rows are annotated `(Category · Severity)`; repo pattern rows are
51+
title-only (the overview doesn't map patterns to category/severity).
52+
- `In total: <before> → <after> issues (net ±N)`.
53+
54+
Notes:
55+
- The overview only exposes **net** per-bucket change, so deltas are signed net
56+
values per dimension, not a literal "added vs removed" split or a
57+
severity×category cross-tab.
58+
- The pattern list is soft-capped at 20 rows with a `… (N more)` line.
59+
- `--output json` emits `{ durationMs, durationHuman, totals, deltas }`.
60+
- On timeout, the spinner fails with a "didn't finish within 20 minutes" message.
61+
62+
Shared logic lives in `src/utils/reanalyze-wait.ts` (`pollForAnalysis`,
63+
`snapshotFromOverview`, `snapshotFromPrIssues`, `diffSnapshots`,
64+
`renderReanalyzeReport`, `reanalyzeJson`, and a `timers.sleep` indirection that
65+
tests stub for instant polling). `formatDuration` lives in `utils/formatting.ts`.
66+
1967
## Updates to existing commands
2068

2169
### `repository` command — About section
@@ -69,6 +117,13 @@ Implemented in `formatAnalysisStatus()` in `src/utils/formatting.ts`.
69117
- [`listRepositoryCommits`](https://api.codacy.com/api/api-docs#listrepositorycommits) with `limit=1` — head commit timing for repo
70118
- [`listCoverageReports`](https://api.codacy.com/api/api-docs#listcoveragereports) with `limit=1` — check `hasCoverageOverview`
71119

120+
Additionally used by `--reanalyze-and-wait`:
121+
- `listRepositoryCommits` (`limit=1`) — repo first-commit analysis timestamps, polled for status
122+
- `getPullRequestCommits` (`limit=1`) — PR first-commit analysis timestamps, polled for status
123+
- `getRepositoryPullRequest` — fetched once to resolve the PR `headCommitSha`
124+
- `issuesOverview` — repo baseline/after issue counts (severity / category / pattern)
125+
- `listPullRequestIssues` (`status="new"`, paginated) — PR baseline/after issue list
126+
72127
## Tasks
73128

74129
- [x] Update analysis status in the About section of the `repository` command
@@ -77,9 +132,11 @@ Implemented in `formatAnalysisStatus()` in `src/utils/formatting.ts`.
77132
- [x] Add `--reanalyze` option to the `pull-request` command
78133
- [x] Update existing tests for the status sections
79134
- [x] Add tests for the new `--reanalyze` option
135+
- [x] Add `--reanalyze-and-wait` (`-w`) blocking variant to both commands (2026-06-02)
80136

81137
## Tests
82138

83-
- `src/utils/formatting.test.ts` — 6 unit tests for `formatAnalysisStatus`
84-
- `src/commands/repository.test.ts` — 4 new tests (analysis status, reanalyze)
85-
- `src/commands/pull-request.test.ts` — 3 new tests (analysis status, reanalyze)
139+
- `src/utils/formatting.test.ts` — 6 unit tests for `formatAnalysisStatus`; + `formatDuration` and `isBeingAnalyzed` tests
140+
- `src/commands/repository.test.ts` — 4 tests (analysis status, reanalyze) + 3 for `--reanalyze-and-wait`
141+
- `src/commands/pull-request.test.ts` — 3 tests (analysis status, reanalyze) + 3 for `--reanalyze-and-wait`
142+
- `src/utils/reanalyze-wait.test.ts` — 12 unit tests (snapshots, diff, poll loop incl. timeout, render, json)

SPECS/commands/issues.md

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,11 @@ codacy is gh my-org my-repo --output json
1919

2020
- [`searchRepositoryIssues`](https://api.codacy.com/api/api-docs#searchrepositoryissues)`AnalysisService.searchRepositoryIssues(provider, org, repo, cursor, limit, body)`
2121
- [`issuesOverview`](https://api.codacy.com/api/api-docs#issuesoverview)`AnalysisService.issuesOverview(provider, org, repo, body)` (only when `--overview` is given)
22+
- [`listTools`](https://api.codacy.com/api/api-docs#listtools)`ToolsService.listTools(cursor, limit)` (only when `--overview` surfaces noisy patterns, to map each pattern's `prefix` to its owning tool)
23+
- [`listRepositoryTools`](https://api.codacy.com/api/api-docs#listrepositorytools)`AnalysisService.listRepositoryTools(provider, org, repo)` (only when `--overview` surfaces noisy patterns, to detect config-file-driven tools)
24+
- [`listRepositoryToolPatterns`](https://api.codacy.com/api/api-docs#listrepositorytoolpatterns)`search=<patternId>` (only for noisy patterns on non-config-file tools, to detect coding-standard enforcement)
2225

23-
Both accept the same `SearchRepositoryIssuesBody` for filtering.
26+
`searchRepositoryIssues` and `issuesOverview` accept the same `SearchRepositoryIssuesBody` for filtering.
2427

2528
## Options
2629

@@ -66,8 +69,46 @@ Shows pagination warning if more results exist.
6669

6770
### Overview mode (`--overview`)
6871

69-
Six count tables sorted descending by count: Category, Severity, Language, Tag, Pattern, Author.
72+
Seven count tables sorted descending by count: Category, Severity, Language, Tag,
73+
Pattern, Author, and False Positives.
74+
75+
The **False Positives** table relabels the API's raw bucket names for readability:
76+
`belowThreshold` → "Not a False Positive", `equalOrAboveThreshold` → "Potential
77+
False Positive" (the bucket is keyed on FP probability vs. the configured
78+
threshold, so at/above threshold = a potential false positive).
79+
80+
After the tables, a **"Suggested actions to reduce noise"** section lists patterns
81+
worth disabling. A pattern is "noisy" when it accounts for **≥10% of all issues**
82+
shown, **or** has **≥3× the average** issues-per-pattern. The owning tool is
83+
resolved by matching the pattern ID against each tool's `prefix` (longest match
84+
wins); patterns whose tool can't be resolved (no/unknown prefix) are dropped
85+
silently. The list is capped at 10 with a "… (N more)" note.
86+
87+
The suggested step depends on **how the pattern is managed**, since not every
88+
pattern can be disabled through the CLI:
89+
90+
```
91+
Suggested actions to reduce noise
92+
93+
Disable "Use of assert detected" (-2.5k issues)
94+
> codacy pattern Bandit Bandit_B101 --disable
95+
```
96+
97+
- **Default** — a runnable `> codacy pattern <tool> <patternId> --disable` command.
98+
- **Tool uses a local configuration file** — no command; instead
99+
`→ Update your local <tool> configuration file to disable the pattern`.
100+
- **Pattern enforced by a coding standard** — no command; instead
101+
`→ Update <standard name(s)> to disable the pattern`.
102+
103+
To classify each noisy pattern, the command additionally fetches the repository
104+
tools (`listRepositoryTools`, for `usesConfigurationFile` and the repo tool
105+
UUID) and, for non-config-file tools, the pattern's `enabledBy` via
106+
`listRepositoryToolPatterns` (`search=<patternId>`, one call per noisy pattern).
107+
A config file takes precedence over coding-standard enforcement. These extra
108+
calls only run when at least one noisy pattern exists.
109+
110+
`--output json` is unaffected (raw counts only — no relabeling or suggestions).
70111

71112
## Tests
72113

73-
File: `src/commands/issues.test.ts`39 tests.
114+
File: `src/commands/issues.test.ts`46 tests.

0 commit comments

Comments
 (0)