In examples below, <skill_path> is this skill's directory (parent of
scripts/ / references/).
Apply the session hook's repo pick via jf setup,
then record it in .jfrog/local/package-resolution.json
via scripts/merge-workspace-binding.sh.
jf setup writes package-manager-native config (.npmrc, pip.conf, uv.toml, …); the binding
lets the hook re-apply on later sessions.
At a glance (always-read core)
Every jf setup this session:
- Cover base
../jfrog/SKILL.mdAt-a-glance / Tier A (Step 0.1) →<UA>,--server-idplacement, single-server, stop-don't-switch. Prefer full base SKILL.md when you can; Tier B (cli-gotchas/jf-api/ …) only if the next action needsjf api/ advanced CLI - Always
--repo+--server-id.<repoKey>← Step 2 (table / binding / global-cache) or user override / unresolved AskQuestion; never self-discover.<SID>← resolver only (never user-selected) - Confirm before first
jf setupunless user asked silent / non-interactive - Exit 0 → merge binding; non-zero → stop, surface CLI verbatim, offer
alternate repo or
abort(2-answer cap) - Binding = decisions, not creds — never write tokens into
.jfrog/local/package-resolution.json - Unresolved / failed: ask with failure verbatim — never switch servers
- Never skip Gotchas + base Tier A hard
rules (
../jfrog/SKILL.mdCautious execution / Server selection / Tier A gotcha floor). Fullcli-gotchas.mdis Tier B — not required forjf setup
Steps: 0 → 1 → 2 → 3 → 4
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 (via
scripts/merge-workspace-binding.sh) when package-manager 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 package manager 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
package manager still works (Step 1's user-mention signal and Step 2's AskQuestion for an
unlisted package manager apply as usual).
Prerequisites
jf setupmutates user state (~/.npmrc,~/.docker/config.json, …). Confirm before the firstjf setupin a session unless the user explicitly requests silent/non-interactive setup.- Covering base At-a-glance / Tier A is required — done as Step 0.1 below.
Out of scope: CLI install/login (../jfrog/references/…).
Gotchas — hard rules (never skip)
Not tips. Do/don'ts and known traps for jf setup — follow every bullet
before binding. Also honor base Tier A hard rules from
../jfrog/SKILL.md (Cautious execution, Server selection,
Tier A gotcha floor). Full cli-gotchas.md is Tier B — load only if this
session also needs jf api / advanced CLI.
- Always pass
--repoand--server-id— omitting--repofails when multiple repos match. Seejf-setup-command.md. jf setupoverwrites package-manager config without backup — skip package managers whose binding already matches (Step 1, signal 2).- Docker / Podman — prefix or stop.
jf setup dockerwrites creds only; baredocker 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. - Persist binding with the merge script — after each successful
jf setup, runscripts/merge-workspace-binding.sh(Step 6). Do not hand-edit the JSON. gradle≠maven. Bind underrepositories.gradle, neverrepositories.maven.- Yarn / Poetry — not APR zero-touch; bind only on explicit user ask (Step 1).
References
| File | When to read |
|---|---|
references/jf-setup-command.md |
CLI flags, supported package managers, exit-code contract, jf setup --help |
references/global-cache-file.md |
Global cache shape, resolution classes, jq one-liners |
references/workspace-binding.md |
Workspace binding schema, package-manager → type map, merge script |
scripts/merge-workspace-binding.sh |
After each successful jf setup — deterministic binding merge (jq required) |
Step 0 — Read the base skill, then ensure jf is ready
- Cover base skill At-a-glance / Tier A before the first non-exempt
jf(even whenjfis already configured). Prefer reading../jfrog/SKILL.mdin full when you can; the At-a-glance Tier A floor is enough forjf setup/ package-manager binding. Load Tier B (cli-gotchas.md,jf-api.md, …) only if the next action needsjf api/ advanced CLI. Then run that skill's Environment check (and exportJFROG_CLI_USER_AGENT) before the firstjfcall. - Ensure
jf+ a configured server (<SID>). Ifjf config showalready succeeds, skip to Step 1; otherwise:jf --versionmissing → install per../jfrog/references/jfrog-cli-install-upgrade.md.jf config showempty → login per../jfrog/references/jfrog-login-flow.mdorjf config addwith access-token (Bearer-only).
- Do not run
jf setupuntil 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:
-
Explicit user mention. Map aliases: python →
pip/uv/pipenv(andpoetryonly if the user named Poetry); java →maven/gradle; node →npm/pnpmby lockfile (yarnonly if the user named Yarn). -
Workspace binding — read
.jfrog/local/package-resolution.json. Drop package managers already bound to the same key unless recovering from 401/403 (re-run same key). Package-manager → type table:workspace-binding.md. -
Workspace manifests when still ambiguous (several package managers of one type may apply — e.g.
requirements.txtanduv.lock):Manifest / signal Package manager package.json,pnpm-lock.yamlnpm(+pnpmifpnpm-lock.yamlpresent)yarn.lock(alone)npm— do not auto-selectyarnrequirements.txtpipPipfilepipenvuv.lockuv— suppresses barepyproject.toml→pip; keeprequirements.txt+uv.lockas multi-PMpyproject.toml[tool.uv]→uv;[tool.poetry]→poetryonly on explicit user ask, else not applicable (do not selectpip); bare PEP 621 with nouv.lock→pippom.xmlmavenbuild.gradle,build.gradle.ktsgradle(bind under typegradle)go.modgoDockerfile,compose.yaml,docker-compose.ymldocker/podman*.csproj,NuGet.Confignuget/dotnetChart.yamlhelmBinary gate (client tools only): missing client on
PATH→ skip as not applicable; do not substitute another package manager or report setup success. Exemptmaven/gradle(config-only). Details:jf-setup-command.md. -
jf setup --help— filter candidates; never hardcode the list. Seejf-setup-command.md. Unsupported → report gap, skip.
Step 2 — Get the resolved repo
For each <package-manager>, recover <repoKey> and <serverId> from the first source
available:
- "Resolved URLs for this session" table (default). Parse
<repoKey>from URL;<serverId>from host. - Workspace binding — if table was trimmed.
repositories.<type>(gradle→repositories.gradle, notmaven). - Global cache — last resort only; never overrides (1) or (2). See
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=<pkgType>"
(Artifactory package type from the binding map — gradle not maven;
uv / pip / pipenv / poetry → pypi), then let the user choose; the
agent still never makes the choice on its own.
Unresolved repo key
Ask via AskQuestion (include the resolver/setup failure text verbatim):
No default repo for
<package-manager>on<SID>. Failure:<verbatim failure>Which Artifactory repository should I use? (repo key, orabort.)
Cap at 2 answers per package manager, then abort. User may override repo only, never server.
Step 3 — Confirm, run jf setup, persist binding
-
Present the plan, one row per package manager:
<package-manager> → <repoKey> on <SID> (source: resolver) <package-manager> → <repoKey> on <SID> (source: user-supplied) -
Show binding diffs when the repo key changes.
-
Confirm via AskQuestion (
apply/change repos/abort) unless the user explicitly requested silent/non-interactive setup — then run directly. -
Sequentially, one package manager at a time:
jf setup <package-manager> --server-id <SID> --repo <repoKey> [--project <key>] -
Exit code
0= success — merge binding (step 6). On non-zero, stop, surface CLI output verbatim, offer alternate repo orabort(2-answer cap). -
On success, run the merge script (do not hand-edit JSON). Pass the IDE workspace root when the shell cwd is not that root:
bash <skill_path>/scripts/merge-workspace-binding.sh \ --package-manager <package-manager> \ --repo <repoKey> \ [--workspace-root <workspace-root>]Requires
jq(same prerequisite as the basejfrogskill). Exit0printsmerged <type> → <repo> into <path>. On non-zero, stop, surface stderr verbatim — do not claim the binding was recorded. Schema and PM → type map:workspace-binding.md.
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.
Before you run jf setup — checklist
At a glance invariants:
- base At-a-glance / Tier A covered;
<UA>exported -
<repoKey>← Step 2 or user override;<SID>← resolver only - confirmed (or explicit silent-setup)
-
jf setup <pm> --server-id <SID> --repo <repoKey> - exit 0 → merge binding (no creds); non-zero → stop + report verbatim; never switch servers
- never skip Gotchas (this skill) + base Tier A hard rules (full
cli-gotchas.mdonly if Tier B path)