diff --git a/.cursor-plugin/marketplace.json b/.cursor-plugin/marketplace.json index e30f8ae..7e28aad 100644 --- a/.cursor-plugin/marketplace.json +++ b/.cursor-plugin/marketplace.json @@ -6,7 +6,7 @@ }, "metadata": { "description": "JFrog Platform plugins for Cursor", - "version": "0.5.6", + "version": "0.5.7", "pluginRoot": "plugins" }, "plugins": [ diff --git a/plugins/jfrog/.cursor-plugin/plugin.json b/plugins/jfrog/.cursor-plugin/plugin.json index 3aa316a..9616d42 100644 --- a/plugins/jfrog/.cursor-plugin/plugin.json +++ b/plugins/jfrog/.cursor-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "jfrog", "displayName": "JFrog Platform", - "version": "0.5.6", + "version": "0.5.7", "description": "JFrog Platform integration with MCP, security skills, supply-chain best practices, and JFrog Agent Guard governance for adding, removing, and listing MCP servers.", "author": { "name": "JFrog", diff --git a/plugins/jfrog/skills/jfrog-ai-catalog-skills/SKILL.md b/plugins/jfrog/skills/jfrog-ai-catalog-skills/SKILL.md index ed273b1..f129918 100644 --- a/plugins/jfrog/skills/jfrog-ai-catalog-skills/SKILL.md +++ b/plugins/jfrog/skills/jfrog-ai-catalog-skills/SKILL.md @@ -56,18 +56,15 @@ Pick the row matching the user's intent and read that reference file. same `` to Agent Guard as `--server ""` so it targets the same server as your `jf` calls. Agent Guard also reads `JFROG_URL` / `JF_URL` directly when set, so make sure the `` you resolved points at that same host. -- **Resolve the project (``) only when needed, and always to a key.** - `` must be the JFrog **project key**, not the display name. It is - required for `--list-skills`, `--list-skill-versions`, and - `--provision-skills-repository`. Take the value from `JF_PROJECT` or the user, - then resolve it to a key against the projects list (see *List all projects* in - the base `jfrog` skill's [`references/projects-api.md`](../jfrog/references/projects-api.md)): - ```bash - jf api '/access/api/v1/projects' --server-id "" \ - | jq -r '.[] | select(.project_key=="" or .display_name=="") | .project_key' - ``` - Use the printed key. If it prints nothing, ask the user for the key. Never - assume `default`, never invent one. Install, update, remove, and publishing to +- **Resolve the project (``) only when needed.** + It is required for `--list-skills`, `--list-skill-versions`, and + `--provision-skills-repository`. Take it from `JF_PROJECT` or the user. + There is no non-admin way to look up or validate project keys (the + `/access/api/v1/projects` list endpoint needs admin), so you cannot + silently correct a display name to a key. If the value looks like a + display name (spaces, mixed case) rather than a short slug, ask the + user to confirm the project **key** specifically. Never assume + `default`, never invent one. Install, update, remove, and publishing to an explicit `--repo` are keyed by skill **name** and/or **repo**, not a project. diff --git a/plugins/jfrog/skills/jfrog-ai-catalog-skills/references/publishing-skills.md b/plugins/jfrog/skills/jfrog-ai-catalog-skills/references/publishing-skills.md index 9cb4933..970d44c 100644 --- a/plugins/jfrog/skills/jfrog-ai-catalog-skills/references/publishing-skills.md +++ b/plugins/jfrog/skills/jfrog-ai-catalog-skills/references/publishing-skills.md @@ -137,6 +137,8 @@ template and do not run `jf skills publish` until the user agrees: > Publishing skill `` uploads it to repository `` on server ``. Do you want to publish it? +Never combine this final confirmation step with the previous signing step into one prompt. + If the user says no or names a different repo/name, use that instead and confirm again. Only proceed to *Publish* after an explicit "yes". diff --git a/plugins/jfrog/skills/jfrog-reference-architecture/SKILL.md b/plugins/jfrog/skills/jfrog-reference-architecture/SKILL.md new file mode 100644 index 0000000..394d000 --- /dev/null +++ b/plugins/jfrog/skills/jfrog-reference-architecture/SKILL.md @@ -0,0 +1,155 @@ +--- +name: jfrog-reference-architecture +description: >- + Guides JFrog Platform topology, sizing (RPM, t-shirt templates), deployment, + multi-site use cases, SaaS vs self-managed, HA, air-gapped, and disaster + recovery using the official Reference Architecture site as the sole source of + facts. Use this skill when the user asks how to size Artifactory or Xray, + which deployment pattern to choose, single vs multi site, active-active, + active-passive, CI/CD separation, Helm/Kubernetes install planning, or + reference architecture (live WebFetch from jfrog.com/reference-architecture). + 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. +compatibility: >- + Requires outbound HTTPS (WebFetch or equivalent). Request full_network when + the runtime blocks fetches. No jf CLI or configured JFrog instance required + for planning-only questions. +metadata: + role: workflow +--- + +# JFrog Reference Architecture + +Planning skill for topology, sizing, and deployment. Answers must come from +**live fetches** of the [JFrog Platform Reference Architecture](https://jfrog.com/reference-architecture/) — not from training data or duplicated tables in this repo. + +## Prerequisites + +- Read `../jfrog/SKILL.md` for JFrog Platform concepts, product vocabulary, and routing to other workflows. +- **No `jf` CLI required** for planning-only questions (no live instance needed). + +## Source of truth + +| Allowed in this skill | Not allowed | +|-----------------------|-------------| +| Fetch procedures, workflows, output templates | Sizing RPM tables, use-case narratives, deployment checklists copied from the site | +| Helm chart **preference** (jfrog-platform on Kubernetes) | Hardcoded slug lists or criteria | + +**Every factual claim** (numbers, template names, limitations, infrastructure guidance) must come from a **`WebFetch` in the current session**. If fetch fails, retry or ask the user to open the URL — do not guess from memory. + +**Citations:** Use the `URL:` line from the relevant section in the fetched content (public HTML URL). You may note content was read from `llms-full.txt`. + +For fetch URLs, size thresholds, and the fallback ladder, see [references/doc-access.md](references/doc-access.md). + +## Gotchas + +| Symptom | Mitigation | +|---------|------------| +| Sizing numbers or use-case names not on the official site | `WebFetch` ref-arch first; cite `URL:` from the fetch — not training data | +| `small` template recommended for production | Re-read production warnings in the fetched Artifactory/Xray sizing sections | +| SaaS section missing or 404 | SaaS paths use prefix **`jfrog-saas`**, not `saas` | +| HA storage guidance wrong | Per ref arch: **`cluster-file-system`** or object storage — not `file-system` for HA | +| `WebFetch` blocked, truncated, or over size limits | Request `full_network`; downgrade per [references/doc-access.md](references/doc-access.md) | + +## Session bootstrap + +Before answering a reference-architecture question: + +1. **`WebFetch`** `https://jfrog.com/reference-architecture/llms-full.txt` (primary). +2. Keep the response in context for follow-ups in the same thread. +3. Note approximate response size. If over **1 MB**, or truncated, follow the downgrade path in `references/doc-access.md` (sitemap + targeted `index.md`). +4. Re-bootstrap when uncertain after long unrelated conversation. + +Request **`full_network`** (or the runtime equivalent) when `WebFetch` is blocked. + +### Parsing llms-full.txt + +Sections are separated by `---` and typically include: + +- `# ` +- `URL: https://jfrog.com/reference-architecture/...` +- Optional `> <summary>` +- Body text (may be condensed vs the HTML page) + +Use only text from the fetch. For recommendations, cite the section’s `URL:` line. + +## Intent routing + +| User intent | Where to look in llms-full | Fallback | +|-------------|---------------------------|----------| +| Sizing | `# Sizing`, `# AWS Sizing`, Azure, GCP sections | `.../self-managed/deployment/sizing/index.md` | +| Topology / use case | Matching title + `URL:` for SaaS (`jfrog-saas`) or self-managed | That path’s `index.md` | +| List use cases | All `URL:` lines containing `/use-cases/` | `sitemap.xml` | +| Deployment / install | Deployment and considerations sections | `.../deployment/index.md` | +| Disaster recovery | DR playbook / tiers sections | Matching `index.md` | + +SaaS paths use prefix **`jfrog-saas`**, not `saas`. + +## Workflow: Sizing + +1. Bootstrap `llms-full.txt` (unless downgraded to single-page fetch). +2. Find the **Sizing** section. Build follow-up questions from **Artifactory Sizing Templates Criteria** in the fetch (peak **Requests Per Minute** and **Concurrent Connections** per template). +3. Use **`AskQuestion`** when available; otherwise numbered options using labels from the fetched table only. +4. If the user mentions Xray or production, use **Xray Sizing Templates Criteria** and production warnings from the same fetch (e.g. small is not for production). +5. If the user names a cloud, use **AWS Sizing** / **Azure** / **GCP** sections from the fetch. +6. Recommend a template (`small` through `2xlarge`) and cite the sizing page `URL:` from the dump. +7. **Helm:** Recommend the [jfrog-platform](https://github.com/jfrog/charts/tree/master/stable/jfrog-platform) chart with `-f sizing/platform-<template>.yaml`. `WebFetch` the chart README if the user wants exact install commands. + +### Sizing output template + +```markdown +## Recommended sizing + +- **Template**: <from fetched table> +- **Artifactory**: <RPM and concurrent connections from fetched table> +- **Xray** (if applicable): <from fetched Xray table> +- **Source**: <URL: line from Sizing section> +- **Helm**: `helm upgrade --install` with `-f sizing/platform-<template>.yaml` on chart `jfrog/jfrog-platform` +- **Caveats**: <Notes / additional factors from fetched Sizing section> +``` + +## Workflow: Topology and use cases + +1. Bootstrap `llms-full.txt`. +2. Ask **1–2 follow-ups per turn** until hosting model and site count are clear: + - SaaS vs self-managed (if unsure, fetch home/overview from dump and mention SaaS value proposition from site text). + - Single site vs multi site. + - For multi-site: DR/failover, geo performance, CI/CD separation, edges, hybrid variants, IoT, subsidiaries/vendors, air-gapped (self-managed only), archiving. +3. **List documented use cases:** Filter all `URL:` lines containing `/use-cases/` from the dump; group under **JFrog SaaS** vs **Self-managed**. Fallback: `sitemap.xml` if the user wants sitemap-complete listing. +4. **Recommend a use case:** Match the user’s answers to sections in the dump; cite each `URL:`. If the dump is insufficient for one page, `WebFetch` `https://jfrog.com/reference-architecture/<path>/index.md` for that `URL:` path. +5. **No exact match:** Suggest combining documented patterns (e.g. active-passive for DR + main-site-with-edges); fetch each component section before describing how they combine. Remind that the ref arch is a starting point for emerging cases. + +## Workflow: Deployment + +1. Bootstrap `llms-full.txt`. +2. Use deployment, considerations, HA, database, storage, and cloud sections from the fetch. +3. **Default policy (not a substitute for ref-arch facts):** Deploy on **Kubernetes** with the **jfrog-platform** Helm chart even when the user only wants Artifactory — enable Artifactory, disable other products in values. Do not steer to legacy standalone Artifactory charts unless the user explicitly requires non-Kubernetes deployment. +4. Production reminders from fetched content where applicable: external managed PostgreSQL (not bundled chart DB for production), object storage / `cluster-file-system` for HA, Enterprise license for `replicaCount > 1`. +5. `WebFetch` the [chart README](https://github.com/jfrog/charts/tree/master/stable/jfrog-platform) when the user needs install snippets, OpenShift (`openshift-values.yaml` last), or RabbitMQ quorum files. + +### Deployment output template + +```markdown +## Deployment recommendation + +- **Runtime**: Kubernetes + jfrog-platform Helm chart +- **Reference**: <URL: from deployment-related sections> +- **Key considerations**: <bullets from fetched considerations sections> +- **Helm** (if requested): <commands from chart README fetch> +``` + +## When to read reference files + +- **Fetch ladder, Markdown URL rule, size governance:** [references/doc-access.md](references/doc-access.md) + +## Examples + +**Sizing:** User asks what sizing to set for Artifactory → bootstrap llms-full → ask peak RPM using fetched table options → recommend template and Helm sizing file. + +**List use cases:** User asks for all documented use cases → bootstrap llms-full → list grouped by SaaS vs self-managed from `URL:` lines in dump. + +**Topology:** User needs DR across two regions → clarify SaaS vs self-managed → recommend active-passive (or related) section from dump with `URL:` citations. + +**Deploy:** User wants Artifactory on EKS → deployment + AWS sizing sections from dump → jfrog-platform chart with external DB and sizing values file. diff --git a/plugins/jfrog/skills/jfrog-reference-architecture/references/doc-access.md b/plugins/jfrog/skills/jfrog-reference-architecture/references/doc-access.md new file mode 100644 index 0000000..3f576b7 --- /dev/null +++ b/plugins/jfrog/skills/jfrog-reference-architecture/references/doc-access.md @@ -0,0 +1,32 @@ +# Reference Architecture — documentation access + +Fetch official content in-session. **Do not copy page bodies into this repo.** + +## Bootstrap and fallback + +| Step | URL | When | +|------|-----|------| +| Primary | https://jfrog.com/reference-architecture/llms-full.txt | Start of every ref-arch session | +| 1 | https://jfrog.com/reference-architecture/llms.txt | llms-full fails or for `index.md` URL pattern | +| 2 | `https://jfrog.com/reference-architecture/<path>/index.md` | One section; append `index.md` to HTML path | +| 3 | https://jfrog.com/reference-architecture/sitemap.xml | Exhaustive URL list | +| 4 | HTML URL (no `index.md`) | If `index.md` fails | + +Parse llms-full by `---`, `# <Title>`, and `URL: https://jfrog.com/reference-architecture/...`. +Base path: `https://jfrog.com/reference-architecture/`. SaaS prefix: **`jfrog-saas`**, not `saas`. + +## Size governance + +| Fetched size | Action | +|--------------|--------| +| Under ~1 MB, not truncated | One llms-full bootstrap per ref-arch thread | +| ~1–2 MB or ref-arch is side context | Prefer sitemap + targeted `index.md` | +| Truncated or over ~2 MB | Skip mandatory llms-full; use fallback ladder only | + +Downgrade early for narrow questions (e.g. sizing only → `.../deployment/sizing/index.md`). +Tell the user when targeted fetches replace a full bootstrap. + +## Citations and Helm + +- User-facing link: the section `URL:` line (HTML). +- Chart details: https://github.com/jfrog/charts/tree/master/stable/jfrog-platform — `WebFetch` README when install commands are needed. diff --git a/plugins/jfrog/skills/jfrog-setup-package-managers/SKILL.md b/plugins/jfrog/skills/jfrog-setup-package-managers/SKILL.md new file mode 100644 index 0000000..e7eeaec --- /dev/null +++ b/plugins/jfrog/skills/jfrog-setup-package-managers/SKILL.md @@ -0,0 +1,184 @@ +--- +name: jfrog-setup-package-managers +description: >- + Use this skill when the user asks to set up, configure, bind, or connect a + package manager (npm, pip, maven, gradle, go, docker, helm, …) to JFrog + Artifactory via `jf setup` and `.jfrog/local/package-resolution.json`; when a + workspace manifest exists with no matching binding entry; or when a session + hook reports PM config missing. Skip when the binding already has the same + repo key — the session hook reapplies each start. Never pick a repo by + discovery; use resolver output only (unless the user explicitly names or + asks to browse repos). On unresolved or failed setup, ask for a repo key + with the failure verbatim — never switch servers. +metadata: + role: workflow +--- + +# JFrog — Setup Package Managers for Artifactory + +Apply the session hook's repo pick via [`jf setup`](references/jf-setup-command.md), +then record it in [`.jfrog/local/package-resolution.json`](references/workspace-binding.md). +`jf setup` writes PM-native config (`.npmrc`, `pip.conf`, …); the binding +lets the hook re-apply on later sessions. + +## Scope (this skill vs session hook) + +**Session-start hook:** resolves repo keys per package type, injects the +"Resolved URLs for this session" table, refreshes the global cache. The same +renderer is available on demand via `modules/package-resolution/scripts/print-policy.mjs` (the enforce +notice embeds the exact command), so the policy can be loaded after setup. + +**This skill:** reads that output, runs `jf setup`, and persists the workspace +binding at `.jfrog/local/package-resolution.json` when PM config is still missing. + +**Honor the injected policy's governed scope.** The session policy lists the +package managers it governs. Do **not** *proactively* onboard a PM the policy +doesn't govern (e.g. a stray `Dockerfile` when only `pypi`/`npm` are governed) — +those are intentionally out of scope. An **explicit user request** to set up any +PM still works (Step 1's user-mention signal and Step 2's AskQuestion for an +unlisted PM apply as usual). + +## Prerequisites + +- `jf setup` **mutates user state** (`~/.npmrc`, `~/.docker/config.json`, …). + Confirm before the first `jf setup` in a session unless the user explicitly + requests silent/non-interactive setup. +- Reading [`../jfrog/SKILL.md`](../jfrog/SKILL.md) is required — done as Step 0.1 below. + +**Out of scope:** CLI install/login (`../jfrog/references/…`). + +## Gotchas + +- **Always pass `--repo` and `--server-id`** — omitting `--repo` fails when + multiple repos match. See [`jf-setup-command.md`](references/jf-setup-command.md). +- **`jf setup` overwrites PM config** without backup — skip PMs whose binding + already matches (Step 1, signal 2). +- **Docker / Podman — prefix or stop.** `jf setup docker` writes creds only; + bare `docker pull <img>` hits Docker Hub. Complete setup, then pull via + `<host>/<repoKey>/<img>`. +- **Binding holds decisions, not credentials** — never write tokens into + `.jfrog/local/package-resolution.json`. + +## References + +| File | When to read | +|------|--------------| +| [`references/jf-setup-command.md`](references/jf-setup-command.md) | CLI flags, supported PMs, exit-code contract, `jf setup --help` | +| [`references/global-cache-file.md`](references/global-cache-file.md) | Global cache shape, resolution classes, jq one-liners | +| [`references/workspace-binding.md`](references/workspace-binding.md) | Workspace binding schema, PM → type map, merge semantics | + +## Step 0 — Read the base skill, then ensure `jf` is ready + +1. **Read [`../jfrog/SKILL.md`](../jfrog/SKILL.md) fully first — always, before any + `jf` command, even when `jf` is already configured.** It carries the `jf` + invariants this skill relies on. After reading, run that skill's + *Environment check* (and export `JFROG_CLI_USER_AGENT`) before the first + `jf` call. +2. Ensure `jf` + a configured server (`<SID>`). If `jf config show` already + succeeds, skip to Step 1; otherwise: + - **`jf --version`** missing → install per + [`../jfrog/references/jfrog-cli-install-upgrade.md`](../jfrog/references/jfrog-cli-install-upgrade.md). + - **`jf config show`** empty → login per + [`../jfrog/references/jfrog-login-flow.md`](../jfrog/references/jfrog-login-flow.md) + or `jf config add` with access-token (Bearer-only). +3. Do not run `jf setup` until both succeed. Confirm before install/login. + +## Step 1 — Identify package managers to bind + +Combine four signals, in order; intersect with `jf setup --help` supported list: + +1. **Explicit user mention.** Map aliases: python → `pip`/`poetry`; java → + `maven`/`gradle`; node → `npm`/`yarn`/`pnpm` by lockfile. +2. **Workspace binding** — read `.jfrog/local/package-resolution.json`. Drop PMs + already bound to the same key unless recovering from 401/403 (re-run same + key). PM → type table: [`workspace-binding.md`](references/workspace-binding.md). +3. **Workspace manifests** when still ambiguous: + + | Manifest file | Package manager | + |---|---| + | `package.json`, `pnpm-lock.yaml`, `yarn.lock` | `npm` (+ `yarn`/`pnpm` if lockfiles present) | + | `requirements.txt`, `Pipfile` | `pip` (`pipenv` for `Pipfile`) | + | `pyproject.toml` | `poetry` if `[tool.poetry]`; else `pip` | + | `pom.xml` | `maven` | + | `build.gradle`, `build.gradle.kts` | `gradle` | + | `go.mod` | `go` | + | `Dockerfile`, `compose.yaml`, `docker-compose.yml` | `docker` / `podman` | + | `*.csproj`, `NuGet.Config` | `nuget` / `dotnet` | + | `Chart.yaml` | `helm` | + +4. **`jf setup --help`** — filter candidates; never hardcode the PM list. See + [`jf-setup-command.md`](references/jf-setup-command.md). Unsupported PM → + report gap, skip. + +## Step 2 — Get the resolved repo + +For each `<pm>`, recover `<repoKey>` and `<serverId>` from the first source +available: + +1. **"Resolved URLs for this session"** table (default). Parse `<repoKey>` + from URL; `<serverId>` from host. +2. **Workspace binding** — if table was trimmed. `repositories.<type>`. +3. **Global cache** — last resort only; never overrides (1) or (2). See + [`global-cache-file.md`](references/global-cache-file.md). + +Cache disagreeing with (1)/(2) is not a reason to change the repo. + +**Don't choose a repo yourself:** no listing, enumerating, probing, or iterating +`--server-id` to pick one, and don't second-guess the resolver — use resolver +output only. If the user explicitly asks to browse repos, list them via +`jf api "/artifactory/api/repositories?type=virtual&packageType=<pm>"` (filter by +repo type — prefer `virtual` — and package type), then let the user choose; the +agent still never makes the choice on its own. + +### Unresolved repo key + +Ask via AskQuestion: + +> No default repo for `<pm>` on `<SID>`. +> Which Artifactory repository should I use? (repo key, or `abort`.) + +Cap at **2 answers per PM**, then abort. User may override repo only, never server. + +## Step 3 — Confirm, run `jf setup`, persist binding + +1. Present the plan, one row per PM: + + ```text + <pm> → <repoKey> on <SID> (source: resolver) + <pm> → <repoKey> on <SID> (source: user-supplied) + ``` + +2. Show binding diffs when the repo key changes. + +3. **Confirm** via AskQuestion (`apply` / `change repos` / `abort`) unless the + user explicitly requested silent/non-interactive setup — then run directly. + +4. Sequentially, one PM at a time: + + ```bash + jf setup <pm> --server-id <SID> --repo <repoKey> [--project <key>] + ``` + +5. **Exit code `0` = success** — merge binding (step 6). On non-zero, **stop**, + surface CLI output verbatim, offer alternate repo or `abort` (2-answer cap). + +6. On success, merge into `.jfrog/local/package-resolution.json` per + [`workspace-binding.md`](references/workspace-binding.md): + + ```json + { "repositories": { "<pkgType>": "<repoKey>" } } + ``` + + Map PM → type via the reference table. Merge atomically. + +## Step 4 — Load the routing policy + +If this session started with the "routing NOT READY" (enforce) notice, that +notice includes a refresh command (`node <plugin>/modules/package-resolution/scripts/print-policy.mjs`). +After Step 3 succeeds, run that exact command and treat its stdout as the +authoritative, now-current policy — it prints the resolved Artifactory URLs and +hard rules. Continue the original request using those URLs. + +If the command prints nothing, routing is off by config +(`packageResolution.enabled` is not `true`) — an admin opt-in. Report that to +the user and let them decide whether to enable it. diff --git a/plugins/jfrog/skills/jfrog-setup-package-managers/references/global-cache-file.md b/plugins/jfrog/skills/jfrog-setup-package-managers/references/global-cache-file.md new file mode 100644 index 0000000..1287964 --- /dev/null +++ b/plugins/jfrog/skills/jfrog-setup-package-managers/references/global-cache-file.md @@ -0,0 +1,114 @@ +# `package-resolution.json` — Global Resolver Cache + +The session-start hook runs a small resolver that picks the Artifactory +repository key per package type for the current JFrog server and caches +the result in: + +``` +~/.jfrog/skills-cache/package-resolution.json +``` + +This skill **reads** that file in Step 2 to recover the repo key per PM +without re-doing discovery. The file is the canonical, machine-readable +mirror of the "Resolved URLs for this session" table that the hook +injects into agent context — the latter can be trimmed by long-context +pruning, the file cannot. + +> This is a **read-only contract** for this skill. The cache is written by +> the session-start hook; never write or hand-edit it. + +> **Not** the workspace binding file — that lives at +> `.jfrog/local/package-resolution.json` (see [`workspace-binding.md`](workspace-binding.md)). + +## Shape + +```json +{ + "schemaVersion": 1, + "servers": { + "<serverId>": { + "repositories": { + "npm": "npm-virtual", + "pypi": "pypi-virtual", + "maven": "libs-release", + "go": "go-virtual", + "docker":"docker-virtual", + "helm": "helm-virtual", + "nuget": "nuget-virtual" + }, + "cached_at": "2026-05-27T09:30:00Z", + "source": "verified", + "agentsConfigMtimeMs": 1719158400000 + } + } +} +``` + +Each `servers.<serverId>` entry holds `repositories`, `cached_at`, `source`, and +`agentsConfigMtimeMs` (mtime of `~/.jfrog/agents-conf.json` at last refresh). +The workspace binding file at +[`.jfrog/local/package-resolution.json`](workspace-binding.md) holds +only `repositories`. The map key **is** the `serverId`. + +| Field | Meaning | +|---|---| +| `schemaVersion` | Always `1` for this schema. | +| `servers.<serverId>.repositories.<pkgType>` | Resolver's chosen repo key for this package type, on this server. **Missing key = `unresolved`** for that PM. | +| `servers.<serverId>.cached_at` | ISO-8601 timestamp of the last refresh. TTL from `packageResolution.cacheTtlDays` in agents-conf.json (default 7). | +| `servers.<serverId>.agentsConfigMtimeMs` | Invalidates cache when `~/.jfrog/agents-conf.json` changes. | +| `servers.<serverId>.source` | `verified` = keys from agents-conf.json checked via `GET /api/repositories/{key}`; `agents-config` = trusted without HTTP (`verifyRepos: false`). | + +Package type keys used in the file are `npm`, `pypi`, `maven`, `go`, +`docker`, `helm`, `nuget`. Note `pypi` (not `pip`) — same convention the +JFrog API uses. The PM names accepted by `jf setup` (`pip`, `poetry`, +`gradle`, `pnpm`, `yarn`, `podman`, `dotnet`, `pipenv`, `twine`) collapse +onto these package-type keys. + +## Three result classes per PM + +When you look up a PM in this file, you get one of: + +| Class | Detect | What the resolver did | HTTP-verified? | +|---|---|---|---| +| **resolved (verified)** | `repositories.<pkg>` present, `source` is `verified` | Key from `~/.jfrog/agents-conf.json` `defaultGlobalRepos`, checked via `GET /api/repositories/<key>` | **Yes** (at last refresh) | +| **resolved (trusted)** | `repositories.<pkg>` present, `source` is `agents-config` | Key from agents-conf.json with `verifyRepos: false` | **No** | +| **unresolved** | `repositories.<pkg>` is missing | No mapping in agents-conf.json, verify failed, or type not configured | n/a | + +The skill relies on `jf setup --repo` to validate the repo key at apply +time (`GET /api/repositories/<repoKey>` inside the CLI). + +## Reading the cache from the skill + +The current `serverId` for this session comes from `jf config export` +(the default server). Read the cache with: + +```bash +SID="$(jf c show --server-id 2>/dev/null | awk '/Server ID/ {print $3; exit}')" +CACHE="$HOME/.jfrog/skills-cache/package-resolution.json" + +# Get a repo key for a package type (empty if unresolved): +jq -r --arg sid "$SID" --arg type "<pkgType>" '.servers[$sid].repositories[$type] // ""' "$CACHE" + +# Dump every resolved (pkgType, repoKey) pair for this SID: +jq -r --arg sid "$SID" '.servers[$sid].repositories | to_entries[] | "\(.key)\t\(.value)"' "$CACHE" + +# Inspect resolution source: +jq -r --arg sid "$SID" '.servers[$sid].source' "$CACHE" +``` + +If `$CACHE` does not exist, or the SID branch is missing, the hook has +not yet resolved on this machine for this server — fall back to reading +the injected "Resolved URLs for this session" table in agent context +(parse the URL to recover `repoKey`), and if that is also absent, treat +every PM as `unresolved` and prompt the user (Step 2). + +The resolver refreshes stale entries on session start (TTL + agents-conf.json mtime). +This skill never invalidates the cache — if `jf setup` fails on a repo key, ask the user. + +## Not in this file + +These belong elsewhere and the skill must not look for them here: + +- Tokens, credentials, refresh tokens. (Stored by `jf config`.) +- Per-workspace bindings. (Stored in + [`.jfrog/local/package-resolution.json`](workspace-binding.md).) diff --git a/plugins/jfrog/skills/jfrog-setup-package-managers/references/jf-setup-command.md b/plugins/jfrog/skills/jfrog-setup-package-managers/references/jf-setup-command.md new file mode 100644 index 0000000..12eeaf1 --- /dev/null +++ b/plugins/jfrog/skills/jfrog-setup-package-managers/references/jf-setup-command.md @@ -0,0 +1,45 @@ +# `jf setup` Command Reference + +Configures a local PM to resolve from / publish to Artifactory. CLI install +and server config: [`../../jfrog/SKILL.md`](../../jfrog/SKILL.md). + +## Invocation + +```bash +jf setup <pm> --server-id <SID> --repo <repo-key> [--project <project-key>] +``` + +Always pass `--server-id` and `--repo`. Without `--repo`, multiple matching +repos trigger an interactive prompt or error (`Please provide the repository +name using '--repo' flag`). + +`docker` / `podman` use the same shape — CLI validates the repo via +`GET /artifactory/api/repositories/<key>` before configuring. Record +`repositories.docker` in the workspace marker for pull URL composition. + +## Supported PM list + +Drifts across CLI versions — always parse from the installed binary: + +```bash +jf setup --help +``` + +Look for the "Supported package managers are:" line. Never hardcode. + +## Success and failure + +| Signal | Meaning | Action | +|---|---|---| +| Exit `0` | Success | Merge marker, continue | +| Non-zero | Failure | Stop; surface stdout+stderr verbatim | +| `repository <key> not found` | Bad key, wrong type, or permissions | AskQuestion for alternate repo | +| `401` / `403` | Token issue | Re-login same server — [`jfrog-login-flow.md`](../../jfrog/references/jfrog-login-flow.md) | +| Wrong server `404` | Bad `<SID>` | Stop — never iterate servers | + +Do not continue to the next PM after a failure. + +## Agent notes + +- `pyproject.toml` with `[tool.poetry]` → `poetry`; plain PEP 621 → `pip`. +- `jf setup --help` is the authoritative flag reference. diff --git a/plugins/jfrog/skills/jfrog-setup-package-managers/references/workspace-binding.md b/plugins/jfrog/skills/jfrog-setup-package-managers/references/workspace-binding.md new file mode 100644 index 0000000..2b97767 --- /dev/null +++ b/plugins/jfrog/skills/jfrog-setup-package-managers/references/workspace-binding.md @@ -0,0 +1,90 @@ +# `.jfrog/local/package-resolution.json` — Workspace Binding File + +This skill records workspace repo bindings in a file the session-start hook +reads to override org defaults from `~/.jfrog/skills-cache/package-resolution.json`. + +The file is the **decisions** record, not a credential store. Tokens live +in `jf config` and in PM-native files written by `jf setup` itself. + +## Location + +``` +<workspace-root>/.jfrog/local/package-resolution.json +``` + +`<workspace-root>` is the directory the user opened in the IDE — **not** +`$HOME`. Workspace-scoped on purpose: different projects can override +different Artifactory repos. + +## Schema + +```json +{ + "repositories": { + "npm": "<repository-key>", + "pypi": "<repository-key>", + "maven": "<repository-key>", + "go": "<repository-key>", + "docker": "<repository-key>", + "helm": "<repository-key>", + "nuget": "<repository-key>" + } +} +``` + +| Field | Required | Description | +|---|---|---| +| `repositories` | yes | Map keyed by **package type** — same keys as `servers.<serverId>.repositories` in the global resolver cache. Omit package types you do not override. | + +### PM name → package type (when merging after `jf setup`) + +| `jf setup` PM | `repositories` key | +|---|---| +| `npm`, `yarn`, `pnpm` | `npm` | +| `pip`, `pipenv`, `poetry`, `twine` | `pypi` | +| `maven`, `gradle` | `maven` | +| `go` | `go` | +| `docker`, `podman` | `docker` | +| `helm` | `helm` | +| `nuget`, `dotnet` | `nuget` | + +## Operations + +### 1. Load + +Before setup, **read** the file (if it exists). For each PM in the +to-bind set, map the PM to a package type and compare +`repositories.<type>` against what the resolver chose in Step 2: + +| Case | Action | +|---|---| +| Missing type in `repositories` | Run `jf setup` and merge in Step 6. | +| Same repo key | **Skip** `jf setup` — hook already applies overrides on session start. | +| Different repo key | Show diff and confirm via AskQuestion before overwriting. | + +### 2. Write / merge + +After each successful `jf setup`: + +1. Read the current file (treat ENOENT as `{ "repositories": {} }`). +2. Set `repositories[<pkgType>] = <repoKey>` using the PM → type table above. +3. Atomically write `{ "repositories": { ... } }` — preserve other package + types already in the map. + +JSON must use 2-space indent. + +### 3. Never write + +- Credentials (`accessToken`, passwords, …). +- PM-native config paths — those are owned by `jf setup`. + +## Integration contract + +| Consumer | What it reads | +|---|---| +| Session-start hook | `repositories` — first workspace root with this file (multi-root) | +| This skill | Round-trip load → diff → confirm → write | +| `opencode-jfrog-plugin` | **Not updated** — out of scope until it reads this file | + +Changing the `repositories` key semantics is a breaking change; coordinate +with the hook before altering them. diff --git a/plugins/jfrog/skills/jfrog/SKILL.md b/plugins/jfrog/skills/jfrog/SKILL.md index 973ccf4..18e973e 100644 --- a/plugins/jfrog/skills/jfrog/SKILL.md +++ b/plugins/jfrog/skills/jfrog/SKILL.md @@ -18,7 +18,7 @@ compatibility: >- Requires jq on PATH. metadata: role: base - version: "0.16.0" + version: "0.19.0" --- # JFrog Skill @@ -93,12 +93,11 @@ bash <skill_path>/scripts/check-environment.sh <model-slug> # stderr: JSON state (cached 24h at ${JFROG_CLI_HOME_DIR:-$HOME/.jfrog}/skills-cache/jfrog-skill-state.json) ``` -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`. +Pass your own model slug, lowercased, with version (e.g. `opus-4.7`, +`gpt-5.6-sol`, `gemini-2.5-pro`, `composer-2-fast`). Examples, not an +allowlist — emit a new/unlisted name verbatim, not `unknown`. Not +harness/role (`subagent`, `agent`) or bare family (`claude`, `gpt`); +subagents inherit the parent's slug. `unknown` only if truly unidentifiable. ### Export `JFROG_CLI_USER_AGENT` once per bash invocation @@ -112,9 +111,12 @@ jf api /artifactory/api/system/version ``` Do **not** repeat the assignment per `jf` call (`JFROG_CLI_USER_AGENT='<UA>' 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 `<UA>` in its prompt; subagents do not re-run the script. +on every line). This is a **session-global invariant**: it applies to *every* +`jf` invocation in the session, including `jf` calls you make while following +any workflow skill that builds on this base skill. Examples elsewhere in this +skill and in `references/*.md` omit the export for readability — the rule is +global. When launching a subagent, pass `<UA>` in its prompt; subagents do not +re-run the script. | Exit | Meaning | |------|---------|