From 11e9fa058d4672adf9a669f6122646224585ad7b Mon Sep 17 00:00:00 2001
From: "jfrog-agentic-release-bot[bot]"
<276080306+jfrog-agentic-release-bot[bot]@users.noreply.github.com>
Date: Thu, 27 Aug 2026 10:09:01 +0000
Subject: [PATCH 1/2] [patch] chore: sync skills to v0.31.1
---
.codex-plugin/plugin.json | 2 +-
package.json | 2 +-
scripts/sync-skills-vendor.json | 2 +-
.../SKILL.md | 63 +-
.../references/rca-procedure.md | 568 ++++++++++++++++++
skills/jfrog-reference-architecture/SKILL.md | 2 +-
skills/jfrog/SKILL.md | 2 +-
skills/jfrog/references/INDEX.md | 6 +-
8 files changed, 628 insertions(+), 19 deletions(-)
rename skills/{jfrog-package-safety-and-download => jfrog-package-curation}/SKILL.md (80%)
create mode 100644 skills/jfrog-package-curation/references/rca-procedure.md
diff --git a/.codex-plugin/plugin.json b/.codex-plugin/plugin.json
index 16a577d..6934a14 100644
--- a/.codex-plugin/plugin.json
+++ b/.codex-plugin/plugin.json
@@ -1,6 +1,6 @@
{
"name": "jfrog",
- "version": "0.1.3",
+ "version": "0.1.4",
"description": "JFrog skills and the JFrog MCP server for Codex \u2014 interact with the JFrog Platform.",
"author": {
"name": "JFrog",
diff --git a/package.json b/package.json
index 32d168e..5557e08 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "codex-plugin",
- "version": "0.1.3",
+ "version": "0.1.4",
"private": true,
"type": "module",
"description": "JFrog skills and MCP server plugin for OpenAI Codex.",
diff --git a/scripts/sync-skills-vendor.json b/scripts/sync-skills-vendor.json
index acab867..bf58e43 100644
--- a/scripts/sync-skills-vendor.json
+++ b/scripts/sync-skills-vendor.json
@@ -1,6 +1,6 @@
{
"repo": "jfrog/jfrog-skills",
- "pin": "v0.30.0",
+ "pin": "v0.31.1",
"paths": [
"skills"
]
diff --git a/skills/jfrog-package-safety-and-download/SKILL.md b/skills/jfrog-package-curation/SKILL.md
similarity index 80%
rename from skills/jfrog-package-safety-and-download/SKILL.md
rename to skills/jfrog-package-curation/SKILL.md
index 0adb9cf..300f785 100644
--- a/skills/jfrog-package-safety-and-download/SKILL.md
+++ b/skills/jfrog-package-curation/SKILL.md
@@ -1,25 +1,61 @@
---
-name: jfrog-package-safety-and-download
+name: jfrog-package-curation
description: >-
- Check JFrog Public Catalog and stored packages for a version, interpret
- catalog security signals, and download through Artifactory (JFrog Platform
- locations, remote cache, curation-aware package managers, or repo proxy).
- Use when the user asks whether a package is safe, allowed, curated, or
- wants to download npm, Maven, PyPI, Go, or similar packages via JFrog.
- Do NOT use for pure CVE or vulnerability lookups (e.g. "details on
- CVE-2021-23337") — those are handled by the jfrog skill's Public security
- domain queries without this workflow.
+ Check/download a package (npm, Maven, PyPI, Go...) via JFrog — safe,
+ allowed, curated? Or: package op fails/blocked (ETARGET, 403, blocked by
+ curation policy, missing version, waiver) — root cause it. Checks the JFrog Public
+ Catalog and stored packages for a version, interprets catalog security
+ signals, and downloads through Artifactory (JFrog Platform locations,
+ remote cache, curation-aware package managers, or repo proxy). Do NOT use
+ for pure CVE or vulnerability lookups (e.g. "details on CVE-2021-23337")
+ — those are handled by the jfrog skill's Public security domain queries
+ without this workflow. Do NOT use for installing, listing, or approving MCP
+ servers/tools (even when named like a package, e.g. `@scope/pkg`) — that's
+ `jfrog-mcp-management`.
+compatibility: >-
+ "Check & download" needs only the JFrog CLI (`jf`). "Troubleshoot a
+ failure" needs the JFrog MCP curation tools (`jfs_curation_*`) — `jf` CLI
+ alone does not cover it; if unavailable, that section cannot run (see its
+ Preflight). Its reference file is local; outbound HTTPS is optional there
+ too (on-demand WebFetch of JFrog docs) — without it, it runs on the MCP
+ tools and local reference, degraded.
metadata:
role: workflow
---
-# JFrog Package Safety and Download
+# JFrog Package Curation
## Prerequisites
- Read `../jfrog/SKILL.md` for JFrog Platform concepts, domain model, CLI setup, and API patterns.
- **OneModel shapes drift by server version.** Before inventing GraphQL fields or `where` filters, read `../jfrog/references/onemodel-graphql.md` (schema fetch workflow) and `../jfrog/references/onemodel-query-examples.md` (**Public packages**, **Stored packages**). Regenerate or verify queries against `GET "$JFROG_URL/onemodel/api/v1/supergraph/schema"` when examples fail validation.
+## Which path do I need?
+
+- **Package hasn't been checked or downloaded yet** (is it safe / allowed / curated? download it) → go to **Check & download** below.
+- **A package operation already failed** — `ETARGET` (npm), "no matching distribution" (pip), `NU1102` (NuGet), a version missing/stripped from the registry, `403` / "blocked by curation policy", or an approved waiver not taking effect → go to **Troubleshoot a failure** below.
+
+---
+
+# Troubleshoot a failure
+
+> **Requires the JFrog MCP curation tools** (`jfs_curation_*`); no `jf` CLI
+> equivalent is used in this path. See the skill's `compatibility` note.
+
+Root-causing a curation-attributable failure (`ETARGET`, `403`, a version
+missing from the index, a waiver not taking effect) is a multi-step
+investigation — preflight checks, the two-gate model, symptom classification,
+then a full RCA procedure. It doesn't belong inline here since a plain
+download never touches it. **Open
+[references/rca-procedure.md](references/rca-procedure.md) and follow it from
+the top** — one file, covering preflight, the #1 RULE, the two-gate model,
+symptom classification (Step 0), and the RCA itself (Steps 1–4, worked
+examples, gotchas).
+
+---
+
+# Check & download
+
## Workflow overview
```mermaid
@@ -36,7 +72,7 @@ flowchart TD
I -->|Yes| J[Check curation policy via API]
I -->|No| K[Download via remote repo]
J -->|200 Allowed| K
- J -->|403 Blocked| M[Report curation blocked — stop]
+ J -->|403 Blocked| M[Report curation blocked — see Troubleshoot a failure]
```
### Parallelization opportunities
@@ -207,7 +243,10 @@ fi
download via a remote repo (same as Step 6b).
- **`returned 403` on stderr** → package is **blocked** by a curation
policy. The response body explains which policy rule blocked it. Report
- the block reason to the user and stop — do not attempt to download.
+ the block reason to the user and stop — do not attempt to download. For
+ root-cause detail beyond the block reason (why this version specifically,
+ whether a waiver applies, what to use instead), see **Troubleshoot a
+ failure** above.
- **Any other non-zero exit** → treat as an operational failure (auth, DNS,
endpoint disabled) and report.
diff --git a/skills/jfrog-package-curation/references/rca-procedure.md b/skills/jfrog-package-curation/references/rca-procedure.md
new file mode 100644
index 0000000..3a8f24b
--- /dev/null
+++ b/skills/jfrog-package-curation/references/rca-procedure.md
@@ -0,0 +1,568 @@
+# Curation RCA procedure
+
+> **Requires the JFrog MCP curation tools** (`jfs_curation_*`); no `jf` CLI
+> equivalent is used in this path. See the skill's `compatibility` note.
+
+> **#1 RULE — READ FIRST.** The **public catalog**
+> (`catalog_packages_list_versions`) is the only authority on whether a version
+> exists — quote its version list when you report existence. **Never** assert a
+> version exists or doesn't from the curated registry (`npm view` / `pip index`
+> against `/artifactory/...`, which is curation-filtered, so a CVS-blocked version
+> is simply absent) **or from your own memory**. Decide existence from the catalog,
+> and cross-check the **CVS audit** (`jfs_curation_query_cvs_audit_events`) for why a version was
+> filtered.
+
+Root-cause a curation package failure: correlate the two places Curation acts,
+then report an actionable conclusion. This file is the whole workflow —
+preflight, the two-gate model, symptom classification, evidence gathering,
+interpretation, and the conclusion — read it in order.
+
+This orchestrates the **JFrog MCP curation tools** (no `jf` CLI or raw
+HTTP) and assumes the JFrog MCP server is connected and the tenant is
+Curation-entitled. The curation tools are the **`jfs_curation_*`** family (e.g.
+`jfs_curation_query_cvs_audit_events`); `catalog_packages_list_versions`
+and `artifactory_repositories_get` are different tool families without the prefix.
+
+## Preflight
+
+**Right environment.** The MCP curation tools only ever query the
+**currently connected** JFrog tenant, and a different env has different audit data —
+an answer from the wrong tenant is worse than none. When the failing URL's host
+matches the connected tenant, you're set — proceed. **Confirm with the user only
+when it's ambiguous** (the host doesn't obviously match, or you can't tell which
+tenant is connected); on a real mismatch, **stop** until the tools point at the
+right tenant. Don't ask when it already matches.
+
+**Tools must be present.** These MCP tools are gated at registration
+by platform version + entitlement (min Xray **3.153.0** for the CVS tools and
+`jfs_curation_get_audit_event_policies`; **3.112.0** for `jfs_curation_query_audit_events`). If the tool you
+need is **not in your available tools**, split the diagnosis on what else is
+present:
+
+- **No JFrog MCP tools at all** (not even `catalog_packages_list_versions` /
+ `artifactory_repositories_get`) → the JFrog MCP server isn't connected. Say
+ so; never substitute a `jf` command or raw HTTP.
+- **Other JFrog MCP tools work but `jfs_curation_*` is absent** → the
+ platform is below the required Xray version or the tenant isn't
+ Curation-entitled. **Stop gracefully:** report that the audit-based investigation
+ can't run here and that it needs Xray ≥ 3.153.0 with Curation enabled.
+
+Either way, do **not** fabricate calls or guess results.
+
+## How Curation works
+
+A package manager does two things: **resolve** a version, then **download** it.
+Curation acts at both, with different strictness.
+
+| | **Gate 1 — Resolution** | **Gate 2 — Download** |
+|---|---|---|
+| Client action | asks Artifactory for the version list | requests the binary |
+| Curation evaluates | versions newest-first, **stops at the first approved one** (max evaluated is configurable, typically 1) | **all** policies, against the one requested version |
+| Returns | that version **plus everything older** — or `403` if none approved | the binary — or `403` |
+| Strictness | best-effort filter to reduce downstream blocks; **not a guarantee** | **hard gate** — enforcement is final here |
+
+**The list is truncated at the top, not filtered throughout.** Versions *newer*
+than the approved one are gone. Versions *older* are present but were **never
+evaluated**. Every symptom below follows from that one sentence.
+
+```mermaid
+sequenceDiagram
+ participant CLI as Package manager
+ participant RT as Artifactory
+ participant CUR as Curation
+
+ CLI->>RT: 1. list versions
+ RT->>CUR: evaluate
+ CUR-->>RT: first approved version + all older
+ alt none approved
+ RT-->>CLI: 403
+ else
+ RT-->>CLI: truncated list
+ end
+ Note over CLI: client resolves against this list only
a pin not in it fails here, in the client
+
+ CLI->>RT: 2. download resolved version
+ RT->>CUR: evaluate all policies
+ alt blocked
+ RT-->>CLI: 403
+ else
+ RT-->>CLI: binary
+ end
+```
+
+**Which audit records which gate:**
+
+- **Gate 1 — resolution** → the **CVS audit**:
+ `jfs_curation_query_cvs_audit_events` → `jfs_curation_get_cvs_audit_version_detail`.
+- **Gate 2 — download** → the **package-download audit**:
+ `jfs_curation_query_audit_events` → `jfs_curation_get_audit_event_policies`.
+- A **`403` with nothing resolvable** (gate 1, every version blocked) is recorded in
+ **both** audits — query both.
+
+**Symptom → gate:**
+
+| Symptom | Gate | Read it as |
+|---|---|---|
+| `ETARGET`, "no matching distribution", `NU1102` | 1 | pin is newer than the approved version, so it was never returned. The client failed locally — **Curation wrote no event for this failure** |
+| `403` on a version the client could see | 2 | pin is older than the approved version: passed gate 1 unevaluated, blocked at the hard gate. Expected, not a contradiction |
+| `403` with nothing resolvable | 1 | every version blocked (recorded in both audits) |
+| Visible "latest" is lower than upstream | 1 | normal truncation, not a fault |
+
+Exact error text is **manager- and command-specific** — don't assume `ETARGET`
+(npm `ETARGET`, pip "no matching distribution", NuGet `NU1102`, `E403`; `npm
+install` vs `npm ci`, `pip install ` vs `==`).
+
+Gate 1 and gate 2 disagreeing is **by design** — say so rather than reconciling it.
+
+**"CVS disabled" ≠ no gate 1.** CVS is **enabled by default** for some package types: the
+**malicious** and **immature** policies run at gate 1 even when CVS is switched off
+(npm today; the set grows as types onboard — don't assume it's only npm). So a
+gate-1 block whose `condition_name` is malicious or immature is expected despite a
+"CVS is disabled" claim; a CVE / license / ban condition means CVS is actually on.
+Treat the audit as ground truth — a gate-1 event contemporaneous with the failure
+settles it. Read the `condition_name` and explain it; don't argue with the claim.
+
+> **Don't mix timelines.** The audit tools report the **past** (timestamped
+> events). `jfs_curation_check_remote_package_compliance` returns a **live verdict now** — it
+> runs a fresh CVS evaluation under current policy and may create a new audit row.
+> Anchor the RCA on the audit timeline. Use a live compliance check only per
+> Step 2, and label its result **current** — never as evidence of the
+> failure-time state.
+
+**Docs to consult** — fetch on demand, once (if blocked/airgapped, say so and
+reason from the tool outputs; if the fetch itself fails for any other reason,
+say so and proceed on the tool outputs alone rather than stalling):
+
+- **CVS mechanism, supported types, waiver applicability, limits** — fetch at the
+ start of a gate-1 investigation:
+ `https://docs.jfrog.com/security/docs/compliant-version-selection`
+- **Per-package-manager error text** — don't assume `ETARGET`; the pattern is
+ manager- and command-specific:
+ `https://docs.jfrog.com/security/docs/behavior-by-package`
+- **Curation concepts** (what it is, features & capabilities), only to ground a
+ term you cannot otherwise resolve:
+ `https://docs.jfrog.com/security/docs/curation-intro` and
+ `https://docs.jfrog.com/security/docs/features-and-capabilities-getting-started-5`
+
+## Route by gate
+
+Classify the symptom → gate → tool using the tables above:
+
+- **Gate 1 — resolution** (not resolvable: `ETARGET`, "no matching distribution",
+ `NU1102`, missing from `npm view` / `pip index`) → **CVS audit**:
+ `jfs_curation_query_cvs_audit_events` → `jfs_curation_get_cvs_audit_version_detail`.
+- **Gate 2 — download** (`403` on a version that *is* resolvable) → **package-download
+ audit**: `jfs_curation_query_audit_events` → `jfs_curation_get_audit_event_policies`.
+ Enough to root-cause the 403 on its own — don't reach for a compliance check by reflex.
+
+A `403` with **nothing** resolvable is gate 1 and is recorded in **both** audits —
+query both. The gates can **disagree** (a resolvable version that still 403s) —
+surface it, don't reconcile.
+
+## Step 0 — Capture the symptom
+
+Collect before touching any tool. Ask the user only for what is missing:
+
+- **package type** (npm, PyPI, Maven, Go, NuGet, …) — infer it from the failing
+ URL (`/api//…`, e.g. `/api/npm/`) or the command (`npm`/`pip`/…); ask the
+ user only if neither is available. `package_type` is **required** on the CVS
+ audit call. If the first audit comes back
+ empty, suspect a casing/token mismatch instead: canonical CVS tokens are
+ `npm, PyPI, Maven, Go, NuGet, Gems, Conda, Composer, Gradle,
+ AIEditorExtensions, SBT, Alpine, Docker, Cargo` (case-sensitive, not
+ normalised server-side) — retry with the canonical casing, or ask the user
+ to confirm the ecosystem rather than guessing.
+- **package name** and the **version** in the error.
+- **repository / registry** the client resolved through (from the failing URL /
+ client config), and whether it's a JFrog **curated remote**, a **virtual** (some
+ managers — e.g. Go — require one; npm can use either), or plain public. This
+ decides how Step 1 reads a "not found" (curated vs public) and how Step 2
+ identifies the remote (the audits are keyed by the remote, never a virtual). You
+ often can't tell virtual from remote by name — capture it; if unknown, ask; do
+ not assume.
+- the **exact error text** and the **approximate date + hour** — bounds the audit
+ window (Step 2). Investigate any past failure within **retention** (default ~30 days);
+ the 168h cap limits one query's **width**, not recency. For an older failure,
+ anchor the window near the failure time (not "now") so the query actually covers it.
+
+**Pin the timeline before running any audit.** Read the failure's **date + hour**
+from the error text / log if it's there (e.g. a timestamped log line) — don't ask
+for what the provided context already shows; ask the user only if it's missing
+(and don't settle for "recently"). It bounds the audit window and anchors
+`created_at_end` to the failure, not "now". Then, within that window, if Step 2's
+survey turns up **more than one curated remote** for the package, ask which repo
+they resolved through before interpreting — see Step 2's "Identify the curated
+remote".
+
+Classify into a symptom class. This list is **open-ended** — match the closest
+row; if nothing fits, use the last row (never refuse for lack of an exact match):
+
+| Symptom | Class | Start with |
+|---|---|---|
+| A version can't be resolved/installed — e.g. `ETARGET` (npm), "no matching distribution" (pip), `NU1102` (NuGet), or absent from `npm view` / `pip index`. The exact pattern is **package-manager-specific** — see behavior-by-package | **NOT-RESOLVABLE** | Step 1's client-side-config check, then Gate 1 — CVS audit |
+| `403` / `Forbidden` / "blocked by curation policy" on a version that *is* listed | **DOWNLOAD-BLOCKED** | Gate 2 — package-download audit |
+| Every version fails because nothing resolves at all (empty/near-empty listing) | **NOT-RESOLVABLE** (package-level) | This is a resolution failure, not a download-time block — Gate 1 + both audits |
+| Every version 403s at download while the package is still listed | **PACKAGE-BLOCKED** | Gate 2; expect a package-level block |
+| The blocked name is a dependency of what you installed | **TRANSITIVE** | Gate 2 on the *blocked* name, not the top-level one |
+| Used to install, now blocked (or was blocked, now works) | **DRIFT / TIMELINE** | both audits across time; a live compliance check is the *only* "now" signal |
+| Still blocked despite an approved waiver | **WAIVER-NOT-APPLIED** | Gate 2 + `jfs_curation_query_waiver_requests` (check scope, status, expiry) |
+| "Pending Catalog update" reason, or intermittent/flaky blocks | **PENDING / TRANSIENT** | the relevant audit filtered by `reason` |
+| Anything else curation-related, or unclear | **OTHER** | gather from **both** audits + the client error text, then map via the Step 4 RCA table |
+
+## Step 1 — Rule out the non-curation cause first
+
+Don't assume curation — and for **NOT-RESOLVABLE**, decide existence per the **#1 RULE**.
+
+**Rule out client-side config first.** A resolve failure can look identical to a
+curation block while having nothing to do with curation — the package manager's
+own local filters produce the same symptom shape (`ETARGET`, no matching
+version). Before trusting the symptom table's default routing, check whether
+the client config explains it: a date/version-age filter (npm's `before` /
+`min-release-age`, surfaced via `npm config get before` and `npm config get
+min-release-age`), a registry override in `.npmrc` / `pip.conf` / `nuget.config`
+/ `GOPROXY`, or a CI-injected flag. This applies **regardless of the exact
+error string** — don't wait for a table row that matches your specific
+package manager's wording.
+
+Settle it with **two independent reads — run BOTH, and never gate one behind the
+other**:
+
+1. **Does it exist?** Use **only** `catalog_packages_list_versions` (approved MCP
+ tool). **Never** query a public/upstream registry directly, **never** the
+ curated `/artifactory/...` registry, and **never** `artifactory_packages_get_versions`
+ (curated/cached = **filtered** listing — same trap). Two coverage notes:
+ - **Name translation:** the catalog token differs for some CVS types — **Go →
+ `golang`**, **Gems → `rubygems`**. npm / PyPI / Maven / NuGet are the same.
+ - The catalog covers only some types; for an **unsupported** type it returns
+ nothing — a catalog miss there is **not** proof of non-existence. Fall back
+ to the CVS audit (read 2); if that's also empty, say the existence check is
+ unavailable rather than guessing.
+2. **What did Curation decide?** For a **NOT-RESOLVABLE** symptom, **always** query
+ the CVS audit (`jfs_curation_query_cvs_audit_events`, package + type) — do not
+ skip it because a listing looked empty. It is the authoritative record of whether
+ CVS inspected the version and blocked or approved it: it both proves the version
+ existed and gives the verdict. (For a **403 / download** symptom, the reverse
+ holds — do **not** query the CVS audit; use the download audit only. See Gate 2
+ and the 403 worked example. A **403 with nothing resolvable** is Gate 1
+ too — query **both** audits, per the two-gate model above.)
+
+Read the cross-product:
+
+- **In the catalog (or a CVS record exists) + CVS blocked it** → **curated-index
+ filtering** (the usual answer): CVS blocked the version **and** the client can't
+ resolve it. The audit proves *blocked* (a decision); the **client's ETARGET /
+ absence** is what proves it's missing from the listing — the audit does not state
+ listing membership, so don't claim omission unless the client listing actually
+ lacks the pin. Together → **Gate 1** for the policy detail and the compliant
+ version CVS selected.
+- **In the catalog + no CVS record** → not (yet) curation — remote sync / virtual
+ repo / cache config; investigate outside CVS.
+- **Not in the catalog (and the catalog supports this package type) + no CVS
+ record** → likely never existed / yanked → not curation; report the nearest
+ catalog versions (e.g. `1.18.1`, `0.19.0`) and stop. If the catalog does
+ **not** support this type, a miss is not proof of non-existence — report the
+ existence check as unavailable (Insufficient evidence) instead.
+
+A resolve-time ETARGET fails **in the client**: it mints **no** CVS or download
+audit row and there is **no** 403 to find. The CVS evidence is a
+**prior/historical** evaluation (when the package was last inspected) — never
+expect a row created by this failed install, and never read its absence as
+"curation not involved".
+
+For a **403** symptom the non-curation confounders are auth / permissions / a
+disabled repo — rule those out, then Gate 2.
+
+## Step 2 — Gather evidence
+
+Pick the branch from Step 0. When in doubt, run both; they are independent.
+
+**Identify the curated remote.** The audits are keyed by the curated **remote**,
+never a virtual — so the repo in the client URL is a lookup key, not the answer.
+Capture it as `client_repo` (from the failing URL, e.g. `/api/npm//…`); a
+virtual (some managers, e.g. Go, require one) is **never** the curated remote key,
+so do **not** pre-filter the audit on it. Read the candidate remotes from the
+returned events (`curated_repo_name` for CVS, `curated_repository_name` for the
+download audit) within the failure window.
+
+When `client_repo` is a virtual, or more than one remote has a contemporaneous
+event, resolve which remote in this order — **never infer from names**
+(`npm-virtual` does not imply `npm-remote`):
+
+1. **Artifactory join (preferred).** Call `artifactory_repositories_get(client_repo)`
+ and intersect its member repositories with the candidate remotes from the audit.
+ A single match is the remote.
+2. **Ask (fallback).** If that tool is not accessible (e.g. a 403/permission error),
+ or the intersection is empty or still more than one, **ask the user to confirm
+ the underlying remote** they resolved through. Do not guess.
+
+Until the remote is proven by (1) or (2), do **not** put a repo in the RCA
+**Repository** field — and never the virtual name — and cap that field at
+**Medium** confidence. The *what* (policy/verdict) can still be **High** when every
+candidate remote carries the same verdict, even while the Repository stays Medium.
+
+**Window to the failure time** (from Step 0), not the 7-day default — far fewer
+day-partitions scanned on a busy tenant, and required when the failure is >7 days old.
+The survey bounds `created_at_start`/`created_at_end` are **RFC3339 strings** (e.g.
+`2026-08-18T03:33:00Z`), **not** epoch — epoch ms is only for
+`jfs_curation_get_cvs_audit_version_detail.created_at` (Gate 1 step 3); don't carry that
+format into the survey or it 400s / returns empty.
+
+- **Gate 2 (403):** the block is contemporaneous with the failure, so window it
+ **as tightly as your timestamp precision allows** — with a precise time (e.g. from
+ the client log), use only a clock-skew margin (**±15 min**, at most ±1 h); widen
+ toward ±1 day **only** when the failure time is approximate ("sometime that day").
+ A tight window also avoids sweeping in unrelated contemporaneous events (e.g. a
+ second remote's block for the same package), which is what forces the
+ remote-disambiguation step — so don't widen it past what the timestamp warrants.
+- **Gate 1 (CVS):** the evaluation happens when Artifactory (re)builds the
+ **cached** curated metadata, so it can be **hours or days before** the failed
+ resolve — its timestamp has no fixed relation to the failure clock. Do **not**
+ window symmetrically or tightly: reach **back** — set `created_at_end` to just
+ after the failure, `created_at_start` to **failure − 7 days** (one 168h window),
+ `direction=desc`, and take the **latest event at or before the failure**. Cap the
+ reach-back at 7 days; do **not** page further. If that window is empty, report
+ **"no CVS record in the 7 days before the failure"** (the cached inspection is
+ older than 7 days) — not "no CVS" outright.
+
+### Gate 1 — NOT-RESOLVABLE (CVS audit)
+
+Target the **historical** CVS evaluation — the failed install created no new row.
+The pin may be **absent** from `versions[]` because resolution stopped before it
+(see **How Curation works** above), not because it was "never evaluated".
+
+1. **Survey** with `jfs_curation_query_cvs_audit_events` (do **not** set `include_versions`
+ yet). `package_name` and `package_type` are **required** (no unscoped
+ surveys). Add `inspection_results=no_compliant_versions,compliant_version_selected`
+ (comma-separated IN — both failure buckets in one call), and use `direction=desc` with a
+ small `num_of_rows` for the latest evaluation. **Never** survey
+ `all_versions_compliant` to explain a failure (~99% of the data); if you ever
+ query it, cap the window to **≤1 day**. Set `include_total` only when you need the
+ match count or an exact `next_offset` (to gauge volume or paginate) — it adds a
+ count query, so skip it for a plain latest-event lookup.
+ Read `packages[].summary`, `inspection_results`, `latest_inspected_version`.
+2. **Drill down** on the event you care about: call again with the same
+ `package_name` + `package_type` plus `event_id=`,
+ `include_versions=true`, `num_of_rows` ≤ 10. Find the target version in
+ `packages[].versions[]` and read its `action` + blocking `policies[]`.
+3. **Explain** one version with `jfs_curation_get_cvs_audit_version_detail`
+ (`version_id=versions[].id`, `created_at=` the **event's** `created_at` in
+ **epoch milliseconds** — copy it verbatim).
+
+### Gate 2 — DOWNLOAD-BLOCKED (package-download audit)
+
+1. **Search** `jfs_curation_query_audit_events` by `package_type` + `package_name`
+ (+ `package_version` if known) over the relevant window (add `include_total=true`
+ only if you want the match count).
+2. **Explain** the blocking event with `jfs_curation_get_audit_event_policies`
+ (`event_id=data[i].id`) for the `explanation` + `remediation`. These fields can
+ come back **empty** (policy/condition names and verdict only) — that does **not**
+ lower confidence: when the audit's policy/condition agrees with the client's
+ block notice, the finding is still **High**. Fall back to the Step 4 generic
+ package-blocked remediation and note "no remediation text from the API".
+
+### Optional live compliance check — only when it earns its place
+
+`jfs_curation_check_remote_package_compliance` is **not** a step in the RCA. It is a live,
+current-policy verdict that runs a fresh evaluation (see the
+timeline note above), so it does not tell you what happened at the time of the
+failure and it can muddy a clean audit-based conclusion. Call it **only** when:
+
+- the user asks "what can I install instead?" **and** the audit did not already
+ surface allowed/suggested versions (the client's own block notice often does);
+- you specifically need a **current-vs-past drift** check (e.g. confirm the block
+ still stands under today's policy); or
+- you are about to **change a dependency or recommend a pin** and must validate
+ the candidate live.
+
+Otherwise skip it. When you do call it, report `allowed_versions` and the verdict
+as **current state**, kept separate from the audit-based root cause.
+
+## Step 3 — Interpret
+
+Each tool's own description lists its traps; read them before concluding. The
+ones that most often flip an RCA:
+
+- **Empty CVS result → first rule out "curation not enabled".** The audit tools
+ are **not** feature-gated (they need only `VIEW_POLICIES`), so a disabled feature
+ writes no rows and returns `200` with empty `data` — indistinguishable from
+ "nothing blocked".
+- **For a 403, the download audit is the authority; CVS coverage is partial.**
+ Because resolution stops at the first approved version (see **How Curation
+ works** above), absence from a CVS `versions[]` does **not** mean a version
+ was unevaluated, out of the index, or approved — and it must **never** weaken
+ or override a `jfs_curation_query_audit_events` finding. Versions CVS did not
+ (re)select stay in the index and are enforced by the download flow — that is
+ the 403.
+- **Bad `inspection_results`/`reason` tokens are silently ignored** → a 200
+ **unfiltered** result. Verify returned rows carry the value you filtered on.
+- **Do not invent or generalize a mechanism.** The audit reports the *decision*
+ on a version (blocked/compliant + policy + reason), not why the client listing
+ contains or omits it. Never infer a general rule from a few events — e.g.
+ "CVE policies enforce at download, ban-label at resolve" from two rows. A
+ policy's category does not determine listing membership or enforcement stage;
+ read each event's own fields. If the behavior in question (listing/exposure,
+ enforcement stage) is not stated in the audit rows or the CVS doc, report it as
+ **not determined** — do not synthesize an explanation to fit the data points.
+
+## Before Step 4 — output gate
+
+No conclusion (even hedged) without these tool calls made and reflected in
+Evidence:
+
+- Existence in question (NOT-RESOLVABLE, or any 403 with **nothing**
+ resolvable): `catalog_packages_list_versions` (the #1 RULE).
+- A 403 on a version the client already **resolved/listed**: existence is
+ proven by that successful listing — the catalog call isn't required; go
+ straight to the download audit.
+- NOT-RESOLVABLE, or any 403 with nothing resolvable: `jfs_curation_query_cvs_audit_events`.
+- Any resolvable/listed 403 (DOWNLOAD-BLOCKED, PACKAGE-BLOCKED on a still-listed
+ name, TRANSITIVE): `jfs_curation_query_audit_events`.
+- WAIVER-NOT-APPLIED: + `jfs_curation_query_waiver_requests`.
+
+Missing/unavailable tool ≠ license to guess — use Insufficient evidence instead.
+
+## Step 4 — Conclude the RCA
+
+Map the evidence to a root cause. Report the **first** row that matches.
+
+| Evidence | Root cause | Remediation |
+|---|---|---|
+| Version not in the public catalog (for a package type the catalog supports) and no CVS record | **Not curation** — version does not exist | Correct the version; nothing to unblock. |
+| ETARGET on a curated registry; version present in the **public catalog** but absent from the **curated** listing; CVS history shows it filtered / not selected | **Curated-index filtering** — CVS omitted the version from the curated listing, so the client can't resolve it (fails at resolve, no 403, no fresh audit row) | Use a version present in the curated listing (one CVS selected / `allowed_versions`); or waiver and re-index. |
+| CVS event: target version `action=blocked` + policy P | **CVS non-compliant** — CVS found the version non-compliant under policy P (this is the decision; it does not by itself explain listing membership) | Apply the version's `remediation` from `jfs_curation_get_cvs_audit_version_detail`; pin to an `action=approved` version from the same event (or `allowed_versions`). For waiver applicability under CVS, consult the CVS doc — do not assume. |
+| CVS event: `inspection_results=no_compliant_versions` | **No compliant version found** in the audit | Check `jfs_curation_check_remote_package_compliance` for any allowed version; otherwise raise a waiver (see the CVS doc for applicability). Do not recommend changing the policy. |
+| Package-audit event: version `action=blocked` + policy P | **Download-time block** — the 403 comes from policy P at download | Apply the policy's `remediation` from `jfs_curation_get_audit_event_policies`; use an allowed version; or raise a waiver. |
+| Locked/older pin 403s at download; the recent CVS record selected a *newer* compliant version and has no (or only a stale/earlier) record for the pin | **Pin bypasses CVS selection** — CVS returned a newer compliant version and did not (re)select the pinned one, so the pin stays in the index and is enforced by the download-time policy (the 403). The CVS absence is expected, **not** evidence of anything | Use the compliant version CVS selected (or `allowed_versions`), or waiver the pin. The download audit + `jfs_curation_get_audit_event_policies` is the authority for the block. |
+| Block covers the package itself, not a single version | **Package-blocked** — a policy blocks the whole package | Use a different package, or waiver the package if policy allows (see the block's `remediation`). |
+| The blocked package name ≠ the one the user installed | **Transitive dependency block** — a dependency was blocked | Re-run the RCA on the *blocked* dependency; remediate it (pin/override the transitive) rather than the top-level package. |
+| Approved waiver exists (`jfs_curation_query_waiver_requests`) yet the block persists | **Waiver not effective** — wrong scope/repo, expired, not yet applied, or the index has not refreshed | Verify the waiver's scope, status, and expiry against the failing repo/version. If it's valid, the index likely hasn't re-evaluated — no MCP tool here can force this; tell the **user** to reindex / zap the cache on the curated remote and retry after they do. |
+| `reason = "Pending Catalog update"` | **Transient / pending** — the verdict awaits catalog data | Retry after the catalog updates; treat as not-final, not a hard block. |
+| CVS `approved` **and** a download-time block exist for the same version | **Divergence** — the version is CVS-approved yet blocked at download; causes include a policy change between evaluation and download, or a different policy set on the download path (see the CVS doc) | Confirm current status via `jfs_curation_check_remote_package_compliance`; use a curation-compliant version or raise a waiver. Do not recommend changing the policy. |
+| No CVS **and** no package-audit record, version exists upstream | **Likely not curation** (write lag, CVS disabled or not applicable, no scoped policy, unsupported type) | For the CVS audit, confirm the window already reached the full 7 days (Gate 1's cap) — do not widen past 168h or drop `package_name`/`package_type`. For the download audit, you may widen using the multi-window probe strategy and drop optional filters, but keep `package_type`. If still empty at those caps, investigate outside curation. |
+
+**Failure Reason lookup** — map the matched root cause above to the one **Failure
+Reason** you print below (several root causes map to the same label):
+
+- **Version not available — removed by curation:** Curated-index filtering; CVS
+ non-compliant; No compliant version found.
+- **Download blocked by a curation policy:** Download-time block; Pin bypasses CVS
+ selection; Divergence (CVS-approved yet blocked at download); Waiver not effective
+ (add "waiver not applied" in Failure Details).
+- **Whole package blocked by curation:** Package-blocked.
+- **Curation check still pending:** Transient / pending ("Pending Catalog update").
+- **Not caused by curation:** version does not exist; likely-not-curation (remote
+ sync / cache / no scoped policy / unsupported type); a client-side date/version
+ filter (`min-release-age` / `before`, registry override, CI flag) explains it.
+- **No curation record found:** no CVS and no package-audit record in window.
+- **Transitive** is not its own label — re-run the RCA on the *blocked dependency*
+ and use whichever Failure Reason its own root-cause row maps to.
+
+**If the root cause is "Not caused by curation" or "No curation record
+found," skip the table below — use the "Not a curation problem" block after
+Insufficient evidence instead.** The table's fields are curation-specific
+(Repository, Audit event links) and don't apply to a non-curation cause.
+
+For the other four Failure Reasons, present the conclusion as a **table** —
+render exactly these rows, in this order. Frame every value in **curation**
+terms (this is a curation RCA, not a generic one):
+
+| Field | Value |
+| --- | --- |
+| **Confidence** | **High** / **Medium** / **Low** — ``. Report **Low** when a step returned an ambiguous empty result rather than a positive record; the *what* can stay **High** while **Repository** is **Medium** until the virtual→remote join is proven (Step 2). |
+| **Package** | `` |
+| **Version** | `` (or `all` / the failing pin) |
+| **Failure Reason** | one of: **Version not available — removed by curation** · **Download blocked by a curation policy** · **Whole package blocked by curation** · **Curation check still pending** · **Not caused by curation** · **No curation record found** |
+| **Failure Details** | Failed due to curation policy `` — `` (e.g. "Failed due to curation policy `cve` — cve more than 7 and Half") |
+| **Repository** | the remote from whichever audit you used — `` (CVS) or `` (download audit) |
+| **Failure Timestamp** | the exact failure timestamp, e.g. `2026-08-18T03:33:00Z` |
+| **Audit event links** | the Curation **UI audit page** — `/ui/package-curation/audit` (`` = the platform host from the failing URL / connected tenant) — plus the event(s) that locate the record there: CVS `jfs_curation_query_cvs_audit_events` event `id=` (`created_at=`), download `jfs_curation_query_audit_events` event `id=` |
+
+Then, **below the table, as bullet points** (not table rows):
+
+- **Ruled out:** the non-curation causes you checked — auth / permissions, repo or remote config, a version that never existed.
+- **Evidence:** discrete `key=value` tokens on one line, each individually searchable/copyable — e.g. `tool=jfs_curation_query_cvs_audit_events` `event_id=1290` `created_at=1786000000000` `action=blocked` `policy=cve`. Emit only the tokens you have.
+- **Remediation** *(High confidence only — omit entirely at Medium/Low):* use a curation-**compliant** version (the one CVS selected, or an `allowed_versions` entry), or **raise a waiver** scoped to the curated remote(s) you resolve through. **Never** propose changing or loosening a curation policy (e.g. "lower the CVE threshold in policy `cve`") — a policy change is out of scope for this RCA; the waiver is the only sanctioned exception.
+
+**Insufficient evidence** *(use instead of the table above when the output
+gate stopped you)*: state **Cannot conclude** — name the missing tool call(s)
+or ambiguity (tenant, repo, tool unavailable) — and what's needed to proceed.
+Never fill the table with a guess to avoid this.
+
+**Not a curation problem** *(use instead of the table above for "Not caused
+by curation" / "No curation record found")*: state the actual cause and the
+evidence that rules out curation — e.g. `npm config get before` /
+`min-release-age` returned a value matching the error, or no CVS/download
+record exists in a widened window. No curation-specific fields — Repository
+and Audit event links don't apply here.
+
+**Consistency gate — before you finalize.** Re-check the conclusion against
+**every** event you retrieved, not just the ones that fit. If any row contradicts
+it, the conclusion is wrong — revise it to fit all the data. A conclusion that
+requires an invented mechanism to reconcile two rows is a red flag: prefer "not
+determined from the audit (see the CVS doc)" over a synthesized rule.
+
+## Worked examples (illustrative)
+
+Two end-to-end illustrations. They are **not** the supported set — apply the same
+method (Step 0 → Step 4) to any curation symptom, including the classes above
+that have no example here.
+
+### npm `ETARGET`
+
+> `No matching version found for @vitest/eslint-plugin@1.6.27`
+
+`ETARGET` → NOT-RESOLVABLE (Gate 1). **First** apply Step 1's two reads — do
+**not** trust `npm view` against the curated registry (that's the filtered
+view). (1) Existence via `catalog_packages_list_versions` (the only approved
+oracle — do not query public npm directly) — absent there → "version does not
+exist", report the nearest catalog versions, stop. (2) **Always** survey the CVS audit
+`jfs_curation_query_cvs_audit_events(package_name, package_type="npm")` with
+`direction=desc` for the latest **historical** evaluation (this failed install minted
+no row). Drill down (`event_id`, `include_versions=true`) and look for `1.6.27`:
+`action=blocked` → CVS found it non-compliant; combined with the client's ETARGET
+(the pin is absent from the curated listing), that's curated-index filtering
+— get `explanation`/`remediation` via `jfs_curation_get_cvs_audit_version_detail` and pin to
+the version CVS selected (check the CVS doc before assuming a waiver applies);
+**absent** from `versions[]` → likely CVS stopped after selecting its compliant
+version(s) and never recorded this pin (Step 3), not "never evaluated". Conclude
+per Step 4.
+
+### 403 on a version that `npm view` lists
+
+> `npm view` lists `axios@1.15.1`, but `npm install axios@1.15.1` fails with
+> 403 "blocked by JFrog Packages Curation Service" (policy: CVE). User claim:
+> "it's listed, so CVS approved it — why does download fail?"
+
+403 on a **resolvable** version → **DOWNLOAD-BLOCKED (Gate 2)**. The version
+is already listed, so resolvability — and therefore existence, for this
+purpose — is proven by the client's own successful listing; Step 1's
+catalog/CVS existence check is for when resolvability itself is in question,
+so it doesn't apply here. Then Gate 2 **alone**:
+`jfs_curation_query_audit_events(package_type="npm", package_name="axios", package_version="1.15.1")`
+returns the blocked download event with the policy and CVEs — that **is** the
+root cause. If the user wants the remediation prose, add
+`jfs_curation_get_audit_event_policies(event_id)`.
+
+Answer the misconception without a compliance call: a `npm view` listing is what
+the client resolves against, **not** a per-version CVS approval — a locked/exact
+pin to a non-compliant version is refused at download with no fallback (fetch the
+CVS doc to cite this). You do **not** need `jfs_curation_check_remote_package_compliance` to
+conclude this, and you should not query the CVS audit to "prove approval" — CVS
+audit answering `all_versions_compliant` is a *different timeline and mechanism*
+and will confuse the 403 RCA. Only if the user then asks "what should I install
+instead?" and no allowed versions are already in hand does a compliance check (or
+the client's own suggested-versions notice) earn its place.
+
+## Gotchas
+
+- **MCP tools only.** Never substitute a `jf` command or raw HTTP for any
+ `jfs_curation_*` call. If a curation tool specifically is unavailable while
+ other JFrog MCP tools work, that's a version/entitlement gap, not a
+ disconnected server — see Preflight.
+- **Disjoint id-spaces / repo-name spelling.** CVS event ids go to
+ `jfs_curation_get_cvs_audit_version_detail`, package-audit ids to
+ `jfs_curation_get_audit_event_policies` (crossing them returns nothing). CVS uses
+ `curated_repo_name`; package audit uses `curated_repository_name`.
+- **Don't over-attribute to curation.** The most common `ETARGET` cause is a
+ version that never existed, or a client-side config filter — always complete
+ Step 1 before blaming curation.
diff --git a/skills/jfrog-reference-architecture/SKILL.md b/skills/jfrog-reference-architecture/SKILL.md
index 394d000..b331b24 100644
--- a/skills/jfrog-reference-architecture/SKILL.md
+++ b/skills/jfrog-reference-architecture/SKILL.md
@@ -11,7 +11,7 @@ description: >-
Do NOT use for artifact search or download, repository or permission admin,
CVE or vulnerability lookups, live jf CLI operations against their instance,
or package curation — use the jfrog base skill or
- jfrog-package-safety-and-download instead.
+ jfrog-package-curation instead.
compatibility: >-
Requires outbound HTTPS (WebFetch or equivalent). Request full_network when
the runtime blocks fetches. No jf CLI or configured JFrog instance required
diff --git a/skills/jfrog/SKILL.md b/skills/jfrog/SKILL.md
index 902d453..460d111 100644
--- a/skills/jfrog/SKILL.md
+++ b/skills/jfrog/SKILL.md
@@ -19,7 +19,7 @@ compatibility: >-
(CLI) and Tier 3 (jf api) operations; without it, only MCP (Tier 1) is available.
metadata:
role: base
- version: "0.30.0"
+ version: "0.31.1"
---
# JFrog Skill
diff --git a/skills/jfrog/references/INDEX.md b/skills/jfrog/references/INDEX.md
index c005c53..b40a61f 100644
--- a/skills/jfrog/references/INDEX.md
+++ b/skills/jfrog/references/INDEX.md
@@ -50,7 +50,9 @@ for one operation.
- **Watches, policies, violations, components, or vulnerability scanning (concepts)**: read `references/xray-entities.md`
- **Exposures scanning results (secrets, IaC, service misconfigurations, application security risks)**: read `references/xray-entities.md` § Exposures (Advanced Security)
-- **Curation audit events (approved/blocked packages, dry-run policy evaluations, curation export)**: read `references/xray-entities.md` § Curation audit events
+- **Package install/resolve/download failure blamed on Curation — 403 from a curated remote, `ETARGET` / "no matching version", a version missing from the index, "why was package X blocked?", or a waiver after a block**: load the `jfrog-package-curation` workflow skill and follow its "Troubleshoot a failure" section (index-time CVS audit + download-time package audit → root cause + remediation, MCP only). Its "Check & download" section instead handles proactive "is this package safe to download?".
+- **Is this package safe / allowed / curated? Downloading npm, Maven, PyPI, Go, or similar packages via JFrog**: load the `jfrog-package-curation` workflow skill and follow its "Check & download" section.
+- **Curation audit events (approved/blocked packages, dry-run policy evaluations, curation export) without a failure to triage**: read `references/xray-entities.md` § Curation audit events
## Release lifecycle & distribution
@@ -60,7 +62,7 @@ for one operation.
## Catalog
- **Public or custom catalog, package metadata, vulnerability advisories, licenses, OpenSSF, or MCP services (concepts)**: if the JFrog MCP server exposes a catalog tool, prefer it for single-package lookups. For deeper queries, read `references/catalog-entities.md`
-- **CVE details, vulnerability lookup by CVE ID, or severity/affected-packages/fix-versions for a specific CVE**: prefer an MCP vulnerability-lookup tool if the JFrog MCP server exposes one. Otherwise read `references/onemodel-query-examples.md` § *Public security domain* for the `searchVulnerabilities` query shape — this is self-contained; do not load the `jfrog-package-safety-and-download` skill for pure CVE lookups
+- **CVE details, vulnerability lookup by CVE ID, or severity/affected-packages/fix-versions for a specific CVE**: prefer an MCP vulnerability-lookup tool if the JFrog MCP server exposes one. Otherwise read `references/onemodel-query-examples.md` § *Public security domain* for the `searchVulnerabilities` query shape — this is self-contained; do not load the `jfrog-package-curation` skill for pure CVE lookups
## OneModel (GraphQL)
From da9d997a384dc66e7477a4505866847675c69a02 Mon Sep 17 00:00:00 2001
From: Michael Sverdlov
Date: Thu, 27 Aug 2026 17:51:51 +0300
Subject: [PATCH 2/2] Align plugin docs with renamed v0.31.1 skill directories.
---
README.md | 6 +++---
VENDOR.md | 4 ++--
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/README.md b/README.md
index 5bf79a9..e7a1bbd 100644
--- a/README.md
+++ b/README.md
@@ -11,7 +11,7 @@ The JFrog plugin provides the following capabilities, grouped by component:
| --- | --- | --- |
| **MCP** | JFrog MCP server | Bundled `jfrog` MCP server ([`.mcp.json`](.mcp.json)) at `https:///mcp`; this server signs in via OAuth (`codex mcp login jfrog`), so it needs no API key. |
| **Skill** | JFrog Platform | Interact with Artifactory repositories, builds, permissions, users, access tokens, projects, release bundles, and platform administration via the JFrog CLI and REST/GraphQL APIs. Also covers security audits, CVE lookups, and Advanced Security exposure queries. |
-| **Skill** | Package safety & download | Check whether npm, Maven, PyPI, Go, and other packages are safe, curated, or allowed, then download them through Artifactory remote caches or curation-aware package managers. |
+| **Skill** | Package curation | Check whether npm, Maven, PyPI, Go, and other packages are safe, curated, or allowed, then download them through Artifactory remote caches or curation-aware package managers. |
| **Skill** | Agent Guard | Codex manages MCPs through the JFrog Agent Guard. Through the Agent Guard you can discover, install, configure, update, and remove MCP servers from the JFrog AI Catalog approved for your project, and authenticate to remote HTTP MCPs via OAuth, API key, or bearer token. |
---
@@ -23,7 +23,7 @@ Before installing, make sure you have:
- **JFrog host URL and access token** — Your JFrog platform URL and a valid access token.
- **OpenAI Codex** — Installed, with plugin support (`codex plugin` CLI commands available).
- **Node.js** (≥ 18) — with `npx` on your `PATH` (used by the Agent Guard).
-- **Skill runtime requirements** — `jf` CLI, `jq`, and `curl` on `PATH`, plus a configured JFrog instance. For the minimum versions, see the upstream skills [`Requirements`](https://github.com/jfrog/jfrog-skills/blob/v0.22.0/README.md#requirements). Configure the CLI with `jf config add` — see [Authentication](#authentication).
+- **Skill runtime requirements** — `jf` CLI, `jq`, and `curl` on `PATH`, plus a configured JFrog instance. For the minimum versions, see the upstream skills [`Requirements`](https://github.com/jfrog/jfrog-skills/blob/v0.31.1/README.md#requirements). Configure the CLI with `jf config add` — see [Authentication](#authentication).
- **JFrog AI Catalog** (optional) — If you want to use the Agent Guard feature, your JFrog subscription needs to include the AI Catalog entitlement. Contact your JFrog account team if you're unsure whether it's enabled.
- **JFrog CLI ≥ 2.105.0** (optional) — If you want the Agent Guard to auto-resolve the credentials/server ID from the JFrog CLI configuration.
- **JFrog project** (optional) — If you want to use the Agent Guard feature.
@@ -106,7 +106,7 @@ Examples are grouped by capability.
| "Create a scoped access token for CI." | Creates an access token with the requested scope. |
| "Promote this release bundle to production." | Uses Lifecycle / Distribution APIs to promote the bundle. |
-### Package safety & download skill
+### Package curation skill
| Ask the agent… | What happens |
| --- | --- |
diff --git a/VENDOR.md b/VENDOR.md
index b2dece3..7da94b6 100644
--- a/VENDOR.md
+++ b/VENDOR.md
@@ -8,8 +8,8 @@ The skill packages under `skills/` are vendored from **[jfrog/jfrog-skills](http
| **Pinned release** | see `pin` in [`scripts/sync-skills-vendor.json`](scripts/sync-skills-vendor.json) |
Included skill directories (as of the pinned release): `jfrog/`,
-`jfrog-package-safety-and-download/`, `jfrog-setup-package-managers/`,
-`jfrog-ai-catalog-skills/`, `jfrog-mcp-management/` (JFrog Agent Guard MCP
+`jfrog-package-curation/`, `jfrog-setup-package-managers/`,
+`jfrog-ai-catalog/`, `jfrog-mcp-management/` (JFrog Agent Guard MCP
management, including the Codex harness), and `jfrog-reference-architecture/`.
## Refreshing