-
Notifications
You must be signed in to change notification settings - Fork 0
AX-1694 — Align VS Code copilot-instructions with Claude/Cursor agent-guard docs #21
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
MatanEden1
wants to merge
31
commits into
main
Choose a base branch
from
AX-1694-vscode-plugin-alignment
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
31 commits
Select commit
Hold shift + click to select a range
cba08b4
AX-1694 - Align VS Code copilot-instructions with Claude/Cursor agent…
MatanEden1 5f4db9a
AX-1694 - Apply review feedback: generalize installed-state read, mir…
MatanEden1 e722a81
AX-1694 - Restore type:"http" qualifier on Step 5 OAuth condition
MatanEden1 a033dca
Refine copilot instructions: clarify project switching and input hand…
MatanEden1 0606423
AX-1694 - Apply review feedback for cross-plugin alignment
MatanEden1 6a626ac
AX-1694 - Replace platform-specific scripts with cross-platform Node.…
MatanEden1 3585b81
AX-1694 - Implement full authentication precedence in inject-instruct…
MatanEden1 e344389
AX-1694 - Apply review feedback to inject-instructions
MatanEden1 5cc4d1d
AX-1694 - Clarify --login requires full permissions, not just network
MatanEden1 2c199e5
AX-1694 - Add jf-cli config fallback and refine MCP instructions
MatanEden1 01652bf
Clarify MCP config scope, server ID, and paths
MatanEden1 e247621
Refine MCP configuration instructions for user-level and workspace se…
MatanEden1 2fc87e1
Standardize MCP config on user-level ~/.vscode/mcp.json
MatanEden1 af832bb
Use the real VS Code MCP config locations
MatanEden1 ba067fa
Clarify user-level MCP config path
MatanEden1 dace1a6
Materialize Copilot instructions and change output
MatanEden1 5036f0d
Update copilot-instructions.md
MatanEden1 013dbd8
AX-1694 — Fix user-level MCP config paths and align with claude-plugin
YoniMelki 066b77b
AX-1694 — Address David's review: rename template, drop file-write in…
MatanEden1 f72c13b
AX-1694 — Remove non-VS-Code-specific bare-JFROG_URL block; tidy inje…
MatanEden1 ddd80fd
AX-1694 — Address David's review: bump plugin.json, align with Claude…
MatanEden1 275ae89
AX-1694 — Resolve creds via 'jf config export' instead of parsing the…
MatanEden1 3154978
AX-1694 — Align template with Claude/Cursor; trim injector comments
MatanEden1 7d2767d
AX-1694 — Add injector smoke test + CI (catches template-filename drift)
MatanEden1 b3a531f
AX-1694 — Address review: jf-export timeout, uniform fail-closed, val…
MatanEden1 b96eee5
Rename validation scripts and update injector checks for template con…
MatanEden1 ae0ccfc
Enhance SessionStart injector validation with plugin packaging checks
MatanEden1 484ee52
AX-1694 — Restructure validator: proper main(), grouped checks, manif…
MatanEden1 1dfeb0c
AX-1694 — Align scope vocabulary with Claude/Cursor
MatanEden1 bd96899
Update user instructions for server start: avoid prompting if already…
MatanEden1 2d29afb
AX-1694 — Resolve server via 'jf config show'; simplify 4a
MatanEden1 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| # Copyright (c) JFrog Ltd. 2026 | ||
| # Licensed under the Apache License, Version 2.0 | ||
| # https://www.apache.org/licenses/LICENSE-2.0 | ||
|
|
||
| name: Validate hook injection | ||
|
|
||
| on: | ||
| pull_request: | ||
| branches: [main] | ||
| paths: | ||
| - "plugin/scripts/inject-instructions.mjs" | ||
| - "plugin/templates/jfrog-mcp-management.md" | ||
| - "plugin/hooks/hooks.json" | ||
| - "plugin/.claude-plugin/plugin.json" | ||
| - "marketplace.json" | ||
| - "scripts/validate-hook-injector.mjs" | ||
|
|
||
| jobs: | ||
| validate: | ||
| name: Validate hook injection | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Checkout repository | ||
| uses: actions/checkout@v4 | ||
|
|
||
| - name: Set up Node.js | ||
| uses: actions/setup-node@v4 | ||
| with: | ||
| node-version: "20" | ||
|
|
||
| - name: Run injector validation | ||
| run: node scripts/validate-hook-injector.mjs | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,7 @@ | ||
| { | ||
| "name": "jfrog", | ||
| "description": "JFrog Platform integration with MCP, security skills, and supply-chain best practices", | ||
| "version": "0.1.0", | ||
| "version": "1.0.4", | ||
|
MatanEden1 marked this conversation as resolved.
MatanEden1 marked this conversation as resolved.
|
||
| "author": { "name": "JFrog", "url": "https://jfrog.com" }, | ||
| "hooks": "hooks/hooks.json" | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,11 +1,15 @@ | ||
| { | ||
| "hooks": { | ||
| "sessionStart": [ | ||
| "SessionStart": [ | ||
| { | ||
| "type": "command", | ||
| "command": "${CLAUDE_PLUGIN_ROOT}/scripts/ensure-instructions.sh", | ||
| "windows": "powershell -ExecutionPolicy Bypass -File ${CLAUDE_PLUGIN_ROOT}/scripts/ensure-instructions.ps1" | ||
| "hooks": [ | ||
| { | ||
| "type": "command", | ||
| "command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/inject-instructions.mjs\"", | ||
| "timeout": 7 | ||
|
MatanEden1 marked this conversation as resolved.
|
||
| } | ||
| ] | ||
| } | ||
| ] | ||
| } | ||
| } | ||
| } | ||
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,146 @@ | ||
| #!/usr/bin/env node | ||
| // Copyright (c) JFrog Ltd. 2026 | ||
| // Licensed under the Apache License, Version 2.0 | ||
| // https://www.apache.org/licenses/LICENSE-2.0 | ||
|
|
||
| import { execFileSync } from "node:child_process"; | ||
|
YoniMelki marked this conversation as resolved.
|
||
| import { readFileSync } from "node:fs"; | ||
| import path from "node:path"; | ||
| import process from "node:process"; | ||
| import { fileURLToPath } from "node:url"; | ||
|
|
||
| // Logs go to stderr; stdout is reserved for the hook JSON payload. | ||
| const debugEnabled = process.env.JF_AGENT_GUARD_DEBUG === "true"; | ||
| const log = (message) => console.error(`[jfrog-agent-guard] ${message}`); | ||
| const debug = (message) => { | ||
| if (debugEnabled) log(message); | ||
| }; | ||
|
|
||
| // New JFROG_* env vars take precedence over the legacy JF_* names. | ||
| const env = (newName, oldName) => | ||
| process.env[newName] ?? process.env[oldName]; | ||
|
|
||
| const forceDisabled = | ||
|
MatanEden1 marked this conversation as resolved.
|
||
| env("_JF_AGENT_GUARD_FORCE_DISABLE") === "true"; | ||
| const forceEnabled = | ||
| env("JF_AGENT_GUARD_FORCE_ENABLE") === "true"; | ||
|
|
||
| // Resolve {baseUrl, token} from env vars, falling back to the JFrog CLI's | ||
| // default server. Returns null when nothing resolves. | ||
| function resolveCredentials() { | ||
|
MatanEden1 marked this conversation as resolved.
|
||
| const baseUrl = env("JFROG_URL", "JF_URL"); | ||
| const token = env("JFROG_ACCESS_TOKEN", "JF_ACCESS_TOKEN"); | ||
| if (baseUrl && token) { | ||
| debug("Resolved credentials from environment variables"); | ||
| return { baseUrl, token }; | ||
| } | ||
|
|
||
| // `jf config export` emits the default server as a base64-encoded JSON token. | ||
| let configToken; | ||
| try { | ||
| configToken = execFileSync("jf", ["config", "export"], { | ||
|
MatanEden1 marked this conversation as resolved.
|
||
| encoding: "utf8", | ||
| stdio: ["ignore", "pipe", "ignore"], | ||
| timeout: 3000, | ||
| }).trim(); | ||
| } catch (error) { | ||
| debug(`'jf config export' failed (jf not on PATH or no server configured): ${error.message}`); | ||
| return null; | ||
| } | ||
|
|
||
| let cfg; | ||
| try { | ||
| cfg = JSON.parse(Buffer.from(configToken, "base64").toString("utf8")); | ||
| } catch (error) { | ||
| debug(`Could not decode the jf Config Token: ${error.message}`); | ||
| return null; | ||
| } | ||
|
|
||
| if (!cfg?.url || !cfg?.accessToken) { | ||
| debug("jf Config Token did not contain a usable url + accessToken"); | ||
| return null; | ||
| } | ||
|
|
||
| debug(`Resolved credentials via 'jf config export' (serverId: ${cfg.serverId ?? "<unknown>"})`); | ||
| return { baseUrl: cfg.url, token: cfg.accessToken }; | ||
| } | ||
|
|
||
| async function isAgentGuardEnabledViaSettings() { | ||
| const credentials = resolveCredentials(); | ||
| if (!credentials) { | ||
| debug("No JFrog credentials resolved; skipping settings check"); | ||
| return false; | ||
| } | ||
| const { baseUrl, token } = credentials; | ||
|
|
||
| const url = | ||
| baseUrl.replace(/\/+$/, "") + | ||
| "/ml/core/api/v1/administration/account-settings/mcp_gateway_plugin_enabled"; | ||
|
|
||
| debug(`Fetching agent guard setting from ${url}`); | ||
|
|
||
| const controller = new AbortController(); | ||
| const timeout = setTimeout(() => controller.abort(), 5000); | ||
| try { | ||
| const response = await fetch(url, { | ||
| method: "GET", | ||
| headers: { | ||
| Accept: "application/json", | ||
| Authorization: `Bearer ${token}`, | ||
| }, | ||
| signal: controller.signal, | ||
| }); | ||
| if (!response.ok) { | ||
| const body = await response.text().catch(() => ""); | ||
| debug(`Settings request returned HTTP ${response.status}; body: ${body || "<empty>"}`); | ||
| return false; | ||
| } | ||
| const data = await response.json(); | ||
| const enabled = data?.settings?.mcpGatewayPluginEnabled?.value === true; | ||
| debug(`Settings response indicates agent guard enabled=${enabled}`); | ||
| return enabled; | ||
| } catch (error) { | ||
| const reason = error?.name === "AbortError" ? "timeout" : error?.message ?? "unknown error"; | ||
| debug(`Settings request failed: ${reason}`); | ||
| return false; | ||
| } finally { | ||
| clearTimeout(timeout); | ||
| } | ||
| } | ||
|
|
||
| if (forceDisabled) { | ||
| debug("Force-disable flag is set."); | ||
| process.stdout.write("{}"); | ||
| process.exit(0); | ||
| } else if (forceEnabled) { | ||
| debug("Force-enable flag is set."); | ||
| } else if (!(await isAgentGuardEnabledViaSettings())) { | ||
| debug("Agent Guard not enabled; exiting without injecting instructions"); | ||
| process.stdout.write("{}"); | ||
| process.exit(0); | ||
| } | ||
| debug("Injecting instructions"); | ||
|
|
||
| const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); | ||
|
|
||
| let template; | ||
| try { | ||
| template = readFileSync( | ||
| path.join(root, "templates", "jfrog-mcp-management.md"), | ||
| "utf8", | ||
| ); | ||
| } catch (error) { | ||
| debug(`Could not read instructions template: ${error.message}`); | ||
| process.stdout.write("{}"); | ||
| process.exit(0); | ||
|
MatanEden1 marked this conversation as resolved.
|
||
| } | ||
|
YoniMelki marked this conversation as resolved.
YoniMelki marked this conversation as resolved.
|
||
|
|
||
| // The IDE consumes hookSpecificOutput.additionalContext from a SessionStart hook. | ||
| process.stdout.write( | ||
| JSON.stringify({ | ||
| hookSpecificOutput: { | ||
| hookEventName: "SessionStart", | ||
| additionalContext: template, | ||
| }, | ||
| }), | ||
| ); | ||
|
YoniMelki marked this conversation as resolved.
YoniMelki marked this conversation as resolved.
|
||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.