@cantinasecurity/apex-cli 0.1.19 → 0.1.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -37,7 +37,7 @@ Recommended workflow:
37
37
  4. For repository-scoped work, pass `cwd` explicitly.
38
38
  5. Call `apex-doctor` before starting a scan if workspace binding or source planning might be unclear.
39
39
  6. If the directory is not bound to the right workspace, call `apex-workspaces` and `apex-workspace-use`.
40
- 7. Use `apex-scan`, `apex-status`, `apex-scans`, `apex-findings`, and `apex-export-findings` for the scan lifecycle.
40
+ 7. Use `apex-scan`, `apex-status`, `apex-scans`, `apex-findings`, and `apex-export-findings` for the scan lifecycle. Use `apex-workspace-findings` for the cross-scan workspace browser view.
41
41
  8. For Lite scans, call `apex-scan` with `mode: "lite"` only when the company has Lite Scan beta access and the sources are provider-backed.
42
42
  9. For PR scans, call `apex-scan` with `mode: "pr"` and `pullRequests`.
43
43
  10. Use `apex-finding-comment` and `apex-finding-feedback` when the user wants to leave review notes or valid/invalid feedback on a finding.
@@ -50,7 +50,7 @@ If the Apex MCP server is not configured, fall back to the local CLI:
50
50
  - After a successful scan JSON response, inspect `localPersistence.workspaceBindingSaved`. If it is `false`, the server scan already started: capture and report `binding.lastScanId`, then STOP; do not run status or findings until local repository permissions are repaired.
51
51
  - `apex-trial-status` is MCP-only; do not invoke it as a CLI command. In a CLI-only flow, use the `claimDeadline` returned by `apex redeem <code> --json` without repeating or persisting the secret code.
52
52
  - After a guest scan, tell the user to run `apex claim` on the same machine before that returned claim deadline. If the deadline is unavailable or uncertain, do not guess or start a replacement login; run `apex claim` and let the server validate the proof.
53
- - Claim currently requires a Cantina account that does not already own a personal Apex tenant. If the user already completed Cantina Apex onboarding, explain that CLI linking is not available yet and tell them to contact Cantina support before the claim deadline.
53
+ - Claim moves the guest workspace, scans, and findings into the currently selected Cantina workspace when the account can run scans there. Personal workspace owners receive the trial in their existing workspace instead of a duplicate. If the selected workspace cannot run scans, tell the user to switch to one that can and retry before the claim deadline.
54
54
  - Service-key management has no MCP tools. Never ask a user to paste a service key into a prompt, and never place one in an MCP call, tool result, source file, or log.
55
55
  - If a user explicitly needs a new service key, tell them to run `apex login` if needed, then run `apex service-key create --name <name> --scope <scope[,scope]>` directly in their own terminal. Use only the required scopes: `scans:create` to trigger, `scans:read` to poll status, and `findings:read` to retrieve findings. A P1-only adapter can use `--scope scans:read,findings:read`; omitting `--scope` grants all three. The CLI automatically selects the company when only one is available and prompts when there are several; use `--company <id-or-handle>` to skip that prompt or resolve a non-interactive command. Service-key commands never initiate login, and the secret is shown once.
56
56
  - For a headless HTTP adapter, read the service key from a secret-backed environment variable, send it as a bearer token to `/api/apex/v1`, and redact it from errors and logs. Do not shell out to `apex` or launch MCP from the adapter.
@@ -69,6 +69,7 @@ If the Apex MCP server is not configured, fall back to the local CLI:
69
69
  - `apex-workspace-use` accepts a workspace name, prefix, or ID.
70
70
  - Use `sourceMode: "remote"` only when the user explicitly wants to forbid local snapshot fallbacks.
71
71
  - When checking a scan that is not the workspace binding's latest scan, pass `scanId` to `apex-status`; use `apex scans` or `apex-scans` first if you need to discover scan IDs.
72
+ - Use `apex-workspace-findings` when the user asks for findings across a workspace instead of one scan. Pass `filters: ["status:open"]` for all open findings; supported filter facets are `severity`, `scan`, `status`, `validation`, and `visibility`, and a leading `!` negates a filter. In CLI-only flows, use `apex findings --workspace --filter status:open`.
72
73
  - Finding comments, feedback, and fix review scan starts use the same Apex device-login credentials as read tools. If a write tool reports missing auth, re-run `apex-auth-status`, check `apex-trial-status`, and complete `apex-auth-start` / `apex-auth-wait` only when no unexpired local claim proof is available, instead of asking for browser cookies or auth tokens.
73
74
  - Anonymous telemetry records only sanitized command/tool metadata such as command names, enum modes, counts, durations, success/failure categories, CLI version, and client integration. It must not include raw repository paths, scan IDs, finding IDs, comments, file paths, PR URLs, trial codes, claim tokens, onboarding fingerprints, or bearer tokens.
74
75
  - Invalid finding feedback requires `dismissalReason`; valid feedback can include `suggestedSeverity`, including `extreme`.
@@ -84,5 +85,6 @@ If the Apex MCP server is not configured, fall back to the local CLI:
84
85
  - Start a PR scan: call `apex-scan` with `mode: "pr"` and `pullRequests`.
85
86
  - Check an active scan: call `apex-status`, passing `scanId` when the scan is not the latest binding, then `apex-findings` when the scan completes.
86
87
  - Export findings for review: call `apex-export-findings` with `format` set to `markdown`, `json`, or `gitlab-sast`.
88
+ - Review all open workspace findings: call `apex-workspace-findings` with `filters: ["status:open"]`.
87
89
  - Leave review feedback: call `apex-finding-comment` to add a note, or `apex-finding-feedback` with `status` set to `valid` or `invalid`.
88
90
  - Run a Fix PR review: call `apex-finding-fix-review` with `fixPrUrls`, optionally after recording fixed feedback with `apex-finding-feedback`.
@@ -6,7 +6,7 @@
6
6
  },
7
7
  "metadata": {
8
8
  "description": "Cantina agent plugins for security review workflows.",
9
- "version": "0.1.19"
9
+ "version": "0.1.21"
10
10
  },
11
11
  "plugins": [
12
12
  {
@@ -14,10 +14,10 @@
14
14
  "source": {
15
15
  "source": "npm",
16
16
  "package": "@cantinasecurity/apex-cli",
17
- "version": "0.1.19"
17
+ "version": "0.1.21"
18
18
  },
19
19
  "description": "Run Apex security scans and review findings from Claude Code.",
20
- "version": "0.1.19",
20
+ "version": "0.1.21",
21
21
  "author": {
22
22
  "name": "Cantina",
23
23
  "email": "support@cantina.xyz"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apex-cli",
3
- "version": "0.1.19",
3
+ "version": "0.1.21",
4
4
  "description": "Run Apex security scans and review findings from Claude Code.",
5
5
  "author": {
6
6
  "name": "Cantina",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apex-cli",
3
- "version": "0.1.19",
3
+ "version": "0.1.21",
4
4
  "description": "Run Apex security scans and review findings from Codex.",
5
5
  "author": {
6
6
  "name": "Cantina",
package/.mcp.claude.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "args": [
6
6
  "-y",
7
7
  "-p",
8
- "@cantinasecurity/apex-cli@0.1.19",
8
+ "@cantinasecurity/apex-cli@0.1.21",
9
9
  "apex-mcp"
10
10
  ],
11
11
  "env": {
package/.mcp.codex.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "args": [
5
5
  "-y",
6
6
  "-p",
7
- "@cantinasecurity/apex-cli@0.1.19",
7
+ "@cantinasecurity/apex-cli@0.1.21",
8
8
  "apex-mcp"
9
9
  ],
10
10
  "env": {
package/MARKETPLACE.md CHANGED
@@ -13,7 +13,7 @@ This package is prepared as both a Claude Code plugin and a Codex plugin. The pu
13
13
  The plugin MCP configs launch the pinned npm CLI package with:
14
14
 
15
15
  ```bash
16
- npx -y -p @cantinasecurity/apex-cli@0.1.19 apex-mcp
16
+ npx -y -p @cantinasecurity/apex-cli@0.1.21 apex-mcp
17
17
  ```
18
18
 
19
19
  That keeps marketplace installs independent of a user's global `apex` install.
package/README.md CHANGED
@@ -107,14 +107,14 @@ fingerprint before the first request, exchanges the code for a seven-day guest
107
107
  session, and stores the single-use claim proof only in the mode-0600 Apex
108
108
  credentials file. A one-way code hash distinguishes safe same-code retries
109
109
  without retaining the trial code. `apex claim` is the one onboarding command
110
- that may open a browser; it creates or signs into a Cantina account, then
111
- attaches the existing workspace, scans, and findings to that account. Run it on
112
- the same machine within 30 days. Unclaimed guest data is retained only for the
113
- limited period disclosed during redemption and is then queued for deletion.
114
- Currently, the account used for claim must not already own a personal Apex
115
- tenant. If you already completed Cantina Apex onboarding, the CLI cannot link
116
- the guest tenant to that account yet; contact Cantina support before the claim
117
- deadline.
110
+ that may open a browser; it creates or signs into a Cantina account, then moves
111
+ the guest workspace, scans, and findings into the currently selected Cantina
112
+ workspace when that account can run scans there. Personal workspace owners
113
+ receive the trial in their existing workspace instead of getting a duplicate.
114
+ Run it on the same machine within 30 days. Unclaimed guest data is retained only
115
+ for the limited period disclosed during redemption and is then queued for
116
+ deletion. If the selected Cantina workspace cannot run scans, switch to one
117
+ that can and retry before the claim deadline.
118
118
  Trial redemption, claim, and revocation are hosted-only and fail before sending
119
119
  any onboarding secret when `APEX_BASE_URL` is not `https://ai.cantina.xyz`.
120
120
 
@@ -141,31 +141,31 @@ unclaimed local trial proof so `apex claim` can still use it.
141
141
  Agent-safe copy/paste prompt (substitute the issued code):
142
142
 
143
143
  ```text
144
- Install and run Cantina Apex CLI 0.1.19 to start one free standard security scan of this repository immediately.
144
+ Install and run Cantina Apex CLI 0.1.21 to start one free standard security scan of this repository immediately.
145
145
 
146
146
  Use this trial code only where <TRIAL_CODE> appears below: SPBT-DEVREL-XXXXXXXX
147
147
 
148
148
  Run the commands yourself from the repository root; do not stop after setup or only explain the steps. Use terminal CLI commands only. Do not use an Apex MCP tool or run `apex setup`, `apex mcp`, `apex login`, `apex-auth-start`, or `apex-auth-wait`. Never use `--replace` or `--force`.
149
149
 
150
150
  Use this exact command prefix for every Apex command:
151
- npx -y -p @cantinasecurity/apex-cli@0.1.19 apex
151
+ npx -y -p @cantinasecurity/apex-cli@0.1.21 apex
152
152
 
153
153
  1. Before redeeming, confirm every command will use the same persistent, user-accessible machine or container, HOME, filesystem, and repository root. Confirm Node.js 20+ and npx are available. If `APEX_BASE_URL` is set, continue only when its origin is `https://ai.cantina.xyz`. Run the exact prefix with `--help` and continue only if help lists both `redeem` and `claim`. If any check fails, STOP before sending the code; do not install system packages.
154
154
  2. Redeem, with at most one response-loss recovery retry:
155
- npx -y -p @cantinasecurity/apex-cli@0.1.19 apex redeem <TRIAL_CODE> --json
155
+ npx -y -p @cantinasecurity/apex-cli@0.1.21 apex redeem <TRIAL_CODE> --json
156
156
  This terminal command is the only allowed tool call containing the code. Never echo or restate the code, store it in a file or environment variable, put it in a URL or MCP call, or include it in your response. Continue when the command exits 0 and stdout is JSON with `redeemed: true` and `outcome` equal to `redeemed` or `idempotent`. If and only if the first attempt ends with an ambiguous transport/connection failure or an interrupted/missing response and no typed server error code, retry this exact same command once on the same machine, HOME, and repository root. Never retry a typed server rejection and never make more than one recovery retry. If the allowed retry does not return a successful JSON outcome, STOP.
157
157
  3. Immediately start exactly one scan; do not wait for another instruction:
158
- npx -y -p @cantinasecurity/apex-cli@0.1.19 apex scan --mode standard --non-interactive --no-open --json
158
+ npx -y -p @cantinasecurity/apex-cli@0.1.21 apex scan --mode standard --non-interactive --no-open --json
159
159
  On any error or nonzero exit, STOP. After a successful scan JSON response, inspect `localPersistence.workspaceBindingSaved`. If it is `false`, the server scan already started: capture and report `binding.lastScanId`, then STOP; do not run status or findings until local repository permissions are repaired. Otherwise capture `binding.lastScanId` from the JSON as <SCAN_ID>; if it is absent, STOP.
160
160
  4. About every 60 seconds, poll that exact scan:
161
- npx -y -p @cantinasecurity/apex-cli@0.1.19 apex status --scan <SCAN_ID> --json
161
+ npx -y -p @cantinasecurity/apex-cli@0.1.21 apex status --scan <SCAN_ID> --json
162
162
  Read `progress.progress.status`. Wait while it is `created`, `pending`, `queued`, `starting`, `running`, `in_progress`, or `processing`. Continue on `completed`. STOP on `failed`, `cancelled`, a missing or unknown status, or a command failure.
163
163
  5. Fetch that exact scan's findings:
164
- npx -y -p @cantinasecurity/apex-cli@0.1.19 apex findings --scan <SCAN_ID> --json
164
+ npx -y -p @cantinasecurity/apex-cli@0.1.21 apex findings --scan <SCAN_ID> --json
165
165
  On success, summarize findings by severity, most severe first, including identifier, title, status, and confidence. This command does not return file locations, so do not invent them. On failure, STOP.
166
166
  6. Do not claim automatically. Tell me the claim deadline from step 2 and remind me to run this exact command myself from the same repository root, machine, and HOME:
167
- npx -y -p @cantinasecurity/apex-cli@0.1.19 apex claim
168
- It may open a browser so I can create or sign in to a Cantina account and keep the results. The account must not already own a personal Apex tenant; if it does, tell me to contact Cantina support before the claim deadline because CLI linking is not available yet.
167
+ npx -y -p @cantinasecurity/apex-cli@0.1.21 apex claim
168
+ It may open a browser so I can create or sign in to a Cantina account and keep the results. Claim moves the trial into the currently selected Cantina workspace when I can run scans there, or into my existing personal workspace. If the selected workspace cannot run scans, tell me to switch to one that can and retry before the claim deadline.
169
169
 
170
170
  Never paste raw command transcripts. For any failure, report only the step, exit code, and sanitized error. Redact the trial code and every `apex_at_`, `apex_rt_`, or `apex_ct_` value from all reports.
171
171
  ```
@@ -200,6 +200,7 @@ Supported shell commands:
200
200
  - `/scan pr <pr-number>`
201
201
  - `/scans`
202
202
  - `/findings [scan-id]`
203
+ - `/findings workspace [filter...]`
203
204
  - `/findings comment <finding-id|finding-identifier> <comment>`
204
205
  - `/findings feedback <finding-id|finding-identifier> valid [comment]`
205
206
  - `/findings feedback <finding-id|finding-identifier> invalid <false-positive|by-design|not-relevant> [comment]`
@@ -234,6 +235,7 @@ Supported shell commands:
234
235
  - `apex scan --mode pr --pr <number> [--pr <number>] [--pr-path <path>]`
235
236
  - `apex scans`
236
237
  - `apex findings [--scan <scan-id>]`
238
+ - `apex findings --workspace [--filter <[!]facet:value>]`
237
239
  - `apex findings comment <finding-id|finding-identifier> --content <markdown> [--parent-comment <comment-id>] [--scan <scan-id>]`
238
240
  - `apex findings feedback <finding-id|finding-identifier> <valid|invalid> [comment] [--comment <markdown>] [--scan <scan-id>] [--suggested-severity extreme|critical|high|medium|low|informational] [--dismissal-reason false-positive|by-design|not-relevant] [--label acknowledged|fixed] [--fix-pr-url <github-pr-url>]`
239
241
  - `apex findings fix-review <finding-id|finding-identifier> [--scan <scan-id>] [--review-target pull-request|repository-current] [--fix-pr-url <github-pr-url>]`
@@ -484,6 +486,7 @@ The MCP server exposes Apex-specific tools for:
484
486
  - doctor, credits, and provider connection URLs
485
487
  - workspace inspection and workspace binding
486
488
  - scan start, status, cancellation, findings, and findings export
489
+ - cross-scan workspace findings through `apex-workspace-findings`, with filters such as `status:open` and `!validation:invalid`
487
490
  - Lite scan start by calling `apex-scan` with `mode: "lite"`
488
491
  - PR scan start by calling `apex-scan` with `mode: "pr"` and `pullRequests`
489
492
  - finding comments and valid/invalid feedback with `apex-finding-comment` and `apex-finding-feedback`
@@ -493,6 +496,8 @@ The MCP server does not expose trial redemption, trial claim, or service-key cre
493
496
 
494
497
  For repository-scoped operations, pass `cwd` explicitly so the server can resolve the right `.apex/workspace.json` binding and repository roots.
495
498
 
499
+ Use `apex-workspace-findings` when an agent needs the same cross-scan finding set as the workspace browser. For example, pass `filters: ["status:open"]` to retrieve all open findings without enumerating scans. Repeated filters in one facet are ORed; filters across different facets are ANDed. Supported facets are `severity`, `scan`, `status`, `validation`, and `visibility`.
500
+
496
501
  For Codex-style clients, the packaged skill can be installed with `apex setup codex`. For GitHub Copilot CLI, the same skill is installed into `~/.copilot/skills/apex-cli` with `apex setup copilot`. The repo-local source lives at `skills/apex-cli/SKILL.md`.
497
502
 
498
503
  For Claude Code, the packaged project skill can be installed into the current repository with `apex setup claude`. The repo-local source lives at `.claude/skills/apex-cli/SKILL.md`. Anthropic documents project skills as filesystem directories under `.claude/skills/<name>/SKILL.md`, and the Claude Agent SDK uses the same location when the `Skill` tool is enabled.
@@ -528,7 +533,7 @@ Telemetry event posts do not include bearer tokens. The telemetry endpoint is `P
528
533
  {
529
534
  "schemaVersion": 1,
530
535
  "event": "apex.invocation.completed",
531
- "cliVersion": "0.1.19",
536
+ "cliVersion": "0.1.21",
532
537
  "invocation": {
533
538
  "surface": "mcp",
534
539
  "command": "apex-scan",
@@ -559,7 +564,7 @@ The npm package also includes marketplace-ready plugin artifacts:
559
564
  - `.claude-plugin/plugin.json` and `.mcp.claude.json` for Claude Code plugin installs
560
565
  - `.claude-plugin/marketplace.json` for a Claude marketplace entry backed by the public npm package
561
566
 
562
- These plugin installs launch the pinned npm package with `npx -y -p @cantinasecurity/apex-cli@0.1.19 apex-mcp`, so users do not need to install `apex` globally before enabling the plugin.
567
+ These plugin installs launch the pinned npm package with `npx -y -p @cantinasecurity/apex-cli@0.1.21 apex-mcp`, so users do not need to install `apex` globally before enabling the plugin.
563
568
 
564
569
  The repository also includes `.agents/plugins/marketplace.json` for local Codex marketplace testing from a checkout.
565
570
 
@@ -595,3 +600,4 @@ To move between existing Apex workspaces from the CLI:
595
600
  2. Run `apex workspace use <workspace-name|workspace-prefix|workspace-id>` to bind the current directory.
596
601
  3. If the workspace name contains spaces, quote it, for example `apex workspace use "Core Platform"`.
597
602
  4. Use `apex scans`, `apex status --scan <scan-id>`, `apex findings`, and `apex export findings` against that binding.
603
+ 5. Use `apex findings --workspace --filter status:open` for the workspace browser's cross-scan open-findings view.
package/dist/apex.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { ApexApiClient, formatApiError } from "./api-client.js";
2
2
  import { parseArgs } from "./args.js";
3
- import { commandCancelScan, commandClaim, commandConnect, commandCredits, commandDoctor, commandExportFindings, commandFindingComment, commandFindingFeedback, commandFindingFixReview, commandFindings, commandLogin, commandLogout, commandRedeem, commandScan, commandScans, commandServiceKeyCreate, commandServiceKeyList, commandServiceKeyRevoke, commandSetup, commandStatus, commandTelemetry, commandUpdate, commandWorkspace, commandWorkspaceUse, commandWorkspaces, getRedeemExitCode, } from "./commands.js";
3
+ import { commandCancelScan, commandClaim, commandConnect, commandCredits, commandDoctor, commandExportFindings, commandFindingComment, commandFindingFeedback, commandFindingFixReview, commandFindings, commandWorkspaceFindings, commandLogin, commandLogout, commandRedeem, commandScan, commandScans, commandServiceKeyCreate, commandServiceKeyList, commandServiceKeyRevoke, commandSetup, commandStatus, commandTelemetry, commandUpdate, commandWorkspace, commandWorkspaceUse, commandWorkspaces, getRedeemExitCode, } from "./commands.js";
4
4
  import { CLI_HELP_TEXT } from "./help.js";
5
5
  import { runMcpServer } from "./mcp.js";
6
6
  import { runInteractiveShell } from "./shell.js";
@@ -127,6 +127,10 @@ async function dispatch(parsed) {
127
127
  await commandFindingFixReview(client, cwd, parsed.flags, findingRef);
128
128
  return;
129
129
  }
130
+ if (parsed.flags.workspace === true) {
131
+ await commandWorkspaceFindings(client, cwd, parsed.flags);
132
+ return;
133
+ }
130
134
  await commandFindings(client, cwd, parsed.flags);
131
135
  return;
132
136
  case "export":
@@ -91,7 +91,11 @@ function getFriendlyApiErrorMessage(error) {
91
91
  case "CLAIM_INVALID":
92
92
  return "This trial claim proof is invalid or already consumed. Local trial state was cleared; run `apex login` to use the claimed Cantina account, or contact Cantina support if the results are missing.";
93
93
  case "CLAIM_PERSONAL_TENANT_EXISTS":
94
- return "This Cantina account already owns a personal Apex tenant, so CLI claim linking is not available yet. Contact Cantina support before the trial claim deadline.";
94
+ return "The claim reached a legacy attachment path for an existing personal workspace. Wait a moment and run `apex claim` again; if it repeats, contact Cantina support before the trial claim deadline.";
95
+ case "CLAIM_TARGET_UNAUTHORIZED":
96
+ return "The selected Cantina workspace cannot receive this trial. Switch to a workspace where you can run scans, then run `apex claim` again.";
97
+ case "CLAIM_TARGET_PREFIX_EXHAUSTED":
98
+ return "The selected Cantina workspace could not allocate a workspace prefix. Contact Cantina support before the trial claim deadline.";
95
99
  case "EMAIL_UNVERIFIED":
96
100
  return "Verify your email, then run `apex claim` again. Your guest trial remains recoverable on this machine.";
97
101
  case "FINGERPRINT_MISMATCH":
package/dist/args.js CHANGED
@@ -4,9 +4,17 @@ const BOOLEAN_FLAGS = new Set([
4
4
  "json",
5
5
  "no-open",
6
6
  "replace",
7
+ "workspace",
7
8
  "help",
8
9
  ]);
9
- const REPEATABLE_VALUE_FLAGS = new Set(["repo", "pr", "pr-path", "fix-pr-url", "label"]);
10
+ const REPEATABLE_VALUE_FLAGS = new Set([
11
+ "repo",
12
+ "pr",
13
+ "pr-path",
14
+ "fix-pr-url",
15
+ "label",
16
+ "filter",
17
+ ]);
10
18
  export function parseArgs(argv) {
11
19
  const flags = {};
12
20
  const positional = [];
package/dist/commands.js CHANGED
@@ -4,7 +4,7 @@ import { ApiError, formatApiError, getApiErrorCode, } from "./api-client.js";
4
4
  import { openInBrowser } from "./browser.js";
5
5
  import { getClaimProofState, isClaimProofAvailable, parseClaimDeadlineMs, } from "./claim-proof.js";
6
6
  import { loadConfig, updateConfig } from "./config.js";
7
- import { createFindingComment, fetchScanExport, fetchScanFindings, isFindingUuid, normalizeFindingRef, resolveFindingSelection, resolveScanSelection, startFindingFixReviewScan, submitFindingFeedback, } from "./findings.js";
7
+ import { createFindingComment, fetchScanExport, fetchScanFindings, fetchWorkspaceFindings, isFindingUuid, normalizeFindingRef, resolveFindingSelection, resolveScanSelection, startFindingFixReviewScan, submitFindingFeedback, } from "./findings.js";
8
8
  import { prepareExplicitScanSources, supportsLegacyRemoteFlow, } from "./local-source-scan.js";
9
9
  import { logLine, printJson, withLoadingIndicator } from "./output.js";
10
10
  import { claimOnboardingSession, isProofOnlyOnboardingCredentials, redeemOnboardingCode, revokeGuestOnboardingSession, } from "./onboarding.js";
@@ -1435,6 +1435,42 @@ export async function commandFindings(client, cwd, flags) {
1435
1435
  logGuestScanFooter(authState, flags);
1436
1436
  return payload;
1437
1437
  }
1438
+ export async function commandWorkspaceFindings(client, cwd, flags) {
1439
+ if (getFlagString(flags, "scan") || getFlagString(flags, "limit")) {
1440
+ throw new Error("Workspace findings cannot be combined with --scan or --limit. Use --filter scan:<scan-id> to select a scan.");
1441
+ }
1442
+ const me = await ensureAuthenticated(client, flags);
1443
+ const authState = await getCommandAuthState(client, me);
1444
+ setTelemetryAuthKind(authState.authKind);
1445
+ const binding = await requireWorkspaceBinding(cwd);
1446
+ const response = await withLoadingIndicator("Loading workspace findings...", flags, () => fetchWorkspaceFindings(client, binding.workspaceId, getFlagList(flags, "filter")));
1447
+ const payload = {
1448
+ ...response,
1449
+ binding,
1450
+ };
1451
+ if (isJsonMode(flags)) {
1452
+ printJson(payload);
1453
+ return payload;
1454
+ }
1455
+ logLine(`Workspace: ${response.workspace.name}`, flags);
1456
+ if (response.filters.length > 0) {
1457
+ logLine(`Filters: ${response.filters.join(", ")}`, flags);
1458
+ }
1459
+ if (response.findings.length === 0) {
1460
+ logLine("Findings: none for this workspace view.", flags);
1461
+ logGuestScanFooter(authState, flags);
1462
+ return payload;
1463
+ }
1464
+ for (const finding of response.findings) {
1465
+ const identifier = finding.findingIdentifier ?? finding.id;
1466
+ logLine(`${finding.severity.toUpperCase()} ${identifier} ${finding.title} (${finding.status})`, flags);
1467
+ if (finding.findingUrl) {
1468
+ logLine(`View: ${finding.findingUrl}`, flags);
1469
+ }
1470
+ }
1471
+ logGuestScanFooter(authState, flags);
1472
+ return payload;
1473
+ }
1438
1474
  export async function commandFindingComment(client, cwd, flags, requestedFindingRef, content, parentCommentId) {
1439
1475
  const findingRef = normalizeFindingRefInput(requestedFindingRef);
1440
1476
  const trimmedContent = content.trim();
package/dist/findings.js CHANGED
@@ -1,6 +1,9 @@
1
1
  import { fetchWorkspaceScans, getScanDisplayId, matchesScanId, } from "./scan.js";
2
2
  import { ApiError, CredentialGenerationChangedError, isTerminalGuestSessionError, } from "./api-client.js";
3
3
  const FINDING_UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
4
+ const WORKSPACE_FINDING_FILTER_PATTERN = /^!?(severity|scan|status|validation|visibility):[^:]+$/;
5
+ const MAX_WORKSPACE_FINDING_FILTERS = 50;
6
+ const MAX_WORKSPACE_FINDING_FILTER_LENGTH = 128;
4
7
  const CANTINA_AUTH_TOKEN_ENV = "CANTINA_AUTH_TOKEN";
5
8
  const FINDING_WRITE_AUTH_ERROR = "Finding comments, feedback, and fix review scans require Apex authentication. Run `apex login` or use MCP `apex-auth-start` followed by `apex-auth-wait`, then retry.";
6
9
  function parsePositiveInt(value) {
@@ -176,6 +179,28 @@ export async function fetchScanFindings(client, scanId, limit) {
176
179
  const suffix = params.toString();
177
180
  return client.request(`/api/cli/v1/scans/${encodeURIComponent(scanId)}/findings${suffix ? `?${suffix}` : ""}`);
178
181
  }
182
+ export function normalizeWorkspaceFindingFilters(filters) {
183
+ if (filters.length > MAX_WORKSPACE_FINDING_FILTERS) {
184
+ throw new Error(`Workspace findings accept at most ${MAX_WORKSPACE_FINDING_FILTERS} filters.`);
185
+ }
186
+ return filters.map((filter) => {
187
+ const normalized = filter.trim();
188
+ if (normalized.length === 0 ||
189
+ normalized.length > MAX_WORKSPACE_FINDING_FILTER_LENGTH ||
190
+ !WORKSPACE_FINDING_FILTER_PATTERN.test(normalized)) {
191
+ throw new Error("Invalid workspace finding filter. Use [!]severity|scan|status|validation|visibility:<value>, for example status:open or !validation:invalid.");
192
+ }
193
+ return normalized;
194
+ });
195
+ }
196
+ export async function fetchWorkspaceFindings(client, workspaceId, filters) {
197
+ const params = new URLSearchParams();
198
+ for (const filter of normalizeWorkspaceFindingFilters(filters)) {
199
+ params.append("filter", filter);
200
+ }
201
+ const suffix = params.toString();
202
+ return client.request(`/api/cli/v1/local-workspaces/${encodeURIComponent(workspaceId)}/findings${suffix ? `?${suffix}` : ""}`);
203
+ }
179
204
  export async function fetchScanExport(client, scanId, format) {
180
205
  const params = new URLSearchParams();
181
206
  if (format.trim().length > 0) {
package/dist/help.js CHANGED
@@ -4,6 +4,7 @@ export const CLI_HELP_TEXT = `Usage:
4
4
  apex scan Create or resolve a workspace for this directory and start a scan
5
5
  apex scans List scans for the current workspace binding
6
6
  apex findings List findings for the latest or selected scan
7
+ apex findings --workspace List findings across the workspace browser scan set
7
8
  apex findings comment <finding-id|finding-identifier>
8
9
  Add a comment or note to a finding
9
10
  apex findings feedback <finding-id|finding-identifier> <valid|invalid>
@@ -41,6 +42,8 @@ Flags:
41
42
  --expires-at <ISO-8601> Set an optional service-key expiration time
42
43
  --workspace-name <name> Set the Apex workspace name for this directory
43
44
  --scan <scan-id> Select a specific scan for status, findings, or export
45
+ --workspace Use the cross-scan workspace findings view
46
+ --filter <[!]facet:value> Filter workspace findings; repeat for multiple filters
44
47
  --status valid|invalid Set the finding feedback status explicitly
45
48
  --content <markdown> Supply comment content for apex findings comment
46
49
  --comment <markdown> Supply rationale for apex findings feedback
@@ -81,7 +84,7 @@ Tips:
81
84
  Trial redemption, claim, and revocation require https://ai.cantina.xyz and reject other APEX_BASE_URL origins before sending secrets.
82
85
  apex redeem never starts device login or opens a browser; apex claim is the only onboarding command that may open one.
83
86
  After a guest scan, run apex claim on the same machine within the displayed claim deadline to keep the results.
84
- Claim currently requires an account without an existing personal Apex tenant; otherwise contact Cantina support before the deadline.
87
+ Claim moves trial results into the selected Cantina workspace when you can run scans there, or into your existing personal workspace.
85
88
  Service-key commands require an existing login; run apex login first when needed.
86
89
  Apex automatically selects your company when only one is available. With multiple companies, choose one interactively or pass --company.
87
90
  Service-key creation also requires --name and reveals the secret only once; omit --scope to grant all hosted API scopes.
@@ -89,6 +92,7 @@ Tips:
89
92
  Invalid finding feedback requires --dismissal-reason.
90
93
  Fix review scans use saved or supplied Fix PR URLs when present; otherwise they review the current repository when Bedrock has original scan repository context.
91
94
  Finding identifiers such as KERN2-25 resolve against the selected scan; pass --scan or use the finding UUID directly when needed.
95
+ Use apex findings --workspace --filter status:open to retrieve all open findings across the workspace browser scan set.
92
96
  Anonymous usage telemetry helps Apex understand CLI, MCP, Codex, Claude, and Copilot usage. Disable it with apex telemetry disable or APEX_TELEMETRY_DISABLED=1.
93
97
  Quote workspace names that contain spaces:
94
98
  apex workspace use "Core Platform"
@@ -102,6 +106,8 @@ Commands:
102
106
  /scan pr <pr-number> Start a PR scan for this workspace
103
107
  /scans List scans for this workspace
104
108
  /findings [scan-id] List findings for the latest or selected scan
109
+ /findings workspace [filter...]
110
+ List workspace findings; for example status:open
105
111
  /findings comment <finding-ref> <comment>
106
112
  Add a comment or note to a finding
107
113
  /findings feedback <finding-ref> valid [comment]
@@ -148,8 +154,9 @@ Tips:
148
154
  Lite scans require beta access plus provider-backed GitHub or GitLab sources.
149
155
  /workspace use accepts a workspace name, prefix, or ID.
150
156
  /findings comment, /findings feedback, and /findings fix-review use the current Apex login.
157
+ /findings workspace status:open returns all open findings in the workspace browser scan set.
151
158
  /claim is the only onboarding command that may open a browser. Trial redemption stays scripted-only and is not exposed through MCP.
152
- Claim currently requires an account without an existing personal Apex tenant; otherwise contact Cantina support before the deadline.
159
+ /claim moves trial results into the selected Cantina workspace when you can run scans there, or into your existing personal workspace.
153
160
  Service-key commands automatically use your company when only one is available. With multiple companies, choose one interactively or pass --company.
154
161
  Service-key secrets are shown once. Run these commands in your terminal, not in the interactive shell or through MCP.
155
162
  Invalid finding feedback requires a dismissal reason.
package/dist/mcp.js CHANGED
@@ -5,7 +5,7 @@ import { z } from "zod";
5
5
  import { getMe, logout, readStableDeviceLoginState, startDeviceLogin, waitForDeviceLoginApproval, } from "./auth.js";
6
6
  import { ApexApiClient, formatApiError } from "./api-client.js";
7
7
  import { getClaimProofState, isClaimProofAvailable, } from "./claim-proof.js";
8
- import { commandCancelScan, commandConnect, commandCredits, commandDoctor, commandExportFindings, commandFindingComment, commandFindingFeedback, commandFindingFixReview, commandFindings, commandScan, commandScans, commandStatus, commandWorkspace, commandWorkspaceUse, commandWorkspaces, } from "./commands.js";
8
+ import { commandCancelScan, commandConnect, commandCredits, commandDoctor, commandExportFindings, commandFindingComment, commandFindingFeedback, commandFindingFixReview, commandFindings, commandWorkspaceFindings, commandScan, commandScans, commandStatus, commandWorkspace, commandWorkspaceUse, commandWorkspaces, } from "./commands.js";
9
9
  import { CLI_HELP_TEXT } from "./help.js";
10
10
  import { getLocalTrialStatus, isProofOnlyOnboardingCredentials, revokeGuestOnboardingSession, } from "./onboarding.js";
11
11
  import { createMcpServerTelemetryInvocation, createMcpTelemetryInvocation, emitInvocationCompleted, emitInvocationStarted, emitMcpServerStarted, withTelemetryContext, } from "./telemetry.js";
@@ -19,7 +19,7 @@ Suggested workflow:
19
19
  4. Only when Apex is unauthenticated and no unexpired claim proof or trial code is available, call apex-auth-start and ask the user to approve the device login URL, then call apex-auth-wait with both the returned deviceCode and credentialGeneration.
20
20
  5. Call apex-doctor for the target repository cwd.
21
21
  6. If the directory is not bound to the right workspace, call apex-workspaces and apex-workspace-use.
22
- 7. Start and monitor scans with apex-scan, apex-status, apex-scans, apex-findings, and apex-export-findings.
22
+ 7. Start and monitor scans with apex-scan, apex-status, apex-scans, apex-findings, and apex-export-findings. Use apex-workspace-findings for the cross-scan workspace view.
23
23
  8. For PR scans, call apex-scan with mode "pr" and one or more pullRequests.
24
24
  9. Leave finding comments or valid/invalid feedback with apex-finding-comment and apex-finding-feedback when review collaboration is needed.
25
25
  10. To review a fix, call apex-finding-fix-review. Pass fixPrUrls for PR-based fix reviews, or omit them to review the current repository state when Bedrock has original scan repository context.
@@ -85,6 +85,9 @@ function buildFlags(options) {
85
85
  if (options.fixPrUrls && options.fixPrUrls.length > 0) {
86
86
  flags["fix-pr-url"] = options.fixPrUrls;
87
87
  }
88
+ if (options.filters && options.filters.length > 0) {
89
+ flags.filter = options.filters;
90
+ }
88
91
  if (options.pullRequests && options.pullRequests.length > 0) {
89
92
  flags.pr = options.pullRequests.map((pullRequest) => {
90
93
  const paths = pullRequest.paths
@@ -612,6 +615,23 @@ function registerTools(server) {
612
615
  scanId,
613
616
  limit,
614
617
  }));
618
+ server.registerTool("apex-workspace-findings", {
619
+ title: "List Apex Workspace Findings",
620
+ description: "List findings across the current Apex workspace using the same non-archived scan set as the web findings browser. Pass filters such as status:open or !validation:invalid; repeated filters in one facet are ORed and different facets are ANDed.",
621
+ inputSchema: {
622
+ cwd: z.string().optional(),
623
+ filters: z.array(z.string().min(1).max(128)).max(50).optional(),
624
+ },
625
+ }, async ({ cwd, filters }) => runTool("apex-workspace-findings", async () => {
626
+ const client = new ApexApiClient();
627
+ await requireAuthenticated(client);
628
+ const targetCwd = resolveCwd(cwd);
629
+ const payload = await commandWorkspaceFindings(client, targetCwd, buildFlags({ filters }));
630
+ return {
631
+ cwd: targetCwd,
632
+ ...payload,
633
+ };
634
+ }, (value) => `Fetched Apex workspace findings for ${String(value.cwd)}.`, { cwd, filters }));
615
635
  server.registerTool("apex-finding-comment", {
616
636
  title: "Add Apex Finding Comment",
617
637
  description: "Add a comment or note to an Apex finding using the current Apex login.",
package/dist/shell.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { SHELL_HELP_TEXT } from "./help.js";
2
- import { commandCancelScan, commandClaim, commandConnect, commandCredits, commandDoctor, commandExportFindings, commandFindingComment, commandFindingFeedback, commandFindingFixReview, commandFindings, commandLogout, commandScan, commandScans, commandStatus, commandTelemetry, commandUpdate, commandWorkspace, commandWorkspaceUse, commandWorkspaces, initializeInteractiveSession, openCurrentApexView, } from "./commands.js";
2
+ import { commandCancelScan, commandClaim, commandConnect, commandCredits, commandDoctor, commandExportFindings, commandFindingComment, commandFindingFeedback, commandFindingFixReview, commandFindings, commandWorkspaceFindings, commandLogout, commandScan, commandScans, commandStatus, commandTelemetry, commandUpdate, commandWorkspace, commandWorkspaceUse, commandWorkspaces, initializeInteractiveSession, openCurrentApexView, } from "./commands.js";
3
3
  import { withFlag } from "./args.js";
4
4
  import { printCompanyDetails, printInteractiveSessionSummary, printSourceList, printWorkspaceDetails, } from "./output.js";
5
5
  import { readLine } from "./prompt.js";
@@ -10,7 +10,10 @@ const SHELL_COMPLETIONS = [
10
10
  { command: "credits" },
11
11
  { command: "scan", args: ["standard", "audit", "ultra", "lite", "pr"] },
12
12
  { command: "scans" },
13
- { command: "findings", args: ["comment", "feedback", "fix-review"] },
13
+ {
14
+ command: "findings",
15
+ args: ["workspace", "comment", "feedback", "fix-review"],
16
+ },
14
17
  { command: "export" },
15
18
  { command: "workspaces" },
16
19
  { command: "cancel-scan" },
@@ -245,6 +248,12 @@ async function runShellCommand(client, cwd, parsed, shellFlags, session) {
245
248
  await commandScans(client, cwd, shellFlags);
246
249
  return {};
247
250
  case "findings": {
251
+ if (parsed.args[0] === "workspace") {
252
+ await commandWorkspaceFindings(client, cwd, parsed.args.length > 1
253
+ ? withFlag(shellFlags, "filter", parsed.args.slice(1))
254
+ : shellFlags);
255
+ return {};
256
+ }
248
257
  if (parsed.args[0] === "comment") {
249
258
  if (parsed.args.length < 3) {
250
259
  process.stderr.write("Usage: /findings comment <finding-id|finding-identifier> <comment>\n");
package/dist/telemetry.js CHANGED
@@ -18,6 +18,7 @@ const KNOWN_CLI_FLAG_NAMES = [
18
18
  "fix-pr-url",
19
19
  "force",
20
20
  "format",
21
+ "filter",
21
22
  "help",
22
23
  "json",
23
24
  "label",
@@ -39,6 +40,7 @@ const KNOWN_CLI_FLAG_NAMES = [
39
40
  "status",
40
41
  "suggested-severity",
41
42
  "workspace-name",
43
+ "workspace",
42
44
  ];
43
45
  const KNOWN_CLI_COMMANDS = [
44
46
  "cancel-scan",
@@ -225,6 +227,7 @@ function sanitizeCliFlags(flags) {
225
227
  replace: boolFlag(flags, "replace"),
226
228
  nonInteractive: boolFlag(flags, "non-interactive"),
227
229
  noOpen: boolFlag(flags, "no-open"),
230
+ workspaceFindings: boolFlag(flags, "workspace"),
228
231
  companyProvided: stringFlag(flags, "company") !== null,
229
232
  workspaceNameProvided: stringFlag(flags, "workspace-name") !== null,
230
233
  nameProvided: stringFlag(flags, "name") !== null,
@@ -263,6 +266,7 @@ function sanitizeCliFlags(flags) {
263
266
  prPathCount: flagCount(flags, "pr-path"),
264
267
  labelCount: flagCount(flags, "label"),
265
268
  fixPrUrlCount: flagCount(flags, "fix-pr-url"),
269
+ filterCount: flagCount(flags, "filter"),
266
270
  limitBucket: bucketLimit(flags.limit),
267
271
  };
268
272
  }
@@ -441,6 +445,7 @@ export function sanitizeMcpInput(input = {}) {
441
445
  pullRequestPathCount: pullRequestPathCount(input.pullRequests),
442
446
  labelCount: arrayLength(input.labels),
443
447
  fixPrUrlCount: arrayLength(input.fixPrUrls),
448
+ filterCount: arrayLength(input.filters),
444
449
  contentLengthBucket: bucketLength(input.content),
445
450
  commentLengthBucket: bucketLength(input.comment),
446
451
  limitBucket: bucketLimit(input.limit),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cantinasecurity/apex-cli",
3
- "version": "0.1.19",
3
+ "version": "0.1.21",
4
4
  "description": "Standalone CLI and MCP server for Apex.",
5
5
  "private": false,
6
6
  "type": "module",
@@ -37,7 +37,7 @@ Workflow:
37
37
  4. For repository work, pass `cwd` explicitly.
38
38
  5. Call `apex-doctor` before starting a scan if workspace binding or source planning might be unclear.
39
39
  6. If the directory is not bound to the right workspace, call `apex-workspaces` and `apex-workspace-use`.
40
- 7. Use `apex-scan`, `apex-status`, `apex-scans`, `apex-findings`, and `apex-export-findings` for the scan lifecycle.
40
+ 7. Use `apex-scan`, `apex-status`, `apex-scans`, `apex-findings`, and `apex-export-findings` for the scan lifecycle. Use `apex-workspace-findings` for the cross-scan workspace browser view.
41
41
  8. For Lite scans, call `apex-scan` with `mode: "lite"` only when the company has Lite Scan beta access and the sources are provider-backed.
42
42
  9. For PR scans, call `apex-scan` with `mode: "pr"` and `pullRequests`.
43
43
  10. Use `apex-finding-comment` and `apex-finding-feedback` when the user wants to leave review notes or valid/invalid feedback on a finding.
@@ -50,7 +50,7 @@ If the Apex MCP server is not configured, fall back to the local CLI:
50
50
  - After a successful scan JSON response, inspect `localPersistence.workspaceBindingSaved`. If it is `false`, the server scan already started: capture and report `binding.lastScanId`, then STOP; do not run status or findings until local repository permissions are repaired.
51
51
  - `apex-trial-status` is MCP-only; do not invoke it as a CLI command. In a CLI-only flow, use the `claimDeadline` returned by `apex redeem <code> --json` without repeating or persisting the secret code.
52
52
  - After a guest scan, tell the user to run `apex claim` on the same machine before that returned claim deadline. If the deadline is unavailable or uncertain, do not guess or start a replacement login; run `apex claim` and let the server validate the proof.
53
- - Claim currently requires a Cantina account that does not already own a personal Apex tenant. If the user already completed Cantina Apex onboarding, explain that CLI linking is not available yet and tell them to contact Cantina support before the claim deadline.
53
+ - Claim moves the guest workspace, scans, and findings into the currently selected Cantina workspace when the account can run scans there. Personal workspace owners receive the trial in their existing workspace instead of a duplicate. If the selected workspace cannot run scans, tell the user to switch to one that can and retry before the claim deadline.
54
54
 
55
55
  Guidelines:
56
56
 
@@ -74,6 +74,7 @@ Guidelines:
74
74
  - Use `force: true` on `apex-scan` only when the user explicitly wants to replace or overlap an active scan.
75
75
  - When checking a scan that is not the workspace binding's latest scan, pass `scanId` to `apex-status`; use `apex-scans` first if you need to discover scan IDs.
76
76
  - Prefer `apex-findings` for quick inspection and `apex-export-findings` when the user needs a file artifact.
77
+ - Use `apex-workspace-findings` when the user asks for findings across a workspace instead of one scan. Pass `filters: ["status:open"]` for all open findings; supported filter facets are `severity`, `scan`, `status`, `validation`, and `visibility`, and a leading `!` negates a filter.
77
78
  - Finding comments, feedback, and fix review scan starts use the same Apex device-login credentials as read tools. If a write tool reports missing auth, re-run `apex-auth-status`, check `apex-trial-status`, and complete `apex-auth-start` / `apex-auth-wait` only when no unexpired local claim proof is available, instead of asking for browser cookies or auth tokens.
78
79
  - Anonymous telemetry records only sanitized command/tool metadata such as command names, enum modes, counts, durations, success/failure categories, CLI version, and client integration. It must not include raw repository paths, scan IDs, finding IDs, comments, file paths, PR URLs, trial codes, claim tokens, onboarding fingerprints, or bearer tokens.
79
80
  - Invalid finding feedback requires `dismissalReason`; valid feedback can include `suggestedSeverity`, including `extreme`.