From cf9c3fb5ec59db2b2e0bbc8e7cd7f6d352d4ab7f Mon Sep 17 00:00:00 2001 From: yanivt-jfrog <209457562+yanivt-jfrog@users.noreply.github.com> Date: Thu, 11 Jun 2026 14:29:31 +0000 Subject: [PATCH] chore: sync skills to v0.5.0 --- .claude-plugin/plugin.json | 2 +- .../SKILL.md | 22 +- skills/jfrog/.gitignore | 1 + skills/jfrog/SKILL.md | 445 ++++++++---------- .../references/artifactory-operations.md | 112 +++-- ...eral-bulk-operations-and-agent-patterns.md | 14 +- .../references/general-parallel-execution.md | 2 + .../references/general-use-case-hints.md | 3 +- skills/jfrog/references/jfrog-login-flow.md | 56 +-- skills/jfrog/references/onemodel-graphql.md | 30 +- skills/jfrog/references/projects-api.md | 6 +- skills/jfrog/scripts/check-environment.sh | 86 +--- .../scripts/jfrog-login-save-credentials.sh | 12 +- 13 files changed, 356 insertions(+), 435 deletions(-) create mode 100644 skills/jfrog/.gitignore diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index 67d6122..970872b 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -2,7 +2,7 @@ "name": "jfrog", "displayName": "JFrog", "description": "Official JFrog plugin. Connect Claude Code to JFrog to manage, secure, and govern your software supply chain. Give agents the context to build secure, compliant software.", - "version": "0.2.5", + "version": "0.2.6", "author": { "name": "JFrog Ltd.", "email": "devrel@jfrog.com", diff --git a/skills/jfrog-package-safety-and-download/SKILL.md b/skills/jfrog-package-safety-and-download/SKILL.md index 598318a..e510898 100644 --- a/skills/jfrog-package-safety-and-download/SKILL.md +++ b/skills/jfrog-package-safety-and-download/SKILL.md @@ -2,7 +2,7 @@ name: jfrog-package-safety-and-download description: >- Check JFrog Public Catalog and stored packages for a version, interpret - catalog security signals, and download through Artifactory (JFrog Platform + 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. @@ -37,12 +37,12 @@ When to read this file: flowchart TD A[User requests package check / download] --> B{Package in Public Catalog?} B -->|Yes| C[Get latest version from Catalog] - B -->|No| D{Package in JFrog Platform Stored Packages?} + B -->|No| D{Package in Jfrog Platform Stored Packages?} D -->|Yes| E[Get latest version from Stored Packages] D -->|No| F[Package not found — stop] - C --> G{Latest version in JFrog Platform?} + C --> G{Latest version in Jfrog Platform?} E --> G - G -->|Yes| H[Safe — download from JFrog Platform] + G -->|Yes| H[Safe — download from Jfrog Platform] G -->|No| I{Curation entitled?} I -->|Yes| J[Check curation policy via API] I -->|No| K[Download via remote repo] @@ -60,9 +60,9 @@ reduce total latency: parallel. Use whichever returns data; if the Public Catalog returns a hit, prefer its `latestVersion` for Step 2. - **Step 3 + Step 5**: After determining the version, query stored package - versions (JFrog Platform check) and curation entitlement + versions (Jfrog Platform check) and curation entitlement (`/api/system/version`) in parallel. Both are independent reads — the - curation result is needed immediately if the JFrog Platform check returns + curation result is needed immediately if the Jfrog Platform check returns empty. When issuing parallel Shell calls, each `jf api` call authenticates @@ -104,9 +104,9 @@ type they mean. | Source | Version field | |--------|--------------| | Public Catalog | `latestVersion.version` (object selection required) | -| JFrog Platform Stored Packages | `latestVersionName` on `StoredPackage`, or highest entry from `versionsConnection` | +| Jfrog Platform Stored Packages | `latestVersionName` on `StoredPackage`, or highest entry from `versionsConnection` | -## Step 3: Check if package + latest version exists in JFrog Platform +## Step 3: Check if package + latest version exists in Jfrog Platform Query stored package versions using `storedPackages.searchPackageVersions` with a `hasPackageWith` filter (see `../jfrog/references/onemodel-query-examples.md` @@ -117,11 +117,11 @@ details (`repositoryKey`, `repositoryType`, `leadArtifactPath`). Execute the query through `jf api` (see `../jfrog/references/onemodel-graphql.md` for the invocation pattern). -- **Found with locations** → package is in the JFrog Platform. Report as **safe to +- **Found with locations** → package is in the Jfrog Platform. Report as **safe to download**. Proceed to Step 4. - **Not found** → proceed to Step 5. -## Step 4: Download from JFrog Platform +## Step 4: Download from Jfrog Platform Use the location info from Step 3. Binary artifact downloads go through `jf rt dl` — **not** `jf api`. `jf api` is the unified entry point for the @@ -224,7 +224,7 @@ fi ## Step 6b: Download without curation -When curation is not entitled and the package is not in the JFrog Platform, +When curation is not entitled and the package is not in the Jfrog Platform, download directly through a remote repo. 1. **Find a remote repo** of the right package type: diff --git a/skills/jfrog/.gitignore b/skills/jfrog/.gitignore new file mode 100644 index 0000000..3215b96 --- /dev/null +++ b/skills/jfrog/.gitignore @@ -0,0 +1 @@ +local-cache/ diff --git a/skills/jfrog/SKILL.md b/skills/jfrog/SKILL.md index 67e0391..6b95e18 100644 --- a/skills/jfrog/SKILL.md +++ b/skills/jfrog/SKILL.md @@ -1,7 +1,8 @@ --- name: jfrog +version: "0.5.0" description: >- - Interact with the JFrog Platform via the JFrog CLI, JFrog MCP server and REST/GraphQL APIs. + Interact with the JFrog Platform via the JFrog CLI and REST/GraphQL APIs. Use this skill when the user wants to manage Artifactory repositories, upload or download artifacts, manage builds, configure permissions, manage users and groups, work with access tokens, configure JFrog CLI @@ -17,36 +18,18 @@ compatibility: >- Requires jq on PATH. metadata: role: base - version: "0.11.0" --- # JFrog Skill The foundational skill for all JFrog agent interactions. Covers JFrog Platform concepts, `jf` CLI setup and authentication, and intent routing to workflow skills. -Interact with the JFrog Platform through three tool tiers — see -[Tool selection strategy](#tool-selection-strategy). In code examples below, +Interact with the JFrog Platform through the JFrog CLI (`jf`) and, where the +CLI falls short, through REST APIs and GraphQL. In code examples below, `` refers to this skill's directory and is resolved automatically by the agent. If the agent does not resolve it, determine the path by locating this SKILL.md file and using its parent directory. -## Tool selection strategy - -Try the tiers in order; move to the next only when the current does not -cover the operation or fails: - -1. **JFrog MCP tools** (preferred): `CallMcpTool` against the JFrog MCP - server. Discover available tools from the server's tool list; never - guess tool names. -2. **`jf` CLI subcommands** (fallback): dedicated commands such as - `jf rt upload`, `jf rt dl`, `jf build-publish`. -3. **`jf api`** (last resort): REST/GraphQL endpoints with no dedicated - subcommand. Validate the path first — see rule 6 in - [Cautious execution](#cautious-execution). - -MCP and the CLI may use different token scopes. If one tier returns 403, -try the alternate tier before reporting the operation blocked. - ## Prerequisites The following tools must be available on `PATH`: @@ -55,81 +38,77 @@ The following tools must be available on `PATH`: |------|---------| | `jq` | JSON parsing of CLI and API output | -All JFrog HTTP traffic from Tiers 2 and 3 goes through the `jf` CLI itself +All HTTP traffic to JFrog Platform APIs goes through the `jf` CLI itself (`jf api`, see [Invoking platform APIs with `jf api`](#invoking-platform-apis-with-jf-api) below) — no standalone `curl` is required for any JFrog interaction. -**Runtime permission for JFrog calls.** All `jf` calls that touch the network -need an outbound-HTTPS escalation from the agent runtime. The `~/.jfrog/` -credential save (`jf config add` during login) additionally needs a -filesystem-write escalation. - -| Runtime | Network | Network + `~/.jfrog/` write | -| ----------- | --------------------------------------------- | ------------------------------- | -| Cursor | `required_permissions: ["full_network"]` | `required_permissions: ["all"]` | -| Claude Code | `allowed-tools: Bash(jf:*)` + host allowlist | same + filesystem allowlist | -| Other | Configure at the runtime/sandbox layer | same | - -If `jf` exits 1 with empty output, the runtime's network gate is the first -thing to check — re-run with the appropriate escalation above. - ## Environment check -MCP (Tier 1) operations do not require this check and can proceed immediately. -Before your first Tier 2 or Tier 3 (`jf`) operation in a session, run the -environment check and **remember its stdout** as `` for the rest of the -session: +Before your first JFrog operation in a session, run the environment check. +It verifies the CLI is installed, checks for updates, and exports +`JFROG_CLI_USER_AGENT` so every outbound request is identifiable: ```bash -bash /scripts/check-environment.sh -# stdout (one line): jfrog-skills/ [(tool=; model=)] jfrog-cli-go/ -# stderr: JSON state (cached 24h at ${JFROG_CLI_HOME_DIR:-$HOME/.jfrog}/skills-cache/jfrog-skill-state.json) +eval "$(JFROG_SKILL_MODEL="" bash /scripts/check-environment.sh)" ``` -Pass the precise underlying-model slug with version: `opus-4.7`, -`sonnet-4.5`, `gpt-5-codex`, `gemini-2.5-pro`, `composer-2-fast`. Cursor's -Composer product slug **is** the canonical id — use it as-is. Do **not** -pass harness/role names (`subagent`, `agent`, `assistant`) or bare family -names (`claude`, `gpt`); subagents inherit the parent's slug. If genuinely -unknown, pass `unknown`. - -### Export `JFROG_CLI_USER_AGENT` once per bash invocation +Set `JFROG_SKILL_MODEL` to the precise slug of the underlying LLM, with +version (e.g. `opus-4.7`, `sonnet-4.5`, `gpt-5-codex`, `gemini-2.5-pro`). +**Do not** use harness/role names like `subagent`, `cursor-agent`, `agent`, +`assistant`, or a family without a version (`claude`, `gpt`). Subagents pass +through the parent's slug. If genuinely unknown, use `unknown`. -At the top of every bash invocation that runs `jf`, export `` once; -all `jf` calls in that invocation pick it up: +The `eval` is required — the script outputs +`export JFROG_CLI_USER_AGENT='model/ jfrog-skills/ jfrog-cli-go/'` +on stdout. The JFrog CLI picks this up natively and injects it as the +`User-Agent` header on every HTTP request. JSON state is printed to stderr +for informational purposes (also written to the cache file). -```bash -export JFROG_CLI_USER_AGENT='' -jf config show -jf api /artifactory/api/system/version -``` +The script uses a 24-hour cache at `/local-cache/jfrog-skill-state.json`. If the +cache is fresh, it returns immediately. If stale or missing, it checks whether +`jf` is installed, its version, and whether a newer version is available. -Do **not** repeat the assignment per `jf` call (`JFROG_CLI_USER_AGENT='' jf …` -on every line). Examples elsewhere in this skill and in `references/*.md` -omit the export for readability — the rule is global. When launching a -subagent, pass `` in its prompt; subagents do not re-run the script. +- Exit 0: cache is fresh, CLI is ready — proceed +- Exit 1: cache was stale and has been refreshed, CLI is ready — proceed +- Exit 2: `jf` is not installed — **STOP** (see below) +- Exit 3: `jf` is installed but below the minimum version required by this skill (the script prints the minimum and the detected version to stderr) — **STOP** (see below) -| Exit | Meaning | -|------|---------| -| 0 | Cache fresh — CLI ready (Tiers 2 and 3 available), proceed | -| 1 | Cache refreshed — CLI ready (Tiers 2 and 3 available), proceed | -| 2 | `jf` not installed — Tiers 2 and 3 unavailable; only MCP (Tier 1) remains | -| 3 | `jf` below minimum version — Tiers 2 and 3 unavailable; only MCP (Tier 1) remains | +Bypass the cache only when the user explicitly asks to install, upgrade, or +reconfigure the CLI. -Exit 2 or 3 is not a fatal error. Attempt to install or upgrade the CLI -(see `references/jfrog-cli-install-upgrade.md`). If installation succeeds, -re-run the environment check. If installation is not possible (no permissions, -restricted environment), proceed with MCP (Tier 1) only. Both `jf` CLI commands -(Tier 2) and `jf api` (Tier 3) require a working `jf` installation. +**On exit 2 or 3, stop and ask the user to install or upgrade.** Do not work +around it with `jf rt curl`, raw `curl`, or other fallbacks — see +`references/jfrog-cli-install-upgrade.md`. ### JSON parsing (`jq`) Use **`jq`** for all JSON parsing of CLI and API output (pipes, `-r`, filters). -## `~/.jfrog/skills-cache/` — allowed files only +## Network permissions + +JFrog servers are not on the default sandbox network allowlist. Every Shell +call that contacts a JFrog server requires +`required_permissions: ["full_network"]`. -`${JFROG_CLI_HOME_DIR:-$HOME/.jfrog}/skills-cache/` is **not** a general scratch -or temp directory. Use it **only** for these two artifacts: +Without this permission, commands fail silently: `jf` exits with code 1 and +empty output, and downstream JSON parsing crashes. All JFrog operations that +touch the network need this permission. + +### Agent execution environments + +`check-environment.sh` does **not** call your JFrog server, but it may make an +outbound request to `releases.jfrog.io` for version checking and may **write** +`/local-cache/jfrog-skill-state.json` when the cache is stale. In a **sandboxed** +agent environment, **`full_network` alone may not suffice**: if the workspace +cannot be written, the check can fail before any JFrog call. Request +permissions that allow writing `/local-cache` (or run +outside a restrictive sandbox) when you see filesystem errors from the +environment check. + +### `local-cache/` — allowed files only + +`/local-cache/` is **not** a general scratch or temp directory. Use +it **only** for these two artifacts: 1. **`jfrog-skill-state.json`** — written by `scripts/check-environment.sh` (24-hour CLI check cache). @@ -137,157 +116,50 @@ or temp directory. Use it **only** for these two artifacts: schema (see `references/onemodel-graphql.md`). **Do not** save HTTP response bodies, GraphQL query results, ad-hoc JSON, reports, -or any other temporary files under `skills-cache/`. Write those to a host temp +or any other temporary files under `local-cache/`. Write those to a host temp path instead (for example `/tmp/-$$.json` or `mktemp -d`), echo the path when a follow-up Shell step must read the file — same pattern as *Preserving command output* below. -## Cautious execution - -Do not run commands speculatively. Before executing any JFrog CLI command, -MCP tool call, or API call: - -1. Confirm the operation is needed to fulfill the user's request. - If the request is ambiguous or could refer to multiple systems (e.g. - "builds" could mean Artifactory build-info or CI/CD pipeline runs), - **ask the user for clarification** instead of guessing. Never fetch data - from the wrong system — a wrong answer is worse than asking a question. -2. Resolve the target server using the **Server selection rules** below — - there must be no ambiguity about which server is used -3. For mutating operations (create, update, delete, upload), confirm with the - user unless the intent is clearly implied. This applies to all tiers - (MCP tools, CLI commands, and `jf api` with POST/PUT/DELETE). -4. Prefer read operations first to understand current state before making changes -5. **Never invent preparatory mutations.** If the requested operation fails - because a precondition is not met (artifact missing from the specified repo, - repository does not exist, package not at the expected location, build not - found), **stop and report the gap to the user**. Do not perform copy, move, - upload, create-repo, or any other mutating operation to satisfy the - precondition unless the user explicitly asks for it. These "helper" mutations - can have cascading effects the user has not considered — virtual repository - resolution changes, storage quota consumption, replication triggers, Xray - re-indexing, or permission propagation. -6. **Never guess tool names or API paths.** For MCP tools, confirm the tool - exists in the server's tool list. For `jf api` paths, validate against - `/references/` (or - [JFrog OpenAPI specifications](https://docs.jfrog.com/integrations/docs/openapi-specifications) - if you have web access). On a 404, stop and report — never retry with a guessed - alternative path. - -## Server selection rules (mandatory) - -**Single-server invariant.** Every `jf` call MUST pass `--server-id ` -(default resolved below); for one user request, all `jf` calls use **exactly -one** server-id. A wrong answer from the wrong server is worse than a stop-and-ask. - -**JFrog MCP and CLI use independent auth.** MCP tools authenticate through -the MCP server session (not `jf config`); CLI commands authenticate through -`jf config`. If you switch the CLI target server via `jf config use`, the -MCP connection still points to its original server. Do not mix MCP and CLI -calls targeting different servers in the same session. If the user asks to -switch servers, warn that MCP tools will continue to target the original -server until the MCP connection is re-established. - -**MUST NOT** retry on a second configured server after 401/403/404, empty, or -partial results; **MUST NOT** infer multi-server intent from "my"/"our" or -from seeing extra entries in `jf config show`. **Override:** only when the user -**explicitly** names another id ("on ``, …", "use ``", "compare `` -and ``") — inferred intent is not an override. - -### Resolve the default once per session - -Before your first `jf` call, resolve the default server-id and **remember it** -as `` for the rest of the session, same pattern as ``: - -```bash -jf config show 2>/dev/null \ - | awk '/^Server ID:/{id=$NF} /^Default:[[:space:]]*true/{print id; exit}' -# stdout: the default server-id; if empty, stop and ask which to use -``` - -Pass `--server-id ` to every subsequent `jf` call. The flag goes -**after** the subcommand name, not after `jf` itself: - -- ✅ `jf api --server-id /artifactory/api/system/version` -- ✅ `jf rt ping --server-id ` -- ❌ `jf --server-id api /…` — fails with `flag provided but not defined` - -When launching a subagent, pass `` in its prompt — subagents do not -re-resolve. Examples elsewhere in this skill and in `references/*.md` omit -`--server-id` for readability; the rule is global, same as -`JFROG_CLI_USER_AGENT`. To add a new server, read -`references/jfrog-login-flow.md`. - -### On any error, stop — never switch - -If a `jf` call returns 401/403, 404, network error, timeout, or any other -failure, **stop with no further `jf` calls** and respond: - -> `` returned `` for ``: ``. Other -> configured server(s): `` — I won't query them without your explicit -> instruction. How would you like to proceed? - -## When to read reference files - -Load the most specific file for the task at hand. Avoid loading more than 2-3 -reference files for a single operation — start with the most relevant one and -only load additional files if the first doesn't cover the need. File sizes -vary (~25–640 lines); larger files are noted with approximate line counts -below. - -### Cross-domain - -- **Disambiguating a JFrog entity, understanding entity types, or planning operations that span multiple products**: read `references/jfrog-entity-index.md`, then follow pointers to the relevant domain file -- **Looking up documentation URLs**: read `references/jfrog-url-references.md` - -### Artifactory - -- **Repository types, artifacts, builds, properties, or permission targets (concepts)**: read `references/artifactory-entities.md` (~220 lines) -- **Stored packages, package versions, version locations, or the metadata layer over Artifactory (concepts)**: read `references/stored-packages-entities.md` (~165 lines) -- **Repo, file, build, permission, user/group, or replication operations**: if the JFrog MCP server exposes a tool for the operation, prefer it. For CLI/API fallback, read `references/artifactory-operations.md` (for **listing builds** use AQL with `limit`/`offset` — see § *Listing build names*; for **full build detail** use `GET /api/build//?project=` — see § *Retrieving full build info*) -- **AQL queries**: read `references/artifactory-aql-syntax.md` (~585 lines) -- **Artifactory REST beyond the CLI, structured JSON templates (replacing interactive wizards), or any Artifactory API gap**: read `references/artifactory-api-gaps.md` (~220 lines) - -### Xray & security - -- **Watches, policies, violations, components, or vulnerability scanning (concepts)**: read `references/xray-entities.md` (~290 lines) -- **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 - -### Release lifecycle & distribution - -- **Release bundles, lifecycle stages, distribution, or evidence (concepts)**: read `references/release-lifecycle-entities.md` (~180 lines) -- **Applications, application versions, releasables, promotions, or AppTrust (concepts)**: read `references/apptrust-entities.md` (~155 lines) - -### 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` (~190 lines) -- **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 - -### OneModel (GraphQL) - -- **GraphQL queries** (applications, packages, evidence, release bundles, catalog, cross-domain, or "list/search my" platform entities): read `references/onemodel-graphql.md` (~325 lines) -- **Query templates and domain-specific examples**: read `references/onemodel-query-examples.md` (~555 lines) -- **Pagination, filtering, GraphQL variables, or date formatting**: read `references/onemodel-common-patterns.md` (~280 lines) - -### Platform administration - -- **Platform structure, project/repo membership, or project roles vs environments (concepts)**: read `references/platform-access-entities.md` -- **Access tokens, stats, projects, or system health**: read `references/platform-admin-operations.md` -- **Managing JFrog Projects, members, or environments**: read `references/projects-api.md` (~260 lines) -- **Platform REST beyond the CLI, or any platform-level API gap**: read `references/platform-admin-api-gaps.md` (~180 lines) - -### CLI setup & authentication - -- **Adding a server or logging in**: read `references/jfrog-login-flow.md` (~130 lines) -- **CLI not installed, upgrade needed, or `jq` unavailable**: read `references/jfrog-cli-install-upgrade.md` - -### General patterns - -- **Batching, parallel Shell calls, or launching subagents**: read `references/general-parallel-execution.md` (~135 lines) -- **Large or parallel data gathering, list-vs-detail APIs, cache hygiene**: read `references/general-bulk-operations-and-agent-patterns.md` -- **Standalone HTML report with JFrog-aligned styling**: read `references/jfrog-brand-html-report.md` -- **Reusable gotchas from past tasks**: read or extend `references/general-use-case-hints.md` +Apply `full_network` on the **first** Shell call that hits JFrog. Once +granted for a session, the agent environment typically retains it for +subsequent calls, but always include it explicitly to avoid silent failures. + +## Server management + +Server configuration is always read live from `jf config` (never cached). + +- **List servers**: `jf config show` (local operation, no network needed) +- **Use a specific server**: pass `--server-id ` to any command +- **Switch default**: `jf config use ` +- **Add a new server**: read `references/jfrog-login-flow.md` for the full + login procedure (web login or manual token setup) + +### Server selection rules (mandatory) + +Exactly one server (or an explicit set of servers) must be resolved before any +operation. The rules are strict and apply to every CLI command, API call, and +subagent prompt: + +1. **User named specific server(s)** — use those and only those. Pass + `--server-id ` to every `jf` command. Do not touch any other + configured server. +2. **User did not name a server** — use the current default server and only + it. Determine the default via `jf config show` (the entry marked as + default). If no default is set, stop and ask the user which server to use. +3. **Verify before executing** — after resolving the server, confirm it + exists in `jf config show` output before running any command against it. + If the server-id is not listed, stop and tell the user. + +Do not fall back to a different server. Silently switching servers is +dangerous because different servers hold different data, permissions, and +configurations — an operation that succeeds on the wrong server can corrupt +state, leak data across environments, or produce results the user cannot +reproduce. If the resolved server produces any error — does not exist in +`jf config`, authentication failure (401/403), network error, connection +refused, or any other failure — stop immediately and report the error to the +user. Do not try other configured servers, do not iterate through the server +list, and do not silently switch servers. Ask the user how to proceed. ## Command discovery @@ -328,11 +200,25 @@ Top-level security commands: `audit`, `scan`, `build-scan`, `curation-audit`, Top-level other: `access-token-create` (`atc`), `login`, `how`, `stats`, `generate-summary-markdown`, `exchange-oidc-token`, `completion`. +## Artifactory operations + +Artifactory resources are managed through the `jf rt` namespace — repos, files, +builds, permissions, users/groups, and replication. Read +`references/artifactory-operations.md` when performing any of these operations. + +## Platform administration + +Access tokens, login, stats, projects, and system health. Read +`references/platform-admin-operations.md` when performing any of these +operations. + ## Invoking platform APIs with `jf api` -`jf api` is the Tier 3 entry point for JFrog Platform REST and GraphQL -endpoints, auto-authenticated against the resolved server. **Do not use -`jf rt curl` or `jf xr curl`**; they are superseded by `jf api`. +When the CLI lacks a dedicated subcommand, use `jf api` — the unified entry +point for every JFrog Platform REST and GraphQL endpoint, auto-authenticated +against the resolved server. **Do not use `jf rt curl` or `jf xr curl`** — +they are superseded by `jf api`. All `jf api` calls require +`required_permissions: ["full_network"]` (see [Network permissions](#network-permissions)). ### Product-prefix table @@ -356,7 +242,7 @@ returns 404. ```bash jf api /artifactory/api/repositories -jf api --server-id /artifactory/api/system/version +jf api /artifactory/api/system/version --server-id # AQL (POST with text/plain body) jf api /artifactory/api/search/aql \ @@ -384,7 +270,7 @@ jq . "$RESPONSE" ``` Schema discovery: `jf api /onemodel/api/v1/supergraph/schema > "$SCHEMA_FILE"` -(store only under `~/.jfrog/skills-cache/`, never query responses). Read +(store only under `/local-cache/`, never query responses). Read `references/onemodel-graphql.md` for the full workflow (schema fetch, validation, pagination, errors), plus `references/onemodel-query-examples.md` and `references/onemodel-common-patterns.md` for query shapes, pagination, @@ -406,15 +292,15 @@ this repo GET, see **Any API gap** under [When to read reference files](#when-to ## Gotchas -### MCP tools - -- MCP tools return structured data in the tool result. Read response fields - directly; do not pipe MCP output through shell commands or `jq`. - -### CLI and `jf api` - -- `jf api` requires the **product prefix** in the path. Omitting it returns - 404. See the [product-prefix table](#product-prefix-table) for the full list. +- JFrog network calls require `required_permissions: ["full_network"]` in the + Shell tool. Without it, commands fail silently with empty output. The + environment check does **not** call your JFrog server (it may contact + `releases.jfrog.io` for version checking), but it may need **workspace + write** access for its cache file (see [Agent execution environments](#agent-execution-environments)). +- `jf api` requires the **product prefix** in the path (`/artifactory/...`, `/xray/...`, `/access/...`, `/evidence/...`, + `/lifecycle/...`, `/apptrust/...`, `/distribution/...`, `/onemodel/...`, + `/mc/...`). Omitting the prefix returns 404. See the + [product-prefix table](#product-prefix-table) above. - `jf api` writes the body (success or error JSON) to **stdout** and `[Info] Http Status: NNN` to **stderr** on every call; non-2xx also exits 1 and adds `[Warn] jf api: returned NNN`. Pipe stdout to @@ -479,6 +365,29 @@ this repo GET, see **Any API gap** under [When to read reference files](#when-to — read when debugging odd failures; **append** a short entry when you confirm a new, reusable gotcha. +## Cautious execution + +Do not run commands speculatively. Before executing any JFrog CLI command or +API call: + +1. Confirm the operation is needed to fulfill the user's request +2. Resolve the target server using the **Server selection rules** above — + there must be no ambiguity about which server is used +3. For mutating operations (create, update, delete, upload), confirm with the + user unless the intent is clearly implied +4. Prefer read operations first to understand current state before making changes +5. If any command fails with a server-level error (not found, auth, network), + stop and ask the user — never retry against a different server +6. **Never invent preparatory mutations.** If the requested operation fails + because a precondition is not met (artifact missing from the specified repo, + repository does not exist, package not at the expected location, build not + found), **stop and report the gap to the user**. Do not perform copy, move, + upload, create-repo, or any other mutating operation to satisfy the + precondition unless the user explicitly asks for it. These "helper" mutations + can have cascading effects the user has not considered — virtual repository + resolution changes, storage quota consumption, replication triggers, Xray + re-indexing, or permission propagation. + ## Batch and parallel execution When a task requires multiple independent operations, use the lightest @@ -527,3 +436,65 @@ of re-running the same `jf api` or other identical network-backed command. Do **not** reuse saved output across unrelated steps or changed contexts (different server, user, or intent). The file is only valid for the immediate sequence of operations that motivated the original call. + +## When to read reference files + +Load the most specific file for the task at hand. Avoid loading more than 2-3 +reference files for a single operation — start with the most relevant one and +only load additional files if the first doesn't cover the need. File sizes +vary (~25–640 lines); larger files are noted with approximate line counts +below. + +### Cross-domain + +- **Disambiguating a JFrog entity, understanding entity types, or planning operations that span multiple products**: read `references/jfrog-entity-index.md`, then follow pointers to the relevant domain file +- **Looking up documentation URLs**: read `references/jfrog-url-references.md` + +### Artifactory + +- **Repository types, artifacts, builds, properties, or permission targets (concepts)**: read `references/artifactory-entities.md` (~220 lines) +- **Stored packages, package versions, version locations, or the metadata layer over Artifactory (concepts)**: read `references/stored-packages-entities.md` (~165 lines) +- **Repo, file, build, permission, user/group, or replication operations**: read `references/artifactory-operations.md` (for **listing builds** with a known project key: REST `GET /api/build?project=`, then `GET /api/build/?project=` — see § *Listing builds when the project key is known*) +- **AQL queries**: read `references/artifactory-aql-syntax.md` (~585 lines) +- **Artifactory REST beyond the CLI, structured JSON templates (replacing interactive wizards), or any Artifactory API gap**: read `references/artifactory-api-gaps.md` (~220 lines) + +### Xray & security + +- **Watches, policies, violations, components, or vulnerability scanning (concepts)**: read `references/xray-entities.md` (~290 lines) +- **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 + +### Release lifecycle & distribution + +- **Release bundles, lifecycle stages, distribution, or evidence (concepts)**: read `references/release-lifecycle-entities.md` (~180 lines) +- **Applications, application versions, releasables, promotions, or AppTrust (concepts)**: read `references/apptrust-entities.md` (~155 lines) + +### Catalog + +- **Public or custom catalog, package metadata, vulnerability advisories, licenses, OpenSSF, or MCP services (concepts)**: read `references/catalog-entities.md` (~190 lines) +- **CVE details, vulnerability lookup by CVE ID, or severity/affected-packages/fix-versions for a specific CVE**: go directly to `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 + +### OneModel (GraphQL) + +- **GraphQL queries** (applications, packages, evidence, release bundles, catalog, cross-domain, or "list/search my" platform entities): read `references/onemodel-graphql.md` (~325 lines) +- **Query templates and domain-specific examples**: read `references/onemodel-query-examples.md` (~555 lines) +- **Pagination, filtering, GraphQL variables, or date formatting**: read `references/onemodel-common-patterns.md` (~280 lines) + +### Platform administration + +- **Platform structure, project/repo membership, or project roles vs environments (concepts)**: read `references/platform-access-entities.md` +- **Access tokens, stats, projects, or system health**: read `references/platform-admin-operations.md` +- **Managing JFrog Projects, members, or environments**: read `references/projects-api.md` (~260 lines) +- **Platform REST beyond the CLI, or any platform-level API gap**: read `references/platform-admin-api-gaps.md` (~180 lines) + +### CLI setup & authentication + +- **Adding a server or logging in**: read `references/jfrog-login-flow.md` (~130 lines) +- **CLI not installed, upgrade needed, or `jq` unavailable**: read `references/jfrog-cli-install-upgrade.md` + +### General patterns + +- **Batching, parallel Shell calls, or launching subagents**: read `references/general-parallel-execution.md` (~135 lines) +- **Large or parallel data gathering, list-vs-detail APIs, sandbox/cache issues**: read `references/general-bulk-operations-and-agent-patterns.md` +- **Standalone HTML report with JFrog-aligned styling**: read `references/jfrog-brand-html-report.md` +- **Reusable gotchas from past tasks**: read or extend `references/general-use-case-hints.md` diff --git a/skills/jfrog/references/artifactory-operations.md b/skills/jfrog/references/artifactory-operations.md index bab2b65..a244817 100644 --- a/skills/jfrog/references/artifactory-operations.md +++ b/skills/jfrog/references/artifactory-operations.md @@ -52,17 +52,6 @@ narrow the search further. ## Build info -**Project scoping rule:** Append `?project=` to **every** build detail -API call. When the user provides a project key, use it. When no project key -is provided, use `?project=default` (the built-in default project that covers -the `artifactory-build-info` repo). For AQL queries, scope by -`"repo":"-build-info"` (or `"repo":"artifactory-build-info"` for -the default project). - -**Server rule:** A 404 from a `?project=` build call is **not** a signal -to try a different server. Use only the resolved server; on any failure, -report and stop. See `SKILL.md` § *Server selection rules*. - ### Publishing builds - Collect env: `jf rt build-collect-env ` @@ -71,79 +60,84 @@ report and stop. See `SKILL.md` § *Server selection rules*. - Promote: `jf rt build-promote ` - Discard: `jf rt build-discard ` -### Listing build names +### Retrieving build info -**Do not use `GET /api/build`** — it has no pagination and times out on large -instances. Always use AQL with `limit` and `offset`. +The build detail API (`GET /api/build/{name}/{number}`) returns 404 when the +build is stored in a non-default build-info repo or belongs to a JFrog +Project. **Always resolve the scope before calling the build API:** -**All builds** (no project scope): +1. If the user provided a project key or build-info repo, use it directly. +2. If you need to **list** build names or run numbers and you have a **project + key**, follow [Listing builds when the project key is known](#listing-builds-when-the-project-key-is-known) (REST first — do not jump to AQL). +3. If the project key and build-info repo are still unknown, discover scope + via AQL (see [Discovering build scope without a project key](#discovering-build-scope-without-a-project-key) below). +4. For **detail**, use a scoped detail GET — never call `GET /api/build//` without `?project=` or `?buildRepo=` when the build requires it. ```bash -jf api /artifactory/api/search/aql \ - -X POST -H "Content-Type: text/plain" \ - -d 'builds.find().include("name","number","repo","created").sort({"$desc":["created"]}).offset(0).limit(100)' +jf api "/artifactory/api/build//?buildRepo=" +jf api "/artifactory/api/build//?project=" ``` -**Project-scoped** — filter by the project's build-info repository -(`-build-info`, or `artifactory-build-info` for the default -project): +Scope parameters: -```bash -jf api /artifactory/api/search/aql \ - -X POST -H "Content-Type: text/plain" \ - -d 'builds.find({"repo":"-build-info"}).include("name","number","repo","created").sort({"$desc":["created"]}).offset(0).limit(100)' -``` +- `?buildRepo=` — when the build info is stored in a + non-default build-info repository (anything other than + `artifactory-build-info`) +- `?project=` — when the build belongs to a JFrog Project -**Pagination:** The response includes a `range` object with `total` (total -matching records). If `total` exceeds the `limit`, tell the user: *"Showing -first 100 of N results (paginated). Ask for the next batch if needed."* -For subsequent pages, increment `offset` by 100. +### Listing builds when the project key is known -**Output rule (mandatory):** AQL returns one row per name+number pair. -Extract **unique build names** client-side (e.g. -`jq '[.[].builds.name] | unique'`). Present **only the deduplicated list of -build names** to the user. **Do not** include build numbers, timestamps, run -counts, or any per-run details in the response — not even as a "bonus" or -"most recent" table. The user is asking "what builds exist", not "what runs -happened". Only show run-level details if the user explicitly asks for them -in a follow-up. +When you have a **project key**, use this REST sequence before AQL. It scopes +the server’s work and avoids **unscoped** listing pitfalls (see below). -### Listing runs of a specific build +1. **Build names** (one row per logical build): + `GET /api/build?project=` + Response includes `builds[]` with `uri` (path suffix per name) and + `lastStarted` (latest run for that name). + +2. **Run numbers for one name**: + `GET /api/build/?project=` + Response uses the field **`buildsNumbers`** (exact spelling from the API); + each entry has `uri` (e.g. `/33`) and `started`. The same number may appear + more than once with different `started` values — do not assume uniqueness + by number alone. + +3. **Full build info** (unchanged): + `GET /api/build//?project=` ```bash -jf api /artifactory/api/search/aql \ - -X POST -H "Content-Type: text/plain" \ - -d 'builds.find({"name":""}).include("name","number","repo","created").sort({"$desc":["created"]}).offset(0).limit(100)' +jf api "/artifactory/api/build?project=" +jf api "/artifactory/api/build/?project=" +jf api "/artifactory/api/build//?project=" ``` -Add `"repo":"-build-info"` to the criteria when a project key -is known. Apply the same pagination rules as above. +### Discovering build scope without a project key -### Retrieving full build info +When the user has not provided the project key or build-info repo, discover +it via AQL. **Do not** use **unscoped** `GET /artifactory/api/build` (no +`?project=` or `?buildRepo=`) to list all builds — it can time out on large +instances with thousands of builds. -Use the REST detail endpoint for a **single** build run. Always include -`?project=` (or `?project=default` when no key is provided): +Use AQL `builds.find()` instead. The builds domain **requires** `name`, +`number`, and `repo` in `.include()` for permission reasons — omitting `repo` +produces an error. ```bash -jf api "/artifactory/api/build//?project=" +jf api /artifactory/api/search/aql \ + -X POST -H "Content-Type: text/plain" \ + -d 'builds.find({"name":""}).include("name","number","repo").sort({"$desc":["number"]}).limit(10)' ``` -This is the only `/api/build` endpoint that should be used — it returns a -single record and does not need pagination. - -### When a build is not found - -If the detail call returns 404, the build likely belongs to a different -project. **Ask the user for the project key** rather than searching across -repos or servers. +The `build.repo` field in the response tells you which build-info repository +the build resides in. Use that value as the `buildRepo` parameter in the +detail GET. ### Repository listing vs build-info `GET /artifactory/api/repositories?project=&type=buildinfo` may return an empty list even when project-scoped build info exists (for example under -a `*-build-info` repository). Prefer AQL to -discover builds; do not treat an empty repository -list as proof that no +a `*-build-info` repository). Prefer the **build** endpoints above or AQL to +discover builds; do not treat an empty repository list as proof that no builds exist. ## Permissions diff --git a/skills/jfrog/references/general-bulk-operations-and-agent-patterns.md b/skills/jfrog/references/general-bulk-operations-and-agent-patterns.md index 2b44979..cdebf69 100644 --- a/skills/jfrog/references/general-bulk-operations-and-agent-patterns.md +++ b/skills/jfrog/references/general-bulk-operations-and-agent-patterns.md @@ -44,9 +44,19 @@ line and break parsers (e.g. JSON "Extra data" errors). - One temp file per worker or chunk, then concatenate; or - Use advisory locking (`flock`) if one file must be shared. +## Agent sandboxes and the environment check + +`scripts/check-environment.sh` does **not** call your JFrog server, but it may +make an outbound request to `releases.jfrog.io` for version checking and may +**write** +`/local-cache/jfrog-skill-state.json` when the cache is stale or missing. In a +restricted agent sandbox, **workspace write** access can fail even when +`full_network` is granted. Request permissions that allow writing `/local-cache` +when the check fails with a filesystem error. + For bulk API or CLI output files, use `/tmp` or `mktemp`; do not use -`~/.jfrog/skills-cache/` except for `jfrog-skill-state.json` and the OneModel -schema file (see main SKILL.md). +`local-cache/` except for `jfrog-skill-state.json` and the OneModel schema file +(see main SKILL.md). ## Shell hygiene diff --git a/skills/jfrog/references/general-parallel-execution.md b/skills/jfrog/references/general-parallel-execution.md index a0bb9ba..d7354e9 100644 --- a/skills/jfrog/references/general-parallel-execution.md +++ b/skills/jfrog/references/general-parallel-execution.md @@ -92,6 +92,8 @@ merges their results into a unified report. expanded path, and return a structured summary. 4. Specify what to return (counts, lists, specific fields) so the parent can assemble the final output without re-reading raw data. +5. Remind the subagent to use `required_permissions: ["full_network"]` on + every Shell call that contacts the JFrog server. ### Subagent type selection diff --git a/skills/jfrog/references/general-use-case-hints.md b/skills/jfrog/references/general-use-case-hints.md index 416f1cd..082245a 100644 --- a/skills/jfrog/references/general-use-case-hints.md +++ b/skills/jfrog/references/general-use-case-hints.md @@ -6,12 +6,13 @@ workflow). Keep entries short and actionable. | Area | Symptom | Cause | Mitigation | Notes | |------|---------|-------|------------|-------| +| Agent sandbox | `check-environment.sh` fails with permission denied on `local-cache/` | Cache refresh tries to write `jfrog-skill-state.json` under `/local-cache`; sandbox blocks workspace writes | Run with permissions that allow writing the skill's `local-cache` directory, not only `full_network` | The script does not call your JFrog server but may contact `releases.jfrog.io` for version checking | | Parallel I/O | JSONL or ndjson parse errors ("Extra data", merged objects on one line) | Concurrent unsynchronized `>>` to the same file from parallel jobs | Sequential writes, one file per worker then `cat`, or `flock` | Generic pattern for any bulk CLI output | | APIs (general) | Report or script missing fields that appear in the UI or docs | List endpoint omitted fields; detail GET has the full record | List then GET by id/key when needed; see `general-bulk-operations-and-agent-patterns.md` | Applies across products | | Access / Projects | Project groups list looks empty in code | Response may use `members` for the group entries, not `groups` | Accept both keys when parsing | See `projects-api.md` | | Bulk detail fetches | `jq` parse error ("Extra data" or "Invalid ...") on slurped detail array | One or more detail GETs returned empty/HTML/error body; `jq -s` chokes on non-JSON mixed in | Validate each response with `jq -e .` before appending; write error placeholder for failures; see `general-bulk-operations-and-agent-patterns.md` | Applies to any per-item loop (repos, builds, users) | | Agent timeouts | Shell job silently moves to background; no captured output | `block_until_ms` too low for N sequential API calls (~1-2s each) | Estimate `N * 1.5s + 30s` buffer; set `block_until_ms` accordingly; or use parallel execution | Default 30s insufficient for >20 items | -| Sandbox + workspace writes | Generated report JSON or HTML not written; script exits 0 but file missing | Sandbox blocks some paths; agents sometimes wrongly target `~/.jfrog/skills-cache/` for scratch files (disallowed — only state + schema belong there) | Write reports and API responses under `/tmp` or the user workspace; only the two allowed cache files (`jfrog-skill-state.json`, `onemodel-schema-.graphql`) belong in `~/.jfrog/skills-cache/` | `skills-cache/` is not a temp directory; see SKILL.md **`~/.jfrog/skills-cache/` — allowed files only** | +| Sandbox + workspace writes | Generated report JSON or HTML not written; script exits 0 but file missing | Sandbox blocks some paths; agents sometimes wrongly target `local-cache/` for scratch files (disallowed — only state + schema belong there) | Write reports and API responses under `/tmp` or the user workspace; use `required_permissions: ["all"]` only when you must write inside the skill tree for the two allowed cache files | `local-cache/` is not a temp directory; see SKILL.md **`local-cache/` — allowed files only** | | `jf api` and redirects | Responses are empty or contain redirect HTML instead of expected content | `jf api` does not expose `-L`; it follows the redirect chain the Artifactory REST API returns by default but will not transparently hop across an unexpected 3xx to a binary URL | For remote-repo artifact content (e.g. fetching a file via `/artifactory//`), use `jf rt dl` instead of `jf api` — it handles 302s to CDN hosts. Reserve `jf api` for REST metadata/operation endpoints. | Applies when reading artifact **content**, not REST metadata | | Curation testing | `jf curation-audit` or `jf npm install` through a curated remote shows 1 download for the tested package even though no user downloaded it | The curation test itself fetches the package through Artifactory, which creates a cache entry and increments download stats | Account for this in download history analysis — the download was the curation test, not a real consumer pull | Also applies to `jf npmc` + `jf npm install` flows | | Agent-invented mutations | Agent copies or moves artifacts into a local repo to satisfy a precondition for a different operation (e.g., copy a package so evidence can be created on it) | Requested operation failed because the artifact was not in the specified repo; agent autonomously performed a copy/move/upload to "fix" the gap | **Never** perform unrequested copy, move, upload, or create-repo to work around a failed precondition — stop and report the gap to the user. See SKILL.md § Cautious execution rule 6 | Copying into a local repo can silently change virtual repo resolution for all consumers, trigger replication, and affect Xray indexing | diff --git a/skills/jfrog/references/jfrog-login-flow.md b/skills/jfrog/references/jfrog-login-flow.md index 4bfe5c3..581f813 100644 --- a/skills/jfrog/references/jfrog-login-flow.md +++ b/skills/jfrog/references/jfrog-login-flow.md @@ -9,9 +9,9 @@ Requires Artifactory 7.64.0+ and the JFrog CLI (`jf`). - Never print, echo, or display access tokens in terminal output or chat. - When confirming auth status, say "authenticated as user X" — never show -the token. + the token. - `jf config` is the sole credential store. Never store tokens in files, -env var profiles, or project directories. + env var profiles, or project directories. - Validate URLs with the ping endpoint before using them in shell commands. ## Resolve the active environment @@ -21,17 +21,19 @@ jf config show 2>/dev/null ``` - **0 servers** — ask the user for their JFrog Platform URL, then go to -Web Login. + Web Login. - **1 server** — use it: `jf config use `, done. - **2+ servers** — if the user named a specific server, use that one. Otherwise -use the current default. If no default is set, list server IDs and URLs and -ask the user which to use. **Never iterate through servers or fall back to -another server on error** — see SKILL.md **Server selection rules**. + use the current default. If no default is set, list server IDs and URLs and + ask the user which to use. **Never iterate through servers or fall back to + another server on error** — see SKILL.md **Server selection rules**. ## Web login (preferred) ### 1. Verify server and register session +Run with `required_permissions: ["full_network"]`: + ```bash bash /scripts/jfrog-login-register-session.sh "https://mycompany.jfrog.io" ``` @@ -49,7 +51,6 @@ Exit codes: 0 = success, 2 = server unreachable, 3 = registration failed. ### 2. Show the user the verification code and login link Build the login URL: - ``` ${JFROG_PLATFORM_URL}/ui/login?jfClientSession=${SESSION_UUID}&jfClientName=JFrog-Skills&jfClientCode=1 ``` @@ -58,7 +59,8 @@ Show the verification code prominently, then the clickable link: > ## Verification code: `` > -> Open the login link from above, then enter the code. +> Open this link to log in, then enter the code when prompted: +> [Log in to mycompany.jfrog.io]() > > Let me know when you're done. @@ -66,6 +68,9 @@ Wait for the user to confirm. Do not poll automatically. ### 3. Retrieve token, save credentials, verify +Run with `required_permissions: ["all"]` (the script writes to `~/.jfrog/` +via `jf config add`, which is outside the workspace): + ```bash bash /scripts/jfrog-login-save-credentials.sh \ "https://mycompany.jfrog.io" \ @@ -75,10 +80,8 @@ bash /scripts/jfrog-login-save-credentials.sh \ Substitute the literal platform URL and session UUID from step 1 output. The script retrieves the one-time token, derives a server ID from the URL, -saves credentials via `jf config add`, and verifies with an Artifactory -version check. It leaves the current default `jf` server unchanged — pass -`--server-id=` explicitly on subsequent calls (the SKILL.md "Server -selection rules" require this anyway). On success it outputs: +saves credentials via `jf config add`, sets the server as default, and +verifies with an Artifactory version check. On success it outputs: ``` SERVER_ID= @@ -93,23 +96,12 @@ verification failed. **The token endpoint is one-time-use.** If consumed (even in a failed save), the session UUID is invalidated and the flow must restart from step 1. -## Post-login handoff (mandatory gate) - -Before any other JFrog operation against the new server, ask the user: - -> Logged in to ``. Do you want to make it the default `jf` -> server? (If you say no, I'll keep using `--server-id=` -> explicitly for follow-up calls.) - -- Confirm → `jf config use `, then resume the original task. -- Decline or no answer → keep `--server-id=` on every `jf` call. - ## Fallback: manual token setup If web login fails (server too old, network restrictions): 1. Ask the user to generate a token in the JFrog UI: - **Administration > Identity and Access > Access Tokens > Generate Token** + **Administration > Identity and Access > Access Tokens > Generate Token** 2. Save it non-interactively: ```bash @@ -122,11 +114,13 @@ jf config add \ ## Gotchas - The token endpoint (`/token/{uuid}`) is **one-time-use**. If consumed -(even in a failed save), the session UUID is invalidated and the flow -must restart from step 1. The save-credentials script handles cleanup, -but if it exits non-zero after consuming the token, restart from step 1. + (even in a failed save), the session UUID is invalidated and the flow + must restart from step 1. The save-credentials script handles cleanup, + but if it exits non-zero after consuming the token, restart from step 1. +- Step 3 requires `required_permissions: ["all"]` because `jf config add` + writes to `~/.jfrog/` which is outside the workspace. Using only + `full_network` silently blocks the write. - Server ID is derived from the hostname: `https://mycompany.jfrog.io` -becomes `mycompany`. Self-hosted URLs are slugified: -`https://artifactory.internal.corp` becomes `artifactory-internal-corp`. -- `**jf`**, `**uuidgen**` (register-session), and `**jq**` (save-credentials) must be on PATH. - + becomes `mycompany`. Self-hosted URLs are slugified: + `https://artifactory.internal.corp` becomes `artifactory-internal-corp`. +- **`jf`**, **`uuidgen`** (register-session), and **`jq`** (save-credentials) must be on PATH. diff --git a/skills/jfrog/references/onemodel-graphql.md b/skills/jfrog/references/onemodel-graphql.md index 41e03e5..485fc69 100644 --- a/skills/jfrog/references/onemodel-graphql.md +++ b/skills/jfrog/references/onemodel-graphql.md @@ -12,11 +12,9 @@ pagination, variables, and date formatting, read `onemodel-common-patterns.md`. In examples below, `` is this skill's directory (parent of `references/`). -## `~/.jfrog/skills-cache/` policy +## `local-cache/` policy -The skill cache lives under `${JFROG_CLI_HOME_DIR:-$HOME/.jfrog}/skills-cache/` -(co-located with `jf config`, **not** inside the installed skill tree). It holds -**only**: +The skill’s `local-cache/` directory holds **only**: 1. **`onemodel-schema-${JFROG_SERVER_ID}.graphql`** — this workflow (supergraph SDL cache). **Always** use the path in [Fetch the schema](#2-fetch-the-schema); @@ -25,9 +23,9 @@ The skill cache lives under `${JFROG_CLI_HOME_DIR:-$HOME/.jfrog}/skills-cache/` manage it; do not delete or replace it casually. **Never** store GraphQL **query responses**, REST bodies, reports, or other -scratch files under `skills-cache/`. For responses, use `/tmp` with a unique name +scratch files under `local-cache/`. For responses, use `/tmp` with a unique name (`$$`, `mktemp -d`) as in [Execute the query](#6-execute-the-query) — the -example `RESPONSE_FILE` paths must stay outside `skills-cache/`. +example `RESPONSE_FILE` paths must stay outside `local-cache/`. ## Prerequisites @@ -37,6 +35,9 @@ example `RESPONSE_FILE` paths must stay outside `skills-cache/`. - **`jq`** on `PATH` (same as the base skill). HTTP calls go through `jf api`; no standalone `curl` is needed. +All network calls require `required_permissions: ["full_network"]` in agent +Shell invocations. + ## Workflow Follow these steps in order. Skipping the schema fetch (step 2) is the most @@ -88,25 +89,24 @@ unexpected empty results, fetch the schema (as described below), verify the query against it, and retry. Do not attempt more than one execution without schema verification. -The schema is large. Cache it under the skill cache directory (the JFrog CLI -home — outside the installed skill tree) keyed by the concrete -`JFROG_SERVER_ID` from step 1 (the CLI `serverId`, never a placeholder like -`default`). +The schema is large. Cache it under the skill's local cache (gitignored) keyed +by the concrete `JFROG_SERVER_ID` from step 1 (the CLI `serverId`, never a +placeholder like `default`). **Always use this exact path** — do not save the schema to `/tmp/` or any other location. The cache path is: -`${JFROG_CLI_HOME_DIR:-$HOME/.jfrog}/skills-cache/onemodel-schema-${JFROG_SERVER_ID}.graphql` +`/local-cache/onemodel-schema-${JFROG_SERVER_ID}.graphql` Run the following block as-is. It checks for an existing cached file and only fetches when missing: ```bash -SCHEMA_FILE="${JFROG_CLI_HOME_DIR:-$HOME/.jfrog}/skills-cache/onemodel-schema-${JFROG_SERVER_ID}.graphql" +SCHEMA_FILE="/local-cache/onemodel-schema-${JFROG_SERVER_ID}.graphql" if [ -s "$SCHEMA_FILE" ]; then echo "Schema cache hit: $SCHEMA_FILE ($(wc -l < "$SCHEMA_FILE") lines)" else - mkdir -p "$(dirname "$SCHEMA_FILE")" + mkdir -p "/local-cache" jf api /onemodel/api/v1/supergraph/schema \ --server-id "$JFROG_SERVER_ID" \ > "$SCHEMA_FILE" @@ -259,8 +259,8 @@ jf api /onemodel/api/v1/graphql \ Redirect `jf api`'s stdout to `$RESPONSE_FILE` so you can re-`jq` without re-querying. **Do not pipe `jf api` directly to `jq`** — a wrong filter loses the response. **Do not** set `RESPONSE_FILE` under -`~/.jfrog/skills-cache/` — that directory is only for the schema cache and -`jfrog-skill-state.json` (see [`~/.jfrog/skills-cache/` policy](#jfrogskills-cache-policy) +`/local-cache/` — that directory is only for the schema cache and +`jfrog-skill-state.json` (see [`local-cache/` policy](#local-cache-policy) above). For multiple queries in one shell session, use a temp directory under `/tmp` and diff --git a/skills/jfrog/references/projects-api.md b/skills/jfrog/references/projects-api.md index 06f9f18..25681bf 100644 --- a/skills/jfrog/references/projects-api.md +++ b/skills/jfrog/references/projects-api.md @@ -13,8 +13,10 @@ All endpoints below use full product-prefixed paths (`/access/api/...`, ## Authentication -Credentials are resolved automatically by `jf api` from the active `jf config` -server — no token extraction or `curl` wiring is needed. +All calls in this file require `required_permissions: ["full_network"]` in the +Shell tool (see the Network permissions section in SKILL.md). Credentials are +resolved automatically by `jf api` from the active `jf config` server — no +token extraction or `curl` wiring is needed. ## Projects diff --git a/skills/jfrog/scripts/check-environment.sh b/skills/jfrog/scripts/check-environment.sh index d58ad7e..faf58a7 100755 --- a/skills/jfrog/scripts/check-environment.sh +++ b/skills/jfrog/scripts/check-environment.sh @@ -2,30 +2,26 @@ # check-environment.sh — Cached JFrog CLI environment check # # Checks if jf is installed and its version, using a 24h-TTL cache -# at ${JFROG_CLI_HOME_DIR:-$HOME/.jfrog}/skills-cache/jfrog-skill-state.json -# to avoid redundant checks. The skills-cache/ dir holds only this file and -# the OneModel schema cache — not temp API output. +# at /local-cache/jfrog-skill-state.json to avoid redundant checks. +# local-cache/ is only for this file and the OneModel schema cache — not temp API output. # -# Usage: -# bash check-environment.sh [] [--force] -# -# stdout: bare JFROG_CLI_USER_AGENT value (one line) — agent captures it -# and runs `export JFROG_CLI_USER_AGENT=''` once at the top of -# every bash invocation that calls jf +# stdout: eval-able shell exports (JFROG_CLI_USER_AGENT) # stderr: JSON state (informational, also written to cache file) # # Exit codes: -# 0 — cache fresh, CLI ready -# 1 — cache refreshed, CLI ready -# 2 — jf not installed -# 3 — jf below MIN_CLI_VERSION (required for `jf api`) +# 0 — Cache is fresh, CLI is ready +# 1 — Cache was stale/missing and has been refreshed +# 2 — jf is not installed +# 3 — jf is installed but below MIN_CLI_VERSION (required for `jf api`) +# +# Usage: +# eval "$(bash check-environment.sh [--force])" set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" SKILL_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" -JFROG_HOME="${JFROG_CLI_HOME_DIR:-$HOME/.jfrog}" -CACHE_DIR="$JFROG_HOME/skills-cache" +CACHE_DIR="$SKILL_ROOT/local-cache" CACHE_FILE="$CACHE_DIR/jfrog-skill-state.json" DEFAULT_TTL_HOURS=24 FORCE=false @@ -35,14 +31,9 @@ FORCE=false # skill) landed in 2.100.0; older CLIs fail with "unknown command: api". MIN_CLI_VERSION="2.100.0" -MODEL_SLUG="" -for arg in "$@"; do - if [[ "$arg" == "--force" ]]; then - FORCE=true - elif [[ -z "$MODEL_SLUG" ]]; then - MODEL_SLUG="$arg" - fi -done +if [[ "${1:-}" == "--force" ]]; then + FORCE=true +fi now_epoch() { date -u +%s @@ -143,56 +134,19 @@ EOF return 1 } -# Detect the calling harness from environment signals. Output is one of: -# claude, cursor, gemini, goose, copilot, codex, unknown — or empty -# string when no agent signal is present (direct CLI/CI invocation). -# Naming matches the JFrog CLI's DetectExecutionContext() vocabulary. -detect_harness() { - if [[ -n "${CLAUDECODE:-}" || -n "${CLAUDE_CODE_ENTRYPOINT:-}" ]]; then - echo "claude" - elif [[ -n "${CURSOR_AGENT:-}" || -n "${CURSOR_CLI:-}" || -n "${CURSOR_TRACE_ID:-}" ]]; then - echo "cursor" - elif [[ -n "${GEMINI_CLI:-}" ]]; then - echo "gemini" - elif [[ -n "${GOOSE_TERMINAL:-}" ]]; then - echo "goose" - elif [[ -n "${COPILOT_CLI:-}" ]]; then - echo "copilot" - elif [[ -n "${CODEX_CI:-}" || -n "${CODEX_THREAD_ID:-}" || -n "${CODEX_SANDBOX:-}" ]]; then - echo "codex" - elif [[ -n "${AGENT:-}" || -n "$MODEL_SLUG" ]]; then - # Agent invoked us but we can't name it. - echo "unknown" - fi - # No match → print nothing; emitter omits the parens block entirely. -} - # Emit skill-level env vars to stdout (for eval by the caller) emit_skill_env() { - local skill_version cli_version ua harness + local skill_version cli_version ua # Parse version from SKILL.md YAML frontmatter (metadata.version) skill_version="$(awk '/^---$/{n++; next} n==1 && /^[[:space:]]*version:/{gsub(/["'"'"']/, "", $2); print $2; exit}' "$SKILL_ROOT/SKILL.md" 2>/dev/null | tr -d '[:space:]')" skill_version="${skill_version:-unknown}" cli_version=$(jq -r '.cli_version // "unknown"' "$CACHE_FILE" 2>/dev/null || echo "unknown") - harness=$(detect_harness) - # Build the parens block: semicolon-separated key=value pairs. - local meta="" - if [[ -n "$harness" ]]; then - meta="tool=${harness}" - fi - if [[ -n "$MODEL_SLUG" ]]; then - if [[ -n "$meta" ]]; then - meta="${meta}; model=${MODEL_SLUG}" - else - meta="model=${MODEL_SLUG}" - fi - fi - ua="jfrog-skills/${skill_version}" - if [[ -n "$meta" ]]; then - ua="${ua} (${meta})" + ua="" + if [[ -n "${JFROG_SKILL_MODEL:-}" ]]; then + ua="model/${JFROG_SKILL_MODEL} " fi - ua="${ua} jfrog-cli-go/${cli_version}" - printf '%s\n' "$ua" + ua="${ua}jfrog-skills/${skill_version} jfrog-cli-go/${cli_version}" + echo "export JFROG_CLI_USER_AGENT='${ua}'" } # Main diff --git a/skills/jfrog/scripts/jfrog-login-save-credentials.sh b/skills/jfrog/scripts/jfrog-login-save-credentials.sh index f6ba42b..7721009 100755 --- a/skills/jfrog/scripts/jfrog-login-save-credentials.sh +++ b/skills/jfrog/scripts/jfrog-login-save-credentials.sh @@ -6,10 +6,6 @@ # Bootstrap token exchange uses `jf api --url` (before any server exists in # `jf config`); verification uses `jf api` with --server-id. # -# Leaves the current default `jf` server unchanged. Subsequent calls should -# pass `--server-id=` explicitly (the SKILL.md "Server selection rules" -# require this anyway). -# # IMPORTANT: The token endpoint is one-time-use. If this script fails after # consuming the token (e.g. jf config write blocked by sandbox), the session # is burned and login must restart from register-session. @@ -113,6 +109,8 @@ if ! jf config add "$JFROG_HOST" \ exit 4 fi +jf config use "$JFROG_HOST" + echo "SERVER_ID=${JFROG_HOST}" echo "--- Verifying authentication ---" @@ -120,9 +118,3 @@ if ! jf api "/artifactory/api/system/version" --server-id="$JFROG_HOST"; then echo "ERROR: Authentication verification failed. Token may not have saved correctly." >&2 exit 4 fi - -echo -echo "NEXT (mandatory): ask the user whether to make '${JFROG_HOST}' the default jf server." -echo " - If yes: run 'jf config use ${JFROG_HOST}'" -echo " - If no: pass '--server-id=${JFROG_HOST}' on every subsequent jf call" -echo "Do not start any other JFrog operation against this server until this question is asked and answered."