Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .devin-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "jfrog",
"displayName": "JFrog Platform",
"version": "0.3.3",
"version": "0.3.4",
"description": "Official JFrog plugin for Devin. Ships the JFrog skills bundle (platform ops, init, MCP management, AI Catalog, package safety, reference architecture, package-manager setup) and registers the JFrog Platform MCP server (remote HTTP + OAuth).",
"author": {
"name": "JFrog Ltd.",
Expand Down
2 changes: 1 addition & 1 deletion .github/scripts/sync-skills-vendor.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"repo": "jfrog/jfrog-skills",
"pin": "v0.32.0",
"pin": "v0.33.0",
"paths": [
"skills"
]
Expand Down
43 changes: 21 additions & 22 deletions skills/jfrog-init/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ description: Set up and verify the JFrog plugin. Run on first install, to comple
disable-model-invocation: true
compatibility: >-
Requires Node.js 18 or newer, and network access to the JFrog platform.
allowed-tools: Bash(node --version) Bash(node "${CLAUDE_SKILL_DIR}/scripts/jfrog-detect-catalog-runtime.mjs"*) Bash(node "${CLAUDE_SKILL_DIR}/scripts/jfrog-detect-jf-cli.mjs"*) Bash(node "${CLAUDE_SKILL_DIR}/scripts/jfrog-detect-jf-config.mjs"*) Bash(node "${CLAUDE_SKILL_DIR}/scripts/jfrog-detect-jfrog-mcp.mjs"*) Bash(node "${CLAUDE_SKILL_DIR}/scripts/jfrog-detect-project.mjs"*) Bash(node "${CLAUDE_SKILL_DIR}/scripts/jfrog-detect-server-ping.mjs"*) Bash(node "${CLAUDE_SKILL_DIR}/scripts/jfrog-re*.mjs"*) Bash(node "${CLAUDE_SKILL_DIR}/scripts/jfrog-state-file.mjs" get*) Bash(node "${CLAUDE_SKILL_DIR}/scripts/jfrog-state-file.mjs" path*) Bash(node -e "import('${CLAUDE_SKILL_DIR}/scripts/jfrog-resolve-mcp-config.mjs').then(function(m){console.log(m.detectHarness())})") Bash(npx --version) Bash(uname:*) AskUserQuestion
allowed-tools: Bash(node --version) Bash(node "${CLAUDE_SKILL_DIR}/scripts/jfrog-detect-catalog-runtime.mjs"*) Bash(node "${CLAUDE_SKILL_DIR}/scripts/jfrog-detect-jf-cli.mjs"*) Bash(node "${CLAUDE_SKILL_DIR}/scripts/jfrog-detect-jf-config.mjs"*) Bash(node "${CLAUDE_SKILL_DIR}/scripts/jfrog-detect-jfrog-mcp.mjs"*) Bash(node "${CLAUDE_SKILL_DIR}/scripts/jfrog-detect-project.mjs"*) Bash(node "${CLAUDE_SKILL_DIR}/scripts/jfrog-detect-server-ping.mjs"*) Bash(node "${CLAUDE_SKILL_DIR}/scripts/jfrog-reinstall-jfrog-plugin.mjs"*) Bash(node "${CLAUDE_SKILL_DIR}/scripts/jfrog-state-file.mjs" get*) Bash(node "${CLAUDE_SKILL_DIR}/scripts/jfrog-state-file.mjs" path*) Bash(node -e "import('${CLAUDE_SKILL_DIR}/scripts/jfrog-resolve-mcp-config.mjs').then(function(m){console.log(m.detectHarness())})") Bash(npx --version) Bash(uname:*) AskUserQuestion
metadata:
role: workflow
---
Expand Down Expand Up @@ -72,8 +72,9 @@ absolute path of this file's directory yourself, same as before.
Node (Step 1) or `jf` (Step 2); `AskUserQuestion` picker for
web-login vs. token (Step 3/4); `AskUserQuestion` picker for project
selection (Step 6). Everything else is read-only except Step 5's
placeholder substitution, Step 8's `~/.netrc` write, and the Final
summary's state write.
placeholder substitution (plus, for kiro-cli, creating or merging the
`jfrog` entry into `~/.kiro/settings/mcp.json`), Step 8's `~/.netrc`
write, and the Final summary's state write.
- **Never surface the checklist.** Run silently — no step narration, no
raw JSON/exit codes, no branch-reasoning said out loud. See
[Customer-facing output](#customer-facing-output).
Expand Down Expand Up @@ -139,6 +140,11 @@ user needs to know or act on:
follow silently, not to summarize out loud — the field names in it are
never user-facing. The only output the user sees at an ask point is
the prompt itself.
- **Do not** narrate whether the `AskUserQuestion` tool is available in
the current harness before falling back to the plain-text prompt
(e.g. "the AskUserQuestion tool isn't available here, I'll present
this as a plain question instead"). If it isn't available, silently
use the plain-text fallback already documented for that ask point.
- **Do not** announce that you're about to run the checklist, or name
which check comes first — not even generically ("I'll run the setup
checklist silently, starting with the JFrog CLI check" is itself a
Expand Down Expand Up @@ -331,7 +337,14 @@ blocks) — required behavior, not optional background.

## Step 5: JFrog MCP plugin file has a jfrog entry?

If you are Kiro or Kiro CLI, prefix the command with
`JFROG_INIT_HARNESS=kiro` or `JFROG_INIT_HARNESS=kiro-cli` on the
same line — no other harness needs this. Silent, like every other
setup command in this walk.

```bash
JFROG_INIT_HARNESS=kiro node "${CLAUDE_SKILL_DIR}/scripts/jfrog-detect-jfrog-mcp.mjs" "[server-id]"; rc=$?; true
JFROG_INIT_HARNESS=kiro-cli node "${CLAUDE_SKILL_DIR}/scripts/jfrog-detect-jfrog-mcp.mjs" "[server-id]"; rc=$?; true
node "${CLAUDE_SKILL_DIR}/scripts/jfrog-detect-jfrog-mcp.mjs" "[server-id]"; rc=$?; true
```

Expand All @@ -342,7 +355,9 @@ it for the placeholder fix instead of re-resolving from scratch.
**Read-only against the JFrog plugin's own `mcp.json` — with one
exception: automatic placeholder substitution** of an unresolved
`${JFROG_PLATFORM_URL}` / `${JFROG_URL}` with the real JPD URL from
`jf config`.
`jf config` (plus, for kiro-cli, adding the `jfrog` entry to
`~/.kiro/settings/mcp.json` when no plugin has put one there — see
`references/mcp-plugin-config.md`).

**Stop and read `references/mcp-plugin-config.md` in full** — exactly
how the substitution works, the per-harness plugin-config paths, and
Expand All @@ -361,24 +376,8 @@ node "${CLAUDE_SKILL_DIR}/scripts/jfrog-detect-project.mjs" "[server-id]" "[proj
the exact "reuse `<KEY>`?" `AskUserQuestion` and the jpdUrl-drift check
this step requires, not optional background.

**Where the project list comes from.** `jfrog-detect-project.mjs` fetches
`GET <JPD>/access/api/v1/projects` (the
[GetProjectsList](https://docs.jfrog.com/projects/reference/getprojectslist)
endpoint, authenticated with credentials from `jf config export`) once
per walk and caches it in memory for a short TTL (`lib/project-cache.mjs`)
— the interactive picker re-invokes this script once per user attempt,
and re-enumerating on every typed guess would be wasted network traffic.
This is the list every "enumerated project list" / `candidatesWithNames`
reference below draws from.

**Name-or-key input.** The user answers with **either** the project's
canonical key OR its display name — whichever is easier for them.
`jfrog-detect-project.mjs` resolves it against the enumerated project
list (exact key, exact name, then progressively fuzzier tiers — see
`references/project-matching.md` for the exact algorithm), confirms
existence, and emits the canonical key on green in the JSON
`resolvedKey` field. An ambiguous input exits red with `candidates`
listing the tied keys.
Name-or-key input is resolved via exact match, then progressively
fuzzier tiers — see `references/project-matching.md` for the algorithm.

**Picking a project, interactively.** Whenever the detector needs the
user to choose — no input was passed, the typed input didn't match
Expand Down
15 changes: 11 additions & 4 deletions skills/jfrog-init/references/how-to-ask-user.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ When the skill needs a Yes/No answer, a selection, or any other input
from the user, use the **native interactive prompt tool** built into
your harness so the user can click or select rather than type:

| Harness | Preferred tool |
|-------------|------------------------|
| Claude Code | `AskUserQuestion` |
| Codex | `request_user_input` |
| Harness | Preferred tool |
|-----------------|------------------------|
| Claude Code | `AskUserQuestion` |
| Codex | `request_user_input` |
| Kiro / Kiro CLI | none — no native prompt tool exists; go straight to the plain-text fallback |

Each reference file specifies the question text and option labels; use
your harness's native tool to present them. Native prompt tools already
Expand All @@ -17,3 +18,9 @@ add a duplicate "Other" option yourself.
**Fallback**: if no native prompt tool is available, or the tool
returns without a selection, surface the question as plain text in
your reply — never silently stop without presenting it.

**Kiro / Kiro CLI**: check `$JFROG_INIT_HARNESS` (already exported by
Step 5) before reaching for `AskUserQuestion` — if it's `kiro` or
`kiro-cli`, skip the tool call entirely and use the plain-text fallback
directly. Calling it anyway surfaces a "tool does not exist" error to
the user before you fall back.
41 changes: 36 additions & 5 deletions skills/jfrog-init/references/mcp-plugin-config.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,10 @@ placeholder pattern anywhere in the file, it calls
file is not preserved byte-for-byte.
5. Is idempotent — subsequent runs find no placeholder and no-op.

This is the ONLY place `/jfrog-init` writes to the plugin-owned
`mcp.json`. Everything else in Step 5 is read-only.
This is the only place `/jfrog-init` writes to a harness's plugin-owned
`mcp.json` — with one further exception for Kiro CLI: ensuring a `jfrog`
entry exists in `~/.kiro/settings/mcp.json`, which no plugin ships (see
below). Everything else in Step 5 is read-only.

**Per-harness plugin-owned config file:**

Expand All @@ -55,11 +57,26 @@ This is the ONLY place `/jfrog-init` writes to the plugin-owned
| VS Code | `~/.vscode/agent-plugins/github.com/jfrog/vscode-plugin/plugin/.mcp.json` |
| Claude Code | `~/.claude/plugins/cache/<marketplace>/jfrog/<version>/.mcp.json` (glob) |
| Codex | `$CODEX_HOME/plugins/cache/codex-plugin/jfrog/<version>/.mcp.json` (glob → newest; `$CODEX_HOME` defaults to `~/.codex`) |
| Kiro (IDE) | `~/.kiro/powers/installed/jfrog-kiro-power/mcp.json` (stable path) |
| Kiro CLI | `~/.kiro/settings/mcp.json` — Kiro's own global MCP config, not shipped by any plugin, so the `jfrog` entry is **created or merged in** with a placeholder url, then substituted like every other row above |

The Kiro CLI merge is additive and never destructive: the file normally
holds the user's other MCP servers, so a `jfrog` entry that already has a
url is left untouched (a placeholder in it is the substitution step's
job), other servers and the file's mode are preserved, a symlinked config
stays a symlink, and a file that isn't valid JSON is reported rather than
rewritten.

Harness detection (in priority order): `CODEX_SANDBOX` / `CLAUDECODE` /
`CURSOR_TRACE_ID` / `VSCODE_PID` / `TERM_PROGRAM`. Override with
`JFROG_INIT_HARNESS=claude|cursor|vscode|codex` or a specific file via
`JFROG_INIT_MCP_CONFIG=/abs/path`.
`JFROG_INIT_HARNESS=claude|cursor|vscode|codex|kiro|kiro-cli` or a
specific file via `JFROG_INIT_MCP_CONFIG=/abs/path`. Neither Kiro target
has an auto-detect signal yet — both are reachable only via the
`JFROG_INIT_HARNESS=kiro` / `kiro-cli` overrides.

`SKILL.md`'s Step 5 already has you export `JFROG_INIT_HARNESS=kiro` /
`kiro-cli` up front when you're running as one of those two — before
the detector ever runs, so Exit 3 below isn't the trigger for it.

**What the detector verifies** (three things):

Expand All @@ -83,7 +100,7 @@ reachable.
credentials, never through the JFrog MCP, so a broken or
missing plugin `mcp.json` doesn't affect whether those checks are
accurate — there's nothing to gain by stopping the walk over it.
Tell the two red causes apart from the detector's `detail` for the
Tell the red causes apart from the detector's `detail` for the
Final Summary note:
- Plugin file missing / empty / lacks a valid `jfrog` entry. Fix:
**reinstall or update the JFrog plugin.** If the user asks why or
Expand All @@ -98,8 +115,22 @@ reachable.
- Plugin file has a placeholder and automatic substitution failed
with no url set for the resolved server-id. Fix: **resolve `jf
config`**. Reinstalling the plugin does not fix this.
- Kiro CLI only: it could not create or update its own
`~/.kiro/settings/mcp.json` — no plugin ships this file, so there's
nothing to reinstall. The detail names the actual cause (e.g. the
parent path blocked by a non-directory, or a permissions error).
Fix: **correct the file or parent-directory permissions/path**,
then re-run.
- (Exit 3 only) Harness could not be detected, or plugin file is
invalid JSON / unreadable. Show the raw detector error in the note.
**Do not react to this by guessing a harness or trying
`JFROG_INIT_HARNESS` values to see what resolves it.** If this is
Kiro or Kiro CLI, the override was already exported before the
detector's first run (top of Step 5), so it should not reach Exit
3 for that cause at all. Otherwise this is Exit 3, non-blocking
like every other cause above: note it and move on to Step 6 in the
same turn, with zero visible pause — do not stop to read this file
or any other reference doc over it.
- **Exit 2 (`ask`)** → the one outcome that still blocks: placeholder
present, but the jf server-id is ambiguous — every step from here on
needs a resolved server-id, so there's nothing to skip ahead to.
Expand Down
10 changes: 10 additions & 0 deletions skills/jfrog-init/references/project-picker.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,16 @@ over the generic first-two when present:
accepted input format (no *"(name or key)"*, no *"you can type a key
or name"*, no *"either the display name or key works"*).

- **If `AskUserQuestion` is not available as a tool at all** (some
harnesses don't expose it — check `$JFROG_INIT_HARNESS`: `kiro` /
`kiro-cli` never have it, so skip the tool call and go straight to
the fallback instead of calling it and surfacing its error), use the
plain-text fallback line above —
*"Which project do you want to use?"*, nothing else — regardless of
how many entries `candidatesWithNames` has. Never explain that the
tool is unavailable, never mention the candidate count or that a
picker would otherwise have been used; the user only ever sees the
one line.
- **Never surface the full candidate list or a count** to the user in
any case — the picker's two options (plus "Other") or the plain-text
fallback are the entire user-facing surface.
Expand Down
7 changes: 7 additions & 0 deletions skills/jfrog-init/references/project-resolution-branches.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@
full whenever `jfrog-detect-project.mjs` returns anything other than a
clean exit 0.

**Everything below — exit codes, `unresolved`, and which bullet you
land on — is reasoning for you to follow silently, never to narrate.**
Never repeat this table's own words back to the user (e.g. "Exit 2,
no `unresolved`" or "this is an ask with no input"). The only output
the user sees is the resulting prompt itself, the raw detector error
where one is shown, or the Final Summary.

- **Exit 2 (`ask`) with `"unresolved": "server"`** → not a project ask
— the server-id is ambiguous. Follow "Resolving `<server-id>` for
Steps 4-7" in `SKILL.md` (prompt for a server from `candidates`), then
Expand Down
7 changes: 5 additions & 2 deletions skills/jfrog-init/scripts/jfrog-detect-all.mjs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
#!/usr/bin/env node
// Runs the detectors in dependency order and reports one JSON summary line.
// Idempotent, read-only, zero mutation (aside from the state-file hint on
// green) — safe to run repeatedly.
// Idempotent and safe to run repeatedly. Mostly read-only — with two
// exceptions: (1) for the kiro-cli harness, Step 5 (detectJfrogMcp →
// resolveMcpConfig) creates or merges ~/.kiro/settings/mcp.json when the
// jfrog entry is absent, the same write that jfrog-detect-jfrog-mcp.mjs
// performs; (2) on green, a state-file hint is written to ~/.jfrog/setup.json.
//
// Usage: node jfrog-detect-all.mjs [server-id] [project-input]
//
Expand Down
26 changes: 25 additions & 1 deletion skills/jfrog-init/scripts/jfrog-reinstall-jfrog-plugin.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,17 @@
// Always exits 0 after printing.

import { existsSync } from "node:fs";
import { homedir } from "node:os";
import { join } from "node:path";
import { detectHarness, resolveMcpConfig } from "./jfrog-resolve-mcp-config.mjs";

const resolved = resolveMcpConfig();
const harness = detectHarness();
// kiro-cli's resolveMcpConfig() writes to disk as a side effect (ensureKiroCliJfrogEntry),
// which this diagnostic-only script must never do — derive the path directly instead.
const resolved =
harness === "kiro-cli"
? { path: join(homedir(), ".kiro", "settings", "mcp.json") }
: resolveMcpConfig();

console.log(`The JFrog MCP entry lives inside the JFrog plugin's own mcp.json file.
This script never writes to it — it only diagnoses and prints the fix.
Expand Down Expand Up @@ -56,12 +63,27 @@ Restart VS Code, then re-run /jfrog-init.`);

Restart Codex, then re-run /jfrog-init.`);
break;
case "kiro":
console.log(`Kiro IDE:
Open the Powers panel → Add Custom Power → Import from GitHub →
enter the JFrog Kiro Power repository URL.
Restart Kiro, then re-run /jfrog-init.`);
break;
case "kiro-cli":
console.log(`Kiro CLI:
No plugin reinstall needed — the jfrog entry in ~/.kiro/settings/mcp.json
is created automatically by /jfrog-init. Re-run /jfrog-init to recreate it.
If /jfrog-init reports the file is invalid, open ~/.kiro/settings/mcp.json,
fix the JSON (keep the other MCP server entries), then re-run /jfrog-init.`);
break;
default:
console.log(`Reinstall the JFrog plugin in whichever IDE you're using:
Cursor: Settings → Plugins → search "JFrog" → reinstall.
VS Code: code --install-extension JFrog.jfrog-vscode-extension --force
Claude Code: claude plugin install jfrog-beta/jfrog
Codex: codex plugin marketplace add jfrog/codex-plugin && codex plugin add jfrog@codex-plugin
Kiro: Powers panel → Add Custom Power → Import from GitHub.
Kiro CLI: no reinstall needed — entry is created automatically by /jfrog-init.

Restart the IDE afterwards, then re-run /jfrog-init.`);
}
Expand All @@ -74,6 +96,8 @@ Expected plugin-owned paths (for reference):
Claude: ~/.claude/plugins/cache/<marketplace>/jfrog/<version>/.mcp.json
Codex: $CODEX_HOME/plugins/cache/codex-plugin/jfrog/<version>/.mcp.json
($CODEX_HOME defaults to ~/.codex)
Kiro: ~/.kiro/powers/installed/jfrog-kiro-power/mcp.json
Kiro CLI: ~/.kiro/settings/mcp.json (not plugin-owned; created by /jfrog-init)
`);

if (resolved.path && existsSync(resolved.path)) {
Expand Down
Loading
Loading