You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
You can now authenticate with a **repository token** — scoped to a single repository — instead of a personal account API token that reaches every organization and repository you can see. This is the right credential for CI and for the auto-configuration agent: if it leaks, the blast radius is one repository.
Get one from **Codacy > Repository > Settings > Integrations > Project API token**. The new `--repository-token <token>` flag is accepted by every command, and `CODACY_PROJECT_TOKEN` is picked up automatically.
16
+
17
+
**Token precedence** (identical to the Codacy Analysis CLI): `--repository-token` > `CODACY_PROJECT_TOKEN` > `CODACY_API_TOKEN` > stored `codacy login`. An explicit `--repository-token` wins outright, so a deliberately scoped run is never silently widened. Note that `CODACY_PROJECT_TOKEN` outranks `CODACY_API_TOKEN` — unset it if you want your account token used.
18
+
19
+
**Not every command accepts a repository token**, because Codacy only honours them on a limited set of repository-scoped operations:
-**Partially supported:**`repository` works but omits the pull request and coverage sections. In `--output json`, `pullRequests` stays an empty array and a new `unavailable: ["pullRequests"]` field marks what couldn't be fetched. Output under an account token is unchanged.
Unsupported combinations now fail immediately with a message naming the operation, why a repository token can't perform it, and which token is in use — instead of sending a request that comes back as a bare `Unauthorized`.
26
+
27
+
`codacy login` continues to store account tokens only; repository tokens are passed per command or via the environment.
28
+
29
+
Also fixed: `codacy repository` no longer loses the entire dashboard when the pull request lookup fails, and `codacy login` no longer reports a repository token as "invalid" when it is rejected for being the wrong kind of token.
Copy file name to clipboardExpand all lines: AGENTS.md
+29-6Lines changed: 29 additions & 6 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -86,9 +86,26 @@ codacy-cloud-cli/
86
86
- Prefer that over calling `setTimeout`/`sleep` directly in a command, unless you have a clear reason not to.
87
87
- Default cadence is `POLL_INTERVAL_MS` (10s), capped at `MAX_WAIT_MS` (20min).
88
88
-**Error handling:** Use `try/catch` with the shared `handleError()` from `src/utils/error.ts`
89
-
-**Authentication:** All commands that call the API must call `checkApiToken()` from `src/utils/auth.ts` before making requests
90
89
-**API base URL:**`https://app.codacy.com/api/v3` (configured in `src/index.ts` via `OpenAPI.BASE`)
91
-
-**Auth mechanism:**`CODACY_API_TOKEN` environment variable, sent as `api-token` header
90
+
-**Authentication — two token kinds.** Read `SPECS/repository-tokens.md` before touching auth or adding a command.
91
+
- An **account token** (`api-token` header) reaches everything its owner can see.
92
+
- A **repository token** (`project-token` header) is scoped to one repository. It is accepted only on a fixed whitelist of 13 operations; everywhere else Codacy rejects it as if no token had been sent.
93
+
- Every command that calls the API resolves auth first, via `resolveAuth(this)` from `src/utils/auth.ts` (returns a `RemoteAuth` discriminated union), and declares `.addOption(repositoryTokenOption())` so `--repository-token` parses.
94
+
-**New commands must decide their token scope**, using the whitelist in `SPECS/repository-tokens.md`:
95
+
- account-only end to end → `resolveAccountAuth(this, "<why a repository token can't do it>")`
96
+
- fully whitelisted → `resolveAuth(this)`
97
+
- mixed → `resolveAuth(this)` plus `requireAccountToken(auth, "<operation>", "<why>")` per unsupported flag, or `fetchIfAccountToken(...)` to skip an unsupported sub-call
98
+
-**Guards must run before any request**, and before `resolveRepoArgs()` — that shells out to git and prints an auto-detection line, which is misleading ahead of a refusal.
99
+
- Exception: a command whose endpoints are all whitelisted needs no guard at all — `resolveAuth(this)` alone is correct (see `tool`, `patterns`, `pattern`).
100
+
- Exception: a data-dependent guard runs after the fetch it depends on.
101
+
- Example: `guardForceUnlink` in `tools.ts` needs the coding-standard count.
102
+
- Keep those reads whitelisted, so nothing doomed is sent.
103
+
- Refuse before any prompt or mutation even so.
104
+
- If an operation's scope is genuinely unclear, don't guess a guard.
105
+
- Confirm the whitelist against the API owners instead.
106
+
- Record the answer in `SPECS/repository-tokens.md`.
107
+
- The whitelist is hardcoded in these guards.
108
+
-**Re-verify the whitelist after every `npm run update-api`.**
92
109
93
110
### Command Pattern
94
111
@@ -98,7 +115,7 @@ Every command file follows this structure:
|`CODACY_API_TOKEN`| Yes | API token for authenticating with Codacy. Get it from Codacy > Account > API Tokens |
241
+
|`CODACY_API_TOKEN`| One of the two | Account API token. Get it from Codacy > Account > API Tokens |
242
+
|`CODACY_PROJECT_TOKEN`| One of the two | Repository (project) token, scoped to one repository. Get it from Codacy > Repository > Settings > Integrations > Project API token. **Outranks `CODACY_API_TOKEN`** — see `SPECS/repository-tokens.md`|
Copy file name to clipboardExpand all lines: README.md
+40-1Lines changed: 40 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -22,7 +22,11 @@ npm link
22
22
23
23
## Authentication
24
24
25
-
Log in interactively (recommended):
25
+
The CLI accepts two kinds of token.
26
+
27
+
### Account API token
28
+
29
+
Reaches every organization and repository your account can see. Log in interactively (recommended):
26
30
27
31
```bash
28
32
codacy login
@@ -38,6 +42,40 @@ You can get a token from **Codacy > My Account > Access Management > API Tokens*
38
42
39
43
The `login` command stores the token encrypted at `~/.codacy/credentials`. The environment variable takes precedence over stored credentials when both are present.
40
44
45
+
### Repository (project) token
46
+
47
+
Scoped to a single repository — the right choice for CI, since a leaked token can't reach anything else. Get one from **Codacy > Repository > Settings > Integrations > Project API token**.
Codacy accepts repository tokens on a **limited set of repository-scoped operations**, so some commands require an account token and say so explicitly rather than failing with a generic authorization error:
56
+
57
+
| Works with a repository token | Requires an account token |
`codacy repository` works, but omits the pull request and coverage-report sections — those endpoints don't accept repository tokens. In `--output json` it marks them as `"unavailable": ["pullRequests", "coverageReports"]`, so a consumer can tell "none" apart from "couldn't look". Note that skipping coverage reports also suppresses the "waiting for / missing coverage reports" hint on the Analysis row.
65
+
66
+
`codacy login` stores account tokens only; pass repository tokens per command or via `CODACY_PROJECT_TOKEN`.
67
+
68
+
### Token precedence
69
+
70
+
1.`--repository-token <token>`
71
+
2.`CODACY_PROJECT_TOKEN`
72
+
3.`CODACY_API_TOKEN`
73
+
4. Stored credentials from `codacy login`
74
+
75
+
An explicit `--repository-token` wins outright, so a deliberately scoped run is never silently widened by an environment variable or a stale login. Note that `CODACY_PROJECT_TOKEN` outranks `CODACY_API_TOKEN` (matching the [Codacy Analysis CLI](https://github.com/codacy/analysis-cli)) — unset it if you want your account token used.
76
+
77
+
Passing `--repository-token` with an **empty** value is an error rather than a fallback. `--repository-token "$CODACY_PROJECT_TOKEN"` with the secret unset is a common CI mistake, and quietly falling back to an account token would run with much wider access than you asked for. An empty *environment variable*, by contrast, simply means "unset".
78
+
41
79
## Usage
42
80
43
81
```bash
@@ -50,6 +88,7 @@ codacy <command> --help # Detailed usage for any command
50
88
| Option | Description |
51
89
|---|---|
52
90
|`-o, --output <format>`| Output format: `table` (default) or `json`|
91
+
|`--repository-token <token>`| Repository (project) token, scoped to one repository (env: `CODACY_PROJECT_TOKEN`) |
Copy file name to clipboardExpand all lines: SPECS/README.md
+3Lines changed: 3 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -37,6 +37,8 @@ _No pending tasks._ All commands implemented.
37
37
38
38
-[setup.md](setup.md) — test framework, build, CI/CD setup
39
39
-[deployment.md](deployment.md) — npm publishing, brew formula
40
+
-[repository-tokens.md](repository-tokens.md) — **read before touching auth or adding a command**: the two token kinds, precedence, the 13-operation backend whitelist, and the per-command support matrix
41
+
-[missing-endpoints.md](missing-endpoints.md) — API v3 operations that don't accept repository tokens yet, ranked; candidate Linear tasks
40
42
41
43
## Changelog
42
44
@@ -83,3 +85,4 @@ _No pending tasks._ All commands implemented.
83
85
| 2026-07-28 | (OD-296, findings side) `SrmItem` gained its own `advisoryInformation` field server-side (bumped pinned API `57.3.0` → `57.3.9`), closing the gap noted on 2026-07-24. `findings` (list) now shows the same compact "Vulnerable functions: fn1, fn2 (+N more)" line as `issues`, via the newly-exported `summarizeFunctions`. `finding` (detail) shows the full `printAdvisoryBlock` — but only when there's no linked Codacy issue, since `printIssueCodeContext` already renders the equivalent block from `issue.advisoryInformation` in that case; this is what makes vulnerable functions visible for SCA/dependency findings (and any other non-Codacy-source finding) that have no linked issue to borrow it from at all. Added to both commands' JSON `pickDeep` whitelists (6 new tests, 494 total) |
84
86
| 2026-07-28 | (OD-378) New `pull-requests` (`prs`) command — the plural counterpart to `pull-request`, listing PRs for a repository with the same analysis-gated table columns as `repository`'s "Open Pull Requests" section (reuses `buildGateStatus`/`formatStandards`/`formatPrIssues`/`formatPrCoverage`/`formatDelta`). `--search-text`/`-q` and `--branch`/`-b` map to the API's `textQuery`/`targetBranch` params added in OD-376; the classification param (`search`, Merged vs. last-updated) is deliberately not exposed — different axis, out of scope. `[provider] [org] [repo]` auto-detect via `resolveRepoArgs`, paginate-to-`--limit` loop matching `findings`. Registered in `src/index.ts` (10 new tests, 516 total) |
85
87
| 2026-07-30 | (OD-378, review follow-up) `pull-requests` table polish + a real data bug. **Bug:** Complexity rendered as "no data" on every PR because the API omits the flat top-level `deltaComplexity` and only returns `quality.deltaComplexity` (while still sending a top-level `deltaClonesCount`) — new shared `prQualityMetric(pr, key)` in `utils/formatting.ts` reads the nested `quality` value first and falls back to the flat field; also applied to `repository`'s Open PR table and `pull-request`'s Analysis section, which had the same bug. **Layout:** `✓` moved to the first column; metric order now matches `repositories` (issues → complexity → duplication → coverage); the Coverage column is dropped entirely when no listed PR has a coverage value (new `hasAnyPrCoverage()` — repos without coverage return `diffCoverage.cause` and no numbers on any PR); missing metric values now render as a dim `-` instead of `N/A` in `formatDelta`/`formatPrCoverage`/`formatPrIssues`, matching `formatStandards`/`formatCountCell`/`formatCoverageCell`; and a zero issue count renders as a bare `0` rather than `+0`/`-0` (`-0` read as a negative), matching what `pull-request`'s Files table and `formatDelta` already did. **JSON:** added `quality.resultReasons`/`coverage.resultReasons` (Codacy review suggestion — they drive the per-metric gate coloring, so consumers need them to see which gates passed/failed) plus the `quality.*` metric mirrors the table actually renders (23 new tests, 544 total) |
88
+
| 2026-08-11 | (OD-489) Repository (project) token support. New `--repository-token <token>` on every command (plus `CODACY_PROJECT_TOKEN`), sent as the `project-token` header; account tokens keep `api-token`. `src/utils/auth.ts` rewritten around a `RemoteAuth` discriminated union carrying both kind and source, replacing `checkApiToken()` with `resolveAuth(this)` / `resolveAccountAuth(this, why)` / `requireAccountToken(...)` / `fetchIfAccountToken(...)`. Precedence matches `codacy-analysis` exactly — flag > `CODACY_PROJECT_TOKEN` > `CODACY_API_TOKEN` > stored login — so `vitest.config.mts` now blanks `CODACY_PROJECT_TOKEN` (it outranks the account token and is exported job-wide by the coverage reporter, so tests would otherwise depend on the developer's shell). Codacy whitelists only 13 operations for repository tokens, so `tool`/`patterns`/`pattern` work unchanged, `issues` (incl. `--overview`) and `tools --import` work, and the 9 account-only commands plus `repository`'s 6 management flags, `issues --ignore`/`--ignored`, and `tools --import --force` (only when standards exist) **fail fast before any request** with a message naming the operation, the reason, and where the token came from. `repository`'s dashboard skips the two non-whitelisted calls: the table keeps the "Open Pull Requests" header with an explanatory line, and JSON keeps `pullRequests: []` (so `jq '.pullRequests[]'` still works) plus an additive `unavailable: ["pullRequests"]` — under an account token the payload is byte-identical. Also added the long-missing `.catch()` on the PR call so an account token lacking PR access degrades instead of losing the whole dashboard, and fixed `login`'s 401 message, which told repository-token users their token was "invalid" when it is rejected by `/user` by design. New `SPECS/repository-tokens.md` (whitelist + matrix, re-verify on every `npm run update-api`) and `SPECS/missing-endpoints.md` (ranked gaps for follow-up Linear tasks) (40 new tests, 606 total) |
0 commit comments