From a2be74ff24f6740f711a6dbfdc5a4b66794ac501 Mon Sep 17 00:00:00 2001 From: Yoni Melki Date: Tue, 25 Aug 2026 13:25:15 +0300 Subject: [PATCH 1/7] AX-2162 - Align Cursor README with shared install verify recovery flow Co-authored-by: Cursor --- README.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/README.md b/README.md index 46924e9..7dd0ea8 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ JFrog plugin for [Cursor](https://cursor.com): artifact management, security scanning, supply-chain best practices, and Agent Guard. +> **Install flow:** [Shared install, verify, and recovery guide](https://github.com/jfrog/claude-plugin/blob/main/docs/shared-install-and-verify.md) — canonical cross-harness rules. This README covers **Cursor-only** steps. + ## What's new - **Agent Package Resolution (Preview).** A hook automatically routes the packages your AI agent installs through your JFrog Artifactory instead of public registries. See [Agent Package Resolution](#agent-package-resolution-preview). @@ -46,6 +48,24 @@ Use either the marketplace link from the [Configure Cursor](https://docs.jfrog.c 3. Search for **JFrog** and open the **JFrog** plugin. 4. Choose **Add to Cursor**, then **Add Plugin**. +Run **`/jfrog-init`** after install, **restart Cursor** if MCP config changed, then complete [Verify](#verify). + +--- + +## Verify + +Verification is a required install step, not a troubleshooting fallback: + +1. **Cursor Settings → Plugins** — the JFrog plugin is installed. +2. **`/jfrog-init`** — the readiness walk completes without blocking errors. +3. `jf rt ping` — succeeds against your configured server. + +`JFROG_PLATFORM_URL` is for MCP placeholder resolution only, as described in the +[shared env-var rules](https://github.com/jfrog/claude-plugin/blob/main/docs/shared-install-and-verify.md#environment-variables--what-actually-helps); +setting it does not repair a failed init. If a check fails, fix the reported step, +re-run `/jfrog-init`, and restart — see the +[shared recovery playbook](https://github.com/jfrog/claude-plugin/blob/main/docs/shared-install-and-verify.md#recovery-playbook). + --- ## Authentication From e79a9ce09d4e5b44e61bcb6cb8c12fea0a21b605 Mon Sep 17 00:00:00 2001 From: Yoni Melki Date: Tue, 25 Aug 2026 18:33:53 +0300 Subject: [PATCH 2/7] AX-2162 - Keep Cursor install docs self-contained Inline verify and recovery in this README instead of sending readers to the Claude plugin repository. Co-authored-by: Cursor --- README.md | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 7dd0ea8..fae670d 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,6 @@ JFrog plugin for [Cursor](https://cursor.com): artifact management, security scanning, supply-chain best practices, and Agent Guard. -> **Install flow:** [Shared install, verify, and recovery guide](https://github.com/jfrog/claude-plugin/blob/main/docs/shared-install-and-verify.md) — canonical cross-harness rules. This README covers **Cursor-only** steps. - ## What's new - **Agent Package Resolution (Preview).** A hook automatically routes the packages your AI agent installs through your JFrog Artifactory instead of public registries. See [Agent Package Resolution](#agent-package-resolution-preview). @@ -60,11 +58,17 @@ Verification is a required install step, not a troubleshooting fallback: 2. **`/jfrog-init`** — the readiness walk completes without blocking errors. 3. `jf rt ping` — succeeds against your configured server. -`JFROG_PLATFORM_URL` is for MCP placeholder resolution only, as described in the -[shared env-var rules](https://github.com/jfrog/claude-plugin/blob/main/docs/shared-install-and-verify.md#environment-variables--what-actually-helps); -setting it does not repair a failed init. If a check fails, fix the reported step, -re-run `/jfrog-init`, and restart — see the -[shared recovery playbook](https://github.com/jfrog/claude-plugin/blob/main/docs/shared-install-and-verify.md#recovery-playbook). +`JFROG_PLATFORM_URL` is for MCP placeholder resolution only. Setting it does not +repair a failed `/jfrog-init`. If a check fails, fix the reported step, re-run +`/jfrog-init`, and restart Cursor. + +### Recovery + +| Symptom | Do this | Do **not** do this | +| --- | --- | --- | +| MCP missing after install | Run `/jfrog-init`, complete OAuth if prompted, **restart Cursor**, re-check MCP tools. | Assume `JFROG_PLATFORM_URL` alone will register MCP. | +| `/jfrog-init` stopped at CLI/auth | Follow the skill prompt (`jf config add`, web login, or token path), then **re-run `/jfrog-init`**. | Skip init and only export env vars. | +| Placeholder URL still in plugin MCP config | Fix `jf config` for the intended server, re-run `/jfrog-init`. | Reinstall the plugin when the detector says auth/URL resolution failed. | --- From 6f2644eab6596798489e45dd822378bb592792aa Mon Sep 17 00:00:00 2001 From: Yoni Melki Date: Tue, 25 Aug 2026 19:01:41 +0300 Subject: [PATCH 3/7] AX-2162 - Document JFROG_PLATFORM_URL instead of JFROG_URL Agent Guard credential docs now name the host-only env var only. Co-authored-by: Cursor --- plugins/jfrog/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/jfrog/README.md b/plugins/jfrog/README.md index f32cf8e..301bf8a 100644 --- a/plugins/jfrog/README.md +++ b/plugins/jfrog/README.md @@ -10,7 +10,7 @@ JFrog Platform integration for Cursor — artifact management, security scanning - Toggle the **MCP Server** option ON and save. 3. Set the `JFROG_PLATFORM_URL` environment variable to your JFrog instance (e.g., `mycompany.jfrog.io`). 4. **JFrog CLI** (`jf`) is used by the skills for authentication and REST/GraphQL API operations. If missing, the agent will attempt to install it. You can also install manually via `brew install jfrog-cli` or the [official install script](https://jfrog.com/help/r/jfrog-cli/install-the-jfrog-cli). -5. **JFrog CLI ≥ 2.105.0** (optional) — required if you want the Agent Guard to auto-resolve credentials/server ID from the JFrog CLI instead of `JFROG_URL`/`JFROG_ACCESS_TOKEN` env vars. Older CLIs don't support the `--format` flag used by `jf config show`/`jf config export` for this. +5. **JFrog CLI ≥ 2.105.0** (optional) — required if you want the Agent Guard to auto-resolve credentials/server ID from the JFrog CLI instead of `JFROG_PLATFORM_URL`/`JFROG_ACCESS_TOKEN` env vars. Older CLIs don't support the `--format` flag used by `jf config show`/`jf config export` for this. CLI authentication options: run `jf login` for browser-based setup, or set the `JFROG_ACCESS_TOKEN` environment variable. MCP-based workflows authenticate via **OAuth** and require no additional configuration. From b5ceeef2984eb48eac08e06d8498c22c7885d091 Mon Sep 17 00:00:00 2001 From: Yoni Melki Date: Thu, 27 Aug 2026 15:40:32 +0300 Subject: [PATCH 4/7] Stop naming the wrong Agent Guard env var and add the install-docs CI guard. --- .github/workflows/validate-template.yml | 5 ++ README.md | 9 +-- plugins/jfrog/README.md | 2 +- scripts/validate-install-docs.mjs | 97 +++++++++++++++++++++++++ scripts/validate-install-docs.test.mjs | 68 +++++++++++++++++ 5 files changed, 175 insertions(+), 6 deletions(-) create mode 100644 scripts/validate-install-docs.mjs create mode 100644 scripts/validate-install-docs.test.mjs diff --git a/.github/workflows/validate-template.yml b/.github/workflows/validate-template.yml index c82c73e..c7582a7 100644 --- a/.github/workflows/validate-template.yml +++ b/.github/workflows/validate-template.yml @@ -27,3 +27,8 @@ jobs: - name: Run template validation run: node scripts/validate-template.mjs + + - name: Validate install/recovery docs + run: | + node --test scripts/validate-install-docs.test.mjs + JFROG_PLUGIN_HARNESS=cursor node scripts/validate-install-docs.mjs diff --git a/README.md b/README.md index fae670d..f25688a 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ Before installing, make sure you have: - **Node.js** (≥ 18) — with `npx` on your `PATH`. - **Skill runtime requirements** — `jf` CLI, `jq`, and `curl` on `PATH`, plus a configured JFrog instance. For the minimum versions, see the upstream skills [`Requirements`](https://github.com/jfrog/jfrog-skills/blob/v0.11.0/README.md#requirements). Configure the CLI with `jf config add` — see [Authentication](#authentication). - **JFrog Platform access** (optional) — If you want to use the Agent Guard feature, your JFrog subscription needs to include the AI Catalog entitlement. Contact your JFrog account team if you're unsure whether it's enabled. -- **JFrog CLI ≥ 2.105.0** (optional) — If you want the Agent Guard to auto-resolve credentials/server ID from the JFrog CLI instead of `JFROG_PLATFORM_URL`/`JFROG_ACCESS_TOKEN` env vars. Older CLIs don't support the `--format` flag used by `jf config show`/`jf config export` for this. +- **JFrog CLI ≥ 2.105.0** (optional) — If you want the Agent Guard to auto-resolve the credentials/server ID from the JFrog CLI configuration. Older CLIs don't support the `--format` flag used by `jf config show`/`jf config export` for this. - **JFrog project** (optional) — If you want to use the Agent Guard feature. --- @@ -58,11 +58,10 @@ Verification is a required install step, not a troubleshooting fallback: 2. **`/jfrog-init`** — the readiness walk completes without blocking errors. 3. `jf rt ping` — succeeds against your configured server. -`JFROG_PLATFORM_URL` is for MCP placeholder resolution only. Setting it does not -repair a failed `/jfrog-init`. If a check fails, fix the reported step, re-run -`/jfrog-init`, and restart Cursor. +If a check fails, see [Recovery](#recovery). `JFROG_PLATFORM_URL` is for MCP +placeholder resolution only. Setting it does not repair a failed `/jfrog-init`. -### Recovery +## Recovery | Symptom | Do this | Do **not** do this | | --- | --- | --- | diff --git a/plugins/jfrog/README.md b/plugins/jfrog/README.md index 301bf8a..27bf93a 100644 --- a/plugins/jfrog/README.md +++ b/plugins/jfrog/README.md @@ -10,7 +10,7 @@ JFrog Platform integration for Cursor — artifact management, security scanning - Toggle the **MCP Server** option ON and save. 3. Set the `JFROG_PLATFORM_URL` environment variable to your JFrog instance (e.g., `mycompany.jfrog.io`). 4. **JFrog CLI** (`jf`) is used by the skills for authentication and REST/GraphQL API operations. If missing, the agent will attempt to install it. You can also install manually via `brew install jfrog-cli` or the [official install script](https://jfrog.com/help/r/jfrog-cli/install-the-jfrog-cli). -5. **JFrog CLI ≥ 2.105.0** (optional) — required if you want the Agent Guard to auto-resolve credentials/server ID from the JFrog CLI instead of `JFROG_PLATFORM_URL`/`JFROG_ACCESS_TOKEN` env vars. Older CLIs don't support the `--format` flag used by `jf config show`/`jf config export` for this. +5. **JFrog CLI ≥ 2.105.0** (optional) — required if you want the Agent Guard to auto-resolve the credentials/server ID from the JFrog CLI configuration. Older CLIs don't support the `--format` flag used by `jf config show`/`jf config export` for this. CLI authentication options: run `jf login` for browser-based setup, or set the `JFROG_ACCESS_TOKEN` environment variable. MCP-based workflows authenticate via **OAuth** and require no additional configuration. diff --git a/scripts/validate-install-docs.mjs b/scripts/validate-install-docs.mjs new file mode 100644 index 0000000..33f5268 --- /dev/null +++ b/scripts/validate-install-docs.mjs @@ -0,0 +1,97 @@ +#!/usr/bin/env node +// Copyright (c) JFrog Ltd. 2026 +// Licensed under the Apache License, Version 2.0 +// Validates install/recovery documentation invariants. + +import { readFileSync, existsSync } from 'node:fs'; +import { join } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const repoRoot = process.cwd(); + +const HARNESS_OWN_REPO = { + claude: 'claude-plugin', + codex: 'codex-plugin', + cursor: 'cursor-plugin', + devin: 'devin-plugin', + opencode: 'opencode-jfrog-plugin', + vscode: 'vscode-plugin', +}; + +const ALL_PLUGIN_REPOS = Object.values(HARNESS_OWN_REPO); + +const REQUIRED_README_MARKERS = ['## Verify', '## Recovery']; + +const FORBIDDEN_PATTERNS = [ + { + re: /setting\s+(?:the\s+)?environment\s+variables?\s+after\s+a\s+failed\s+init\s+may\s+repair/i, + message: 'must not claim env vars repair failed init', + }, + { + re: /JFROG_URL/, + message: 'must not document the legacy JFROG_URL env var; use JFROG_PLATFORM_URL', + }, + { + re: /atlassian\.net/i, + message: 'must not reference JFrog Jira (atlassian.net) in repo files', + }, + { + re: /\b(?:AX|MLD)-\d+\b/, + message: 'must not include Jira ticket keys in repo files', + }, +]; + +export function validateInstallDocs({ repoRoot: root, harness }) { + const errors = []; + const readmePath = join(root, 'README.md'); + if (!existsSync(readmePath)) { + return [`${harness}: missing README.md`]; + } + const files = [{ label: 'README.md', text: readFileSync(readmePath, 'utf8') }]; + + const readme = files[0].text; + for (const marker of REQUIRED_README_MARKERS) { + if (!readme.includes(marker)) { + errors.push(`${harness}: README.md missing required marker: ${marker}`); + } + } + + const ownRepo = HARNESS_OWN_REPO[harness]; + const otherRepos = ALL_PLUGIN_REPOS.filter((name) => name !== ownRepo); + + for (const { label, text } of files) { + for (const { re, message } of FORBIDDEN_PATTERNS) { + if (re.test(text)) errors.push(`${harness}: ${label} ${message}`); + } + for (const other of otherRepos) { + if (text.includes(`github.com/jfrog/${other}`)) { + errors.push(`${harness}: ${label} must not link to github.com/jfrog/${other}`); + } + } + } + + return errors; +} + +function main() { + const harness = process.env.JFROG_PLUGIN_HARNESS ?? inferHarness(repoRoot); + const errors = validateInstallDocs({ repoRoot, harness }); + if (errors.length) { + console.error('install-docs validation failed:'); + for (const e of errors) console.error(` - ${e}`); + process.exit(1); + } + console.log('install-docs validation passed'); +} + +function inferHarness(root) { + if (existsSync(join(root, '.codex-plugin'))) return 'codex'; + if (existsSync(join(root, '.devin-plugin'))) return 'devin'; + if (existsSync(join(root, '.claude-plugin'))) return 'claude'; + if (existsSync(join(root, 'plugins', 'jfrog', '.cursor-plugin'))) return 'cursor'; + if (existsSync(join(root, 'plugin', '.claude-plugin'))) return 'vscode'; + if (existsSync(join(root, 'package.json')) && root.endsWith('opencode-jfrog-plugin')) return 'opencode'; + return 'unknown'; +} + +if (process.argv[1] === fileURLToPath(import.meta.url)) main(); diff --git a/scripts/validate-install-docs.test.mjs b/scripts/validate-install-docs.test.mjs new file mode 100644 index 0000000..0836b35 --- /dev/null +++ b/scripts/validate-install-docs.test.mjs @@ -0,0 +1,68 @@ +// Copyright (c) JFrog Ltd. 2026 +import { test } from 'node:test'; +import assert from 'node:assert/strict'; +import { mkdtempSync, writeFileSync } from 'node:fs'; +import { tmpdir } from 'node:os'; +import { join } from 'node:path'; +import { validateInstallDocs } from './validate-install-docs.mjs'; + +function writeReadme(root, body) { + writeFileSync(join(root, 'README.md'), body); +} + +test('validateInstallDocs passes when README has Verify, Recovery, and no other-plugin links', () => { + const root = mkdtempSync(join(tmpdir(), 'cursor-docs-')); + writeReadme(root, '# Cursor\n\n## Verify\n\n1. list plugins\n\n## Recovery\n\n'); + assert.deepEqual(validateInstallDocs({ repoRoot: root, harness: 'cursor' }), []); +}); + +test('validateInstallDocs flags missing Verify section', () => { + const root = mkdtempSync(join(tmpdir(), 'cursor-docs-')); + writeReadme(root, '# Cursor\n\nInstall the plugin.\n'); + const errors = validateInstallDocs({ repoRoot: root, harness: 'cursor' }); + assert.ok(errors.some((e) => e.includes('## Verify'))); +}); + +test('validateInstallDocs flags missing Recovery section', () => { + const root = mkdtempSync(join(tmpdir(), 'cursor-docs-')); + writeReadme(root, '# Cursor\n\n## Verify\n\n1. list plugins\n'); + const errors = validateInstallDocs({ repoRoot: root, harness: 'cursor' }); + assert.ok(errors.some((e) => e.includes('## Recovery'))); +}); + +test('validateInstallDocs rejects contradictory failed-init env-var recovery claims', () => { + const root = mkdtempSync(join(tmpdir(), 'cursor-docs-')); + writeReadme( + root, + '# x\n## Verify\n## Recovery\nSetting environment variables after a failed init may repair MCP registration.' + ); + const errors = validateInstallDocs({ repoRoot: root, harness: 'cursor' }); + assert.ok(errors.some((e) => e.includes('env vars repair failed init'))); +}); + +test('validateInstallDocs rejects the legacy JFROG_URL env var', () => { + const root = mkdtempSync(join(tmpdir(), 'cursor-docs-')); + writeReadme(root, '# Cursor\n## Verify\n## Recovery\nSet `JFROG_URL` to your platform.\n'); + const errors = validateInstallDocs({ repoRoot: root, harness: 'cursor' }); + assert.ok(errors.some((e) => e.includes('JFROG_URL'))); +}); + +test('validateInstallDocs rejects links to other plugin GitHub repos', () => { + const root = mkdtempSync(join(tmpdir(), 'cursor-docs-')); + writeReadme( + root, + '# Cursor\n## Verify\n## Recovery\nSee https://github.com/jfrog/claude-plugin/blob/main/README.md\n' + ); + const errors = validateInstallDocs({ repoRoot: root, harness: 'cursor' }); + assert.ok(errors.some((e) => e.includes('claude-plugin'))); +}); + +test('validateInstallDocs rejects Jira URLs and ticket keys', () => { + const root = mkdtempSync(join(tmpdir(), 'cursor-docs-')); + const host = ['jfrog-int', 'atlassian', 'net'].join('.'); + const key = ['AX', '1780'].join('-'); + writeReadme(root, `# Cursor\n## Verify\n## Recovery\nSee [${key}](https://${host}/browse/${key}).\n`); + const errors = validateInstallDocs({ repoRoot: root, harness: 'cursor' }); + assert.ok(errors.some((e) => e.includes('atlassian.net'))); + assert.ok(errors.some((e) => e.includes('Jira ticket keys'))); +}); From 9eda110113af1a6e29e67c73d7d50eb110945d6a Mon Sep 17 00:00:00 2001 From: Yoni Melki Date: Thu, 27 Aug 2026 16:22:35 +0300 Subject: [PATCH 5/7] Drop the install-docs README checker and keep this repo self-contained. --- .github/workflows/validate-template.yml | 5 - docs/package-resolution-user-guide.md | 7 +- .../package-resolution/scripts/onboarding.mjs | 16 +-- scripts/validate-install-docs.mjs | 97 ------------------- scripts/validate-install-docs.test.mjs | 68 ------------- 5 files changed, 5 insertions(+), 188 deletions(-) delete mode 100644 scripts/validate-install-docs.mjs delete mode 100644 scripts/validate-install-docs.test.mjs diff --git a/.github/workflows/validate-template.yml b/.github/workflows/validate-template.yml index c7582a7..c82c73e 100644 --- a/.github/workflows/validate-template.yml +++ b/.github/workflows/validate-template.yml @@ -27,8 +27,3 @@ jobs: - name: Run template validation run: node scripts/validate-template.mjs - - - name: Validate install/recovery docs - run: | - node --test scripts/validate-install-docs.test.mjs - JFROG_PLUGIN_HARNESS=cursor node scripts/validate-install-docs.mjs diff --git a/docs/package-resolution-user-guide.md b/docs/package-resolution-user-guide.md index 297800d..970a27d 100644 --- a/docs/package-resolution-user-guide.md +++ b/docs/package-resolution-user-guide.md @@ -101,12 +101,9 @@ To turn it off in the config file itself, set `"enabled": false`. If your file i This is a preview, and your feedback directly shapes what ships next. Please tell us about anything that felt confusing, broken, or surprising, good or bad. -File an issue on GitHub, in whichever plugin repo you use: +File an issue on GitHub: [github.com/jfrog/cursor-plugin/issues](https://github.com/jfrog/cursor-plugin/issues) -- Cursor: [github.com/jfrog/cursor-plugin/issues](https://github.com/jfrog/cursor-plugin/issues) -- Claude Code: [github.com/jfrog/claude-plugin/issues](https://github.com/jfrog/claude-plugin/issues) -- VS Code: [github.com/jfrog/vscode-plugin/issues](https://github.com/jfrog/vscode-plugin/issues) -- Email: plugins-feedback@jfrog.com +Email: plugins-feedback@jfrog.com --- diff --git a/plugins/jfrog/modules/package-resolution/scripts/onboarding.mjs b/plugins/jfrog/modules/package-resolution/scripts/onboarding.mjs index 8c5ce6a..5dcb92a 100644 --- a/plugins/jfrog/modules/package-resolution/scripts/onboarding.mjs +++ b/plugins/jfrog/modules/package-resolution/scripts/onboarding.mjs @@ -34,26 +34,16 @@ const log = createLogger("onboarding"); const here = path.dirname(fileURLToPath(import.meta.url)); const NUDGE_TEMPLATE = path.join(here, "../onboarding/session-start-nudge.md"); -export const CURSOR_ADMIN_GUIDE_URL = +export const ADMIN_GUIDE_URL = "https://github.com/jfrog/cursor-plugin/blob/main/docs/package-resolution-admin-guide.md"; -export const CLAUDE_ADMIN_GUIDE_URL = - "https://github.com/jfrog/claude-plugin/blob/main/docs/package-resolution-admin-guide.md"; -export const COPILOT_ADMIN_GUIDE_URL = - "https://github.com/jfrog/vscode-plugin/blob/main/docs/package-resolution-admin-guide.md"; - -const ADMIN_GUIDE_URL_BY_IDE = { - claude_code: CLAUDE_ADMIN_GUIDE_URL, - cursor: CURSOR_ADMIN_GUIDE_URL, - copilot: COPILOT_ADMIN_GUIDE_URL, -}; /** Human-readable list of APR package types (keeps nudge copy in sync with code). */ export function supportedTypesPhrase() { return PACKAGE_TYPES.join(", "); } -function adminGuideUrlForIde(ide) { - return ADMIN_GUIDE_URL_BY_IDE[ide] ?? CLAUDE_ADMIN_GUIDE_URL; +function adminGuideUrlForIde(_ide) { + return ADMIN_GUIDE_URL; } function configureCommandPath() { diff --git a/scripts/validate-install-docs.mjs b/scripts/validate-install-docs.mjs deleted file mode 100644 index 33f5268..0000000 --- a/scripts/validate-install-docs.mjs +++ /dev/null @@ -1,97 +0,0 @@ -#!/usr/bin/env node -// Copyright (c) JFrog Ltd. 2026 -// Licensed under the Apache License, Version 2.0 -// Validates install/recovery documentation invariants. - -import { readFileSync, existsSync } from 'node:fs'; -import { join } from 'node:path'; -import { fileURLToPath } from 'node:url'; - -const repoRoot = process.cwd(); - -const HARNESS_OWN_REPO = { - claude: 'claude-plugin', - codex: 'codex-plugin', - cursor: 'cursor-plugin', - devin: 'devin-plugin', - opencode: 'opencode-jfrog-plugin', - vscode: 'vscode-plugin', -}; - -const ALL_PLUGIN_REPOS = Object.values(HARNESS_OWN_REPO); - -const REQUIRED_README_MARKERS = ['## Verify', '## Recovery']; - -const FORBIDDEN_PATTERNS = [ - { - re: /setting\s+(?:the\s+)?environment\s+variables?\s+after\s+a\s+failed\s+init\s+may\s+repair/i, - message: 'must not claim env vars repair failed init', - }, - { - re: /JFROG_URL/, - message: 'must not document the legacy JFROG_URL env var; use JFROG_PLATFORM_URL', - }, - { - re: /atlassian\.net/i, - message: 'must not reference JFrog Jira (atlassian.net) in repo files', - }, - { - re: /\b(?:AX|MLD)-\d+\b/, - message: 'must not include Jira ticket keys in repo files', - }, -]; - -export function validateInstallDocs({ repoRoot: root, harness }) { - const errors = []; - const readmePath = join(root, 'README.md'); - if (!existsSync(readmePath)) { - return [`${harness}: missing README.md`]; - } - const files = [{ label: 'README.md', text: readFileSync(readmePath, 'utf8') }]; - - const readme = files[0].text; - for (const marker of REQUIRED_README_MARKERS) { - if (!readme.includes(marker)) { - errors.push(`${harness}: README.md missing required marker: ${marker}`); - } - } - - const ownRepo = HARNESS_OWN_REPO[harness]; - const otherRepos = ALL_PLUGIN_REPOS.filter((name) => name !== ownRepo); - - for (const { label, text } of files) { - for (const { re, message } of FORBIDDEN_PATTERNS) { - if (re.test(text)) errors.push(`${harness}: ${label} ${message}`); - } - for (const other of otherRepos) { - if (text.includes(`github.com/jfrog/${other}`)) { - errors.push(`${harness}: ${label} must not link to github.com/jfrog/${other}`); - } - } - } - - return errors; -} - -function main() { - const harness = process.env.JFROG_PLUGIN_HARNESS ?? inferHarness(repoRoot); - const errors = validateInstallDocs({ repoRoot, harness }); - if (errors.length) { - console.error('install-docs validation failed:'); - for (const e of errors) console.error(` - ${e}`); - process.exit(1); - } - console.log('install-docs validation passed'); -} - -function inferHarness(root) { - if (existsSync(join(root, '.codex-plugin'))) return 'codex'; - if (existsSync(join(root, '.devin-plugin'))) return 'devin'; - if (existsSync(join(root, '.claude-plugin'))) return 'claude'; - if (existsSync(join(root, 'plugins', 'jfrog', '.cursor-plugin'))) return 'cursor'; - if (existsSync(join(root, 'plugin', '.claude-plugin'))) return 'vscode'; - if (existsSync(join(root, 'package.json')) && root.endsWith('opencode-jfrog-plugin')) return 'opencode'; - return 'unknown'; -} - -if (process.argv[1] === fileURLToPath(import.meta.url)) main(); diff --git a/scripts/validate-install-docs.test.mjs b/scripts/validate-install-docs.test.mjs deleted file mode 100644 index 0836b35..0000000 --- a/scripts/validate-install-docs.test.mjs +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright (c) JFrog Ltd. 2026 -import { test } from 'node:test'; -import assert from 'node:assert/strict'; -import { mkdtempSync, writeFileSync } from 'node:fs'; -import { tmpdir } from 'node:os'; -import { join } from 'node:path'; -import { validateInstallDocs } from './validate-install-docs.mjs'; - -function writeReadme(root, body) { - writeFileSync(join(root, 'README.md'), body); -} - -test('validateInstallDocs passes when README has Verify, Recovery, and no other-plugin links', () => { - const root = mkdtempSync(join(tmpdir(), 'cursor-docs-')); - writeReadme(root, '# Cursor\n\n## Verify\n\n1. list plugins\n\n## Recovery\n\n'); - assert.deepEqual(validateInstallDocs({ repoRoot: root, harness: 'cursor' }), []); -}); - -test('validateInstallDocs flags missing Verify section', () => { - const root = mkdtempSync(join(tmpdir(), 'cursor-docs-')); - writeReadme(root, '# Cursor\n\nInstall the plugin.\n'); - const errors = validateInstallDocs({ repoRoot: root, harness: 'cursor' }); - assert.ok(errors.some((e) => e.includes('## Verify'))); -}); - -test('validateInstallDocs flags missing Recovery section', () => { - const root = mkdtempSync(join(tmpdir(), 'cursor-docs-')); - writeReadme(root, '# Cursor\n\n## Verify\n\n1. list plugins\n'); - const errors = validateInstallDocs({ repoRoot: root, harness: 'cursor' }); - assert.ok(errors.some((e) => e.includes('## Recovery'))); -}); - -test('validateInstallDocs rejects contradictory failed-init env-var recovery claims', () => { - const root = mkdtempSync(join(tmpdir(), 'cursor-docs-')); - writeReadme( - root, - '# x\n## Verify\n## Recovery\nSetting environment variables after a failed init may repair MCP registration.' - ); - const errors = validateInstallDocs({ repoRoot: root, harness: 'cursor' }); - assert.ok(errors.some((e) => e.includes('env vars repair failed init'))); -}); - -test('validateInstallDocs rejects the legacy JFROG_URL env var', () => { - const root = mkdtempSync(join(tmpdir(), 'cursor-docs-')); - writeReadme(root, '# Cursor\n## Verify\n## Recovery\nSet `JFROG_URL` to your platform.\n'); - const errors = validateInstallDocs({ repoRoot: root, harness: 'cursor' }); - assert.ok(errors.some((e) => e.includes('JFROG_URL'))); -}); - -test('validateInstallDocs rejects links to other plugin GitHub repos', () => { - const root = mkdtempSync(join(tmpdir(), 'cursor-docs-')); - writeReadme( - root, - '# Cursor\n## Verify\n## Recovery\nSee https://github.com/jfrog/claude-plugin/blob/main/README.md\n' - ); - const errors = validateInstallDocs({ repoRoot: root, harness: 'cursor' }); - assert.ok(errors.some((e) => e.includes('claude-plugin'))); -}); - -test('validateInstallDocs rejects Jira URLs and ticket keys', () => { - const root = mkdtempSync(join(tmpdir(), 'cursor-docs-')); - const host = ['jfrog-int', 'atlassian', 'net'].join('.'); - const key = ['AX', '1780'].join('-'); - writeReadme(root, `# Cursor\n## Verify\n## Recovery\nSee [${key}](https://${host}/browse/${key}).\n`); - const errors = validateInstallDocs({ repoRoot: root, harness: 'cursor' }); - assert.ok(errors.some((e) => e.includes('atlassian.net'))); - assert.ok(errors.some((e) => e.includes('Jira ticket keys'))); -}); From 5fd46af76d12363b6c7bf76068f4f45e85487088 Mon Sep 17 00:00:00 2001 From: Yoni Melki Date: Thu, 27 Aug 2026 16:30:11 +0300 Subject: [PATCH 6/7] Stop mentioning the VS Code plugin from Cursor-owned hook code. --- plugins/jfrog/modules/copilot-session-start.mjs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plugins/jfrog/modules/copilot-session-start.mjs b/plugins/jfrog/modules/copilot-session-start.mjs index 9d0c275..65c0ad9 100644 --- a/plugins/jfrog/modules/copilot-session-start.mjs +++ b/plugins/jfrog/modules/copilot-session-start.mjs @@ -1,6 +1,5 @@ #!/usr/bin/env node -// GitHub Copilot Chat SessionStart hook runner (installed via the VS Code -// Copilot plugin — see jfrog/vscode-plugin). +// GitHub Copilot Chat SessionStart hook runner. // // Usage: node copilot-session-start.mjs // Example: node copilot-session-start.mjs package-resolution From 9a439e10f3f2d346064f80acf476e1ad9962164b Mon Sep 17 00:00:00 2001 From: Yoni Melki Date: Thu, 27 Aug 2026 16:40:01 +0300 Subject: [PATCH 7/7] Restore vendored modules to the jfrog-agent-hooks copy. --- plugins/jfrog/modules/copilot-session-start.mjs | 3 ++- .../package-resolution/scripts/onboarding.mjs | 16 +++++++++++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/plugins/jfrog/modules/copilot-session-start.mjs b/plugins/jfrog/modules/copilot-session-start.mjs index 65c0ad9..9d0c275 100644 --- a/plugins/jfrog/modules/copilot-session-start.mjs +++ b/plugins/jfrog/modules/copilot-session-start.mjs @@ -1,5 +1,6 @@ #!/usr/bin/env node -// GitHub Copilot Chat SessionStart hook runner. +// GitHub Copilot Chat SessionStart hook runner (installed via the VS Code +// Copilot plugin — see jfrog/vscode-plugin). // // Usage: node copilot-session-start.mjs // Example: node copilot-session-start.mjs package-resolution diff --git a/plugins/jfrog/modules/package-resolution/scripts/onboarding.mjs b/plugins/jfrog/modules/package-resolution/scripts/onboarding.mjs index 5dcb92a..8c5ce6a 100644 --- a/plugins/jfrog/modules/package-resolution/scripts/onboarding.mjs +++ b/plugins/jfrog/modules/package-resolution/scripts/onboarding.mjs @@ -34,16 +34,26 @@ const log = createLogger("onboarding"); const here = path.dirname(fileURLToPath(import.meta.url)); const NUDGE_TEMPLATE = path.join(here, "../onboarding/session-start-nudge.md"); -export const ADMIN_GUIDE_URL = +export const CURSOR_ADMIN_GUIDE_URL = "https://github.com/jfrog/cursor-plugin/blob/main/docs/package-resolution-admin-guide.md"; +export const CLAUDE_ADMIN_GUIDE_URL = + "https://github.com/jfrog/claude-plugin/blob/main/docs/package-resolution-admin-guide.md"; +export const COPILOT_ADMIN_GUIDE_URL = + "https://github.com/jfrog/vscode-plugin/blob/main/docs/package-resolution-admin-guide.md"; + +const ADMIN_GUIDE_URL_BY_IDE = { + claude_code: CLAUDE_ADMIN_GUIDE_URL, + cursor: CURSOR_ADMIN_GUIDE_URL, + copilot: COPILOT_ADMIN_GUIDE_URL, +}; /** Human-readable list of APR package types (keeps nudge copy in sync with code). */ export function supportedTypesPhrase() { return PACKAGE_TYPES.join(", "); } -function adminGuideUrlForIde(_ide) { - return ADMIN_GUIDE_URL; +function adminGuideUrlForIde(ide) { + return ADMIN_GUIDE_URL_BY_IDE[ide] ?? CLAUDE_ADMIN_GUIDE_URL; } function configureCommandPath() {