diff --git a/.codeql-version b/.codeql-version index eae4b41..7db5226 100644 --- a/.codeql-version +++ b/.codeql-version @@ -1 +1 @@ -v2.24.0 +v2.24.1 diff --git a/.github/instructions/prompts.instructions.md b/.github/instructions/github_prompts.instructions.md similarity index 81% rename from .github/instructions/prompts.instructions.md rename to .github/instructions/github_prompts.instructions.md index 83a733e..f480153 100644 --- a/.github/instructions/prompts.instructions.md +++ b/.github/instructions/github_prompts.instructions.md @@ -1,9 +1,9 @@ --- -applyTo: '**/*.prompt.md' +applyTo: '.github/prompts/*.prompt.md' description: 'Instructions for managing .prompt.md files throughout the advanced-security/codeql-development-mcp-server repository.' --- -# Instructions for managing `*.prompt.md` files +# Instructions for managing `.github/prompts/*.prompt.md` files ## PURPOSE @@ -27,13 +27,11 @@ That higher-level component should link (i.e. point) to at least one `.github/pr - ALWAYS follow best practices for writing markdown files, including proper use of headings, lists, links, and code blocks. This explicitly includes inserting a newline before and after code blocks, lists, and headings. - ALWAYS check formatting with `npm run lint && npm run format:check` from the repo root directory to ensure consistent formatting after making changes. - ALWAYS fix linting and formatting errors by running `npm run lint:fix && npm run format` from the repo root directory before committing changes. -- ALWAYS start each `*.prompt.md` file with the following YAML block of frontmatter: - - ```yaml - --- - mode: agent - --- - ``` +- ALWAYS start each `*.prompt.md` file with a YAML front-matter block containing, at minimum, values for fields such as: + - `agent` -> pointing to the name of the agent this prompt is intended for (e.g. `agent: ql-mcp-tool-tester`) + - `name` -> a unique name for the prompt (e.g. `name: validate-ql-mcp-tools-via-workshop`) + - `description` -> a concise description of the prompt's purpose and functionality + - `argument-hint` -> a brief hint about the expected arguments for the prompt ## PREFERENCES diff --git a/.github/instructions/server_src_prompts_md.instructions.md b/.github/instructions/server_src_prompts_md.instructions.md new file mode 100644 index 0000000..919cc05 --- /dev/null +++ b/.github/instructions/server_src_prompts_md.instructions.md @@ -0,0 +1,44 @@ +--- +applyTo: 'server/src/prompts/*.prompt.md' +description: 'Instructions for MCP-server-hosted workflow prompt files.' +--- + +# Copilot Instructions for `server/src/prompts/*.prompt.md` prompt files + +## PURPOSE + +This file contains instructions for working with workflow prompt files in the `server/src/prompts/` directory. These prompts are registered by the MCP server (via `workflow-prompts.ts`) and exposed as slash commands in VS Code Copilot Chat. + +## REQUIREMENTS + +- ALWAYS start each prompt file with the following YAML frontmatter block: + + ```yaml + --- + agent: agent + --- + ``` + + Note: VS Code has deprecated `mode: agent` in favor of `agent: agent`. + +- ALWAYS ensure each prompt has a corresponding registration in `server/src/prompts/workflow-prompts.ts`, including a Zod parameter schema and a `server.prompt()` call. +- ALWAYS ensure the prompt name in `WORKFLOW_PROMPT_NAMES` matches the registration. +- ALWAYS write prompts that work in **any environment** where `codeql` and the MCP server tools are available, including terminal-only environments without an IDE. +- ALWAYS use explicit, numeric tool parameters (e.g., `file_path`, `line`, `character`) instead of IDE-centric language like "position the cursor" or "click on". +- ALWAYS document when MCP tools use **0-based** positions (all `codeql_lsp_*` tools) versus **1-based** positions (`find_predicate_position`, `find_class_position`, `read_file`). +- ALWAYS note the `workspace_uri` requirement for LSP tools: it must be a **plain directory path** to the pack root containing `codeql-pack.yml`, not a `file://` URI. +- **ALWAYS run `npm run tidy` from the repo root directory to apply (markdown) linting for all prompt files.** + +## PREFERENCES + +- PREFER referencing other prompts by their registered MCP name (e.g., `codeql://prompts/ql_lsp_iterative_development`) to enable cross-prompt navigation. +- PREFER including a "Worked Example" section showing concrete tool invocations with realistic parameter values. +- PREFER a validation tools comparison table when multiple tools can validate queries at different fidelity levels (e.g., `validate_codeql_query` vs `codeql_lsp_diagnostics` vs `codeql_query_compile`). +- PREFER documenting tool limitations discovered through actual usage (e.g., `codeql_lsp_diagnostics` cannot resolve imports; `find_class_position` finds `class` only, not `module`). + +## CONSTRAINTS + +- NEVER leave any trailing whitespace on any line. +- NEVER use four backticks to nest one code block inside another. +- NEVER assume the calling LLM has access to an IDE, cursor, or editor UI. +- NEVER reference `file://` URIs for the `workspace_uri` parameter — use plain directory paths. diff --git a/.github/prompts/validate-ql-mcp-server-tools-via-workshop.prompt.md b/.github/prompts/validate-ql-mcp-server-tools-via-workshop.prompt.md index 8ddc8f9..4eaef39 100644 --- a/.github/prompts/validate-ql-mcp-server-tools-via-workshop.prompt.md +++ b/.github/prompts/validate-ql-mcp-server-tools-via-workshop.prompt.md @@ -3,7 +3,7 @@ agent: mcp-enabled-ql-workshop-developer name: validate-ql-mcp-server-tools-via-workshop description: 'A prompt for validating the real-world functionality of the CodeQL Development MCP Server tools by creating a CodeQL query development workshop from scratch using an existing, production-grade CodeQL query as the workshop "solution".' argument-hint: 'Provide the absolute or relative path to a local ".ql" or ".qlref" file associated with a production-grade CodeQL query to be used as the "solution" for the last stage of the to-be-created workshop.' -model: Claude Opus 4.5 (copilot) +model: Claude Opus 4.6 (copilot) --- # `validate-ql-mcp-server-tools-via-workshop` Prompt diff --git a/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/README.md b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/README.md new file mode 100644 index 0000000..c39ec2f --- /dev/null +++ b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/README.md @@ -0,0 +1,201 @@ +# JavaScript XSS Taint-Tracking Workshop + +A CodeQL query development workshop that teaches you to find client-side cross-site scripting (XSS) vulnerabilities using taint-tracking analysis. Inspired by a production SAP UI5 XSS query pattern. + +## Background + +This workshop is modeled after a real-world production query (`UI5Xss.ql`) that detects XSS vulnerabilities in SAP UI5 applications. The production query uses the `DataFlow::ConfigSig` pattern with: + +- **Sources**: `RemoteFlowSource` and DOM-based XSS sources +- **Sinks**: HTML injection sinks (`innerHTML`, `document.write`, etc.) +- **Barriers**: Sanitizer functions (`encodeHTML`, `encodeJS`, etc.) +- **Path-problem**: Full taint path visualization + +Since the production query depends on custom SAP libraries, this workshop uses **only** `codeql/javascript-all` (version `2.6.19`) to teach the same core concepts with standard JavaScript APIs. + +## Prerequisites + +- CodeQL CLI v2.23.9 or later +- `codeql/javascript-all` pack (installed automatically via `codeql pack install`) + +## Setup + +```bash +# Navigate to the workshop directory +cd .github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss + +# Install dependencies for all packs +codeql pack install solutions +codeql pack install solutions-tests +codeql pack install exercises +codeql pack install exercises-tests + +# Build test databases (optional — tests do this automatically) +chmod +x build-databases.sh +./build-databases.sh +``` + +## Workshop Structure + +```text +exercises/ — Your workspace (incomplete queries to complete) +exercises-tests/ — Tests that validate your solutions +solutions/ — Reference solutions (don't peek!) +solutions-tests/ — Tests for the solutions +tests-common/ — Shared test source code +``` + +## Test Code Overview + +The test file (`tests-common/test.js`) contains carefully crafted cases: + +| Case | Function | Source | Sink | Expected | +| ---------- | ------------------------- | ------------------------------- | -------------------------------------- | ------------------------ | +| POSITIVE 1 | `positiveDirectInnerHTML` | `document.location.search` | `innerHTML` | Detected | +| POSITIVE 2 | `positiveDocumentWrite` | `window.location.hash` | `document.write()` | Detected | +| POSITIVE 3 | `positiveOuterHTML` | URL param via `URLSearchParams` | `outerHTML` | Detected | +| NEGATIVE 1 | `negativeSanitized` | `document.location.search` | `innerHTML` (via `DOMPurify.sanitize`) | NOT detected (barrier) | +| NEGATIVE 2 | `negativeHardcoded` | hardcoded string | `innerHTML` | NOT detected (no source) | +| EDGE CASE | `edgeCaseEval` | `window.location.hash` | `eval()` | Detected | +| NEGATIVE 3 | `negativeEncoded` | `document.location.search` | `innerHTML` (via `encodeURIComponent`) | NOT detected (barrier) | + +## Exercises + +Each exercise builds on the previous one, progressively teaching more advanced CodeQL concepts. + +### Exercise 1: Find XSS Sinks + +**Goal**: Write a query that identifies dangerous DOM operations that could introduce XSS. + +**Concepts**: `DataFlow::Node`, `DataFlow::PropWrite`, `DataFlow::CallNode`, `DataFlow::globalVarRef` + +**What you'll find**: + +- Property writes to `innerHTML` and `outerHTML` +- Calls to `document.write()` +- Calls to `eval()` + +**Validate**: + +```bash +codeql test run exercises-tests/Exercise1 +``` + +### Exercise 2: Find Remote Flow Sources + +**Goal**: Identify all user-controlled inputs that could be XSS sources. + +**Concepts**: `RemoteFlowSource`, `getSourceType()` + +**What you'll find**: + +- `document.location.search`, `window.location.hash` +- URL parameters via `URLSearchParams` +- Any other browser API that returns user-controlled data + +**Validate**: + +```bash +codeql test run exercises-tests/Exercise2 +``` + +### Exercise 3: Basic Taint-Tracking Configuration + +**Goal**: Connect sources to sinks using CodeQL's taint-tracking framework. + +**Concepts**: `DataFlow::ConfigSig`, `TaintTracking::Global`, `isSource`, `isSink`, `flow()` + +**Key pattern**: + +```ql +module MyConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { ... } + predicate isSink(DataFlow::Node sink) { ... } +} +module MyFlow = TaintTracking::Global; +``` + +This exercise will find ALL XSS flows including the sanitized ones (no barriers yet). + +**Validate**: + +```bash +codeql test run exercises-tests/Exercise3 +``` + +### Exercise 4: Add Sanitizer Barriers + +**Goal**: Reduce false positives by adding barriers for sanitizer functions. + +**Concepts**: `isBarrier` predicate, `DataFlow::CallNode`, `getCalleeName()` + +**What changes**: Flows through `DOMPurify.sanitize()` and `encodeURIComponent()` are blocked. + +**Validate**: + +```bash +codeql test run exercises-tests/Exercise4 +``` + +### Exercise 5: Full Path-Problem Query + +**Goal**: Convert the query to a production-style `path-problem` query with full taint paths. + +**Concepts**: `@kind path-problem`, `PathGraph`, `PathNode`, `flowPath()` + +**What changes**: + +- Query metadata includes `@kind path-problem`, `@security-severity`, CWE tags +- Uses `PathNode` instead of `Node` for source/sink +- Imports `PathGraph` for visualization +- Shows complete taint propagation paths in results + +**Validate**: + +```bash +codeql test run exercises-tests/Exercise5 +``` + +## Validating Solutions + +```bash +# Run all solution tests (should pass after accepting results) +codeql test run solutions-tests + +# Run a specific exercise test +codeql test run solutions-tests/Exercise3 + +# Accept current results as expected baseline +codeql test run solutions-tests --learn +``` + +## Learning Path + +``` +Exercise 1 (Sinks) + ↓ +Exercise 2 (Sources) + ↓ +Exercise 3 (Taint Tracking) ← Core concept + ↓ +Exercise 4 (Barriers) ← Reducing false positives + ↓ +Exercise 5 (Path Problem) ← Production-ready query +``` + +## Relation to Production Query + +| Workshop Concept | Production Query (`UI5Xss.ql`) | +| ---------------------------------------- | -------------------------------------------- | +| `RemoteFlowSource` | `RemoteFlowSource` + SAP-specific sources | +| `innerHTML`/`document.write` sinks | SAP UI5 HTML injection sinks | +| `sanitize`/`encodeURIComponent` barriers | `encodeHTML`/`encodeJS`/`encodeCSS` barriers | +| `DataFlow::ConfigSig` | Same pattern | +| `path-problem` | Same query kind | + +## Tips + +- Start with Exercise 1 and work sequentially — each builds on the previous +- Use `codeql test run --learn` to accept current output as expected results +- If stuck, look at the solution for the PREVIOUS exercise as a starting point +- The `tests-common/test.js` file has comments explaining each test case diff --git a/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/build-databases.sh b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/build-databases.sh new file mode 100644 index 0000000..9b627d7 --- /dev/null +++ b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/build-databases.sh @@ -0,0 +1,34 @@ +#!/bin/bash +set -e + +WORKSHOP_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +echo "Building test databases for SAP UI5 XSS workshop..." + +# Build solution test databases +for exercise_dir in "${WORKSHOP_ROOT}"/solutions-tests/Exercise*/; do + exercise_name="$(basename "$exercise_dir")" + DB_PATH="${exercise_dir}/${exercise_name}.testproj" + + echo " Creating database: ${exercise_name} (solutions)" + rm -rf "${DB_PATH}" + + codeql test extract --search-path="${WORKSHOP_ROOT}/solutions" "$exercise_dir" +done + +# Build exercise test databases +for exercise_dir in "${WORKSHOP_ROOT}"/exercises-tests/Exercise*/; do + exercise_name="$(basename "$exercise_dir")" + DB_PATH="${exercise_dir}/${exercise_name}.testproj" + + echo " Creating database: ${exercise_name} (exercises)" + rm -rf "${DB_PATH}" + + codeql test extract --search-path="${WORKSHOP_ROOT}/exercises" "$exercise_dir" +done + +echo "Database creation complete!" +echo "" +echo "To run tests:" +echo " codeql test run solutions-tests # Validate solutions" +echo " codeql test run exercises-tests # Test your exercises" diff --git a/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/codeql-workspace.yml b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/codeql-workspace.yml new file mode 100644 index 0000000..568a57f --- /dev/null +++ b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/codeql-workspace.yml @@ -0,0 +1,2 @@ +provide: + - '*/codeql-pack.yml' diff --git a/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/exercises-tests/Exercise1/Exercise1.expected b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/exercises-tests/Exercise1/Exercise1.expected new file mode 100644 index 0000000..e69de29 diff --git a/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/exercises-tests/Exercise1/Exercise1.qlref b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/exercises-tests/Exercise1/Exercise1.qlref new file mode 100644 index 0000000..0565cfa --- /dev/null +++ b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/exercises-tests/Exercise1/Exercise1.qlref @@ -0,0 +1 @@ +Exercise1.ql diff --git a/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/exercises-tests/Exercise1/test.js b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/exercises-tests/Exercise1/test.js new file mode 100644 index 0000000..268ec8f --- /dev/null +++ b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/exercises-tests/Exercise1/test.js @@ -0,0 +1,68 @@ +// Test cases for XSS taint-tracking workshop +// Inspired by SAP UI5 XSS production query patterns + +// ============================================================ +// POSITIVE CASE 1: Direct XSS via document.location.search → innerHTML +// ============================================================ +function positiveDirectInnerHTML() { + var userInput = document.location.search; // source: URL query string + var container = document.getElementById("output"); + container.innerHTML = userInput; // sink: innerHTML assignment — XSS! +} + +// ============================================================ +// POSITIVE CASE 2: XSS via window.location.hash → document.write() +// ============================================================ +function positiveDocumentWrite() { + var hashValue = window.location.hash; // source: URL hash fragment + document.write(hashValue); // sink: document.write — XSS! +} + +// ============================================================ +// POSITIVE CASE 3: XSS via URL param parsed → outerHTML +// ============================================================ +function positiveOuterHTML() { + var params = new URLSearchParams(window.location.search); + var name = params.get("name"); // source: user-controlled URL parameter + var el = document.getElementById("profile"); + el.outerHTML = "
" + name + "
"; // sink: outerHTML assignment — XSS! +} + +// ============================================================ +// NEGATIVE CASE 1: Sanitized value via DOMPurify.sanitize() → innerHTML +// Should NOT be flagged because the input is sanitized +// ============================================================ +function negativeSanitized() { + var userInput = document.location.search; // source + var clean = DOMPurify.sanitize(userInput); // barrier: sanitization + var container = document.getElementById("safe-output"); + container.innerHTML = clean; // safe: sanitized input +} + +// ============================================================ +// NEGATIVE CASE 2: Hardcoded string → innerHTML +// Should NOT be flagged because there is no user-controlled source +// ============================================================ +function negativeHardcoded() { + var container = document.getElementById("static-content"); + container.innerHTML = "

Welcome to the application

"; // safe: hardcoded +} + +// ============================================================ +// EDGE CASE: XSS via eval() with URL input +// ============================================================ +function edgeCaseEval() { + var code = window.location.hash.substring(1); // source: URL hash + eval(code); // sink: eval — code injection / XSS! +} + +// ============================================================ +// NEGATIVE CASE 3: encodeURIComponent barrier +// Should NOT be flagged because input is encoded +// ============================================================ +function negativeEncoded() { + var userInput = document.location.search; + var encoded = encodeURIComponent(userInput); // barrier: encoding + var container = document.getElementById("encoded-output"); + container.innerHTML = encoded; // safe: encoded input +} diff --git a/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/exercises-tests/Exercise2/Exercise2.expected b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/exercises-tests/Exercise2/Exercise2.expected new file mode 100644 index 0000000..e69de29 diff --git a/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/exercises-tests/Exercise2/Exercise2.qlref b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/exercises-tests/Exercise2/Exercise2.qlref new file mode 100644 index 0000000..3d135a2 --- /dev/null +++ b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/exercises-tests/Exercise2/Exercise2.qlref @@ -0,0 +1 @@ +Exercise2.ql diff --git a/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/exercises-tests/Exercise2/test.js b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/exercises-tests/Exercise2/test.js new file mode 100644 index 0000000..268ec8f --- /dev/null +++ b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/exercises-tests/Exercise2/test.js @@ -0,0 +1,68 @@ +// Test cases for XSS taint-tracking workshop +// Inspired by SAP UI5 XSS production query patterns + +// ============================================================ +// POSITIVE CASE 1: Direct XSS via document.location.search → innerHTML +// ============================================================ +function positiveDirectInnerHTML() { + var userInput = document.location.search; // source: URL query string + var container = document.getElementById("output"); + container.innerHTML = userInput; // sink: innerHTML assignment — XSS! +} + +// ============================================================ +// POSITIVE CASE 2: XSS via window.location.hash → document.write() +// ============================================================ +function positiveDocumentWrite() { + var hashValue = window.location.hash; // source: URL hash fragment + document.write(hashValue); // sink: document.write — XSS! +} + +// ============================================================ +// POSITIVE CASE 3: XSS via URL param parsed → outerHTML +// ============================================================ +function positiveOuterHTML() { + var params = new URLSearchParams(window.location.search); + var name = params.get("name"); // source: user-controlled URL parameter + var el = document.getElementById("profile"); + el.outerHTML = "
" + name + "
"; // sink: outerHTML assignment — XSS! +} + +// ============================================================ +// NEGATIVE CASE 1: Sanitized value via DOMPurify.sanitize() → innerHTML +// Should NOT be flagged because the input is sanitized +// ============================================================ +function negativeSanitized() { + var userInput = document.location.search; // source + var clean = DOMPurify.sanitize(userInput); // barrier: sanitization + var container = document.getElementById("safe-output"); + container.innerHTML = clean; // safe: sanitized input +} + +// ============================================================ +// NEGATIVE CASE 2: Hardcoded string → innerHTML +// Should NOT be flagged because there is no user-controlled source +// ============================================================ +function negativeHardcoded() { + var container = document.getElementById("static-content"); + container.innerHTML = "

Welcome to the application

"; // safe: hardcoded +} + +// ============================================================ +// EDGE CASE: XSS via eval() with URL input +// ============================================================ +function edgeCaseEval() { + var code = window.location.hash.substring(1); // source: URL hash + eval(code); // sink: eval — code injection / XSS! +} + +// ============================================================ +// NEGATIVE CASE 3: encodeURIComponent barrier +// Should NOT be flagged because input is encoded +// ============================================================ +function negativeEncoded() { + var userInput = document.location.search; + var encoded = encodeURIComponent(userInput); // barrier: encoding + var container = document.getElementById("encoded-output"); + container.innerHTML = encoded; // safe: encoded input +} diff --git a/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/exercises-tests/Exercise3/Exercise3.expected b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/exercises-tests/Exercise3/Exercise3.expected new file mode 100644 index 0000000..e69de29 diff --git a/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/exercises-tests/Exercise3/Exercise3.qlref b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/exercises-tests/Exercise3/Exercise3.qlref new file mode 100644 index 0000000..e73d6bd --- /dev/null +++ b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/exercises-tests/Exercise3/Exercise3.qlref @@ -0,0 +1 @@ +Exercise3.ql diff --git a/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/exercises-tests/Exercise3/test.js b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/exercises-tests/Exercise3/test.js new file mode 100644 index 0000000..268ec8f --- /dev/null +++ b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/exercises-tests/Exercise3/test.js @@ -0,0 +1,68 @@ +// Test cases for XSS taint-tracking workshop +// Inspired by SAP UI5 XSS production query patterns + +// ============================================================ +// POSITIVE CASE 1: Direct XSS via document.location.search → innerHTML +// ============================================================ +function positiveDirectInnerHTML() { + var userInput = document.location.search; // source: URL query string + var container = document.getElementById("output"); + container.innerHTML = userInput; // sink: innerHTML assignment — XSS! +} + +// ============================================================ +// POSITIVE CASE 2: XSS via window.location.hash → document.write() +// ============================================================ +function positiveDocumentWrite() { + var hashValue = window.location.hash; // source: URL hash fragment + document.write(hashValue); // sink: document.write — XSS! +} + +// ============================================================ +// POSITIVE CASE 3: XSS via URL param parsed → outerHTML +// ============================================================ +function positiveOuterHTML() { + var params = new URLSearchParams(window.location.search); + var name = params.get("name"); // source: user-controlled URL parameter + var el = document.getElementById("profile"); + el.outerHTML = "
" + name + "
"; // sink: outerHTML assignment — XSS! +} + +// ============================================================ +// NEGATIVE CASE 1: Sanitized value via DOMPurify.sanitize() → innerHTML +// Should NOT be flagged because the input is sanitized +// ============================================================ +function negativeSanitized() { + var userInput = document.location.search; // source + var clean = DOMPurify.sanitize(userInput); // barrier: sanitization + var container = document.getElementById("safe-output"); + container.innerHTML = clean; // safe: sanitized input +} + +// ============================================================ +// NEGATIVE CASE 2: Hardcoded string → innerHTML +// Should NOT be flagged because there is no user-controlled source +// ============================================================ +function negativeHardcoded() { + var container = document.getElementById("static-content"); + container.innerHTML = "

Welcome to the application

"; // safe: hardcoded +} + +// ============================================================ +// EDGE CASE: XSS via eval() with URL input +// ============================================================ +function edgeCaseEval() { + var code = window.location.hash.substring(1); // source: URL hash + eval(code); // sink: eval — code injection / XSS! +} + +// ============================================================ +// NEGATIVE CASE 3: encodeURIComponent barrier +// Should NOT be flagged because input is encoded +// ============================================================ +function negativeEncoded() { + var userInput = document.location.search; + var encoded = encodeURIComponent(userInput); // barrier: encoding + var container = document.getElementById("encoded-output"); + container.innerHTML = encoded; // safe: encoded input +} diff --git a/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/exercises-tests/Exercise4/Exercise4.expected b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/exercises-tests/Exercise4/Exercise4.expected new file mode 100644 index 0000000..e69de29 diff --git a/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/exercises-tests/Exercise4/Exercise4.qlref b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/exercises-tests/Exercise4/Exercise4.qlref new file mode 100644 index 0000000..8b891f6 --- /dev/null +++ b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/exercises-tests/Exercise4/Exercise4.qlref @@ -0,0 +1 @@ +Exercise4.ql diff --git a/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/exercises-tests/Exercise4/test.js b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/exercises-tests/Exercise4/test.js new file mode 100644 index 0000000..268ec8f --- /dev/null +++ b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/exercises-tests/Exercise4/test.js @@ -0,0 +1,68 @@ +// Test cases for XSS taint-tracking workshop +// Inspired by SAP UI5 XSS production query patterns + +// ============================================================ +// POSITIVE CASE 1: Direct XSS via document.location.search → innerHTML +// ============================================================ +function positiveDirectInnerHTML() { + var userInput = document.location.search; // source: URL query string + var container = document.getElementById("output"); + container.innerHTML = userInput; // sink: innerHTML assignment — XSS! +} + +// ============================================================ +// POSITIVE CASE 2: XSS via window.location.hash → document.write() +// ============================================================ +function positiveDocumentWrite() { + var hashValue = window.location.hash; // source: URL hash fragment + document.write(hashValue); // sink: document.write — XSS! +} + +// ============================================================ +// POSITIVE CASE 3: XSS via URL param parsed → outerHTML +// ============================================================ +function positiveOuterHTML() { + var params = new URLSearchParams(window.location.search); + var name = params.get("name"); // source: user-controlled URL parameter + var el = document.getElementById("profile"); + el.outerHTML = "
" + name + "
"; // sink: outerHTML assignment — XSS! +} + +// ============================================================ +// NEGATIVE CASE 1: Sanitized value via DOMPurify.sanitize() → innerHTML +// Should NOT be flagged because the input is sanitized +// ============================================================ +function negativeSanitized() { + var userInput = document.location.search; // source + var clean = DOMPurify.sanitize(userInput); // barrier: sanitization + var container = document.getElementById("safe-output"); + container.innerHTML = clean; // safe: sanitized input +} + +// ============================================================ +// NEGATIVE CASE 2: Hardcoded string → innerHTML +// Should NOT be flagged because there is no user-controlled source +// ============================================================ +function negativeHardcoded() { + var container = document.getElementById("static-content"); + container.innerHTML = "

Welcome to the application

"; // safe: hardcoded +} + +// ============================================================ +// EDGE CASE: XSS via eval() with URL input +// ============================================================ +function edgeCaseEval() { + var code = window.location.hash.substring(1); // source: URL hash + eval(code); // sink: eval — code injection / XSS! +} + +// ============================================================ +// NEGATIVE CASE 3: encodeURIComponent barrier +// Should NOT be flagged because input is encoded +// ============================================================ +function negativeEncoded() { + var userInput = document.location.search; + var encoded = encodeURIComponent(userInput); // barrier: encoding + var container = document.getElementById("encoded-output"); + container.innerHTML = encoded; // safe: encoded input +} diff --git a/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/exercises-tests/Exercise5/Exercise5.expected b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/exercises-tests/Exercise5/Exercise5.expected new file mode 100644 index 0000000..e69de29 diff --git a/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/exercises-tests/Exercise5/Exercise5.qlref b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/exercises-tests/Exercise5/Exercise5.qlref new file mode 100644 index 0000000..1267a96 --- /dev/null +++ b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/exercises-tests/Exercise5/Exercise5.qlref @@ -0,0 +1 @@ +Exercise5.ql diff --git a/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/exercises-tests/Exercise5/test.js b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/exercises-tests/Exercise5/test.js new file mode 100644 index 0000000..268ec8f --- /dev/null +++ b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/exercises-tests/Exercise5/test.js @@ -0,0 +1,68 @@ +// Test cases for XSS taint-tracking workshop +// Inspired by SAP UI5 XSS production query patterns + +// ============================================================ +// POSITIVE CASE 1: Direct XSS via document.location.search → innerHTML +// ============================================================ +function positiveDirectInnerHTML() { + var userInput = document.location.search; // source: URL query string + var container = document.getElementById("output"); + container.innerHTML = userInput; // sink: innerHTML assignment — XSS! +} + +// ============================================================ +// POSITIVE CASE 2: XSS via window.location.hash → document.write() +// ============================================================ +function positiveDocumentWrite() { + var hashValue = window.location.hash; // source: URL hash fragment + document.write(hashValue); // sink: document.write — XSS! +} + +// ============================================================ +// POSITIVE CASE 3: XSS via URL param parsed → outerHTML +// ============================================================ +function positiveOuterHTML() { + var params = new URLSearchParams(window.location.search); + var name = params.get("name"); // source: user-controlled URL parameter + var el = document.getElementById("profile"); + el.outerHTML = "
" + name + "
"; // sink: outerHTML assignment — XSS! +} + +// ============================================================ +// NEGATIVE CASE 1: Sanitized value via DOMPurify.sanitize() → innerHTML +// Should NOT be flagged because the input is sanitized +// ============================================================ +function negativeSanitized() { + var userInput = document.location.search; // source + var clean = DOMPurify.sanitize(userInput); // barrier: sanitization + var container = document.getElementById("safe-output"); + container.innerHTML = clean; // safe: sanitized input +} + +// ============================================================ +// NEGATIVE CASE 2: Hardcoded string → innerHTML +// Should NOT be flagged because there is no user-controlled source +// ============================================================ +function negativeHardcoded() { + var container = document.getElementById("static-content"); + container.innerHTML = "

Welcome to the application

"; // safe: hardcoded +} + +// ============================================================ +// EDGE CASE: XSS via eval() with URL input +// ============================================================ +function edgeCaseEval() { + var code = window.location.hash.substring(1); // source: URL hash + eval(code); // sink: eval — code injection / XSS! +} + +// ============================================================ +// NEGATIVE CASE 3: encodeURIComponent barrier +// Should NOT be flagged because input is encoded +// ============================================================ +function negativeEncoded() { + var userInput = document.location.search; + var encoded = encodeURIComponent(userInput); // barrier: encoding + var container = document.getElementById("encoded-output"); + container.innerHTML = encoded; // safe: encoded input +} diff --git a/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/exercises-tests/codeql-pack.yml b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/exercises-tests/codeql-pack.yml new file mode 100644 index 0000000..86bef27 --- /dev/null +++ b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/exercises-tests/codeql-pack.yml @@ -0,0 +1,5 @@ +name: codeql-sap-js-ui5-xss-exercises-tests +version: 0.0.1 +dependencies: + codeql-sap-js-ui5-xss-exercises: '*' +extractor: javascript diff --git a/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/exercises/Exercise1.ql b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/exercises/Exercise1.ql new file mode 100644 index 0000000..8c79076 --- /dev/null +++ b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/exercises/Exercise1.ql @@ -0,0 +1,26 @@ +/** + * @name Find XSS sinks + * @description Find DOM properties and methods that can introduce XSS + * @kind problem + * @id js/workshop/xss-sinks + * @problem.severity warning + */ + +import javascript + +from DataFlow::Node sink +where + // TODO: Implement - find nodes that write to innerHTML or outerHTML properties + // Hint: Use DataFlow::PropWrite to find property writes. + // pw.getPropertyName() gives you the property name. + // pw.getRhs() gives you the right-hand side (the value being assigned). + // + // TODO: Implement - find calls to document.write() + // Hint: Use DataFlow::globalVarRef("document").getAMemberCall("write") + // to find calls to document.write. + // + // TODO: Implement - find calls to eval() + // Hint: Use DataFlow::globalVarRef("eval").getACall() + // to find calls to eval. + none() +select sink, "Potential XSS sink" diff --git a/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/exercises/Exercise2.ql b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/exercises/Exercise2.ql new file mode 100644 index 0000000..ea78f4f --- /dev/null +++ b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/exercises/Exercise2.ql @@ -0,0 +1,18 @@ +/** + * @name Find remote flow sources + * @description Find user-controlled inputs that could be XSS sources + * @kind problem + * @id js/workshop/xss-sources + * @problem.severity warning + */ + +import javascript + +// TODO: Implement - find all RemoteFlowSource instances +// Hint: The RemoteFlowSource class represents user-controlled inputs such as +// document.location, window.location, URL parameters, etc. +// Use `source instanceof RemoteFlowSource` in your from/where/select. +// The getSourceType() method returns a descriptive string for each source. +from RemoteFlowSource source +where none() +select source, "User-controlled input: " + source.getSourceType() diff --git a/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/exercises/Exercise3.ql b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/exercises/Exercise3.ql new file mode 100644 index 0000000..0e84125 --- /dev/null +++ b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/exercises/Exercise3.ql @@ -0,0 +1,36 @@ +/** + * @name Basic XSS taint tracking + * @description Track taint from user input to XSS sinks + * @kind problem + * @id js/workshop/xss-basic-taint + * @problem.severity error + */ + +import javascript + +// TODO: Implement the XssConfig module using DataFlow::ConfigSig +// This module defines how taint flows from sources to sinks. +// +// Hint: You need two predicates: +// - isSource(DataFlow::Node source): identify user-controlled inputs +// (use RemoteFlowSource from Exercise 2) +// - isSink(DataFlow::Node sink): identify dangerous DOM operations +// (use the sink patterns from Exercise 1) +module XssConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { + // TODO: Implement - use RemoteFlowSource + none() + } + + predicate isSink(DataFlow::Node sink) { + // TODO: Implement - reuse sink patterns from Exercise 1 + none() + } +} + +// Instantiate the taint-tracking analysis +module XssFlow = TaintTracking::Global; + +from DataFlow::Node source, DataFlow::Node sink +where XssFlow::flow(source, sink) +select sink, "XSS vulnerability from $@.", source, "user input" diff --git a/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/exercises/Exercise4.ql b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/exercises/Exercise4.ql new file mode 100644 index 0000000..a5c2272 --- /dev/null +++ b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/exercises/Exercise4.ql @@ -0,0 +1,42 @@ +/** + * @name XSS with sanitizer barriers + * @description Track taint with sanitizer functions as barriers + * @kind problem + * @id js/workshop/xss-barriers + * @problem.severity error + */ + +import javascript + +// TODO: Implement XssWithBarriersConfig +// This extends Exercise 3 by adding an isBarrier predicate that blocks +// taint flow through sanitizer functions. +// +// Hint: The isBarrier predicate identifies nodes where taint is neutralized. +// Look for calls to functions with sanitizer-like names: +// "sanitize", "encodeHTML", "escapeHtml", "encodeURIComponent" +// Use DataFlow::CallNode and getCalleeName() to match function names. +module XssWithBarriersConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { + // TODO: Implement - same as Exercise 3 + none() + } + + predicate isSink(DataFlow::Node sink) { + // TODO: Implement - same as Exercise 3 + none() + } + + predicate isBarrier(DataFlow::Node node) { + // TODO: Implement - block taint flow through sanitizer calls + // Hint: Find DataFlow::CallNode instances whose callee name matches + // known sanitizer function names. The call node itself is the barrier. + none() + } +} + +module XssWithBarriersFlow = TaintTracking::Global; + +from DataFlow::Node source, DataFlow::Node sink +where XssWithBarriersFlow::flow(source, sink) +select sink, "XSS vulnerability from $@.", source, "user input" diff --git a/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/exercises/Exercise5.ql b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/exercises/Exercise5.ql new file mode 100644 index 0000000..9a4c7bd --- /dev/null +++ b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/exercises/Exercise5.ql @@ -0,0 +1,52 @@ +/** + * @name Client-side cross-site scripting + * @description Writing user input directly to DOM properties allows for XSS + * @kind path-problem + * @id js/workshop/xss-path + * @problem.severity error + * @security-severity 7.8 + * @precision high + * @tags security + * external/cwe/cwe-079 + */ + +import javascript + +// TODO: Implement XssFullConfig +// This is the final, production-style query. It combines everything from +// Exercises 3 and 4, but uses @kind path-problem to show complete taint paths. +// +// Hint: The config is identical to Exercise 4's XssWithBarriersConfig. +// The difference is in the query metadata (@kind path-problem) and +// the from/where/select clause which uses PathNode instead of Node. +module XssFullConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { + // TODO: Implement - same as Exercise 4 + none() + } + + predicate isSink(DataFlow::Node sink) { + // TODO: Implement - same as Exercise 4 + none() + } + + predicate isBarrier(DataFlow::Node node) { + // TODO: Implement - same as Exercise 4 + none() + } +} + +module XssFullFlow = TaintTracking::Global; + +// TODO: Import the PathGraph module for path-problem visualization +// Hint: import XssFullFlow::PathGraph +import XssFullFlow::PathGraph + +// TODO: Update the from/where/select to use PathNode +// Hint: Use XssFullFlow::PathNode instead of DataFlow::Node +// Use XssFullFlow::flowPath(source, sink) instead of flow +// Use source.getNode() and sink.getNode() for the select message +from XssFullFlow::PathNode source, XssFullFlow::PathNode sink +where XssFullFlow::flowPath(source, sink) +select sink.getNode(), source, sink, "XSS vulnerability due to $@.", source.getNode(), + "user-provided value" diff --git a/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/exercises/codeql-pack.yml b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/exercises/codeql-pack.yml new file mode 100644 index 0000000..ecefff0 --- /dev/null +++ b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/exercises/codeql-pack.yml @@ -0,0 +1,5 @@ +name: codeql-sap-js-ui5-xss-exercises +version: 0.0.1 +library: false +dependencies: + codeql/javascript-all: 2.6.19 diff --git a/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/solutions-tests/Exercise1/Exercise1.expected b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/solutions-tests/Exercise1/Exercise1.expected new file mode 100644 index 0000000..49a51d9 --- /dev/null +++ b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/solutions-tests/Exercise1/Exercise1.expected @@ -0,0 +1,7 @@ +| test.js:10:25:10:33 | userInput | Potential XSS sink | +| test.js:18:18:18:26 | hashValue | Potential XSS sink | +| test.js:28:18:28:42 | "
" ...
" | Potential XSS sink | +| test.js:39:25:39:29 | clean | Potential XSS sink | +| test.js:48:25:48:59 | "

Wel ... on

" | Potential XSS sink | +| test.js:56:8:56:11 | code | Potential XSS sink | +| test.js:67:25:67:31 | encoded | Potential XSS sink | diff --git a/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/solutions-tests/Exercise1/Exercise1.qlref b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/solutions-tests/Exercise1/Exercise1.qlref new file mode 100644 index 0000000..0565cfa --- /dev/null +++ b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/solutions-tests/Exercise1/Exercise1.qlref @@ -0,0 +1 @@ +Exercise1.ql diff --git a/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/solutions-tests/Exercise1/test.js b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/solutions-tests/Exercise1/test.js new file mode 100644 index 0000000..268ec8f --- /dev/null +++ b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/solutions-tests/Exercise1/test.js @@ -0,0 +1,68 @@ +// Test cases for XSS taint-tracking workshop +// Inspired by SAP UI5 XSS production query patterns + +// ============================================================ +// POSITIVE CASE 1: Direct XSS via document.location.search → innerHTML +// ============================================================ +function positiveDirectInnerHTML() { + var userInput = document.location.search; // source: URL query string + var container = document.getElementById("output"); + container.innerHTML = userInput; // sink: innerHTML assignment — XSS! +} + +// ============================================================ +// POSITIVE CASE 2: XSS via window.location.hash → document.write() +// ============================================================ +function positiveDocumentWrite() { + var hashValue = window.location.hash; // source: URL hash fragment + document.write(hashValue); // sink: document.write — XSS! +} + +// ============================================================ +// POSITIVE CASE 3: XSS via URL param parsed → outerHTML +// ============================================================ +function positiveOuterHTML() { + var params = new URLSearchParams(window.location.search); + var name = params.get("name"); // source: user-controlled URL parameter + var el = document.getElementById("profile"); + el.outerHTML = "
" + name + "
"; // sink: outerHTML assignment — XSS! +} + +// ============================================================ +// NEGATIVE CASE 1: Sanitized value via DOMPurify.sanitize() → innerHTML +// Should NOT be flagged because the input is sanitized +// ============================================================ +function negativeSanitized() { + var userInput = document.location.search; // source + var clean = DOMPurify.sanitize(userInput); // barrier: sanitization + var container = document.getElementById("safe-output"); + container.innerHTML = clean; // safe: sanitized input +} + +// ============================================================ +// NEGATIVE CASE 2: Hardcoded string → innerHTML +// Should NOT be flagged because there is no user-controlled source +// ============================================================ +function negativeHardcoded() { + var container = document.getElementById("static-content"); + container.innerHTML = "

Welcome to the application

"; // safe: hardcoded +} + +// ============================================================ +// EDGE CASE: XSS via eval() with URL input +// ============================================================ +function edgeCaseEval() { + var code = window.location.hash.substring(1); // source: URL hash + eval(code); // sink: eval — code injection / XSS! +} + +// ============================================================ +// NEGATIVE CASE 3: encodeURIComponent barrier +// Should NOT be flagged because input is encoded +// ============================================================ +function negativeEncoded() { + var userInput = document.location.search; + var encoded = encodeURIComponent(userInput); // barrier: encoding + var container = document.getElementById("encoded-output"); + container.innerHTML = encoded; // safe: encoded input +} diff --git a/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/solutions-tests/Exercise2/Exercise2.expected b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/solutions-tests/Exercise2/Exercise2.expected new file mode 100644 index 0000000..e40c56b --- /dev/null +++ b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/solutions-tests/Exercise2/Exercise2.expected @@ -0,0 +1,12 @@ +| test.js:8:19:8:35 | document.location | User-controlled input: Window location | +| test.js:8:19:8:42 | documen ... .search | User-controlled input: Window location | +| test.js:17:19:17:33 | window.location | User-controlled input: Window location | +| test.js:17:19:17:38 | window.location.hash | User-controlled input: Window location | +| test.js:25:36:25:50 | window.location | User-controlled input: Window location | +| test.js:25:36:25:57 | window. ... .search | User-controlled input: Window location | +| test.js:36:19:36:35 | document.location | User-controlled input: Window location | +| test.js:36:19:36:42 | documen ... .search | User-controlled input: Window location | +| test.js:55:14:55:28 | window.location | User-controlled input: Window location | +| test.js:55:14:55:33 | window.location.hash | User-controlled input: Window location | +| test.js:64:19:64:35 | document.location | User-controlled input: Window location | +| test.js:64:19:64:42 | documen ... .search | User-controlled input: Window location | diff --git a/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/solutions-tests/Exercise2/Exercise2.qlref b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/solutions-tests/Exercise2/Exercise2.qlref new file mode 100644 index 0000000..3d135a2 --- /dev/null +++ b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/solutions-tests/Exercise2/Exercise2.qlref @@ -0,0 +1 @@ +Exercise2.ql diff --git a/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/solutions-tests/Exercise2/test.js b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/solutions-tests/Exercise2/test.js new file mode 100644 index 0000000..268ec8f --- /dev/null +++ b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/solutions-tests/Exercise2/test.js @@ -0,0 +1,68 @@ +// Test cases for XSS taint-tracking workshop +// Inspired by SAP UI5 XSS production query patterns + +// ============================================================ +// POSITIVE CASE 1: Direct XSS via document.location.search → innerHTML +// ============================================================ +function positiveDirectInnerHTML() { + var userInput = document.location.search; // source: URL query string + var container = document.getElementById("output"); + container.innerHTML = userInput; // sink: innerHTML assignment — XSS! +} + +// ============================================================ +// POSITIVE CASE 2: XSS via window.location.hash → document.write() +// ============================================================ +function positiveDocumentWrite() { + var hashValue = window.location.hash; // source: URL hash fragment + document.write(hashValue); // sink: document.write — XSS! +} + +// ============================================================ +// POSITIVE CASE 3: XSS via URL param parsed → outerHTML +// ============================================================ +function positiveOuterHTML() { + var params = new URLSearchParams(window.location.search); + var name = params.get("name"); // source: user-controlled URL parameter + var el = document.getElementById("profile"); + el.outerHTML = "
" + name + "
"; // sink: outerHTML assignment — XSS! +} + +// ============================================================ +// NEGATIVE CASE 1: Sanitized value via DOMPurify.sanitize() → innerHTML +// Should NOT be flagged because the input is sanitized +// ============================================================ +function negativeSanitized() { + var userInput = document.location.search; // source + var clean = DOMPurify.sanitize(userInput); // barrier: sanitization + var container = document.getElementById("safe-output"); + container.innerHTML = clean; // safe: sanitized input +} + +// ============================================================ +// NEGATIVE CASE 2: Hardcoded string → innerHTML +// Should NOT be flagged because there is no user-controlled source +// ============================================================ +function negativeHardcoded() { + var container = document.getElementById("static-content"); + container.innerHTML = "

Welcome to the application

"; // safe: hardcoded +} + +// ============================================================ +// EDGE CASE: XSS via eval() with URL input +// ============================================================ +function edgeCaseEval() { + var code = window.location.hash.substring(1); // source: URL hash + eval(code); // sink: eval — code injection / XSS! +} + +// ============================================================ +// NEGATIVE CASE 3: encodeURIComponent barrier +// Should NOT be flagged because input is encoded +// ============================================================ +function negativeEncoded() { + var userInput = document.location.search; + var encoded = encodeURIComponent(userInput); // barrier: encoding + var container = document.getElementById("encoded-output"); + container.innerHTML = encoded; // safe: encoded input +} diff --git a/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/solutions-tests/Exercise3/Exercise3.expected b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/solutions-tests/Exercise3/Exercise3.expected new file mode 100644 index 0000000..8cf50ff --- /dev/null +++ b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/solutions-tests/Exercise3/Exercise3.expected @@ -0,0 +1,5 @@ +| test.js:10:25:10:33 | userInput | XSS vulnerability from $@. | test.js:8:19:8:42 | documen ... .search | user input | +| test.js:18:18:18:26 | hashValue | XSS vulnerability from $@. | test.js:17:19:17:38 | window.location.hash | user input | +| test.js:28:18:28:42 | "
" ...
" | XSS vulnerability from $@. | test.js:25:36:25:57 | window. ... .search | user input | +| test.js:56:8:56:11 | code | XSS vulnerability from $@. | test.js:55:14:55:33 | window.location.hash | user input | +| test.js:67:25:67:31 | encoded | XSS vulnerability from $@. | test.js:64:19:64:42 | documen ... .search | user input | diff --git a/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/solutions-tests/Exercise3/Exercise3.qlref b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/solutions-tests/Exercise3/Exercise3.qlref new file mode 100644 index 0000000..e73d6bd --- /dev/null +++ b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/solutions-tests/Exercise3/Exercise3.qlref @@ -0,0 +1 @@ +Exercise3.ql diff --git a/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/solutions-tests/Exercise3/test.js b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/solutions-tests/Exercise3/test.js new file mode 100644 index 0000000..268ec8f --- /dev/null +++ b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/solutions-tests/Exercise3/test.js @@ -0,0 +1,68 @@ +// Test cases for XSS taint-tracking workshop +// Inspired by SAP UI5 XSS production query patterns + +// ============================================================ +// POSITIVE CASE 1: Direct XSS via document.location.search → innerHTML +// ============================================================ +function positiveDirectInnerHTML() { + var userInput = document.location.search; // source: URL query string + var container = document.getElementById("output"); + container.innerHTML = userInput; // sink: innerHTML assignment — XSS! +} + +// ============================================================ +// POSITIVE CASE 2: XSS via window.location.hash → document.write() +// ============================================================ +function positiveDocumentWrite() { + var hashValue = window.location.hash; // source: URL hash fragment + document.write(hashValue); // sink: document.write — XSS! +} + +// ============================================================ +// POSITIVE CASE 3: XSS via URL param parsed → outerHTML +// ============================================================ +function positiveOuterHTML() { + var params = new URLSearchParams(window.location.search); + var name = params.get("name"); // source: user-controlled URL parameter + var el = document.getElementById("profile"); + el.outerHTML = "
" + name + "
"; // sink: outerHTML assignment — XSS! +} + +// ============================================================ +// NEGATIVE CASE 1: Sanitized value via DOMPurify.sanitize() → innerHTML +// Should NOT be flagged because the input is sanitized +// ============================================================ +function negativeSanitized() { + var userInput = document.location.search; // source + var clean = DOMPurify.sanitize(userInput); // barrier: sanitization + var container = document.getElementById("safe-output"); + container.innerHTML = clean; // safe: sanitized input +} + +// ============================================================ +// NEGATIVE CASE 2: Hardcoded string → innerHTML +// Should NOT be flagged because there is no user-controlled source +// ============================================================ +function negativeHardcoded() { + var container = document.getElementById("static-content"); + container.innerHTML = "

Welcome to the application

"; // safe: hardcoded +} + +// ============================================================ +// EDGE CASE: XSS via eval() with URL input +// ============================================================ +function edgeCaseEval() { + var code = window.location.hash.substring(1); // source: URL hash + eval(code); // sink: eval — code injection / XSS! +} + +// ============================================================ +// NEGATIVE CASE 3: encodeURIComponent barrier +// Should NOT be flagged because input is encoded +// ============================================================ +function negativeEncoded() { + var userInput = document.location.search; + var encoded = encodeURIComponent(userInput); // barrier: encoding + var container = document.getElementById("encoded-output"); + container.innerHTML = encoded; // safe: encoded input +} diff --git a/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/solutions-tests/Exercise4/Exercise4.expected b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/solutions-tests/Exercise4/Exercise4.expected new file mode 100644 index 0000000..2d7805a --- /dev/null +++ b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/solutions-tests/Exercise4/Exercise4.expected @@ -0,0 +1,4 @@ +| test.js:10:25:10:33 | userInput | XSS vulnerability from $@. | test.js:8:19:8:42 | documen ... .search | user input | +| test.js:18:18:18:26 | hashValue | XSS vulnerability from $@. | test.js:17:19:17:38 | window.location.hash | user input | +| test.js:28:18:28:42 | "
" ...
" | XSS vulnerability from $@. | test.js:25:36:25:57 | window. ... .search | user input | +| test.js:56:8:56:11 | code | XSS vulnerability from $@. | test.js:55:14:55:33 | window.location.hash | user input | diff --git a/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/solutions-tests/Exercise4/Exercise4.qlref b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/solutions-tests/Exercise4/Exercise4.qlref new file mode 100644 index 0000000..8b891f6 --- /dev/null +++ b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/solutions-tests/Exercise4/Exercise4.qlref @@ -0,0 +1 @@ +Exercise4.ql diff --git a/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/solutions-tests/Exercise4/test.js b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/solutions-tests/Exercise4/test.js new file mode 100644 index 0000000..268ec8f --- /dev/null +++ b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/solutions-tests/Exercise4/test.js @@ -0,0 +1,68 @@ +// Test cases for XSS taint-tracking workshop +// Inspired by SAP UI5 XSS production query patterns + +// ============================================================ +// POSITIVE CASE 1: Direct XSS via document.location.search → innerHTML +// ============================================================ +function positiveDirectInnerHTML() { + var userInput = document.location.search; // source: URL query string + var container = document.getElementById("output"); + container.innerHTML = userInput; // sink: innerHTML assignment — XSS! +} + +// ============================================================ +// POSITIVE CASE 2: XSS via window.location.hash → document.write() +// ============================================================ +function positiveDocumentWrite() { + var hashValue = window.location.hash; // source: URL hash fragment + document.write(hashValue); // sink: document.write — XSS! +} + +// ============================================================ +// POSITIVE CASE 3: XSS via URL param parsed → outerHTML +// ============================================================ +function positiveOuterHTML() { + var params = new URLSearchParams(window.location.search); + var name = params.get("name"); // source: user-controlled URL parameter + var el = document.getElementById("profile"); + el.outerHTML = "
" + name + "
"; // sink: outerHTML assignment — XSS! +} + +// ============================================================ +// NEGATIVE CASE 1: Sanitized value via DOMPurify.sanitize() → innerHTML +// Should NOT be flagged because the input is sanitized +// ============================================================ +function negativeSanitized() { + var userInput = document.location.search; // source + var clean = DOMPurify.sanitize(userInput); // barrier: sanitization + var container = document.getElementById("safe-output"); + container.innerHTML = clean; // safe: sanitized input +} + +// ============================================================ +// NEGATIVE CASE 2: Hardcoded string → innerHTML +// Should NOT be flagged because there is no user-controlled source +// ============================================================ +function negativeHardcoded() { + var container = document.getElementById("static-content"); + container.innerHTML = "

Welcome to the application

"; // safe: hardcoded +} + +// ============================================================ +// EDGE CASE: XSS via eval() with URL input +// ============================================================ +function edgeCaseEval() { + var code = window.location.hash.substring(1); // source: URL hash + eval(code); // sink: eval — code injection / XSS! +} + +// ============================================================ +// NEGATIVE CASE 3: encodeURIComponent barrier +// Should NOT be flagged because input is encoded +// ============================================================ +function negativeEncoded() { + var userInput = document.location.search; + var encoded = encodeURIComponent(userInput); // barrier: encoding + var container = document.getElementById("encoded-output"); + container.innerHTML = encoded; // safe: encoded input +} diff --git a/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/solutions-tests/Exercise5/Exercise5.expected b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/solutions-tests/Exercise5/Exercise5.expected new file mode 100644 index 0000000..1feb8bc --- /dev/null +++ b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/solutions-tests/Exercise5/Exercise5.expected @@ -0,0 +1,40 @@ +edges +| test.js:8:7:8:15 | userInput | test.js:10:25:10:33 | userInput | provenance | | +| test.js:8:19:8:42 | documen ... .search | test.js:8:7:8:15 | userInput | provenance | | +| test.js:17:7:17:15 | hashValue | test.js:18:18:18:26 | hashValue | provenance | | +| test.js:17:19:17:38 | window.location.hash | test.js:17:7:17:15 | hashValue | provenance | | +| test.js:25:7:25:12 | params [MapValue] | test.js:26:14:26:19 | params [MapValue] | provenance | | +| test.js:25:16:25:58 | new URL ... search) [MapValue] | test.js:25:7:25:12 | params [MapValue] | provenance | | +| test.js:25:36:25:57 | window. ... .search | test.js:25:16:25:58 | new URL ... search) [MapValue] | provenance | | +| test.js:26:7:26:10 | name | test.js:28:28:28:31 | name | provenance | | +| test.js:26:14:26:19 | params [MapValue] | test.js:26:14:26:31 | params.get("name") | provenance | | +| test.js:26:14:26:31 | params.get("name") | test.js:26:7:26:10 | name | provenance | | +| test.js:28:28:28:31 | name | test.js:28:18:28:42 | "
" ...
" | provenance | | +| test.js:55:7:55:10 | code | test.js:56:8:56:11 | code | provenance | | +| test.js:55:14:55:33 | window.location.hash | test.js:55:14:55:46 | window. ... ring(1) | provenance | | +| test.js:55:14:55:46 | window. ... ring(1) | test.js:55:7:55:10 | code | provenance | | +nodes +| test.js:8:7:8:15 | userInput | semmle.label | userInput | +| test.js:8:19:8:42 | documen ... .search | semmle.label | documen ... .search | +| test.js:10:25:10:33 | userInput | semmle.label | userInput | +| test.js:17:7:17:15 | hashValue | semmle.label | hashValue | +| test.js:17:19:17:38 | window.location.hash | semmle.label | window.location.hash | +| test.js:18:18:18:26 | hashValue | semmle.label | hashValue | +| test.js:25:7:25:12 | params [MapValue] | semmle.label | params [MapValue] | +| test.js:25:16:25:58 | new URL ... search) [MapValue] | semmle.label | new URL ... search) [MapValue] | +| test.js:25:36:25:57 | window. ... .search | semmle.label | window. ... .search | +| test.js:26:7:26:10 | name | semmle.label | name | +| test.js:26:14:26:19 | params [MapValue] | semmle.label | params [MapValue] | +| test.js:26:14:26:31 | params.get("name") | semmle.label | params.get("name") | +| test.js:28:18:28:42 | "
" ...
" | semmle.label | "
" ...
" | +| test.js:28:28:28:31 | name | semmle.label | name | +| test.js:55:7:55:10 | code | semmle.label | code | +| test.js:55:14:55:33 | window.location.hash | semmle.label | window.location.hash | +| test.js:55:14:55:46 | window. ... ring(1) | semmle.label | window. ... ring(1) | +| test.js:56:8:56:11 | code | semmle.label | code | +subpaths +#select +| test.js:10:25:10:33 | userInput | test.js:8:19:8:42 | documen ... .search | test.js:10:25:10:33 | userInput | XSS vulnerability due to $@. | test.js:8:19:8:42 | documen ... .search | user-provided value | +| test.js:18:18:18:26 | hashValue | test.js:17:19:17:38 | window.location.hash | test.js:18:18:18:26 | hashValue | XSS vulnerability due to $@. | test.js:17:19:17:38 | window.location.hash | user-provided value | +| test.js:28:18:28:42 | "
" ...
" | test.js:25:36:25:57 | window. ... .search | test.js:28:18:28:42 | "
" ...
" | XSS vulnerability due to $@. | test.js:25:36:25:57 | window. ... .search | user-provided value | +| test.js:56:8:56:11 | code | test.js:55:14:55:33 | window.location.hash | test.js:56:8:56:11 | code | XSS vulnerability due to $@. | test.js:55:14:55:33 | window.location.hash | user-provided value | diff --git a/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/solutions-tests/Exercise5/Exercise5.qlref b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/solutions-tests/Exercise5/Exercise5.qlref new file mode 100644 index 0000000..1267a96 --- /dev/null +++ b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/solutions-tests/Exercise5/Exercise5.qlref @@ -0,0 +1 @@ +Exercise5.ql diff --git a/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/solutions-tests/Exercise5/test.js b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/solutions-tests/Exercise5/test.js new file mode 100644 index 0000000..268ec8f --- /dev/null +++ b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/solutions-tests/Exercise5/test.js @@ -0,0 +1,68 @@ +// Test cases for XSS taint-tracking workshop +// Inspired by SAP UI5 XSS production query patterns + +// ============================================================ +// POSITIVE CASE 1: Direct XSS via document.location.search → innerHTML +// ============================================================ +function positiveDirectInnerHTML() { + var userInput = document.location.search; // source: URL query string + var container = document.getElementById("output"); + container.innerHTML = userInput; // sink: innerHTML assignment — XSS! +} + +// ============================================================ +// POSITIVE CASE 2: XSS via window.location.hash → document.write() +// ============================================================ +function positiveDocumentWrite() { + var hashValue = window.location.hash; // source: URL hash fragment + document.write(hashValue); // sink: document.write — XSS! +} + +// ============================================================ +// POSITIVE CASE 3: XSS via URL param parsed → outerHTML +// ============================================================ +function positiveOuterHTML() { + var params = new URLSearchParams(window.location.search); + var name = params.get("name"); // source: user-controlled URL parameter + var el = document.getElementById("profile"); + el.outerHTML = "
" + name + "
"; // sink: outerHTML assignment — XSS! +} + +// ============================================================ +// NEGATIVE CASE 1: Sanitized value via DOMPurify.sanitize() → innerHTML +// Should NOT be flagged because the input is sanitized +// ============================================================ +function negativeSanitized() { + var userInput = document.location.search; // source + var clean = DOMPurify.sanitize(userInput); // barrier: sanitization + var container = document.getElementById("safe-output"); + container.innerHTML = clean; // safe: sanitized input +} + +// ============================================================ +// NEGATIVE CASE 2: Hardcoded string → innerHTML +// Should NOT be flagged because there is no user-controlled source +// ============================================================ +function negativeHardcoded() { + var container = document.getElementById("static-content"); + container.innerHTML = "

Welcome to the application

"; // safe: hardcoded +} + +// ============================================================ +// EDGE CASE: XSS via eval() with URL input +// ============================================================ +function edgeCaseEval() { + var code = window.location.hash.substring(1); // source: URL hash + eval(code); // sink: eval — code injection / XSS! +} + +// ============================================================ +// NEGATIVE CASE 3: encodeURIComponent barrier +// Should NOT be flagged because input is encoded +// ============================================================ +function negativeEncoded() { + var userInput = document.location.search; + var encoded = encodeURIComponent(userInput); // barrier: encoding + var container = document.getElementById("encoded-output"); + container.innerHTML = encoded; // safe: encoded input +} diff --git a/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/solutions-tests/codeql-pack.lock.yml b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/solutions-tests/codeql-pack.lock.yml new file mode 100644 index 0000000..2fc8548 --- /dev/null +++ b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/solutions-tests/codeql-pack.lock.yml @@ -0,0 +1,30 @@ +--- +lockVersion: 1.0.0 +dependencies: + codeql/concepts: + version: 0.0.13 + codeql/controlflow: + version: 2.0.23 + codeql/dataflow: + version: 2.0.23 + codeql/javascript-all: + version: 2.6.19 + codeql/mad: + version: 1.0.39 + codeql/regex: + version: 1.0.39 + codeql/ssa: + version: 2.0.15 + codeql/threat-models: + version: 1.0.39 + codeql/tutorial: + version: 1.0.39 + codeql/typetracking: + version: 2.0.23 + codeql/util: + version: 2.0.26 + codeql/xml: + version: 1.0.39 + codeql/yaml: + version: 1.0.39 +compiled: false diff --git a/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/solutions-tests/codeql-pack.yml b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/solutions-tests/codeql-pack.yml new file mode 100644 index 0000000..1d84c7d --- /dev/null +++ b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/solutions-tests/codeql-pack.yml @@ -0,0 +1,5 @@ +name: codeql-sap-js-ui5-xss-solutions-tests +version: 0.0.1 +dependencies: + codeql-sap-js-ui5-xss-solutions: '*' +extractor: javascript diff --git a/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/solutions/Exercise1.ql b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/solutions/Exercise1.ql new file mode 100644 index 0000000..c27a628 --- /dev/null +++ b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/solutions/Exercise1.ql @@ -0,0 +1,27 @@ +/** + * @name Find XSS sinks + * @description Find DOM properties and methods that can introduce XSS + * @kind problem + * @id js/workshop/xss-sinks + * @problem.severity warning + */ + +import javascript + +from DataFlow::Node sink +where + exists(DataFlow::PropWrite pw | + pw.getPropertyName() = ["innerHTML", "outerHTML"] and + sink = pw.getRhs() + ) + or + exists(DataFlow::CallNode call | + call = DataFlow::globalVarRef("document").getAMemberCall("write") and + sink = call.getArgument(0) + ) + or + exists(DataFlow::CallNode call | + call = DataFlow::globalVarRef("eval").getACall() and + sink = call.getArgument(0) + ) +select sink, "Potential XSS sink" diff --git a/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/solutions/Exercise2.ql b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/solutions/Exercise2.ql new file mode 100644 index 0000000..7f92d43 --- /dev/null +++ b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/solutions/Exercise2.ql @@ -0,0 +1,12 @@ +/** + * @name Find remote flow sources + * @description Find user-controlled inputs that could be XSS sources + * @kind problem + * @id js/workshop/xss-sources + * @problem.severity warning + */ + +import javascript + +from RemoteFlowSource source +select source, "User-controlled input: " + source.getSourceType() diff --git a/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/solutions/Exercise3.ql b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/solutions/Exercise3.ql new file mode 100644 index 0000000..897fe27 --- /dev/null +++ b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/solutions/Exercise3.ql @@ -0,0 +1,38 @@ +/** + * @name Basic XSS taint tracking + * @description Track taint from user input to XSS sinks + * @kind problem + * @id js/workshop/xss-basic-taint + * @problem.severity error + */ + +import javascript + +module XssConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { + source instanceof RemoteFlowSource + } + + predicate isSink(DataFlow::Node sink) { + exists(DataFlow::PropWrite pw | + pw.getPropertyName() = ["innerHTML", "outerHTML"] and + sink = pw.getRhs() + ) + or + exists(DataFlow::CallNode call | + call = DataFlow::globalVarRef("document").getAMemberCall("write") and + sink = call.getArgument(0) + ) + or + exists(DataFlow::CallNode call | + call = DataFlow::globalVarRef("eval").getACall() and + sink = call.getArgument(0) + ) + } +} + +module XssFlow = TaintTracking::Global; + +from DataFlow::Node source, DataFlow::Node sink +where XssFlow::flow(source, sink) +select sink, "XSS vulnerability from $@.", source, "user input" diff --git a/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/solutions/Exercise4.ql b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/solutions/Exercise4.ql new file mode 100644 index 0000000..f47ef41 --- /dev/null +++ b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/solutions/Exercise4.ql @@ -0,0 +1,46 @@ +/** + * @name XSS with sanitizer barriers + * @description Track taint with sanitizer functions as barriers + * @kind problem + * @id js/workshop/xss-barriers + * @problem.severity error + */ + +import javascript + +module XssWithBarriersConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { + source instanceof RemoteFlowSource + } + + predicate isSink(DataFlow::Node sink) { + exists(DataFlow::PropWrite pw | + pw.getPropertyName() = ["innerHTML", "outerHTML"] and + sink = pw.getRhs() + ) + or + exists(DataFlow::CallNode call | + call = DataFlow::globalVarRef("document").getAMemberCall("write") and + sink = call.getArgument(0) + ) + or + exists(DataFlow::CallNode call | + call = DataFlow::globalVarRef("eval").getACall() and + sink = call.getArgument(0) + ) + } + + predicate isBarrier(DataFlow::Node node) { + exists(DataFlow::CallNode sanitizeCall | + sanitizeCall.getCalleeName() = + ["sanitize", "encodeHTML", "escapeHtml", "encodeURIComponent"] and + node = sanitizeCall + ) + } +} + +module XssWithBarriersFlow = TaintTracking::Global; + +from DataFlow::Node source, DataFlow::Node sink +where XssWithBarriersFlow::flow(source, sink) +select sink, "XSS vulnerability from $@.", source, "user input" diff --git a/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/solutions/Exercise5.ql b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/solutions/Exercise5.ql new file mode 100644 index 0000000..b573817 --- /dev/null +++ b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/solutions/Exercise5.ql @@ -0,0 +1,53 @@ +/** + * @name Client-side cross-site scripting + * @description Writing user input directly to DOM properties allows for XSS + * @kind path-problem + * @id js/workshop/xss-path + * @problem.severity error + * @security-severity 7.8 + * @precision high + * @tags security + * external/cwe/cwe-079 + */ + +import javascript + +module XssFullConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { + source instanceof RemoteFlowSource + } + + predicate isSink(DataFlow::Node sink) { + exists(DataFlow::PropWrite pw | + pw.getPropertyName() = ["innerHTML", "outerHTML"] and + sink = pw.getRhs() + ) + or + exists(DataFlow::CallNode call | + call = DataFlow::globalVarRef("document").getAMemberCall("write") and + sink = call.getArgument(0) + ) + or + exists(DataFlow::CallNode call | + call = DataFlow::globalVarRef("eval").getACall() and + sink = call.getArgument(0) + ) + } + + predicate isBarrier(DataFlow::Node node) { + exists(DataFlow::CallNode sanitizeCall | + sanitizeCall.getCalleeName() = + ["sanitize", "encodeHTML", "escapeHtml", "encodeURIComponent"] and + node = sanitizeCall + ) + } +} + +module XssFullFlow = TaintTracking::Global; + +import XssFullFlow::PathGraph + +from XssFullFlow::PathNode source, XssFullFlow::PathNode sink +where XssFullFlow::flowPath(source, sink) +select sink.getNode(), source, sink, "XSS vulnerability due to $@.", source.getNode(), + "user-provided value" diff --git a/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/solutions/codeql-pack.lock.yml b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/solutions/codeql-pack.lock.yml new file mode 100644 index 0000000..2fc8548 --- /dev/null +++ b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/solutions/codeql-pack.lock.yml @@ -0,0 +1,30 @@ +--- +lockVersion: 1.0.0 +dependencies: + codeql/concepts: + version: 0.0.13 + codeql/controlflow: + version: 2.0.23 + codeql/dataflow: + version: 2.0.23 + codeql/javascript-all: + version: 2.6.19 + codeql/mad: + version: 1.0.39 + codeql/regex: + version: 1.0.39 + codeql/ssa: + version: 2.0.15 + codeql/threat-models: + version: 1.0.39 + codeql/tutorial: + version: 1.0.39 + codeql/typetracking: + version: 2.0.23 + codeql/util: + version: 2.0.26 + codeql/xml: + version: 1.0.39 + codeql/yaml: + version: 1.0.39 +compiled: false diff --git a/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/solutions/codeql-pack.yml b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/solutions/codeql-pack.yml new file mode 100644 index 0000000..3dd3ea6 --- /dev/null +++ b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/solutions/codeql-pack.yml @@ -0,0 +1,5 @@ +name: codeql-sap-js-ui5-xss-solutions +version: 0.0.1 +library: false +dependencies: + codeql/javascript-all: 2.6.19 diff --git a/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/tests-common/codeql-pack.yml b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/tests-common/codeql-pack.yml new file mode 100644 index 0000000..b052ed4 --- /dev/null +++ b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/tests-common/codeql-pack.yml @@ -0,0 +1,2 @@ +name: codeql-sap-js-ui5-xss-tests-common +version: 0.0.1 diff --git a/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/tests-common/test.js b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/tests-common/test.js new file mode 100644 index 0000000..268ec8f --- /dev/null +++ b/.github/skills/create-codeql-query-development-workshop/examples/codeql-sap-js-ui5-xss/tests-common/test.js @@ -0,0 +1,68 @@ +// Test cases for XSS taint-tracking workshop +// Inspired by SAP UI5 XSS production query patterns + +// ============================================================ +// POSITIVE CASE 1: Direct XSS via document.location.search → innerHTML +// ============================================================ +function positiveDirectInnerHTML() { + var userInput = document.location.search; // source: URL query string + var container = document.getElementById("output"); + container.innerHTML = userInput; // sink: innerHTML assignment — XSS! +} + +// ============================================================ +// POSITIVE CASE 2: XSS via window.location.hash → document.write() +// ============================================================ +function positiveDocumentWrite() { + var hashValue = window.location.hash; // source: URL hash fragment + document.write(hashValue); // sink: document.write — XSS! +} + +// ============================================================ +// POSITIVE CASE 3: XSS via URL param parsed → outerHTML +// ============================================================ +function positiveOuterHTML() { + var params = new URLSearchParams(window.location.search); + var name = params.get("name"); // source: user-controlled URL parameter + var el = document.getElementById("profile"); + el.outerHTML = "
" + name + "
"; // sink: outerHTML assignment — XSS! +} + +// ============================================================ +// NEGATIVE CASE 1: Sanitized value via DOMPurify.sanitize() → innerHTML +// Should NOT be flagged because the input is sanitized +// ============================================================ +function negativeSanitized() { + var userInput = document.location.search; // source + var clean = DOMPurify.sanitize(userInput); // barrier: sanitization + var container = document.getElementById("safe-output"); + container.innerHTML = clean; // safe: sanitized input +} + +// ============================================================ +// NEGATIVE CASE 2: Hardcoded string → innerHTML +// Should NOT be flagged because there is no user-controlled source +// ============================================================ +function negativeHardcoded() { + var container = document.getElementById("static-content"); + container.innerHTML = "

Welcome to the application

"; // safe: hardcoded +} + +// ============================================================ +// EDGE CASE: XSS via eval() with URL input +// ============================================================ +function edgeCaseEval() { + var code = window.location.hash.substring(1); // source: URL hash + eval(code); // sink: eval — code injection / XSS! +} + +// ============================================================ +// NEGATIVE CASE 3: encodeURIComponent barrier +// Should NOT be flagged because input is encoded +// ============================================================ +function negativeEncoded() { + var userInput = document.location.search; + var encoded = encodeURIComponent(userInput); // barrier: encoding + var container = document.getElementById("encoded-output"); + container.innerHTML = encoded; // safe: encoded input +} diff --git a/.github/skills/upgrade-codeql-cli-and-packs/SKILL.md b/.github/skills/upgrade-codeql-cli-and-packs/SKILL.md index 50d8cc7..28b73c1 100644 --- a/.github/skills/upgrade-codeql-cli-and-packs/SKILL.md +++ b/.github/skills/upgrade-codeql-cli-and-packs/SKILL.md @@ -24,9 +24,9 @@ This skill guides you through upgrading the CodeQL CLI version used by the MCP s This repository uses a **CLI-aligned versioning strategy** across all version-bearing files: -1. **`.codeql-version`**: Contains the target CLI version (e.g., `v2.24.0`) -2. **`package.json` versions**: All `package.json` files (root, client, server) use the CLI version number without the "v" prefix (e.g., `2.24.0`) -3. **`ql-mcp-*` pack versions**: Use the CLI version number without the "v" prefix (e.g., `2.24.0`) +1. **`.codeql-version`**: Contains the target CLI version (e.g., `v2.24.1`) +2. **`package.json` versions**: All `package.json` files (root, client, server) use the CLI version number without the "v" prefix (e.g., `2.24.1`) +3. **`ql-mcp-*` pack versions**: Use the CLI version number without the "v" prefix (e.g., `2.24.1`) 4. **`codeql/*-all` dependencies**: Must have `cliVersion <= target CLI version` ### Why Database Compatibility Matters @@ -69,17 +69,25 @@ gh codeql set-version vX.XX.Y codeql version # Verify installation ``` -#### 1.3 Update package.json Versions +#### 1.3 Update All Version-Bearing Files -All `package.json` files must have their `version` field set to match the CLI version (without the "v" prefix): +Use the `update-release-version.sh` script to deterministically update `.codeql-version`, all `package.json` files, and all `codeql-pack.yml` files in a single command: -| File | Field to Update | -| --------------------- | --------------- | -| `package.json` | `version` | -| `client/package.json` | `version` | -| `server/package.json` | `version` | +```bash +./server/scripts/update-release-version.sh X.XX.Y +``` + +This updates all 22 version-bearing files. Preview changes first with `--dry-run`: + +```bash +./server/scripts/update-release-version.sh --dry-run X.XX.Y +``` -Example: If `.codeql-version` is `v2.24.0`, set all `package.json` versions to `"version": "2.24.0"`. +Verify consistency with `--check`: + +```bash +./server/scripts/update-release-version.sh --check X.XX.Y +``` After updating, regenerate the lock file: @@ -125,6 +133,8 @@ Then re-verify the `cliVersion` is compatible. ### Phase 3: Update codeql-pack.yml Files +> **Note**: The `version` field in all `codeql-pack.yml` files is already updated by the `update-release-version.sh` script in Phase 1.3. This phase focuses on updating `codeql/*-all` **dependency versions** for compatibility. + #### 3.1 Files to Update All `codeql-pack.yml` files under `server/ql/*/tools/`: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 995eab0..8eee4e0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -6,6 +6,11 @@ on: - 'v*' workflow_dispatch: inputs: + publish_codeql_packs: + default: true + description: 'Publish CodeQL tool query packs to GHCR. Disable for pre-release or re-run scenarios where packs already exist.' + required: false + type: boolean version: description: 'Release version (e.g., vX.Y.Z). Must start with "v".' required: true @@ -27,6 +32,8 @@ jobs: - name: Release - Checkout repository uses: actions/checkout@v6 with: + # Fetch tags so workflow_dispatch can resolve and checkout the target tag + fetch-tags: true # Explicitly checkout the tag ref to ensure we build the correct commit # For tag pushes: refs/tags/vX.Y.Z # For workflow_dispatch: refs/heads/ (will be validated below) @@ -56,7 +63,7 @@ jobs: echo "version=${VERSION}" >> $GITHUB_OUTPUT echo "release_name=${VERSION#v}" >> $GITHUB_OUTPUT - - name: Release - Checkout tag for workflow_dispatch + - name: Release - Checkout or create tag for workflow_dispatch if: github.event_name == 'workflow_dispatch' run: | # For workflow_dispatch, we need to checkout the specific tag @@ -65,8 +72,11 @@ jobs: echo "Checking out existing tag: ${TAG}" git checkout "refs/tags/${TAG}" else - echo "::error::Tag '${TAG}' does not exist. Create the tag first before running this workflow." - exit 1 + echo "Tag '${TAG}' does not exist. Creating it now..." + CURRENT_SHA=$(git rev-parse HEAD) + git tag "${TAG}" "${CURRENT_SHA}" + git push origin "${TAG}" + echo "✅ Created and pushed tag: ${TAG} at commit ${CURRENT_SHA:0:8}" fi - name: Release - Verify checkout matches expected version @@ -85,20 +95,31 @@ jobs: echo "::warning::Tag ${TAG} not found, building from current checkout at ${CURRENT_SHA:0:8}" fi + - name: Release - Setup CodeQL environment + uses: ./.github/actions/setup-codeql-environment + with: + add-to-path: true + install-language-runtimes: false + + - name: Release - Update release version + run: | + TAG_VERSION="${{ steps.version.outputs.release_name }}" + echo "Updating all version-bearing files to '${TAG_VERSION}'..." + ./server/scripts/update-release-version.sh "${TAG_VERSION}" + - name: Release - Install dependencies - run: npm ci --include=optional + run: npm install --include=optional - name: Release - Build server run: npm run build -w server - - name: Release - Initialize CodeQL - uses: github/codeql-action/init@v3 - with: - languages: javascript-typescript - tools: latest + - name: Release - Install CodeQL pack dependencies + run: server/scripts/install-packs.sh - name: Release - Publish CodeQL tool query packs - if: startsWith(github.ref, 'refs/tags/') + if: | + (startsWith(github.ref, 'refs/tags/') || github.event_name == 'workflow_dispatch') + && (github.event_name != 'workflow_dispatch' || inputs.publish_codeql_packs) env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | @@ -115,8 +136,36 @@ jobs: fi done + - name: Release - Skip CodeQL tool query pack publishing + if: github.event_name == 'workflow_dispatch' && !inputs.publish_codeql_packs + run: echo "⏭️ CodeQL tool query pack publishing disabled via workflow input" + + - name: Release - Bundle CodeQL tool query packs + run: | + mkdir -p dist-packs + LANGUAGES="actions cpp csharp go java javascript python ruby swift" + echo "Bundling CodeQL tool query packs..." + for lang in ${LANGUAGES}; do + PACK_DIR="server/ql/${lang}/tools/src" + if [ -d "${PACK_DIR}" ]; then + PACK_NAME="ql-mcp-${lang}-tools-src" + OUTPUT="dist-packs/${PACK_NAME}.tar.gz" + echo "📦 Bundling ${PACK_DIR} -> ${OUTPUT}..." + codeql pack bundle --threads=-1 --output="${OUTPUT}" -- "${PACK_DIR}" + echo "✅ Bundled ${PACK_NAME}" + fi + done + echo "Bundled packs:" + ls -lh dist-packs/ + + - name: Release - Upload CodeQL pack artifacts + uses: actions/upload-artifact@v6 + with: + name: codeql-tool-query-packs-${{ steps.version.outputs.version }} + path: dist-packs/*.tar.gz + - name: Release - Publish npm package - if: startsWith(github.ref, 'refs/tags/') + if: startsWith(github.ref, 'refs/tags/') || github.event_name == 'workflow_dispatch' working-directory: server env: NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -135,9 +184,6 @@ jobs: cp -r server/ql dist-package/server/ cp server/package.json dist-package/server/ - # Copy root package-lock.json for npm ci (monorepo lockfile) - cp package-lock.json dist-package/server/ - # Copy root files cp README.md dist-package/ cp LICENSE dist-package/ @@ -152,7 +198,7 @@ jobs: - name: Release - Install production dependencies working-directory: dist-package/server - run: npm ci --omit=dev --include=optional + run: npm install --omit=dev --include=optional - name: Release - Create archive run: | @@ -165,21 +211,31 @@ jobs: path: codeql-development-mcp-server-${{ steps.version.outputs.version }}.tar.gz - name: Release - Create GitHub Release - if: startsWith(github.ref, 'refs/tags/') + if: startsWith(github.ref, 'refs/tags/') || github.event_name == 'workflow_dispatch' uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0 with: - files: codeql-development-mcp-server-${{ steps.version.outputs.version }}.tar.gz + files: | + codeql-development-mcp-server-${{ steps.version.outputs.version }}.tar.gz + dist-packs/*.tar.gz generate_release_notes: true + tag_name: ${{ steps.version.outputs.version }} - name: Release - Summary run: | echo "## Release Summary" >> $GITHUB_STEP_SUMMARY - echo "✅ Server built successfully" >> $GITHUB_STEP_SUMMARY - echo "✅ npm package published to GitHub Packages" >> $GITHUB_STEP_SUMMARY - echo "✅ CodeQL tool query packs published to GHCR" >> $GITHUB_STEP_SUMMARY - echo "✅ Distribution package created" >> $GITHUB_STEP_SUMMARY - echo "✅ Production dependencies installed" >> $GITHUB_STEP_SUMMARY - echo "✅ Archive created: codeql-development-mcp-server-${{ steps.version.outputs.version }}.tar.gz" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "| Step | Status |" >> $GITHUB_STEP_SUMMARY + echo "| ---- | ------ |" >> $GITHUB_STEP_SUMMARY + echo "| Server build | ✅ Success |" >> $GITHUB_STEP_SUMMARY + echo "| Version validation | ✅ All files match ${{ steps.version.outputs.release_name }} |" >> $GITHUB_STEP_SUMMARY + if [[ "${{ github.event_name }}" == "workflow_dispatch" && "${{ inputs.publish_codeql_packs }}" != "true" ]]; then + echo "| CodeQL pack publish | ⏭️ Skipped (disabled via input) |" >> $GITHUB_STEP_SUMMARY + else + echo "| CodeQL pack publish | ✅ Published to GHCR |" >> $GITHUB_STEP_SUMMARY + fi + echo "| npm package | ✅ Published to GitHub Packages |" >> $GITHUB_STEP_SUMMARY + echo "| Distribution archive | ✅ Created |" >> $GITHUB_STEP_SUMMARY + echo "| CodeQL pack bundles | ✅ Bundled |" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "### Package Contents" >> $GITHUB_STEP_SUMMARY echo "- \`server/dist/\` - Bundled JavaScript output" >> $GITHUB_STEP_SUMMARY diff --git a/.prettierignore b/.prettierignore index efdfd63..3ca7cbe 100644 --- a/.prettierignore +++ b/.prettierignore @@ -7,10 +7,12 @@ *.testproj/ options +.tmp/ build client/**/after/ client/**/before/ coverage node_modules server/dist/ - +query-results* +workshops/ diff --git a/SECURITY.md b/SECURITY.md index f0d75ed..f1950ce 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -1,7 +1,7 @@ -Thanks for helping make GitHub safe for everyone. - # Security +Thanks for helping make GitHub safe for everyone. + GitHub takes the security of our software products and services seriously, including all of the open source code repositories managed through our GitHub organizations, such as [GitHub](https://github.com/GitHub). Even though [open source repositories are outside of the scope of our bug bounty program](https://bounty.github.com/index.html#scope) and therefore not eligible for bounty rewards, we will ensure that your finding gets passed along to the appropriate maintainers for remediation. diff --git a/client/CLI-USAGE.md b/client/CLI-USAGE.md index 882f157..ef4ebd1 100644 --- a/client/CLI-USAGE.md +++ b/client/CLI-USAGE.md @@ -154,7 +154,7 @@ Set the output format for `list` commands. Accepts `text` (default) or `json`. **Text Format** - Human-readable output: -``` +```text codeql_bqrs_decode (tools/codeql_bqrs_decode) : Decode BQRS result files to human-readable formats ``` @@ -348,7 +348,7 @@ node src/ql-mcp-client.js list tools Output: -``` +```text codeql_bqrs_decode (tools/codeql_bqrs_decode) : Decode BQRS result files to human-readable formats codeql_bqrs_info (tools/codeql_bqrs_info) : Get metadata and information about BQRS result files ... @@ -451,7 +451,7 @@ The old CLI flags have been removed and new commands have been added: - New: `node src/ql-mcp-client.js` → shows help - To run integration tests: `node src/ql-mcp-client.js integration-tests` -### New Features: +### New Features - **`list` commands**: Discover MCP server primitives (auto-starts server if needed) - `list primitives` - List all primitives (prompts, resources, tools) @@ -464,7 +464,7 @@ The old CLI flags have been removed and new commands have been added: - **`--format` option**: Control output format (text/json) for list commands - **Server auto-start**: List commands automatically start the server if not running -### Script Replacement: +### Script Replacement The new `server` commands replace the shell scripts: diff --git a/client/integration-tests/primitives/tools/codeql_bqrs_interpret/sarif_format/after/results.sarif b/client/integration-tests/primitives/tools/codeql_bqrs_interpret/sarif_format/after/results.sarif index 53b9172..22727fc 100644 --- a/client/integration-tests/primitives/tools/codeql_bqrs_interpret/sarif_format/after/results.sarif +++ b/client/integration-tests/primitives/tools/codeql_bqrs_interpret/sarif_format/after/results.sarif @@ -1 +1 @@ -{"$schema":"https://json.schemastore.org/sarif-2.1.0.json","version":"2.1.0","runs":[{"tool":{"driver":{"name":"CodeQL","organization":"GitHub","semanticVersion":"2.24.0","rules":[{"id":"test/query","name":"test/query","shortDescription":{"text":"ExampleQuery1"},"fullDescription":{"text":"Example query for integration testing of the codeql_test_extract MCP server tool."},"defaultConfiguration":{"enabled":true,"level":"warning"},"help":{"text":"# Query Help for JavaScript ExampleQuery1\n\nTODO\n","markdown":"# Query Help for JavaScript ExampleQuery1\n\nTODO\n"},"properties":{"tags":["mcp-integration-tests"],"description":"Example query for integration testing of the codeql_test_extract MCP server tool.","id":"test/query","kind":"problem","name":"ExampleQuery1","precision":"medium","problem.severity":"warning"}}]},"extensions":[{"name":"mcp-client-integration-tests-static-javascript-src","semanticVersion":"0.0.1+fe0e7d2a7059ebb6c6075ff8eaea04f382747656","locations":[{"uri":"file:///home/runner/work/codeql-development-mcp-server/codeql-development-mcp-server/client/integration-tests/static/javascript/src/","description":{"text":"The QL pack root directory."},"properties":{"tags":["CodeQL/LocalPackRoot"]}},{"uri":"file:///home/runner/work/codeql-development-mcp-server/codeql-development-mcp-server/client/integration-tests/static/javascript/src/codeql-pack.yml","description":{"text":"The QL pack definition file."},"properties":{"tags":["CodeQL/LocalPackDefinitionFile"]}}]},{"name":"codeql/javascript-all","semanticVersion":"2.6.11+ce9c8e6e9fd41ef0967b13849bb6ae2183caf9ad","locations":[{"uri":"file:///home/runner/.codeql/packages/codeql/javascript-all/2.6.11/","description":{"text":"The QL pack root directory."},"properties":{"tags":["CodeQL/LocalPackRoot"]}},{"uri":"file:///home/runner/.codeql/packages/codeql/javascript-all/2.6.11/qlpack.yml","description":{"text":"The QL pack definition file."},"properties":{"tags":["CodeQL/LocalPackDefinitionFile"]}}]},{"name":"codeql/threat-models","semanticVersion":"1.0.31+ce9c8e6e9fd41ef0967b13849bb6ae2183caf9ad","locations":[{"uri":"file:///home/runner/.codeql/packages/codeql/threat-models/1.0.31/","description":{"text":"The QL pack root directory."},"properties":{"tags":["CodeQL/LocalPackRoot"]}},{"uri":"file:///home/runner/.codeql/packages/codeql/threat-models/1.0.31/qlpack.yml","description":{"text":"The QL pack definition file."},"properties":{"tags":["CodeQL/LocalPackDefinitionFile"]}}]}]},"artifacts":[{"location":{"uri":"file:/home/runner/work/codeql-development-mcp-server/codeql-development-mcp-server/client/integration-tests/static/javascript/test/ExampleQuery1/ExampleQuery1.js","index":0}}],"results":[{"ruleId":"test/query","ruleIndex":0,"rule":{"id":"test/query","index":0},"message":{"text":"Example test code file found for codeql_test_extract example query."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"file:/home/runner/work/codeql-development-mcp-server/codeql-development-mcp-server/client/integration-tests/static/javascript/test/ExampleQuery1/ExampleQuery1.js","index":0}}}]}],"columnKind":"utf16CodeUnits","properties":{"semmle.formatSpecifier":"sarif-latest"}}]} \ No newline at end of file +{"$schema":"https://json.schemastore.org/sarif-2.1.0.json","version":"2.1.0","runs":[{"tool":{"driver":{"name":"CodeQL","organization":"GitHub","semanticVersion":"2.24.1","rules":[{"id":"test/query","name":"test/query","shortDescription":{"text":"ExampleQuery1"},"fullDescription":{"text":"Example query for integration testing of the codeql_test_extract MCP server tool."},"defaultConfiguration":{"enabled":true,"level":"warning"},"help":{"text":"# Query Help for JavaScript ExampleQuery1\n\nTODO\n","markdown":"# Query Help for JavaScript ExampleQuery1\n\nTODO\n"},"properties":{"tags":["mcp-integration-tests"],"description":"Example query for integration testing of the codeql_test_extract MCP server tool.","id":"test/query","kind":"problem","name":"ExampleQuery1","precision":"medium","problem.severity":"warning"}}]},"extensions":[{"name":"mcp-client-integration-tests-static-javascript-src","semanticVersion":"0.0.1+fe0e7d2a7059ebb6c6075ff8eaea04f382747656","locations":[{"uri":"file:///home/runner/work/codeql-development-mcp-server/codeql-development-mcp-server/client/integration-tests/static/javascript/src/","description":{"text":"The QL pack root directory."},"properties":{"tags":["CodeQL/LocalPackRoot"]}},{"uri":"file:///home/runner/work/codeql-development-mcp-server/codeql-development-mcp-server/client/integration-tests/static/javascript/src/codeql-pack.yml","description":{"text":"The QL pack definition file."},"properties":{"tags":["CodeQL/LocalPackDefinitionFile"]}}]},{"name":"codeql/javascript-all","semanticVersion":"2.6.11+ce9c8e6e9fd41ef0967b13849bb6ae2183caf9ad","locations":[{"uri":"file:///home/runner/.codeql/packages/codeql/javascript-all/2.6.11/","description":{"text":"The QL pack root directory."},"properties":{"tags":["CodeQL/LocalPackRoot"]}},{"uri":"file:///home/runner/.codeql/packages/codeql/javascript-all/2.6.11/qlpack.yml","description":{"text":"The QL pack definition file."},"properties":{"tags":["CodeQL/LocalPackDefinitionFile"]}}]},{"name":"codeql/threat-models","semanticVersion":"1.0.31+ce9c8e6e9fd41ef0967b13849bb6ae2183caf9ad","locations":[{"uri":"file:///home/runner/.codeql/packages/codeql/threat-models/1.0.31/","description":{"text":"The QL pack root directory."},"properties":{"tags":["CodeQL/LocalPackRoot"]}},{"uri":"file:///home/runner/.codeql/packages/codeql/threat-models/1.0.31/qlpack.yml","description":{"text":"The QL pack definition file."},"properties":{"tags":["CodeQL/LocalPackDefinitionFile"]}}]}]},"artifacts":[{"location":{"uri":"file:/home/runner/work/codeql-development-mcp-server/codeql-development-mcp-server/client/integration-tests/static/javascript/test/ExampleQuery1/ExampleQuery1.js","index":0}}],"results":[{"ruleId":"test/query","ruleIndex":0,"rule":{"id":"test/query","index":0},"message":{"text":"Example test code file found for codeql_test_extract example query."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"file:/home/runner/work/codeql-development-mcp-server/codeql-development-mcp-server/client/integration-tests/static/javascript/test/ExampleQuery1/ExampleQuery1.js","index":0}}}]}],"columnKind":"utf16CodeUnits","properties":{"semmle.formatSpecifier":"sarif-latest"}}]} \ No newline at end of file diff --git a/client/package.json b/client/package.json index 6237386..2d314e7 100644 --- a/client/package.json +++ b/client/package.json @@ -1,6 +1,6 @@ { "name": "codeql-development-mcp-server_client", - "version": "2.24.0", + "version": "2.24.1", "description": "MCP client for integration testing of the CodeQL development MCP server", "main": "src/ql-mcp-client.js", "type": "module", @@ -32,8 +32,8 @@ "js-yaml": "^4.1.1" }, "devDependencies": { - "@eslint/js": "^9.39.2", - "eslint": "^9.39.2", + "@eslint/js": "^10.0.1", + "eslint": "^10.0.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.5", "prettier": "^3.8.1" diff --git a/client/src/lib/commands/basic-commands.js b/client/src/lib/commands/basic-commands.js index 2a9a0ff..78975e7 100644 --- a/client/src/lib/commands/basic-commands.js +++ b/client/src/lib/commands/basic-commands.js @@ -151,7 +151,9 @@ export async function executeSourceRootValidateCommand(_client, _options = {}) { // Write file with UTF-8 encoding await writeFile(outputFile, output, "utf-8"); } catch (error) { - throw new Error(`Failed to write output file ${outputFile}: ${error.message}`); + throw new Error(`Failed to write output file ${outputFile}: ${error.message}`, { + cause: error + }); } } diff --git a/client/src/lib/commands/metadata-commands.js b/client/src/lib/commands/metadata-commands.js index fed7fec..ddd86a2 100644 --- a/client/src/lib/commands/metadata-commands.js +++ b/client/src/lib/commands/metadata-commands.js @@ -67,7 +67,9 @@ export async function executeQueriesMetadataCollectCommand(client, _options = {} // Write file with UTF-8 encoding await writeFile(outputFile, output, "utf-8"); } catch (error) { - throw new Error(`Failed to write output file ${outputFile}: ${error.message}`); + throw new Error(`Failed to write output file ${outputFile}: ${error.message}`, { + cause: error + }); } // Log summary to stderr @@ -123,7 +125,9 @@ export async function executeQueriesMetadataProcessCommand(_client, _options = { // Write file with UTF-8 encoding await writeFile(outputFile, output, "utf-8"); } catch (error) { - throw new Error(`Failed to write output file ${outputFile}: ${error.message}`); + throw new Error(`Failed to write output file ${outputFile}: ${error.message}`, { + cause: error + }); } } else { throw new Error( @@ -179,7 +183,9 @@ export async function executeQueriesMetadataFilterCommand(_client, _options = {} // Write file with UTF-8 encoding await writeFile(outputFile, output, "utf-8"); } catch (error) { - throw new Error(`Failed to write output file ${outputFile}: ${error.message}`); + throw new Error(`Failed to write output file ${outputFile}: ${error.message}`, { + cause: error + }); } // Log summary to stderr @@ -232,7 +238,9 @@ export async function executeResolveAllQueriesCommand(client, _options = {}) { // Write file with UTF-8 encoding await writeFile(outputFile, output, "utf-8"); } catch (error) { - throw new Error(`Failed to write output file ${outputFile}: ${error.message}`); + throw new Error(`Failed to write output file ${outputFile}: ${error.message}`, { + cause: error + }); } // Log summary to stderr diff --git a/docs/public.md b/docs/public.md index 1cef040..91dd99e 100644 --- a/docs/public.md +++ b/docs/public.md @@ -69,7 +69,7 @@ codeql pack download advanced-security/ql-mcp-swift-tools-src To pin a version, append `@`: ```bash -codeql pack download advanced-security/ql-mcp-javascript-tools-src@2.24.0 +codeql pack download advanced-security/ql-mcp-javascript-tools-src@2.24.1 ``` Each tool query pack provides AST printing, control-flow graph printing, and call-graph queries used by the MCP server to give AI assistants structural insight into CodeQL databases. @@ -236,10 +236,10 @@ Published to the GitHub Container Registry under the `advanced-security` scope. ```yaml # Example: server/ql/javascript/tools/src/codeql-pack.yml name: advanced-security/ql-mcp-javascript-tools-src -version: 2.24.0 +version: 2.24.1 library: false dependencies: - codeql/javascript-all: 2.6.20 + codeql/javascript-all: 2.6.21 ``` ## Troubleshooting diff --git a/package-lock.json b/package-lock.json index 42fc939..9aa35b6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "codeql-development-mcp-server_repo", - "version": "2.24.0", + "version": "2.24.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "codeql-development-mcp-server_repo", - "version": "2.24.0", + "version": "2.24.1", "license": "SEE LICENSE IN LICENSE", "workspaces": [ "client", @@ -18,7 +18,7 @@ "eslint-plugin-prettier": "^5.5.5", "prettier": "^3.8.1", "typescript": "^5.9.3", - "typescript-eslint": "^8.54.0" + "typescript-eslint": "^8.55.0" }, "engines": { "node": ">=24.13.0", @@ -27,7 +27,7 @@ }, "client": { "name": "codeql-development-mcp-server_client", - "version": "2.24.0", + "version": "2.24.1", "license": "SEE LICENSE IN LICENSE", "dependencies": { "@modelcontextprotocol/sdk": "^1.26.0", @@ -38,8 +38,8 @@ "codeql-mcp-client": "src/ql-mcp-client.js" }, "devDependencies": { - "@eslint/js": "^9.39.2", - "eslint": "^9.39.2", + "@eslint/js": "^10.0.1", + "eslint": "^10.0.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.5", "prettier": "^3.8.1" @@ -49,6 +49,215 @@ "npm": ">=11.6.2" } }, + "client/node_modules/@eslint/config-array": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.1.tgz", + "integrity": "sha512-uVSdg/V4dfQmTjJzR0szNczjOH/J+FyUMMjYtr07xFRXR7EDf9i1qdxrD0VusZH9knj1/ecxzCQQxyic5NzAiA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^3.0.1", + "debug": "^4.3.1", + "minimatch": "^10.1.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "client/node_modules/@eslint/config-helpers": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.5.2.tgz", + "integrity": "sha512-a5MxrdDXEvqnIq+LisyCX6tQMPF/dSJpCfBgBauY+pNZ28yCtSsTvyTYrMhaI+LK26bVyCJfJkT0u8KIj2i1dQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.1.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "client/node_modules/@eslint/core": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.1.0.tgz", + "integrity": "sha512-/nr9K9wkr3P1EzFTdFdMoLuo1PmIxjmwvPozwoSodjNBdefGujXQUF93u1DDZpEaTuDvMsIQddsd35BwtrW9Xw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "client/node_modules/@eslint/js": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-10.0.1.tgz", + "integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "eslint": "^10.0.0" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "client/node_modules/@eslint/object-schema": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.1.tgz", + "integrity": "sha512-P9cq2dpr+LU8j3qbLygLcSZrl2/ds/pUpfnHNNuk5HW7mnngHs+6WSq5C9mO3rqRX8A1poxqLTC9cu0KOyJlBg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "client/node_modules/@eslint/plugin-kit": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.6.0.tgz", + "integrity": "sha512-bIZEUzOI1jkhviX2cp5vNyXQc6olzb2ohewQubuYlMXZ2Q/XjBO0x0XhGPvc9fjSIiUN0vw+0hq53BJ4eQSJKQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.1.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "client/node_modules/eslint": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.0.0.tgz", + "integrity": "sha512-O0piBKY36YSJhlFSG8p9VUdPV/SxxS4FYDWVpr/9GJuMaepzwlf4J8I4ov1b+ySQfDTPhc3DtLaxcT1fN0yqCg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.2", + "@eslint/config-array": "^0.23.0", + "@eslint/config-helpers": "^0.5.2", + "@eslint/core": "^1.1.0", + "@eslint/plugin-kit": "^0.6.0", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.12.4", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^9.1.0", + "eslint-visitor-keys": "^5.0.0", + "espree": "^11.1.0", + "esquery": "^1.7.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "minimatch": "^10.1.1", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "client/node_modules/eslint-scope": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.0.tgz", + "integrity": "sha512-CkWE42hOJsNj9FJRaoMX9waUFYhqY4jmyLFdAdzZr6VaCg3ynLYx4WnOdkaIifGfH4gsUcBTn4OZbHXkpLD0FQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@types/esrecurse": "^4.3.1", + "@types/estree": "^1.0.8", + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "client/node_modules/eslint-visitor-keys": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.0.tgz", + "integrity": "sha512-A0XeIi7CXU7nPlfHS9loMYEKxUaONu/hTEzHTGba9Huu94Cq1hPivf+DE5erJozZOky0LfvXAyrV/tcswpLI0Q==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "client/node_modules/espree": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-11.1.0.tgz", + "integrity": "sha512-WFWYhO1fV4iYkqOOvq8FbqIhr2pYfoDY0kCotMkDeNtGpiGGkZ1iov2u8ydjtgM8yF8rzK7oaTbw2NAzbAbehw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "client/node_modules/minimatch": { + "version": "10.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.2.tgz", + "integrity": "sha512-fu656aJ0n2kcXwsnwnv9g24tkU5uSmOlTjd6WyyaKm2Z+h1qmY6bAjrcaIxF/BslFqbZ8UBtbJi7KgQOZD2PTw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/brace-expansion": "^5.0.1" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@advanced-security/codeql-development-mcp-server": { "resolved": "server", "link": true @@ -763,6 +972,29 @@ "url": "https://github.com/sponsors/nzakas" } }, + "node_modules/@isaacs/balanced-match": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", + "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/brace-expansion": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.1.tgz", + "integrity": "sha512-WMz71T1JS624nWj2n2fnYAuPovhv7EUhk69R6i9dsVyzxt5eM3bjwvgk9L+APE1TRscGysAVMANkB0jh0LQZrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@isaacs/balanced-match": "^4.0.1" + }, + "engines": { + "node": "20 || >=22" + } + }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", @@ -1272,6 +1504,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/esrecurse": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", + "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", @@ -1371,17 +1610,17 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.54.0.tgz", - "integrity": "sha512-hAAP5io/7csFStuOmR782YmTthKBJ9ND3WVL60hcOjvtGFb+HJxH4O5huAcmcZ9v9G8P+JETiZ/G1B8MALnWZQ==", + "version": "8.55.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.55.0.tgz", + "integrity": "sha512-1y/MVSz0NglV1ijHC8OT49mPJ4qhPYjiK08YUQVbIOyu+5k862LKUHFkpKHWu//zmr7hDR2rhwUm6gnCGNmGBQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.54.0", - "@typescript-eslint/type-utils": "8.54.0", - "@typescript-eslint/utils": "8.54.0", - "@typescript-eslint/visitor-keys": "8.54.0", + "@typescript-eslint/scope-manager": "8.55.0", + "@typescript-eslint/type-utils": "8.55.0", + "@typescript-eslint/utils": "8.55.0", + "@typescript-eslint/visitor-keys": "8.55.0", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.4.0" @@ -1394,7 +1633,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.54.0", + "@typescript-eslint/parser": "^8.55.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } @@ -1410,17 +1649,17 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.54.0.tgz", - "integrity": "sha512-BtE0k6cjwjLZoZixN0t5AKP0kSzlGu7FctRXYuPAm//aaiZhmfq1JwdYpYr1brzEspYyFeF+8XF5j2VK6oalrA==", + "version": "8.55.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.55.0.tgz", + "integrity": "sha512-4z2nCSBfVIMnbuu8uinj+f0o4qOeggYJLbjpPHka3KH1om7e+H9yLKTYgksTaHcGco+NClhhY2vyO3HsMH1RGw==", "dev": true, "license": "MIT", "peer": true, "dependencies": { - "@typescript-eslint/scope-manager": "8.54.0", - "@typescript-eslint/types": "8.54.0", - "@typescript-eslint/typescript-estree": "8.54.0", - "@typescript-eslint/visitor-keys": "8.54.0", + "@typescript-eslint/scope-manager": "8.55.0", + "@typescript-eslint/types": "8.55.0", + "@typescript-eslint/typescript-estree": "8.55.0", + "@typescript-eslint/visitor-keys": "8.55.0", "debug": "^4.4.3" }, "engines": { @@ -1436,14 +1675,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.54.0.tgz", - "integrity": "sha512-YPf+rvJ1s7MyiWM4uTRhE4DvBXrEV+d8oC3P9Y2eT7S+HBS0clybdMIPnhiATi9vZOYDc7OQ1L/i6ga6NFYK/g==", + "version": "8.55.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.55.0.tgz", + "integrity": "sha512-zRcVVPFUYWa3kNnjaZGXSu3xkKV1zXy8M4nO/pElzQhFweb7PPtluDLQtKArEOGmjXoRjnUZ29NjOiF0eCDkcQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.54.0", - "@typescript-eslint/types": "^8.54.0", + "@typescript-eslint/tsconfig-utils": "^8.55.0", + "@typescript-eslint/types": "^8.55.0", "debug": "^4.4.3" }, "engines": { @@ -1458,14 +1697,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.54.0.tgz", - "integrity": "sha512-27rYVQku26j/PbHYcVfRPonmOlVI6gihHtXFbTdB5sb6qA0wdAQAbyXFVarQ5t4HRojIz64IV90YtsjQSSGlQg==", + "version": "8.55.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.55.0.tgz", + "integrity": "sha512-fVu5Omrd3jeqeQLiB9f1YsuK/iHFOwb04bCtY4BSCLgjNbOD33ZdV6KyEqplHr+IlpgT0QTZ/iJ+wT7hvTx49Q==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.54.0", - "@typescript-eslint/visitor-keys": "8.54.0" + "@typescript-eslint/types": "8.55.0", + "@typescript-eslint/visitor-keys": "8.55.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1476,9 +1715,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.54.0.tgz", - "integrity": "sha512-dRgOyT2hPk/JwxNMZDsIXDgyl9axdJI3ogZ2XWhBPsnZUv+hPesa5iuhdYt2gzwA9t8RE5ytOJ6xB0moV0Ujvw==", + "version": "8.55.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.55.0.tgz", + "integrity": "sha512-1R9cXqY7RQd7WuqSN47PK9EDpgFUK3VqdmbYrvWJZYDd0cavROGn+74ktWBlmJ13NXUQKlZ/iAEQHI/V0kKe0Q==", "dev": true, "license": "MIT", "engines": { @@ -1493,15 +1732,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.54.0.tgz", - "integrity": "sha512-hiLguxJWHjjwL6xMBwD903ciAwd7DmK30Y9Axs/etOkftC3ZNN9K44IuRD/EB08amu+Zw6W37x9RecLkOo3pMA==", + "version": "8.55.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.55.0.tgz", + "integrity": "sha512-x1iH2unH4qAt6I37I2CGlsNs+B9WGxurP2uyZLRz6UJoZWDBx9cJL1xVN/FiOmHEONEg6RIufdvyT0TEYIgC5g==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.54.0", - "@typescript-eslint/typescript-estree": "8.54.0", - "@typescript-eslint/utils": "8.54.0", + "@typescript-eslint/types": "8.55.0", + "@typescript-eslint/typescript-estree": "8.55.0", + "@typescript-eslint/utils": "8.55.0", "debug": "^4.4.3", "ts-api-utils": "^2.4.0" }, @@ -1518,9 +1757,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.54.0.tgz", - "integrity": "sha512-PDUI9R1BVjqu7AUDsRBbKMtwmjWcn4J3le+5LpcFgWULN3LvHC5rkc9gCVxbrsrGmO1jfPybN5s6h4Jy+OnkAA==", + "version": "8.55.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.55.0.tgz", + "integrity": "sha512-ujT0Je8GI5BJWi+/mMoR0wxwVEQaxM+pi30xuMiJETlX80OPovb2p9E8ss87gnSVtYXtJoU9U1Cowcr6w2FE0w==", "dev": true, "license": "MIT", "engines": { @@ -1532,16 +1771,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.54.0.tgz", - "integrity": "sha512-BUwcskRaPvTk6fzVWgDPdUndLjB87KYDrN5EYGetnktoeAvPtO4ONHlAZDnj5VFnUANg0Sjm7j4usBlnoVMHwA==", + "version": "8.55.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.55.0.tgz", + "integrity": "sha512-EwrH67bSWdx/3aRQhCoxDaHM+CrZjotc2UCCpEDVqfCE+7OjKAGWNY2HsCSTEVvWH2clYQK8pdeLp42EVs+xQw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.54.0", - "@typescript-eslint/tsconfig-utils": "8.54.0", - "@typescript-eslint/types": "8.54.0", - "@typescript-eslint/visitor-keys": "8.54.0", + "@typescript-eslint/project-service": "8.55.0", + "@typescript-eslint/tsconfig-utils": "8.55.0", + "@typescript-eslint/types": "8.55.0", + "@typescript-eslint/visitor-keys": "8.55.0", "debug": "^4.4.3", "minimatch": "^9.0.5", "semver": "^7.7.3", @@ -1586,16 +1825,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.54.0.tgz", - "integrity": "sha512-9Cnda8GS57AQakvRyG0PTejJNlA2xhvyNtEVIMlDWOOeEyBkYWhGPnfrIAnqxLMTSTo6q8g12XVjjev5l1NvMA==", + "version": "8.55.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.55.0.tgz", + "integrity": "sha512-BqZEsnPGdYpgyEIkDC1BadNY8oMwckftxBT+C8W0g1iKPdeqKZBtTfnvcq0nf60u7MkjFO8RBvpRGZBPw4L2ow==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.54.0", - "@typescript-eslint/types": "8.54.0", - "@typescript-eslint/typescript-estree": "8.54.0" + "@typescript-eslint/scope-manager": "8.55.0", + "@typescript-eslint/types": "8.55.0", + "@typescript-eslint/typescript-estree": "8.55.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1610,13 +1849,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.54.0.tgz", - "integrity": "sha512-VFlhGSl4opC0bprJiItPQ1RfUhGDIBokcPwaFH4yiBCaNPeld/9VeXbiPO1cLyorQi1G1vL+ecBk1x8o1axORA==", + "version": "8.55.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.55.0.tgz", + "integrity": "sha512-AxNRwEie8Nn4eFS1FzDMJWIISMGoXMb037sgCBJ3UR6o0fQTzr2tqN9WT+DkWJPhIdQCfV7T6D387566VtnCJA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.54.0", + "@typescript-eslint/types": "8.55.0", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -4113,16 +4352,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.54.0.tgz", - "integrity": "sha512-CKsJ+g53QpsNPqbzUsfKVgd3Lny4yKZ1pP4qN3jdMOg/sisIDLGyDMezycquXLE5JsEU0wp3dGNdzig0/fmSVQ==", + "version": "8.55.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.55.0.tgz", + "integrity": "sha512-HE4wj+r5lmDVS9gdaN0/+iqNvPZwGfnJ5lZuz7s5vLlg9ODw0bIiiETaios9LvFI1U94/VBXGm3CB2Y5cNFMpw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.54.0", - "@typescript-eslint/parser": "8.54.0", - "@typescript-eslint/typescript-estree": "8.54.0", - "@typescript-eslint/utils": "8.54.0" + "@typescript-eslint/eslint-plugin": "8.55.0", + "@typescript-eslint/parser": "8.55.0", + "@typescript-eslint/typescript-estree": "8.55.0", + "@typescript-eslint/utils": "8.55.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4408,7 +4647,7 @@ }, "server": { "name": "@advanced-security/codeql-development-mcp-server", - "version": "2.24.0", + "version": "2.24.1", "license": "SEE LICENSE IN LICENSE", "dependencies": { "@modelcontextprotocol/sdk": "^1.26.0", diff --git a/package.json b/package.json index d4d38fb..2e2b36a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "codeql-development-mcp-server_repo", - "version": "2.24.0", + "version": "2.24.1", "description": "An MCP server supporting LLM requests for CodeQL development tools and resources.", "private": true, "type": "module", @@ -28,7 +28,7 @@ "eslint-plugin-prettier": "^5.5.5", "prettier": "^3.8.1", "typescript": "^5.9.3", - "typescript-eslint": "^8.54.0" + "typescript-eslint": "^8.55.0" }, "scripts": { "build": "npm run build -w server", @@ -49,7 +49,7 @@ "tidy": "npm run lint:fix && npm run format", "tidy:check": "npm run lint && npm run format:check", "upgrade": "npm run upgrade:node", - "upgrade:node": "npx -y npm-check-updates --color --install always --reject zod --root --upgrade --workspaces" + "upgrade:node": "npx -y npm-check-updates --color --install always --peer --reject zod --root --upgrade --workspaces" }, "workspaces": [ "client", diff --git a/server/dist/codeql-development-mcp-server.js b/server/dist/codeql-development-mcp-server.js index cc56dd6..3e5f65c 100755 --- a/server/dist/codeql-development-mcp-server.js +++ b/server/dist/codeql-development-mcp-server.js @@ -9364,7 +9364,7 @@ init_package_paths(); init_logger(); dotenv.config({ path: resolve11(packageRootDir, ".env") }); var PACKAGE_NAME = "codeql-development-mcp-server"; -var VERSION = "2.24.0"; +var VERSION = "2.24.1"; async function startServer(mode = "stdio") { logger.info(`Starting CodeQL Development MCP McpServer v${VERSION} in ${mode} mode`); const codeqlBinary = resolveCodeQLBinary(); diff --git a/server/dist/codeql-development-mcp-server.js.map b/server/dist/codeql-development-mcp-server.js.map index 7bb90fa..5a99de0 100644 --- a/server/dist/codeql-development-mcp-server.js.map +++ b/server/dist/codeql-development-mcp-server.js.map @@ -1,7 +1,7 @@ { "version": 3, "sources": ["../src/utils/logger.ts", "../src/lib/server-config.ts", "../src/utils/package-paths.ts", "../src/utils/temp-dir.ts", "../src/utils/process-ready.ts", "../src/lib/language-server.ts", "../src/lib/query-server.ts", "../src/lib/cli-server.ts", "../src/lib/server-manager.ts", "../src/lib/cli-executor.ts", "../src/codeql-development-mcp-server.ts", "../src/tools/codeql/bqrs-decode.ts", "../src/lib/cli-tool-registry.ts", "../src/lib/query-results-evaluator.ts", "../src/lib/log-directory-manager.ts", "../src/tools/codeql/bqrs-info.ts", "../src/tools/codeql/bqrs-interpret.ts", "../src/tools/codeql/database-analyze.ts", "../src/tools/codeql/database-create.ts", "../src/tools/codeql/find-class-position.ts", "../src/tools/codeql/find-predicate-position.ts", "../src/tools/codeql/find-query-files.ts", "../src/lib/query-file-finder.ts", "../../node_modules/js-yaml/dist/js-yaml.mjs", "../src/lib/metadata-resolver.ts", "../src/tools/codeql/generate-log-summary.ts", "../src/tools/codeql/generate-query-help.ts", "../src/tools/codeql/pack-install.ts", "../src/tools/codeql/pack-ls.ts", "../src/tools/codeql/profile-codeql-query.ts", "../src/tools/codeql/query-compile.ts", "../src/tools/codeql/query-format.ts", "../src/tools/codeql/query-run.ts", "../src/tools/codeql/quick-evaluate.ts", "../src/tools/codeql/register-database.ts", "../src/tools/codeql/resolve-database.ts", "../src/tools/codeql/resolve-languages.ts", "../src/tools/codeql/resolve-library-path.ts", "../src/tools/codeql/resolve-metadata.ts", "../src/tools/codeql/resolve-qlref.ts", "../src/tools/codeql/resolve-queries.ts", "../src/tools/codeql/resolve-tests.ts", "../src/tools/codeql/test-accept.ts", "../src/tools/codeql/test-extract.ts", "../src/tools/codeql/test-run.ts", "../src/tools/codeql-tools.ts", "../src/lib/validation.ts", "../src/lib/query-scaffolding.ts", "../src/lib/resources.ts", "../src/tools/codeql-resources.ts", "../src/tools/lsp/lsp-diagnostics.ts", "../src/tools/lsp/lsp-server-helper.ts", "../src/tools/lsp/lsp-handlers.ts", "../src/tools/lsp/lsp-tools.ts", "../src/resources/language-resources.ts", "../src/types/language-types.ts", "../src/prompts/workflow-prompts.ts", "../src/prompts/prompt-loader.ts", "../src/tools/monitoring-tools.ts", "../../node_modules/lowdb/lib/core/Low.js", "../../node_modules/lowdb/lib/adapters/node/TextFile.js", "../../node_modules/lowdb/lib/adapters/node/DataFile.js", "../../node_modules/lowdb/lib/adapters/node/JSONFile.js", "../src/lib/session-data-manager.ts", "../src/types/monitoring.ts"], - "sourcesContent": ["/**\n * Simple logger utility.\n *\n * All log output is written to stderr. In stdio transport mode, stdout is\n * reserved exclusively for the MCP JSON-RPC protocol \u2014 any non-protocol\n * bytes on stdout would corrupt the message stream.\n */\nexport const logger = {\n info: (message: string, ...args: unknown[]) => {\n console.error(`[INFO] ${new Date().toISOString()} ${message}`, ...args);\n },\n error: (message: string, ...args: unknown[]) => {\n console.error(`[ERROR] ${new Date().toISOString()} ${message}`, ...args);\n },\n warn: (message: string, ...args: unknown[]) => {\n console.error(`[WARN] ${new Date().toISOString()} ${message}`, ...args);\n },\n debug: (message: string, ...args: unknown[]) => {\n if (process.env.DEBUG) {\n console.error(`[DEBUG] ${new Date().toISOString()} ${message}`, ...args);\n }\n },\n};\n", "/**\n * Configuration types for CodeQL background server processes.\n *\n * CodeQL provides three background server types:\n * 1. language-server \u2013 LSP-based QL validation (JSON-RPC over stdio)\n * 2. query-server2 \u2013 Query evaluation (custom protocol over stdio)\n * 3. cli-server \u2013 JVM reuse for CLI commands (custom protocol over stdio)\n *\n * Each server type has its own configuration options, but they share common\n * settings like searchPath and commonCaches.\n */\n\nimport { createHash } from 'crypto';\n\n/**\n * Server types supported by CodeQL.\n */\nexport type CodeQLServerType = 'cli' | 'language' | 'query';\n\n/**\n * Common configuration shared across all server types.\n */\nexport interface BaseServerConfig {\n /** Path to QL packs (like `codeql query compile --search-path`). */\n searchPath?: string;\n /** Location for cached data (compilation plans, downloaded packs). */\n commonCaches?: string;\n /** Directory for detailed logs. */\n logdir?: string;\n}\n\n/**\n * Configuration for the CodeQL Language Server.\n */\nexport interface LanguageServerConfig extends BaseServerConfig {\n /** Error checking mode. Default: ON_CHANGE */\n checkErrors?: 'EXPLICIT' | 'ON_CHANGE';\n /** Log level for the language server. */\n loglevel?: 'ALL' | 'DEBUG' | 'ERROR' | 'INFO' | 'OFF' | 'TRACE' | 'WARN';\n /** Single-threaded execution. */\n synchronous?: boolean;\n /** Verbosity level for progress. */\n verbosity?: 'errors' | 'progress' | 'progress+' | 'progress++' | 'progress+++' | 'warnings';\n}\n\n/**\n * Configuration for the CodeQL Query Server (query-server2).\n */\nexport interface QueryServerConfig extends BaseServerConfig {\n /** Thread count. 0 = one per core, -N = leave N cores free. */\n threads?: number;\n /** Query evaluation timeout in seconds. */\n timeout?: number;\n /** Maximum disk cache size in MB for intermediate results. */\n maxDiskCache?: number;\n /** Path for structured evaluator performance logs. */\n evaluatorLog?: string;\n /** Include tuple counts in evaluation logs. */\n tupleCounting?: boolean;\n /** Enable debug mode. */\n debug?: boolean;\n}\n\n/**\n * Configuration for the CodeQL CLI Server.\n */\nexport interface CLIServerConfig extends BaseServerConfig {\n // cli-server has fewer options \u2014 just commonCaches and logdir.\n}\n\n/**\n * Union of all server configurations (discriminated by usage context).\n */\nexport type ServerConfig = CLIServerConfig | LanguageServerConfig | QueryServerConfig;\n\n/**\n * Compute a deterministic hash for a server configuration.\n * Used to detect configuration changes that require a server restart.\n *\n * @param type - The server type.\n * @param config - The server configuration.\n * @returns A hex-encoded SHA-256 hash of the canonical JSON representation.\n */\nexport function computeConfigHash(type: CodeQLServerType, config: ServerConfig): string {\n // Deep-sort all keys to ensure deterministic serialization regardless of\n // property insertion order.\n const sortKeys = (_key: string, value: unknown): unknown => {\n if (value && typeof value === 'object' && !Array.isArray(value)) {\n const sorted: Record = {};\n for (const k of Object.keys(value as Record).sort()) {\n sorted[k] = (value as Record)[k];\n }\n return sorted;\n }\n return value;\n };\n const canonical = JSON.stringify({ config, type }, sortKeys);\n return createHash('sha256').update(canonical).digest('hex');\n}\n\n/**\n * Build command-line arguments for a language server from its configuration.\n */\nexport function buildLanguageServerArgs(config: LanguageServerConfig): string[] {\n const args: string[] = [\n 'execute', 'language-server',\n `--check-errors=${config.checkErrors ?? 'ON_CHANGE'}`,\n ];\n\n if (config.searchPath) {\n args.push(`--search-path=${config.searchPath}`);\n }\n if (config.commonCaches) {\n args.push(`--common-caches=${config.commonCaches}`);\n }\n if (config.logdir) {\n args.push(`--logdir=${config.logdir}`);\n }\n if (config.loglevel) {\n args.push(`--loglevel=${config.loglevel}`);\n }\n if (config.synchronous) {\n args.push('--synchronous');\n }\n if (config.verbosity) {\n args.push(`--verbosity=${config.verbosity}`);\n }\n\n return args;\n}\n\n/**\n * Build command-line arguments for a query server from its configuration.\n */\nexport function buildQueryServerArgs(config: QueryServerConfig): string[] {\n const args: string[] = [\n 'execute', 'query-server2',\n ];\n\n if (config.searchPath) {\n args.push(`--search-path=${config.searchPath}`);\n }\n if (config.commonCaches) {\n args.push(`--common-caches=${config.commonCaches}`);\n }\n if (config.logdir) {\n args.push(`--logdir=${config.logdir}`);\n }\n if (config.threads !== undefined) {\n args.push(`--threads=${config.threads}`);\n }\n if (config.timeout !== undefined) {\n args.push(`--timeout=${config.timeout}`);\n }\n if (config.maxDiskCache !== undefined) {\n args.push(`--max-disk-cache=${config.maxDiskCache}`);\n }\n if (config.evaluatorLog) {\n args.push(`--evaluator-log=${config.evaluatorLog}`);\n }\n if (config.debug) {\n args.push('--debug');\n args.push('--tuple-counting');\n } else if (config.tupleCounting) {\n args.push('--tuple-counting');\n }\n\n return args;\n}\n\n/**\n * Build command-line arguments for a CLI server from its configuration.\n */\nexport function buildCLIServerArgs(config: CLIServerConfig): string[] {\n const args: string[] = [\n 'execute', 'cli-server',\n ];\n\n if (config.commonCaches) {\n args.push(`--common-caches=${config.commonCaches}`);\n }\n if (config.logdir) {\n args.push(`--logdir=${config.logdir}`);\n }\n\n return args;\n}\n", "/**\n * Utilities for resolving filesystem paths relative to the server package root.\n *\n * The server can run from three different directory layouts:\n *\n * 1. **Source** (dev): `server/src/lib/` \u2192 packageRoot = `server/`\n * 2. **Bundle in monorepo** (dev/CI): `server/dist/` \u2192 packageRoot = `server/`\n * 3. **Bundle via npm** (production): `/dist/` \u2192 packageRoot = `/`\n *\n * In all three cases, the bundled QL tool query packs live at\n * `/ql//tools/src/`.\n *\n * The \"workspace root\" (monorepo root) is one level above packageRoot when\n * running from the monorepo checkout, and the packageRoot itself when running\n * from an npm install (no parent monorepo).\n */\n\nimport { dirname, resolve } from 'path';\nimport { existsSync, readFileSync } from 'fs';\nimport { fileURLToPath } from 'url';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\n\n/**\n * Detect whether the current __dirname looks like source code (`src/lib` or\n * `src/utils`) vs a bundled flat output directory (`dist/`).\n *\n * Uses a tail-of-path check so that unrelated `/src/` segments earlier in the\n * install path (e.g. `~/src/project/node_modules/.../dist`) don't cause a\n * false positive.\n */\nfunction isRunningFromSource(dir: string): boolean {\n const normalized = dir.replace(/\\\\/g, '/');\n return /\\/src(\\/[^/]+)?$/.test(normalized);\n}\n\n/**\n * Get the server package root directory.\n *\n * - From source (`server/src/utils/`): up 2 levels \u2192 `server/`\n * - From bundle (`server/dist/` or `/dist/`): up 1 level \u2192 package root\n */\nexport function getPackageRootDir(currentDir: string = __dirname): string {\n return isRunningFromSource(currentDir)\n ? resolve(currentDir, '..', '..') // src/utils \u2192 server/\n : resolve(currentDir, '..'); // dist/ \u2192 package root\n}\n\n/**\n * Get the workspace root directory (monorepo root when applicable).\n *\n * If a `package.json` with `workspaces` exists one level above the package\n * root, we're in a monorepo and that parent is the workspace root. Otherwise,\n * the packageRoot itself is the workspace root (npm install scenario).\n */\nexport function getWorkspaceRootDir(packageRoot?: string): string {\n const pkgRoot = packageRoot ?? getPackageRootDir();\n const parentDir = resolve(pkgRoot, '..');\n\n // In the monorepo, the parent directory contains a package.json with workspaces\n try {\n const parentPkgPath = resolve(parentDir, 'package.json');\n if (existsSync(parentPkgPath)) {\n const parentPkg = JSON.parse(readFileSync(parentPkgPath, 'utf8'));\n if (parentPkg.workspaces) {\n return parentDir;\n }\n }\n } catch {\n // Not in a monorepo \u2014 fall through\n }\n\n return pkgRoot;\n}\n\n/**\n * Resolve the path to a tool query pack's source directory.\n *\n * @param language - CodeQL language identifier (e.g., \"javascript\", \"cpp\")\n * @param packageRoot - Override the package root (for testing)\n * @returns Absolute path to `ql//tools/src/`\n */\nexport function resolveToolQueryPackPath(language: string, packageRoot?: string): string {\n const pkgRoot = packageRoot ?? getPackageRootDir();\n return resolve(pkgRoot, 'ql', language, 'tools', 'src');\n}\n\n/**\n * Read the package version from the nearest package.json.\n *\n * Cached at first call so the file is read at most once per process.\n */\nlet _cachedVersion: string | undefined;\nexport function getPackageVersion(): string {\n if (_cachedVersion !== undefined) return _cachedVersion;\n try {\n const pkgPath = resolve(getPackageRootDir(), 'package.json');\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));\n _cachedVersion = pkg.version ?? '0.0.0';\n } catch {\n _cachedVersion = '0.0.0';\n }\n return _cachedVersion as string;\n}\n\n/**\n * Get the effective workspace directory for resolving user-supplied relative\n * paths (test directories, database paths, pack dirs, etc.).\n *\n * In a monorepo checkout the workspace root is the monorepo parent. In an\n * npm-installed layout, `workspaceRootDir` falls back to `packageRootDir`\n * which may be read-only and is not the user's project. In that case we\n * fall back to `process.cwd()` so that relative paths resolve against the\n * directory the user actually invoked the server from.\n *\n * Override with `CODEQL_MCP_WORKSPACE` for deterministic behavior.\n */\nexport function getUserWorkspaceDir(): string {\n if (process.env.CODEQL_MCP_WORKSPACE) {\n return process.env.CODEQL_MCP_WORKSPACE;\n }\n // When workspaceRootDir === packageRootDir we are NOT in a monorepo\n // (npm-installed), so fall back to process.cwd().\n if (workspaceRootDir === packageRootDir) {\n return process.cwd();\n }\n return workspaceRootDir;\n}\n\n// Pre-computed values for use throughout the server\nexport const packageRootDir = getPackageRootDir();\nexport const workspaceRootDir = getWorkspaceRootDir(packageRootDir);\n", "/**\n * Secure project-local temporary directory utilities.\n *\n * All temporary files are created under `/.tmp/` which is\n * `.gitignore`d. This avoids writing to the OS temp directory\n * (`os.tmpdir()` / `/tmp`), which is world-readable and triggers\n * CWE-377 / CWE-378 (js/insecure-temporary-file).\n */\n\nimport { mkdirSync, mkdtempSync } from 'fs';\nimport { isAbsolute, join, resolve } from 'path';\nimport { getPackageRootDir } from './package-paths';\n\n/**\n * Base directory for all project-local temporary data.\n *\n * Resolution order:\n * 1. `CODEQL_MCP_TMP_DIR` environment variable \u2014 for read-only package root\n * scenarios (e.g., npm global installs where the package directory is not\n * writable). Relative paths are resolved against process.cwd().\n * 2. `/.tmp` \u2014 default; excluded from version control.\n */\nconst PROJECT_TMP_BASE = process.env.CODEQL_MCP_TMP_DIR\n ? (isAbsolute(process.env.CODEQL_MCP_TMP_DIR) \n ? process.env.CODEQL_MCP_TMP_DIR \n : resolve(process.cwd(), process.env.CODEQL_MCP_TMP_DIR))\n : join(getPackageRootDir(), '.tmp');\n\n/**\n * Return the project-local `.tmp` base directory, creating it if needed.\n */\nexport function getProjectTmpBase(): string {\n mkdirSync(PROJECT_TMP_BASE, { recursive: true });\n return PROJECT_TMP_BASE;\n}\n\n/**\n * Create a unique temporary directory under the project `.tmp` root.\n *\n * Works identically to `fs.mkdtempSync(os.tmpdir(), prefix)` but is\n * scoped to the repository.\n *\n * @param prefix - Directory name prefix (e.g. `'codeql-external-'`).\n * @returns Absolute path to the newly created directory.\n */\nexport function createProjectTempDir(prefix: string): string {\n const base = getProjectTmpBase();\n return mkdtempSync(join(base, prefix));\n}\n\n/**\n * Return a deterministic subdirectory under `.tmp/`, creating it\n * if it does not already exist.\n *\n * Useful for well-known scratch areas such as `query-logs` or `quickeval`.\n *\n * @param name - Subdirectory name (e.g. `'query-logs'`).\n * @returns Absolute path to the subdirectory.\n */\nexport function getProjectTmpDir(name: string): string {\n const dir = join(getProjectTmpBase(), name);\n mkdirSync(dir, { recursive: true });\n return dir;\n}\n", "/**\n * Utility for waiting until a spawned child process is ready.\n *\n * CodeQL background servers (cli-server, query-server2, language-server) run\n * on the JVM and emit stderr log output once the JVM has initialised. Rather\n * than sleeping for a hard-coded duration \u2014 which is fragile on both fast and\n * slow machines \u2014 this helper resolves as soon as the first stderr output\n * arrives (indicating the JVM is alive), or when the maximum timeout expires.\n * It also rejects immediately if the process exits or errors before becoming\n * ready, giving callers a clear error instead of a silent hang.\n */\n\nimport { ChildProcess } from 'child_process';\nimport { clearTimeout, setTimeout } from 'timers';\nimport { logger } from './logger';\n\n/** Default maximum wait for a CodeQL server to become ready (30 s). */\nconst DEFAULT_READY_TIMEOUT_MS = 30_000;\n\n/**\n * Options for {@link waitForProcessReady}.\n */\nexport interface ProcessReadyOptions {\n /**\n * Maximum time in milliseconds to wait for the process to emit its first\n * stderr output. If the timeout is reached without a signal the promise\n * still resolves (best-effort) so the caller can attempt communication.\n *\n * Default: 30 000 ms.\n */\n timeoutMs?: number;\n}\n\n/**\n * Wait until a child process signals readiness.\n *\n * \"Ready\" is defined as any of:\n * 1. The process emits data on **stderr** (JVM startup log line).\n * 2. The process emits data on **stdout** (initial protocol message).\n * 3. The maximum timeout elapses (best-effort resolve).\n *\n * The promise **rejects** if the process emits an `error` event or exits\n * before any of the above conditions are met.\n *\n * @param child - The spawned child process.\n * @param name - A human-readable label for log messages.\n * @param opts - Optional configuration.\n */\nexport function waitForProcessReady(\n child: ChildProcess,\n name: string,\n opts?: ProcessReadyOptions,\n): Promise {\n const timeoutMs = opts?.timeoutMs ?? DEFAULT_READY_TIMEOUT_MS;\n\n return new Promise((resolve, reject) => {\n let settled = false;\n\n const cleanup = () => {\n settled = true;\n child.stderr?.removeListener('data', onStderr);\n child.stdout?.removeListener('data', onStdout);\n child.removeListener('error', onError);\n child.removeListener('exit', onExit);\n clearTimeout(timer);\n };\n\n const onStderr = () => {\n if (settled) return;\n logger.debug(`${name}: ready (stderr output detected)`);\n cleanup();\n resolve();\n };\n\n const onStdout = () => {\n if (settled) return;\n logger.debug(`${name}: ready (stdout output detected)`);\n cleanup();\n resolve();\n };\n\n const onError = (error: Error) => {\n if (settled) return;\n cleanup();\n reject(new Error(`${name} failed to start: ${error.message}`));\n };\n\n const onExit = (code: number | null) => {\n if (settled) return;\n cleanup();\n reject(new Error(`${name} exited before becoming ready (code: ${code})`));\n };\n\n const timer = setTimeout(() => {\n if (settled) return;\n logger.warn(`${name}: readiness timeout (${timeoutMs} ms) \u2014 proceeding anyway`);\n cleanup();\n resolve(); // best-effort: let the caller attempt communication\n }, timeoutMs);\n\n child.stderr?.on('data', onStderr);\n child.stdout?.on('data', onStdout);\n child.on('error', onError);\n child.on('exit', onExit);\n\n // If the process was dead before we even attached listeners, reject now\n if (child.killed || child.exitCode !== null) {\n cleanup();\n reject(new Error(`${name} is not running (exitCode: ${child.exitCode})`));\n }\n });\n}\n", "/**\n * CodeQL Language Server manager for LSP communication\n * Manages the lifecycle and communication with the CodeQL language server process\n */\n\nimport { spawn, ChildProcess } from 'child_process';\nimport { EventEmitter } from 'events';\nimport { setTimeout, clearTimeout } from 'timers';\nimport { pathToFileURL } from 'url';\nimport { delimiter, join } from 'path';\nimport { logger } from '../utils/logger';\nimport { getPackageVersion } from '../utils/package-paths';\nimport { getProjectTmpDir } from '../utils/temp-dir';\nimport { getResolvedCodeQLDir } from './cli-executor';\nimport { waitForProcessReady } from '../utils/process-ready';\n\nexport interface LSPMessage {\n jsonrpc: '2.0';\n id?: number | string;\n method: string;\n params?: unknown;\n result?: unknown;\n error?: {\n code: number;\n message: string;\n data?: unknown;\n };\n}\n\nexport interface Diagnostic {\n range: {\n start: { line: number; character: number };\n end: { line: number; character: number };\n };\n severity: number; // 1=Error, 2=Warning, 3=Information, 4=Hint\n source?: string;\n message: string;\n code?: string | number;\n}\n\nexport interface PublishDiagnosticsParams {\n uri: string;\n diagnostics: Diagnostic[];\n}\n\nexport interface LanguageServerOptions {\n searchPath?: string;\n logdir?: string;\n loglevel?: 'ALL' | 'DEBUG' | 'ERROR' | 'INFO' | 'OFF' | 'TRACE' | 'WARN';\n synchronous?: boolean;\n verbosity?: 'errors' | 'progress' | 'progress+' | 'progress++' | 'progress+++' | 'warnings';\n}\n\n/**\n * Position in a text document (0-based line and character).\n */\nexport interface LSPPosition {\n character: number;\n line: number;\n}\n\n/**\n * A range in a text document.\n */\nexport interface LSPRange {\n end: LSPPosition;\n start: LSPPosition;\n}\n\n/**\n * A location in a resource (file URI + range).\n */\nexport interface LSPLocation {\n range: LSPRange;\n uri: string;\n}\n\n/**\n * Identifies a text document by its URI.\n */\nexport interface TextDocumentIdentifier {\n uri: string;\n}\n\n/**\n * A text document position (document + position within it).\n */\nexport interface TextDocumentPositionParams {\n position: LSPPosition;\n textDocument: TextDocumentIdentifier;\n}\n\n/**\n * A completion item returned by the language server.\n */\nexport interface CompletionItem {\n detail?: string;\n documentation?: string | { kind: string; value: string };\n insertText?: string;\n kind?: number;\n label: string;\n sortText?: string;\n}\n\nexport class CodeQLLanguageServer extends EventEmitter {\n private server: ChildProcess | null = null;\n private messageId = 1;\n private pendingResponses = new Map void; reject: (_error: Error) => void }>();\n private isInitialized = false;\n private currentWorkspaceUri: string | undefined;\n private messageBuffer = '';\n\n constructor(private _options: LanguageServerOptions = {}) {\n super();\n }\n\n async start(): Promise {\n if (this.server) {\n throw new Error('Language server is already running');\n }\n\n logger.info('Starting CodeQL Language Server...');\n\n const args = [\n 'execute', 'language-server',\n '--check-errors=ON_CHANGE'\n ];\n\n // Add optional arguments\n if (this._options.searchPath) {\n args.push(`--search-path=${this._options.searchPath}`);\n }\n if (this._options.logdir) {\n args.push(`--logdir=${this._options.logdir}`);\n }\n if (this._options.loglevel) {\n args.push(`--loglevel=${this._options.loglevel}`);\n }\n if (this._options.synchronous) {\n args.push('--synchronous');\n }\n if (this._options.verbosity) {\n args.push(`--verbosity=${this._options.verbosity}`);\n }\n\n // Build environment with CODEQL_PATH directory prepended to PATH\n // (mirrors the approach in cli-executor.ts getSafeEnvironment).\n const spawnEnv = { ...process.env };\n const codeqlDir = getResolvedCodeQLDir();\n if (codeqlDir && spawnEnv.PATH) {\n spawnEnv.PATH = `${codeqlDir}${delimiter}${spawnEnv.PATH}`;\n } else if (codeqlDir) {\n spawnEnv.PATH = codeqlDir;\n }\n\n this.server = spawn('codeql', args, {\n stdio: ['pipe', 'pipe', 'pipe'],\n env: spawnEnv\n });\n\n this.server.stderr?.on('data', (data) => {\n logger.debug('CodeQL LS stderr:', data.toString());\n });\n\n this.server.stdout?.on('data', (data) => {\n this.handleStdout(data);\n });\n\n this.server.on('error', (error) => {\n logger.error('CodeQL Language Server error:', error);\n this.emit('error', error);\n });\n\n this.server.on('exit', (code) => {\n logger.info('CodeQL Language Server exited with code:', code);\n this.server = null;\n this.isInitialized = false;\n this.emit('exit', code);\n });\n\n // Wait for the JVM to initialise (resolves on first stderr/stdout output)\n await waitForProcessReady(this.server, 'CodeQL Language Server');\n }\n\n private handleStdout(data: Buffer): void {\n this.messageBuffer += data.toString();\n \n let headerEnd = this.messageBuffer.indexOf('\\r\\n\\r\\n');\n while (headerEnd !== -1) {\n const header = this.messageBuffer.substring(0, headerEnd);\n const contentLengthMatch = header.match(/Content-Length: (\\d+)/);\n \n if (contentLengthMatch) {\n const contentLength = parseInt(contentLengthMatch[1]);\n const messageStart = headerEnd + 4;\n const messageEnd = messageStart + contentLength;\n \n if (this.messageBuffer.length >= messageEnd) {\n const messageContent = this.messageBuffer.substring(messageStart, messageEnd);\n this.messageBuffer = this.messageBuffer.substring(messageEnd);\n \n try {\n const message: LSPMessage = JSON.parse(messageContent);\n this.handleMessage(message);\n } catch (error) {\n logger.error('Failed to parse LSP message:', error, messageContent);\n }\n \n headerEnd = this.messageBuffer.indexOf('\\r\\n\\r\\n');\n } else {\n break;\n }\n } else {\n logger.error('Invalid LSP header:', header);\n this.messageBuffer = '';\n break;\n }\n }\n }\n\n private handleMessage(message: LSPMessage): void {\n logger.debug('Received LSP message:', message);\n\n // Handle responses to our requests\n if (message.id !== undefined && this.pendingResponses.has(Number(message.id))) {\n const pending = this.pendingResponses.get(Number(message.id))!;\n this.pendingResponses.delete(Number(message.id));\n \n if (message.error) {\n pending.reject(new Error(`LSP Error: ${message.error.message}`));\n } else {\n pending.resolve(message.result);\n }\n return;\n }\n\n // Handle notifications from server\n if (message.method === 'textDocument/publishDiagnostics') {\n this.emit('diagnostics', message.params as PublishDiagnosticsParams);\n }\n }\n\n private sendMessage(message: LSPMessage): void {\n if (!this.server?.stdin) {\n throw new Error('Language server is not running');\n }\n\n const messageStr = JSON.stringify(message);\n const contentLength = Buffer.byteLength(messageStr, 'utf8');\n const header = `Content-Length: ${contentLength}\\r\\n\\r\\n`;\n const fullMessage = header + messageStr;\n\n logger.debug('Sending LSP message:', fullMessage);\n this.server.stdin.write(fullMessage);\n }\n\n private sendRequest(method: string, params?: unknown): Promise {\n const id = this.messageId++;\n const message: LSPMessage = {\n jsonrpc: '2.0',\n id,\n method,\n params\n };\n\n return new Promise((resolve, reject) => {\n // Wrap resolve/reject to clear the timer when the promise settles.\n const timer = setTimeout(() => {\n if (this.pendingResponses.has(id)) {\n this.pendingResponses.delete(id);\n reject(new Error(`LSP request timeout for method: ${method}`));\n }\n }, 60_000); // 60 second timeout (Windows CI cold JVM can exceed 30s)\n\n this.pendingResponses.set(id, {\n reject: (err: Error) => { clearTimeout(timer); reject(err); },\n resolve: (val: unknown) => { clearTimeout(timer); resolve(val); },\n });\n this.sendMessage(message);\n });\n }\n\n private sendNotification(method: string, params?: unknown): void {\n const message: LSPMessage = {\n jsonrpc: '2.0',\n method,\n params\n };\n this.sendMessage(message);\n }\n\n /**\n * Initialize the language server with an optional workspace URI.\n *\n * If the server is already initialized with a different workspace, a\n * `workspace/didChangeWorkspaceFolders` notification is sent to update\n * the workspace context instead of requiring a full restart.\n */\n async initialize(workspaceUri?: string): Promise {\n if (this.isInitialized) {\n // If workspace changed, notify the server\n if (workspaceUri && workspaceUri !== this.currentWorkspaceUri) {\n await this.updateWorkspace(workspaceUri);\n }\n return;\n }\n\n logger.info('Initializing CodeQL Language Server...');\n\n const initParams = {\n processId: process.pid,\n clientInfo: {\n name: 'codeql-development-mcp-server',\n version: getPackageVersion()\n },\n capabilities: {\n textDocument: {\n completion: { completionItem: { snippetSupport: false } },\n definition: {},\n publishDiagnostics: {},\n references: {},\n synchronization: {\n didClose: true,\n didChange: true,\n didOpen: true,\n },\n },\n workspace: {\n workspaceFolders: true,\n },\n }\n };\n\n if (workspaceUri) {\n (initParams as unknown as { workspaceFolders: unknown[] }).workspaceFolders = [{\n uri: workspaceUri,\n name: 'codeql-workspace'\n }];\n }\n\n await this.sendRequest('initialize', initParams);\n this.sendNotification('initialized', {});\n\n this.currentWorkspaceUri = workspaceUri;\n this.isInitialized = true;\n logger.info('CodeQL Language Server initialized successfully');\n }\n\n /**\n * Update the workspace folders on a running, initialized server.\n */\n private async updateWorkspace(newUri: string): Promise {\n logger.info(`Updating workspace from ${this.currentWorkspaceUri} to ${newUri}`);\n\n const removed = this.currentWorkspaceUri\n ? [{ uri: this.currentWorkspaceUri, name: 'codeql-workspace' }]\n : [];\n\n this.sendNotification('workspace/didChangeWorkspaceFolders', {\n event: {\n added: [{ uri: newUri, name: 'codeql-workspace' }],\n removed,\n },\n });\n\n this.currentWorkspaceUri = newUri;\n }\n\n /**\n * Get the current workspace URI.\n */\n getWorkspaceUri(): string | undefined {\n return this.currentWorkspaceUri;\n }\n\n async evaluateQL(qlCode: string, uri?: string): Promise {\n if (!this.isInitialized) {\n throw new Error('Language server is not initialized');\n }\n\n // Default to a project-local virtual URI rather than /tmp\n const documentUri = uri || pathToFileURL(join(getProjectTmpDir('lsp-eval'), 'eval.ql')).href;\n\n return new Promise((resolve, reject) => {\n let diagnosticsReceived = false;\n const timeout = setTimeout(() => {\n if (!diagnosticsReceived) {\n this.removeListener('diagnostics', diagnosticsHandler);\n reject(new Error('Timeout waiting for diagnostics'));\n }\n }, 90_000); // 90s \u2014 first call triggers JVM start + compilation; Windows CI is slow\n\n // Listen for diagnostics\n const diagnosticsHandler = (params: PublishDiagnosticsParams) => {\n if (params.uri === documentUri) {\n diagnosticsReceived = true;\n clearTimeout(timeout);\n this.removeListener('diagnostics', diagnosticsHandler);\n\n // Close the document\n this.sendNotification('textDocument/didClose', {\n textDocument: { uri: documentUri }\n });\n\n resolve(params.diagnostics);\n }\n };\n\n this.on('diagnostics', diagnosticsHandler);\n\n // Open the document with the QL code\n this.sendNotification('textDocument/didOpen', {\n textDocument: {\n uri: documentUri,\n languageId: 'ql',\n version: 1,\n text: qlCode\n }\n });\n });\n }\n\n // ---- LSP feature methods (issue #1) ----\n\n /**\n * Get code completions at a position in a document.\n */\n async getCompletions(params: TextDocumentPositionParams): Promise {\n if (!this.isInitialized) {\n throw new Error('Language server is not initialized');\n }\n if (!this.isRunning()) {\n throw new Error('Language server process is not running');\n }\n const result = await this.sendRequest('textDocument/completion', params);\n // The result may be a CompletionList or CompletionItem[]\n if (result && typeof result === 'object' && 'items' in (result as object)) {\n return (result as { items: CompletionItem[] }).items;\n }\n return (result as CompletionItem[]) || [];\n }\n\n /**\n * Find the definition(s) of a symbol at a position.\n */\n async getDefinition(params: TextDocumentPositionParams): Promise {\n if (!this.isInitialized) {\n throw new Error('Language server is not initialized');\n }\n if (!this.isRunning()) {\n throw new Error('Language server process is not running');\n }\n const result = await this.sendRequest('textDocument/definition', params);\n return this.normalizeLocations(result);\n }\n\n /**\n * Find all references to a symbol at a position.\n */\n async getReferences(params: TextDocumentPositionParams & { context?: { includeDeclaration: boolean } }): Promise {\n if (!this.isInitialized) {\n throw new Error('Language server is not initialized');\n }\n if (!this.isRunning()) {\n throw new Error('Language server process is not running');\n }\n const result = await this.sendRequest('textDocument/references', {\n ...params,\n context: params.context ?? { includeDeclaration: true },\n });\n return this.normalizeLocations(result);\n }\n\n /**\n * Open a text document in the language server.\n * The document must be opened before requesting completions, definitions, etc.\n */\n openDocument(uri: string, text: string, languageId = 'ql', version = 1): void {\n if (!this.isInitialized) {\n throw new Error('Language server is not initialized');\n }\n this.sendNotification('textDocument/didOpen', {\n textDocument: { uri, languageId, version, text },\n });\n }\n\n /**\n * Close a text document in the language server.\n */\n closeDocument(uri: string): void {\n if (!this.isInitialized) {\n throw new Error('Language server is not initialized');\n }\n this.sendNotification('textDocument/didClose', {\n textDocument: { uri },\n });\n }\n\n /**\n * Normalize a definition/references/implementation result to Location[].\n * The LSP spec allows Location | Location[] | LocationLink[].\n */\n private normalizeLocations(result: unknown): LSPLocation[] {\n if (!result) return [];\n if (Array.isArray(result)) {\n return result.map((item) => {\n // LocationLink has targetUri/targetRange\n if ('targetUri' in item) {\n return { uri: item.targetUri, range: item.targetRange } as LSPLocation;\n }\n return item as LSPLocation;\n });\n }\n // Single Location\n if (typeof result === 'object' && 'uri' in (result as object)) {\n return [result as LSPLocation];\n }\n return [];\n }\n\n async shutdown(): Promise {\n if (!this.server) {\n return;\n }\n\n logger.info('Shutting down CodeQL Language Server...');\n\n try {\n await this.sendRequest('shutdown', {});\n if (this.server) {\n this.sendNotification('exit', {});\n }\n } catch (error) {\n logger.warn('Error during graceful shutdown:', error);\n }\n\n // Force kill if needed\n await new Promise((resolve) => {\n const timer = setTimeout(() => {\n if (this.server) {\n this.server.kill('SIGTERM');\n }\n resolve();\n }, 1000);\n\n if (this.server) {\n this.server.once('exit', () => {\n clearTimeout(timer);\n this.server = null;\n resolve();\n });\n } else {\n clearTimeout(timer);\n resolve();\n }\n });\n\n this.isInitialized = false;\n }\n\n isRunning(): boolean {\n return this.server !== null && !this.server.killed;\n }\n}", "/**\n * CodeQL Query Server (query-server2) client.\n *\n * Manages a long-lived `codeql execute query-server2` process that evaluates\n * queries using a custom JSON-RPC protocol over stdio. Reusing the server\n * avoids repeated JVM startup for each query evaluation.\n *\n * Protocol: The query-server2 uses JSON-RPC with Content-Length headers\n * (same framing as LSP) over stdin/stdout.\n */\n\nimport { ChildProcess, spawn } from 'child_process';\nimport { delimiter } from 'path';\nimport { EventEmitter } from 'events';\nimport { clearTimeout, setTimeout } from 'timers';\nimport { buildQueryServerArgs, QueryServerConfig } from './server-config';\nimport { getResolvedCodeQLDir } from './cli-executor';\nimport { logger } from '../utils/logger';\nimport { waitForProcessReady } from '../utils/process-ready';\n\n/**\n * A pending request awaiting a response from the query server.\n */\ninterface PendingRequest {\n reject: (_error: Error) => void;\n resolve: (_value: unknown) => void;\n}\n\n/**\n * Client for the CodeQL query-server2 process.\n *\n * Spawns `codeql execute query-server2` and communicates over stdin/stdout\n * using JSON-RPC with Content-Length framing.\n */\nexport class CodeQLQueryServer extends EventEmitter {\n private messageBuffer = '';\n private messageId = 1;\n private pendingRequests = new Map();\n private process: ChildProcess | null = null;\n private readonly config: QueryServerConfig;\n\n constructor(config: QueryServerConfig) {\n super();\n this.config = config;\n }\n\n /**\n * Start the query-server2 process.\n */\n async start(): Promise {\n if (this.process) {\n throw new Error('Query server is already running');\n }\n\n logger.info('Starting CodeQL Query Server (query-server2)...');\n\n const args = buildQueryServerArgs(this.config);\n\n // Build environment with CODEQL_PATH directory prepended to PATH\n const spawnEnv = { ...process.env };\n const codeqlDir = getResolvedCodeQLDir();\n if (codeqlDir && spawnEnv.PATH) {\n spawnEnv.PATH = `${codeqlDir}${delimiter}${spawnEnv.PATH}`;\n } else if (codeqlDir) {\n spawnEnv.PATH = codeqlDir;\n }\n\n this.process = spawn('codeql', args, {\n stdio: ['pipe', 'pipe', 'pipe'],\n env: spawnEnv,\n });\n\n this.process.stderr?.on('data', (data: Buffer) => {\n logger.debug('QueryServer2 stderr:', data.toString());\n });\n\n this.process.stdout?.on('data', (data: Buffer) => {\n this.handleStdout(data);\n });\n\n this.process.on('error', (error: Error) => {\n logger.error('Query server process error:', error);\n this.emit('error', error);\n });\n\n this.process.on('exit', (code: number | null) => {\n logger.info(`Query server exited with code: ${code}`);\n this.rejectAllPending(new Error(`Query server exited with code: ${code}`));\n this.process = null;\n this.emit('exit', code);\n });\n\n // Wait for the JVM to initialise (resolves on first stderr/stdout output)\n await waitForProcessReady(this.process, 'CodeQL Query Server');\n logger.info('CodeQL Query Server started');\n }\n\n /**\n * Send a request to the query server and await the response.\n *\n * @param method - The JSON-RPC method name.\n * @param params - The method parameters.\n * @param timeoutMs - Request timeout in milliseconds (default: 300000 = 5 min).\n * @returns The result from the server.\n */\n sendRequest(method: string, params?: unknown, timeoutMs = 300_000): Promise {\n const id = this.messageId++;\n const message = {\n id,\n jsonrpc: '2.0' as const,\n method,\n params,\n };\n\n return new Promise((resolve, reject) => {\n this.pendingRequests.set(id, { reject, resolve });\n\n try {\n this.sendRaw(message);\n } catch (error) {\n // Clean up immediately \u2014 sendRaw() failed so no response will arrive.\n this.pendingRequests.delete(id);\n reject(error instanceof Error ? error : new Error(String(error)));\n return;\n }\n\n const timer = setTimeout(() => {\n if (this.pendingRequests.has(id)) {\n this.pendingRequests.delete(id);\n reject(new Error(`Query server request timeout for method: ${method}`));\n }\n }, timeoutMs);\n\n // Clear the timeout when the promise settles\n const originalResolve = resolve;\n const originalReject = reject;\n const wrapped = {\n reject: (err: Error) => { clearTimeout(timer); originalReject(err); },\n resolve: (val: unknown) => { clearTimeout(timer); originalResolve(val); },\n };\n this.pendingRequests.set(id, wrapped);\n });\n }\n\n /**\n * Gracefully shut down the query server.\n */\n async shutdown(): Promise {\n if (!this.process) {\n return;\n }\n\n logger.info('Shutting down CodeQL Query Server...');\n\n try {\n await this.sendRequest('shutdown', {}, 5000);\n } catch (error) {\n logger.warn('Error during query server graceful shutdown:', error);\n }\n\n // Force kill if process lingers\n await new Promise((resolve) => {\n const timer = setTimeout(() => {\n if (this.process) {\n this.process.kill('SIGTERM');\n this.process = null;\n }\n resolve();\n }, 2000);\n\n if (this.process) {\n this.process.once('exit', () => {\n clearTimeout(timer);\n this.process = null;\n resolve();\n });\n } else {\n clearTimeout(timer);\n resolve();\n }\n });\n }\n\n /**\n * Whether the query server process is running.\n */\n isRunning(): boolean {\n return this.process !== null && !this.process.killed;\n }\n\n // ---- private helpers ----\n\n private handleStdout(data: Buffer): void {\n this.messageBuffer += data.toString();\n\n let headerEnd = this.messageBuffer.indexOf('\\r\\n\\r\\n');\n while (headerEnd !== -1) {\n const header = this.messageBuffer.substring(0, headerEnd);\n const contentLengthMatch = header.match(/Content-Length: (\\d+)/);\n\n if (contentLengthMatch) {\n const contentLength = parseInt(contentLengthMatch[1]);\n const messageStart = headerEnd + 4;\n const messageEnd = messageStart + contentLength;\n\n if (this.messageBuffer.length >= messageEnd) {\n const messageContent = this.messageBuffer.substring(messageStart, messageEnd);\n this.messageBuffer = this.messageBuffer.substring(messageEnd);\n\n try {\n const message = JSON.parse(messageContent);\n this.handleMessage(message);\n } catch (error) {\n logger.error('Failed to parse query server message:', error);\n }\n\n headerEnd = this.messageBuffer.indexOf('\\r\\n\\r\\n');\n } else {\n break;\n }\n } else {\n logger.error('Invalid query server header:', header);\n this.messageBuffer = '';\n break;\n }\n }\n }\n\n private handleMessage(message: { error?: { message: string }; id?: number; method?: string; params?: unknown; result?: unknown }): void {\n logger.debug('QueryServer2 message:', message);\n\n // Handle responses\n if (message.id !== undefined && this.pendingRequests.has(Number(message.id))) {\n const pending = this.pendingRequests.get(Number(message.id))!;\n this.pendingRequests.delete(Number(message.id));\n\n if (message.error) {\n pending.reject(new Error(`Query server error: ${message.error.message}`));\n } else {\n pending.resolve(message.result);\n }\n return;\n }\n\n // Handle notifications (progress, etc.)\n if (message.method) {\n this.emit('notification', { method: message.method, params: message.params });\n }\n }\n\n private rejectAllPending(error: Error): void {\n for (const [id, pending] of this.pendingRequests) {\n pending.reject(error);\n this.pendingRequests.delete(id);\n }\n }\n\n private sendRaw(message: object): void {\n if (!this.process?.stdin) {\n throw new Error('Query server is not running');\n }\n\n const body = JSON.stringify(message);\n const contentLength = Buffer.byteLength(body, 'utf8');\n const frame = `Content-Length: ${contentLength}\\r\\n\\r\\n${body}`;\n this.process.stdin.write(frame);\n }\n}\n", "/**\n * CodeQL CLI Server client.\n *\n * Manages a long-lived `codeql execute cli-server` process that executes CLI\n * commands without repeated JVM startup overhead. Commands are serialized as\n * JSON arrays followed by a NUL byte, and responses are NUL-terminated JSON.\n *\n * Inspired by the `CodeQLCliServer` class in github/vscode-codeql.\n */\n\nimport { ChildProcess, spawn } from 'child_process';\nimport { delimiter } from 'path';\nimport { EventEmitter } from 'events';\nimport { clearTimeout, setTimeout } from 'timers';\nimport { buildCLIServerArgs, CLIServerConfig } from './server-config';\nimport { getResolvedCodeQLDir } from './cli-executor';\nimport { logger } from '../utils/logger';\nimport { waitForProcessReady } from '../utils/process-ready';\n\n/**\n * A queued command waiting to be sent to the CLI server.\n */\ninterface QueuedCommand {\n args: string[];\n reject: (_error: Error) => void;\n resolve: (_value: string) => void;\n}\n\n/**\n * Client for the CodeQL CLI Server process.\n *\n * The cli-server uses a simple NUL-delimited protocol:\n * - **Request**: JSON array of command arguments, followed by a NUL byte.\n * - **Response**: command stdout, terminated by a NUL byte on stdout.\n * stderr is forwarded as-is.\n */\nexport class CodeQLCLIServer extends EventEmitter {\n private commandInProgress = false;\n private commandQueue: Array<() => void> = [];\n private readonly config: CLIServerConfig;\n private currentReject: ((_error: Error) => void) | null = null;\n private currentResolve: ((_value: string) => void) | null = null;\n private nullBuffer = Buffer.alloc(1);\n private process: ChildProcess | null = null;\n private stdoutBuffer = '';\n\n constructor(config: CLIServerConfig) {\n super();\n this.config = config;\n }\n\n /**\n * Start the cli-server process.\n */\n async start(): Promise {\n if (this.process) {\n throw new Error('CLI server is already running');\n }\n\n logger.info('Starting CodeQL CLI Server...');\n\n const args = buildCLIServerArgs(this.config);\n\n const spawnEnv = { ...process.env };\n const codeqlDir = getResolvedCodeQLDir();\n if (codeqlDir && spawnEnv.PATH) {\n spawnEnv.PATH = `${codeqlDir}${delimiter}${spawnEnv.PATH}`;\n } else if (codeqlDir) {\n spawnEnv.PATH = codeqlDir;\n }\n\n this.process = spawn('codeql', args, {\n stdio: ['pipe', 'pipe', 'pipe'],\n env: spawnEnv,\n });\n\n this.process.stdout?.on('data', (data: Buffer) => {\n this.handleStdout(data);\n });\n\n this.process.stderr?.on('data', (data: Buffer) => {\n logger.debug('CLIServer stderr:', data.toString());\n });\n\n this.process.on('error', (error: Error) => {\n logger.error('CLI server process error:', error);\n if (this.currentReject) {\n this.currentReject(error);\n this.currentReject = null;\n this.currentResolve = null;\n }\n this.emit('error', error);\n });\n\n this.process.on('exit', (code: number | null) => {\n logger.info(`CLI server exited with code: ${code}`);\n if (this.currentReject) {\n this.currentReject(new Error(`CLI server exited unexpectedly with code: ${code}`));\n this.currentReject = null;\n this.currentResolve = null;\n }\n this.process = null;\n this.emit('exit', code);\n });\n\n // Wait for the JVM to initialise (resolves on first stderr/stdout output)\n await waitForProcessReady(this.process, 'CodeQL CLI Server');\n logger.info('CodeQL CLI Server started');\n }\n\n /**\n * Run a CodeQL CLI command through the persistent server.\n *\n * Commands are serialized and queued; only one command runs at a time.\n *\n * @param args - The full command arguments (e.g. `['resolve', 'qlpacks']`).\n * @returns The stdout output from the command.\n */\n runCommand(args: string[]): Promise {\n return new Promise((resolve, reject) => {\n const execute = () => {\n this.executeCommand({ args, reject, resolve });\n };\n\n if (this.commandInProgress) {\n this.commandQueue.push(execute);\n } else {\n execute();\n }\n });\n }\n\n /**\n * Gracefully shut down the CLI server.\n */\n async shutdown(): Promise {\n if (!this.process) {\n return;\n }\n\n logger.info('Shutting down CodeQL CLI Server...');\n\n try {\n // Send shutdown command\n this.process.stdin?.write(JSON.stringify(['shutdown']), 'utf8');\n this.process.stdin?.write(this.nullBuffer);\n } catch (error) {\n logger.warn('Error during CLI server shutdown request:', error);\n }\n\n // Give it a moment, then force kill\n await new Promise((resolve) => {\n const timer = setTimeout(() => {\n if (this.process) {\n this.process.kill('SIGTERM');\n this.process = null;\n }\n resolve();\n }, 2000);\n\n if (this.process) {\n this.process.once('exit', () => {\n clearTimeout(timer);\n this.process = null;\n resolve();\n });\n } else {\n clearTimeout(timer);\n resolve();\n }\n });\n\n this.commandInProgress = false;\n this.commandQueue = [];\n logger.info('CodeQL CLI Server stopped');\n }\n\n /**\n * Whether the CLI server process is running.\n */\n isRunning(): boolean {\n return this.process !== null && !this.process.killed;\n }\n\n // ---- private helpers ----\n\n private executeCommand(cmd: QueuedCommand): void {\n if (!this.process?.stdin) {\n cmd.reject(new Error('CLI server is not running'));\n return;\n }\n\n this.commandInProgress = true;\n this.currentResolve = cmd.resolve;\n this.currentReject = cmd.reject;\n\n try {\n this.process.stdin.write(JSON.stringify(cmd.args), 'utf8');\n this.process.stdin.write(this.nullBuffer);\n } catch (error) {\n this.commandInProgress = false;\n this.currentResolve = null;\n this.currentReject = null;\n cmd.reject(error instanceof Error ? error : new Error(String(error)));\n this.runNext();\n }\n }\n\n private handleStdout(data: Buffer): void {\n this.stdoutBuffer += data.toString();\n\n // Process all NUL-delimited responses in the buffer\n let nulIndex = this.stdoutBuffer.indexOf('\\0');\n while (nulIndex !== -1) {\n const result = this.stdoutBuffer.substring(0, nulIndex);\n this.stdoutBuffer = this.stdoutBuffer.substring(nulIndex + 1);\n\n if (this.currentResolve) {\n this.currentResolve(result);\n this.currentResolve = null;\n this.currentReject = null;\n }\n\n this.commandInProgress = false;\n this.runNext();\n\n nulIndex = this.stdoutBuffer.indexOf('\\0');\n }\n }\n\n private runNext(): void {\n const next = this.commandQueue.shift();\n if (next) {\n next();\n }\n }\n}\n", "/**\n * CodeQL Server Manager\n *\n * Manages the lifecycle of CodeQL background server processes:\n * - language-server (LSP-based QL validation)\n * - query-server2 (query evaluation)\n * - cli-server (JVM reuse for CLI commands)\n *\n * Servers are keyed by a hash of their configuration. When a caller requests\n * a server with a different configuration, the old server is shut down and a\n * new one is started. Session-specific cache directories provide isolation.\n */\n\nimport { mkdirSync } from 'fs';\nimport { join } from 'path';\nimport { randomUUID } from 'crypto';\nimport {\n CLIServerConfig,\n CodeQLServerType,\n computeConfigHash,\n LanguageServerConfig,\n QueryServerConfig,\n ServerConfig,\n} from './server-config';\nimport { CodeQLLanguageServer } from './language-server';\nimport { CodeQLQueryServer } from './query-server';\nimport { CodeQLCLIServer } from './cli-server';\nimport { getProjectTmpDir } from '../utils/temp-dir';\nimport { logger } from '../utils/logger';\n\n/**\n * Entry in the managed servers map.\n */\ninterface ManagedServer {\n configHash: string;\n server: CodeQLCLIServer | CodeQLLanguageServer | CodeQLQueryServer;\n sessionId: string;\n type: CodeQLServerType;\n}\n\n/**\n * Options for creating a session-specific cache layout.\n */\nexport interface SessionCacheOptions {\n /** Override the session ID (defaults to a random UUID). */\n sessionId?: string;\n}\n\n/**\n * Manages CodeQL background server processes with config-aware caching.\n *\n * Callers should use `getLanguageServer()`, `getQueryServer()`, or\n * `getCLIServer()` to obtain a running server. If the requested\n * configuration differs from the currently running server of that type, the\n * old server is stopped and replaced.\n */\nexport class CodeQLServerManager {\n /** Keyed by `CodeQLServerType` \u2014 at most one per type at a time. */\n private servers = new Map();\n\n /** In-flight `getOrRestart` promises, keyed by server type, to serialize concurrent calls. */\n private pendingStarts = new Map>();\n\n /** The session ID used for cache isolation. */\n private sessionId: string;\n\n /** Root directory for session-specific caches. */\n private sessionCacheDir: string;\n\n constructor(options?: SessionCacheOptions) {\n this.sessionId = options?.sessionId ?? randomUUID();\n this.sessionCacheDir = join(\n getProjectTmpDir('codeql-cache'),\n this.sessionId,\n );\n // Pre-create the cache directory tree\n for (const subdir of ['compilation-cache', 'logs', 'query-cache']) {\n mkdirSync(join(this.sessionCacheDir, subdir), { recursive: true });\n }\n logger.info(`CodeQLServerManager initialized (session: ${this.sessionId})`);\n }\n\n // ---- Public API ----\n\n /**\n * Get the current session ID.\n */\n getSessionId(): string {\n return this.sessionId;\n }\n\n /**\n * Get the session-specific cache directory.\n */\n getCacheDir(): string {\n return this.sessionCacheDir;\n }\n\n /**\n * Return the session-specific log directory.\n */\n getLogDir(): string {\n return join(this.sessionCacheDir, 'logs');\n }\n\n /**\n * Get or create a Language Server with the given configuration.\n *\n * If a language server is already running with the same config it is reused.\n * If the config has changed the old server is shut down first.\n */\n async getLanguageServer(config: LanguageServerConfig): Promise {\n const enriched = this.enrichConfig(config) as LanguageServerConfig;\n return this.getOrRestart('language', enriched, () => {\n // Convert LanguageServerConfig to the LanguageServerOptions the existing class expects\n return new CodeQLLanguageServer({\n loglevel: enriched.loglevel,\n logdir: enriched.logdir,\n searchPath: enriched.searchPath,\n synchronous: enriched.synchronous,\n verbosity: enriched.verbosity,\n });\n }) as Promise;\n }\n\n /**\n * Get or create a Query Server with the given configuration.\n */\n async getQueryServer(config: QueryServerConfig): Promise {\n const enriched = this.enrichConfig(config) as QueryServerConfig;\n return this.getOrRestart('query', enriched, () => {\n return new CodeQLQueryServer(enriched);\n }) as Promise;\n }\n\n /**\n * Get or create a CLI Server with the given configuration.\n */\n async getCLIServer(config: CLIServerConfig): Promise {\n const enriched = this.enrichConfig(config) as CLIServerConfig;\n return this.getOrRestart('cli', enriched, () => {\n return new CodeQLCLIServer(enriched);\n }) as Promise;\n }\n\n /**\n * Shut down a specific server type.\n */\n async shutdownServer(type: CodeQLServerType): Promise {\n const managed = this.servers.get(type);\n if (!managed) return;\n\n logger.info(`Shutting down ${type} server (session: ${managed.sessionId})`);\n await this.stopServer(managed);\n this.servers.delete(type);\n }\n\n /**\n * Shut down all managed servers.\n */\n async shutdownAll(): Promise {\n logger.info(`Shutting down all servers for session: ${this.sessionId}`);\n const shutdownPromises = Array.from(this.servers.entries()).map(\n async ([type, managed]) => {\n try {\n await this.stopServer(managed);\n } catch (error) {\n logger.error(`Error shutting down ${type} server:`, error);\n }\n },\n );\n await Promise.all(shutdownPromises);\n this.servers.clear();\n logger.info('All servers shut down');\n }\n\n /**\n * Check whether a server of the given type is currently running.\n */\n isRunning(type: CodeQLServerType): boolean {\n const managed = this.servers.get(type);\n if (!managed) return false;\n return managed.server.isRunning();\n }\n\n /**\n * Get status information for all managed servers.\n */\n getStatus(): Record {\n const status: Record = {\n cli: null,\n language: null,\n query: null,\n };\n for (const [type, managed] of this.servers) {\n status[type] = {\n configHash: managed.configHash,\n running: managed.server.isRunning(),\n sessionId: managed.sessionId,\n };\n }\n return status as Record;\n }\n\n // ---- Private helpers ----\n\n /**\n * Eagerly start the language server so the JVM is warm when the first\n * LSP tool call arrives. Uses the default configuration that\n * `lsp-handlers.ts` / `lsp-diagnostics.ts` would create on the first\n * `getLanguageServer()` call. The server is stored in the managed-servers\n * map and reused by subsequent tool invocations.\n *\n * This is fire-and-forget: errors are logged but do not prevent the MCP\n * server from starting.\n */\n async warmUpLanguageServer(): Promise {\n try {\n // Lazy-import to avoid circular dependency at module level\n const { packageRootDir } = await import('../utils/package-paths');\n const { resolve } = await import('path');\n\n const config: LanguageServerConfig = {\n checkErrors: 'ON_CHANGE',\n loglevel: 'WARN',\n searchPath: resolve(packageRootDir, 'ql'),\n };\n logger.info('Warming up language server (background JVM start)...');\n await this.getLanguageServer(config);\n logger.info('Language server warm-up complete');\n } catch (error) {\n logger.warn('Language server warm-up failed (will retry on first tool call):', error);\n }\n }\n\n /**\n * Eagerly start the CLI server so the JVM is warm when the first\n * `executeCodeQLCommand()` call routes through it.\n *\n * The CLI server uses only session-scoped `commonCaches` and `logdir`,\n * both injected by `enrichConfig()`. Passing an empty config is\n * intentional \u2014 it matches what `executeCodeQLCommand()` will request.\n *\n * Fire-and-forget: errors are logged but do not block startup.\n */\n async warmUpCLIServer(): Promise {\n try {\n logger.info('Warming up CLI server (background JVM start)...');\n await this.getCLIServer({});\n logger.info('CLI server warm-up complete');\n } catch (error) {\n logger.warn('CLI server warm-up failed (will retry on first tool call):', error);\n }\n }\n\n /**\n * Enrich a config with session-specific defaults for commonCaches and logdir.\n */\n private enrichConfig(config: T): T {\n return {\n ...config,\n commonCaches: config.commonCaches ?? this.sessionCacheDir,\n logdir: config.logdir ?? this.getLogDir(),\n };\n }\n\n /**\n * Get an existing server if its config matches, otherwise stop the old\n * one and start a new server.\n *\n * Concurrent calls for the same server type are serialized via\n * `pendingStarts` to avoid spawning duplicate server processes.\n */\n private async getOrRestart(\n type: CodeQLServerType,\n config: ServerConfig,\n factory: () => CodeQLCLIServer | CodeQLLanguageServer | CodeQLQueryServer,\n ): Promise {\n // If another call is already starting a server of this type, wait for it\n // to settle (success or failure) and then re-check whether the result is\n // still usable.\n const inflight = this.pendingStarts.get(type);\n if (inflight) {\n try { await inflight; } catch { /* swallow \u2014 original caller handles the rejection */ }\n }\n\n const work = this.doGetOrRestart(type, config, factory);\n this.pendingStarts.set(type, work);\n try {\n return await work;\n } finally {\n // Ensure `work` has settled before removing the pendingStarts entry so\n // that concurrent callers waiting on the same promise see a consistent\n // map state. The try/catch avoids re-throwing a rejection that the\n // outer `try` block already handles.\n try { await work; } catch { /* already handled */ }\n if (this.pendingStarts.get(type) === work) {\n this.pendingStarts.delete(type);\n }\n }\n }\n\n /**\n * Core logic for getOrRestart, separated to allow serialization.\n */\n private async doGetOrRestart(\n type: CodeQLServerType,\n config: ServerConfig,\n factory: () => CodeQLCLIServer | CodeQLLanguageServer | CodeQLQueryServer,\n ): Promise {\n const hash = computeConfigHash(type, config);\n const existing = this.servers.get(type);\n\n // Reuse if config matches and server is still running\n if (existing && existing.configHash === hash && existing.server.isRunning()) {\n logger.debug(`Reusing existing ${type} server (hash: ${hash.substring(0, 8)})`);\n return existing.server;\n }\n\n // Config changed or server died \u2014 stop the old one\n if (existing) {\n logger.info(`${type} server config changed or dead, restarting...`);\n await this.stopServer(existing);\n this.servers.delete(type);\n }\n\n // Start a new server\n const server = factory();\n await server.start();\n\n this.servers.set(type, {\n configHash: hash,\n server,\n sessionId: this.sessionId,\n type,\n });\n\n logger.info(`${type} server started (hash: ${hash.substring(0, 8)})`);\n return server;\n }\n\n /**\n * Stop a managed server, ignoring errors.\n */\n private async stopServer(managed: ManagedServer): Promise {\n try {\n await managed.server.shutdown();\n } catch (error) {\n logger.warn(`Error stopping ${managed.type} server:`, error);\n // Best-effort \u2014 don't propagate\n }\n }\n}\n\n/**\n * Global server manager singleton.\n *\n * Initialized lazily by `getServerManager()`. The MCP server entry point\n * should call `initServerManager()` at startup and `shutdownServerManager()`\n * at graceful shutdown.\n */\nlet globalServerManager: CodeQLServerManager | null = null;\n\n/**\n * Initialize the global server manager (idempotent).\n */\nexport function initServerManager(options?: SessionCacheOptions): CodeQLServerManager {\n if (!globalServerManager) {\n globalServerManager = new CodeQLServerManager(options);\n }\n return globalServerManager;\n}\n\n/**\n * Get the global server manager, creating it if needed.\n */\nexport function getServerManager(): CodeQLServerManager {\n if (!globalServerManager) {\n globalServerManager = new CodeQLServerManager();\n }\n return globalServerManager;\n}\n\n/**\n * Shut down the global server manager and all its servers.\n */\nexport async function shutdownServerManager(): Promise {\n if (globalServerManager) {\n await globalServerManager.shutdownAll();\n globalServerManager = null;\n }\n}\n\n/**\n * Reset the global server manager (for testing only).\n */\nexport function resetServerManager(): void {\n globalServerManager = null;\n}\n", "/**\n * Generic CLI command execution utilities for CodeQL and QLT commands\n */\n\nimport { execFile } from 'child_process';\nimport { existsSync } from 'fs';\nimport { basename, delimiter, dirname, isAbsolute } from 'path';\nimport { promisify } from 'util';\nimport { logger } from '../utils/logger';\n\nconst execFileAsync = promisify(execFile);\n\nexport interface CLIExecutionResult {\n stdout: string;\n stderr: string;\n success: boolean;\n error?: string;\n exitCode?: number;\n}\n\nexport interface CLIExecutionOptions {\n command: string;\n args: string[];\n cwd?: string;\n timeout?: number;\n env?: Record;\n}\n\n// Whitelist of allowed commands to prevent arbitrary command execution\nconst ALLOWED_COMMANDS = new Set([\n 'codeql',\n 'git',\n 'node',\n 'npm',\n 'qlt',\n 'which'\n]);\n\n// Additional commands allowed in test environments\nlet testCommands: Set | null = null;\n\n// Whitelist of safe environment variables to pass to child processes\n// This prevents potentially malicious environment variables from being passed through\nconst SAFE_ENV_VARS = [\n 'HOME', // User home directory\n 'LANG', // Locale setting\n 'LC_ALL', // Locale setting\n 'LC_CTYPE', // Locale setting\n 'PATH', // Required to find executables\n 'SHELL', // User's shell (Unix)\n 'TEMP', // Temporary directory (Windows)\n 'TERM', // Terminal type (Unix)\n 'TMP', // Temporary directory (Windows)\n 'TMPDIR', // Temporary directory (Unix)\n 'USER', // Current user (Unix)\n 'USERNAME', // Current user (Windows)\n] as const;\n\n// Whitelist of safe environment variable prefixes\n// These are needed for CodeQL and Node.js functionality\nconst SAFE_ENV_PREFIXES = [\n 'CODEQL_', // CodeQL-specific variables\n 'NODE_', // Node.js-specific variables (for npm, etc.)\n] as const;\n\n// Pattern for dangerous control characters in CLI arguments\n// Rejected characters:\n// \\x01-\\x08: SOH through BS (start of heading, text, null control chars, backspace)\n// \\x0B: Vertical tab - rarely used legitimately, can cause display issues\n// \\x0C: Form feed - can cause unexpected page breaks in output\n// \\x0E-\\x1F: SO through US (shift out/in, device controls, separators)\n// Allowed characters:\n// \\x00: Null byte - handled separately for clearer error messaging\n// \\x09: Horizontal tab - commonly used in file paths and arguments\n// \\x0A: Newline (LF) - may appear in multi-line arguments\n// \\x0D: Carriage return (CR) - may appear with newlines on Windows\n// eslint-disable-next-line no-control-regex\nconst DANGEROUS_CONTROL_CHARS = /[\\x01-\\x08\\x0B\\x0C\\x0E-\\x1F]/;\n\n/**\n * Enable test-specific commands for testing purposes\n * This should only be called in test environments\n */\nexport function enableTestCommands(): void {\n testCommands = new Set([\n 'cat',\n 'echo',\n 'ls',\n 'sh',\n 'sleep'\n ]);\n}\n\n/**\n * Disable test-specific commands\n */\nexport function disableTestCommands(): void {\n testCommands = null;\n}\n\n/**\n * Check if a command is allowed\n */\nfunction isCommandAllowed(command: string): boolean {\n return ALLOWED_COMMANDS.has(command) || (testCommands !== null && testCommands.has(command));\n}\n\n// Resolved CodeQL binary directory from CODEQL_PATH.\n// When set, this directory is prepended to PATH for all child processes\n// so that `execFile('codeql', ...)` finds the correct binary via execvp().\n// Using PATH lookup (rather than an absolute path) is essential because\n// execvp() handles shell-script shebangs correctly, whereas passing an\n// absolute path to execFile() can fail with ENOENT for shell scripts.\nlet resolvedCodeQLDir: string | null = null;\n\n// Cached result from resolveCodeQLBinary(). `undefined` means not yet resolved.\nlet resolvedBinaryResult: string | undefined;\n\n/**\n * Resolve the CodeQL CLI binary path.\n *\n * Resolution order:\n * 1. `CODEQL_PATH` environment variable \u2014 must point to an existing file whose\n * basename is `codeql` (or `codeql.exe` / `codeql.cmd` on Windows).\n * The parent directory is prepended to PATH for child processes.\n * 2. Falls back to the bare `codeql` command (resolved via PATH at exec time).\n *\n * The resolved value is cached for the lifetime of the process. Call this once\n * at startup; subsequent calls are a no-op and return the cached value.\n */\nexport function resolveCodeQLBinary(): string {\n // Short-circuit if already resolved\n if (resolvedBinaryResult !== undefined) {\n return resolvedBinaryResult;\n }\n\n const envPath = process.env.CODEQL_PATH;\n\n if (!envPath) {\n resolvedCodeQLDir = null;\n resolvedBinaryResult = 'codeql';\n return resolvedBinaryResult;\n }\n\n // Validate the path points to a plausible CodeQL binary\n const base = basename(envPath).toLowerCase();\n const validBaseNames = ['codeql', 'codeql.exe', 'codeql.cmd'];\n if (!validBaseNames.includes(base)) {\n throw new Error(\n `CODEQL_PATH must point to a CodeQL CLI binary (expected basename: codeql), got: ${base}`\n );\n }\n\n // Require an absolute path to avoid ambiguity\n if (!isAbsolute(envPath)) {\n throw new Error(\n `CODEQL_PATH must be an absolute path, got: ${envPath}`\n );\n }\n\n // Verify the file exists\n if (!existsSync(envPath)) {\n throw new Error(\n `CODEQL_PATH points to a file that does not exist: ${envPath}`\n );\n }\n\n resolvedCodeQLDir = dirname(envPath);\n resolvedBinaryResult = 'codeql';\n logger.info(`CodeQL CLI resolved via CODEQL_PATH: ${envPath} (dir: ${resolvedCodeQLDir})`);\n return resolvedBinaryResult;\n}\n\n/**\n * Get the currently resolved CodeQL binary directory, or null if using PATH.\n */\nexport function getResolvedCodeQLDir(): string | null {\n return resolvedCodeQLDir;\n}\n\n/**\n * Reset the resolved CodeQL binary to the default (for testing only).\n */\nexport function resetResolvedCodeQLBinary(): void {\n resolvedCodeQLDir = null;\n resolvedBinaryResult = undefined;\n}\n\n/**\n * Validate that the resolved CodeQL binary is actually callable.\n *\n * Runs `codeql version --format=terse` and verifies the process exits\n * successfully. This catches the case where `CODEQL_PATH` is unset and\n * `codeql` is not on PATH \u2014 the server would otherwise start normally\n * but every tool invocation would fail.\n *\n * @returns The version string reported by the CodeQL CLI.\n * @throws Error if the binary is not reachable or returns a non-zero exit code.\n */\nexport async function validateCodeQLBinaryReachable(): Promise {\n const binary = resolvedBinaryResult ?? 'codeql';\n const env = { ...process.env };\n if (resolvedCodeQLDir) {\n env.PATH = resolvedCodeQLDir + delimiter + (env.PATH || '');\n }\n\n try {\n const { stdout } = await execFileAsync(binary, ['version', '--format=terse'], {\n env,\n timeout: 15_000,\n });\n return stdout.trim();\n } catch (err: unknown) {\n const message = err instanceof Error ? err.message : String(err);\n throw new Error(\n `CodeQL CLI is not reachable (binary: ${binary}). ` +\n `Ensure codeql is on PATH or set the CODEQL_PATH environment variable ` +\n `to the absolute path of the CodeQL CLI binary. Details: ${message}`\n );\n }\n}\n\n/**\n * Sanitize a CLI argument to prevent potential issues with special characters.\n * \n * While execFile() does not spawn a shell (preventing shell injection), we still\n * validate arguments to:\n * 1. Reject null bytes that could truncate strings in C-based commands\n * 2. Reject control characters that could cause unexpected behavior\n * 3. Provide defense-in-depth against potential issues\n * \n * @param arg - The argument to sanitize\n * @returns The sanitized argument\n * @throws Error if the argument contains dangerous characters\n */\nexport function sanitizeCLIArgument(arg: string): string {\n // Reject null bytes - these can truncate strings in C-based commands\n // Error message intentionally excludes argument content to avoid logging potentially sensitive data\n if (arg.includes('\\0')) {\n throw new Error(`CLI argument contains null byte: argument rejected for security`);\n }\n \n // Reject control characters using the module-level constant pattern\n if (DANGEROUS_CONTROL_CHARS.test(arg)) {\n // Error message intentionally excludes argument content to avoid logging potentially sensitive data\n throw new Error(`CLI argument contains control characters: argument rejected for security`);\n }\n \n return arg;\n}\n\n/**\n * Sanitize an array of CLI arguments\n * @param args - The arguments to sanitize\n * @returns The sanitized arguments\n * @throws Error if any argument contains dangerous characters\n */\nexport function sanitizeCLIArguments(args: string[]): string[] {\n return args.map(sanitizeCLIArgument);\n}\n\n/**\n * Filter environment variables to only include safe ones\n * This prevents potentially malicious environment variables from being passed to child processes\n */\nfunction getSafeEnvironment(additionalEnv?: Record): Record {\n const safeEnv: Record = {};\n \n // Copy whitelisted environment variables from process.env\n for (const key of SAFE_ENV_VARS) {\n if (process.env[key] !== undefined) {\n safeEnv[key] = process.env[key]!;\n }\n }\n \n // Copy environment variables with whitelisted prefixes\n for (const [key, value] of Object.entries(process.env)) {\n if (value !== undefined && SAFE_ENV_PREFIXES.some(prefix => key.startsWith(prefix))) {\n safeEnv[key] = value;\n }\n }\n \n // When CODEQL_PATH was set, prepend the resolved directory to PATH so that\n // `execFile('codeql', ...)` finds the user-specified binary via execvp().\n // This approach (PATH manipulation + bare command name) is essential because\n // execvp() handles shell-script shebangs correctly, whereas passing an\n // absolute path to execFile() fails with ENOENT for shell-script launchers.\n if (resolvedCodeQLDir && safeEnv.PATH) {\n safeEnv.PATH = `${resolvedCodeQLDir}${delimiter}${safeEnv.PATH}`;\n } else if (resolvedCodeQLDir) {\n safeEnv.PATH = resolvedCodeQLDir;\n }\n \n // Merge with explicitly provided environment variables\n // These are trusted as they come from the application code, not external sources\n if (additionalEnv) {\n Object.assign(safeEnv, additionalEnv);\n }\n \n return safeEnv;\n}\n\n/**\n * Execute a CLI command and return the result.\n * \n * Security: This function uses execFile() instead of exec() to avoid shell interpretation.\n * Arguments are passed directly to the executable as an array, preventing shell injection.\n * Additional security measures include:\n * - Command whitelist validation\n * - Shell metacharacter detection in command names\n * - CLI argument sanitization (null bytes, control characters)\n * - Environment variable filtering\n */\nexport async function executeCLICommand(options: CLIExecutionOptions): Promise {\n try {\n const { command, args, cwd, timeout = 300000, env } = options; // 5 minute default timeout\n \n // Validate command is in the whitelist to prevent arbitrary command execution\n if (!isCommandAllowed(command)) {\n throw new Error(`Command not allowed: ${command}. Only whitelisted commands can be executed.`);\n }\n \n // Validate command to ensure it doesn't contain shell metacharacters\n if (command.includes(';') || command.includes('|') || command.includes('&') || \n command.includes('$') || command.includes('`') || command.includes('\\n') ||\n command.includes('\\r')) {\n throw new Error(`Invalid command: contains shell metacharacters: ${command}`);\n }\n \n // Sanitize CLI arguments to prevent issues with special characters\n // This provides defense-in-depth even though execFile() doesn't use a shell\n const sanitizedArgs = sanitizeCLIArguments(args);\n \n logger.info(`Executing CLI command: ${command}`, { args: sanitizedArgs, cwd, timeout });\n \n const execOptions = {\n cwd,\n timeout,\n env: getSafeEnvironment(env),\n };\n \n // execFile() is used instead of exec() to avoid shell interpretation\n // Arguments are passed as an array, not interpolated into a command string\n const { stdout, stderr } = await execFileAsync(command, sanitizedArgs, execOptions);\n\n return {\n stdout,\n stderr,\n success: true,\n exitCode: 0\n };\n\n } catch (error: unknown) {\n logger.error('CLI command execution failed:', error);\n \n const err = error as Error & { code?: number; stdout?: string; stderr?: string };\n const errorMessage = err instanceof Error ? err.message : String(error);\n const exitCode = err.code || 1;\n \n return {\n stdout: err.stdout || '',\n stderr: err.stderr || errorMessage,\n success: false,\n error: errorMessage,\n exitCode\n };\n }\n}\n\n/**\n * Build CodeQL command arguments with proper escaping\n */\nexport function buildCodeQLArgs(subcommand: string, options: Record): string[] {\n const args = [subcommand];\n \n // Single-letter parameters that should use -key=value format (with equals sign)\n // Note: CodeQL CLI uses -t=key=value format for metadata, not -t key=value\n const singleLetterParams = new Set(['t', 'o', 'v', 'q', 'h', 'J']);\n \n for (const [key, value] of Object.entries(options)) {\n if (value === undefined || value === null) {\n continue;\n }\n \n const isSingleLetter = key.length === 1 && singleLetterParams.has(key);\n \n if (typeof value === 'boolean') {\n if (value) {\n args.push(isSingleLetter ? `-${key}` : `--${key}`);\n }\n } else if (Array.isArray(value)) {\n // Handle array values (e.g., multiple -t flags for metadata)\n for (const item of value) {\n if (isSingleLetter) {\n // For single-letter params like -t, use -key=value format\n args.push(`-${key}=${String(item)}`);\n } else {\n // For long params, use --key=value format\n args.push(`--${key}=${String(item)}`);\n }\n }\n } else {\n // Handle string, number, and other values\n if (isSingleLetter) {\n // For single-letter params, use -key=value format\n args.push(`-${key}=${String(value)}`);\n } else {\n args.push(`--${key}=${String(value)}`);\n }\n }\n }\n \n return args;\n}\n\n/**\n * Build QLT command arguments with proper escaping\n */\nexport function buildQLTArgs(subcommand: string, options: Record): string[] {\n const args = [subcommand];\n \n for (const [key, value] of Object.entries(options)) {\n if (value === undefined || value === null) {\n continue;\n }\n \n if (typeof value === 'boolean') {\n if (value) {\n args.push(`--${key}`);\n }\n } else if (Array.isArray(value)) {\n // Handle array values\n for (const item of value) {\n args.push(`--${key}`, String(item));\n }\n } else {\n // Handle string, number, and other values\n args.push(`--${key}`, String(value));\n }\n }\n \n return args;\n}\n\n/**\n * CodeQL subcommands that MUST run as fresh processes.\n *\n * These cannot use the cli-server because they:\n * - Spawn extractors or other long-running child processes (database create, test extract)\n * - Produce multi-event NUL-delimited streams (test run)\n * - Are compound orchestration commands (database analyze)\n *\n * Everything else is routed through the persistent cli-server JVM for\n * sub-second execution instead of 2-5 s JVM cold-start per invocation.\n */\nconst FRESH_PROCESS_SUBCOMMANDS = new Set([\n 'database analyze',\n 'database create',\n 'test extract',\n 'test run',\n]);\n\n/**\n * Execute a CodeQL command.\n *\n * By default, commands are routed through the persistent `codeql execute\n * cli-server` process managed by {@link CodeQLServerManager}, eliminating\n * repeated JVM startup overhead (~2-5 s savings per call).\n *\n * Commands listed in {@link FRESH_PROCESS_SUBCOMMANDS} (e.g. `database create`,\n * `test run`) are always executed as fresh processes because they either spawn\n * child extractors, produce streaming output, or require a dedicated working\n * directory.\n *\n * If the cli-server is not available (e.g. during early startup before\n * `initServerManager()` is called), the function falls back transparently to\n * a fresh process.\n */\nexport async function executeCodeQLCommand(\n subcommand: string,\n options: Record,\n additionalArgs: string[] = [],\n cwd?: string\n): Promise {\n const args = buildCodeQLArgs(subcommand, options);\n args.push(...additionalArgs);\n\n // Determine whether this subcommand can use the persistent cli-server.\n // Commands that need a specific CWD also must use a fresh process because\n // the cli-server's CWD is fixed at startup.\n const canUseCLIServer = !FRESH_PROCESS_SUBCOMMANDS.has(subcommand) && !cwd;\n\n if (canUseCLIServer) {\n try {\n // Lazy-import to avoid circular dependency at module level.\n // Use getServerManager() (not initServerManager()) \u2014 if the manager\n // hasn't been initialized yet (e.g. during tests or early startup),\n // this creates one, but we only attempt to *use* the cli-server if\n // it is already running (warmed up at MCP server startup). We never\n // block on starting a new cli-server here \u2014 that would add JVM\n // startup latency to the first fresh-process-fallback call.\n const { getServerManager } = await import('./server-manager');\n const manager = getServerManager();\n\n if (manager.isRunning('cli')) {\n const cliServer = await manager.getCLIServer({});\n const sanitizedArgs = sanitizeCLIArguments(args);\n\n logger.info(`Executing CodeQL command via cli-server: ${subcommand}`, { args: sanitizedArgs });\n\n const stdout = await cliServer.runCommand(sanitizedArgs);\n\n return {\n stdout,\n stderr: '',\n success: true,\n exitCode: 0,\n };\n } else {\n logger.debug(`cli-server not yet running for \"${subcommand}\", using fresh process`);\n }\n } catch (error) {\n // If the cli-server call fails, check whether it's a command-level\n // error (the CLI returned non-zero) or a transport/startup error.\n // For transport errors we fall back to a fresh process; for command\n // errors we return the failure directly.\n const message = error instanceof Error ? error.message : String(error);\n\n // Transport-level errors that warrant a fallback:\n if (message.includes('CLI server is not running') ||\n message.includes('CLI server exited') ||\n message.includes('failed to start')) {\n logger.warn(`cli-server unavailable for \"${subcommand}\", falling back to fresh process: ${message}`);\n // Fall through to fresh-process execution below\n } else {\n // Command-level error \u2014 return it as a failed result\n logger.error(`cli-server command failed for \"${subcommand}\": ${message}`);\n return {\n stdout: '',\n stderr: message,\n success: false,\n error: message,\n exitCode: 1,\n };\n }\n }\n }\n\n // Fresh-process execution (for FRESH_PROCESS_SUBCOMMANDS, CWD-specific\n // calls, or as a fallback when the cli-server is unavailable).\n return executeCLICommand({\n command: 'codeql',\n args,\n cwd\n });\n}\n\n/**\n * Execute a QLT command\n */\nexport async function executeQLTCommand(\n subcommand: string, \n options: Record, \n additionalArgs: string[] = []\n): Promise {\n const args = buildQLTArgs(subcommand, options);\n args.push(...additionalArgs);\n \n return executeCLICommand({\n command: 'qlt',\n args\n });\n}\n\n/**\n * Get help text for a CLI command\n */\nexport async function getCommandHelp(command: string, subcommand?: string): Promise {\n const args = subcommand ? [subcommand, '--help'] : ['--help'];\n \n const result = await executeCLICommand({\n command,\n args\n });\n \n return result.stdout || result.stderr || 'No help available';\n}\n\n/**\n * Validate that a command exists on the system.\n */\nexport async function validateCommandExists(command: string): Promise {\n try {\n const result = await executeCLICommand({\n command: 'which',\n args: [command]\n });\n return result.success;\n } catch {\n return false;\n }\n}", "/**\n * CodeQL Development MCP McpServer\n * Main entry point for the server\n */\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';\nimport express from 'express';\nimport cors from 'cors';\nimport dotenv from 'dotenv';\nimport { resolve } from 'path';\nimport { pathToFileURL } from 'url';\nimport { registerCodeQLTools, registerCodeQLResources } from './tools';\nimport { registerLSPTools } from './tools/lsp';\nimport { registerLanguageResources } from './resources/language-resources';\nimport { registerWorkflowPrompts } from './prompts/workflow-prompts';\nimport { registerMonitoringTools } from './tools/monitoring-tools';\nimport { sessionDataManager } from './lib/session-data-manager';\nimport { resolveCodeQLBinary, validateCodeQLBinaryReachable } from './lib/cli-executor';\nimport { initServerManager, shutdownServerManager } from './lib/server-manager';\nimport { packageRootDir } from './utils/package-paths';\nimport { logger } from './utils/logger';\n\n// Load environment variables from a .env file co-located with the package root.\n// Uses the package directory (not CWD) so that npm-installed users don't\n// accidentally inherit a .env from their project.\ndotenv.config({ path: resolve(packageRootDir, '.env') });\n\nconst PACKAGE_NAME = 'codeql-development-mcp-server';\nconst VERSION = '2.24.0';\n\n/**\n * Start the MCP server\n */\nexport async function startServer(mode: 'stdio' | 'http' = 'stdio'): Promise {\n logger.info(`Starting CodeQL Development MCP McpServer v${VERSION} in ${mode} mode`);\n\n // Resolve the CodeQL CLI binary path (honors CODEQL_PATH env var).\n // This must happen before any tool registration so that all CodeQL commands\n // use the user-specified binary.\n const codeqlBinary = resolveCodeQLBinary();\n logger.info(`CodeQL CLI binary: ${codeqlBinary}`);\n\n // Validate that the resolved binary is actually callable. This catches\n // misconfigurations early (e.g. codeql not on PATH and CODEQL_PATH unset)\n // instead of failing silently and producing confusing tool-level errors.\n const codeqlVersion = await validateCodeQLBinaryReachable();\n logger.info(`CodeQL CLI version: ${codeqlVersion}`);\n\n const server = new McpServer({\n name: PACKAGE_NAME,\n version: VERSION,\n });\n\n // Register CodeQL tools (legacy high-level helpers)\n registerCodeQLTools(server);\n\n // Register LSP-based tools (diagnostics, completion, definition, references)\n registerLSPTools(server);\n\n // Register CodeQL resources (static guides)\n registerCodeQLResources(server);\n\n // Register language-specific resources (AST references, security patterns)\n registerLanguageResources(server);\n\n // Register high-level workflow prompts (complete development workflows)\n registerWorkflowPrompts(server);\n\n // Register monitoring and reporting tools\n registerMonitoringTools(server);\n\n // Initialize session data manager\n await sessionDataManager.initialize();\n\n // Initialize the CodeQL background server manager and eagerly start the\n // language server and CLI server JVMs so they are warm when the first tool\n // calls arrive. This avoids 2-60 s cold-start penalties per JVM.\n const manager = initServerManager();\n Promise.all([\n manager.warmUpLanguageServer(),\n manager.warmUpCLIServer(),\n ]).catch(() => { /* individual errors logged inside each warm-up method */ });\n\n if (mode === 'stdio') {\n const transport = new StdioServerTransport();\n await server.connect(transport);\n logger.info('McpServer started successfully on STDIO transport');\n } else {\n // HTTP mode\n const app = express();\n app.use(cors());\n app.use(express.json());\n\n const transport = new StreamableHTTPServerTransport({\n sessionIdGenerator: () => Math.random().toString(36).substring(7),\n });\n await server.connect(transport);\n\n app.all('/mcp', (req, res) => {\n transport.handleRequest(req, res, req.body).catch((err) => {\n logger.error('Error handling MCP request:', err);\n if (!res.headersSent) {\n res.status(500).json({ error: 'Internal McpServer Error' });\n }\n });\n });\n\n app.get('/', (_req, res) => {\n res.json({\n name: PACKAGE_NAME,\n version: VERSION,\n description: 'CodeQL Development MCP McpServer',\n status: 'running',\n });\n });\n\n const host = process.env.HTTP_HOST || 'localhost';\n const port = Number(process.env.HTTP_PORT || process.env.PORT) || 3000;\n \n // Return a promise that keeps the process alive\n return new Promise((resolve, reject) => {\n const httpServer = app.listen(port, host, () => {\n logger.info(`HTTP server listening on http://${host}:${port}/mcp`);\n resolve();\n });\n \n httpServer.on('error', (error) => {\n logger.error('HTTP server error:', error);\n reject(error);\n });\n });\n }\n\n setupGracefulShutdown(server);\n return server;\n}\n\n/**\n * Set up graceful shutdown handling\n */\nfunction setupGracefulShutdown(server: McpServer): void {\n const shutdown = async () => {\n logger.info('Shutting down server...');\n try {\n // Shut down all CodeQL background servers first\n await shutdownServerManager();\n await server.close();\n logger.info('McpServer closed gracefully');\n process.exit(0);\n } catch (error) {\n logger.error('Error during shutdown:', error);\n process.exit(1);\n }\n };\n\n process.on('SIGINT', shutdown);\n process.on('SIGTERM', shutdown);\n}\n\n/**\n * Main function to start the server\n */\nasync function main(): Promise {\n try {\n const transportMode = (process.env.TRANSPORT_MODE || 'stdio').toLowerCase();\n const mode: 'stdio' | 'http' = transportMode === 'http' ? 'http' : 'stdio';\n await startServer(mode);\n } catch (error) {\n logger.error('Failed to start server:', error);\n process.exit(1);\n }\n}\n\n// Start the server if this file is run directly\nconst scriptPath = process.argv[1] ? resolve(process.argv[1]) : undefined;\nif (scriptPath && import.meta.url === pathToFileURL(scriptPath).href) {\n main();\n}\n", "/**\n * CodeQL BQRS decode tool\n */\n\nimport { z } from 'zod';\nimport { CLIToolDefinition, createCodeQLSchemas, createBQRSResultProcessor } from '../../lib/cli-tool-registry';\n\nexport const codeqlBqrsDecodeTool: CLIToolDefinition = {\n name: 'codeql_bqrs_decode',\n description: 'Decode BQRS result files to human-readable formats',\n command: 'codeql',\n subcommand: 'bqrs decode',\n inputSchema: {\n files: z.array(z.string()).describe('BQRS file(s) to decode'),\n output: createCodeQLSchemas.output(),\n format: z.enum(['csv', 'json']).optional().describe('Output format'),\n 'max-paths': z.number().optional().describe('Maximum number of paths to output'),\n 'start-at': z.number().optional().describe('Start output at result number'),\n 'max-results': z.number().optional().describe('Maximum number of results'),\n verbose: createCodeQLSchemas.verbose(),\n additionalArgs: createCodeQLSchemas.additionalArgs()\n },\n examples: [\n 'codeql bqrs decode --format=csv --output=results.csv results.bqrs',\n 'codeql bqrs decode --format=json --max-results=100 results.bqrs'\n ],\n resultProcessor: createBQRSResultProcessor()\n};", "/**\n * Generic tool registry for creating MCP tools from CLI command definitions\n */\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport { executeCodeQLCommand, executeQLTCommand, CLIExecutionResult } from './cli-executor';\nimport { logger } from '../utils/logger';\nimport { evaluateQueryResults, QueryEvaluationResult, extractQueryMetadata } from './query-results-evaluator';\nimport { getOrCreateLogDirectory } from './log-directory-manager';\nimport { getUserWorkspaceDir, packageRootDir, resolveToolQueryPackPath } from '../utils/package-paths';\nimport { writeFileSync, rmSync, existsSync, mkdirSync } from 'fs';\nimport { basename, dirname, isAbsolute, join, resolve } from 'path';\nimport { createProjectTempDir } from '../utils/temp-dir';\n\nexport type { CLIExecutionResult } from './cli-executor';\n\nexport interface CLIToolDefinition {\n name: string;\n description: string;\n command: 'codeql' | 'qlt';\n subcommand: string;\n inputSchema: Record;\n examples?: string[];\n resultProcessor?: (_result: CLIExecutionResult, _params: Record) => string;\n}\n\n/**\n * Default result processor that formats CLI output appropriately\n */\nexport const defaultCLIResultProcessor = (\n result: CLIExecutionResult, \n _params: Record\n): string => {\n if (!result.success) {\n return `Command failed (exit code ${result.exitCode || 'unknown'}):\\n${result.error || result.stderr}`;\n }\n \n let output = '';\n \n if (result.stdout) {\n output += result.stdout;\n }\n \n if (result.stderr) {\n if (output) {\n output += '\\n\\nWarnings/Info:\\n';\n }\n output += result.stderr;\n }\n \n if (!output) {\n output = 'Command executed successfully (no output)';\n }\n \n return output;\n};\n\n/**\n * Register a CLI tool with the MCP server\n */\nexport function registerCLITool(server: McpServer, definition: CLIToolDefinition): void {\n const {\n name,\n description,\n command,\n subcommand,\n inputSchema,\n resultProcessor = defaultCLIResultProcessor\n } = definition;\n\n server.tool(\n name,\n description,\n inputSchema,\n async (params: Record) => {\n // Track temporary directories for cleanup\n const tempDirsToCleanup: string[] = [];\n \n try {\n logger.info(`Executing CLI tool: ${name}`, { command, subcommand, params });\n\n // Separate positional arguments from named options\n // Extract tool-specific parameters that should not be passed to CLI\n // Note: format is extracted for tools that use it internally but not on CLI\n // For codeql_bqrs_interpret, codeql_bqrs_decode, codeql_generate_query-help, and codeql_database_analyze, format should be passed to CLI\n const formatShouldBePassedToCLI = name === 'codeql_bqrs_interpret' || name === 'codeql_bqrs_decode' || name === 'codeql_generate_query-help' || name === 'codeql_database_analyze';\n \n const extractedParams = formatShouldBePassedToCLI\n ? {\n _positional: params._positional || [],\n files: params.files,\n file: params.file,\n dir: params.dir,\n packDir: params.packDir,\n tests: params.tests,\n query: params.query,\n queryName: params.queryName,\n queryLanguage: params.queryLanguage,\n queryPack: params.queryPack,\n sourceFiles: params.sourceFiles,\n sourceFunction: params.sourceFunction,\n targetFunction: params.targetFunction,\n interpretedOutput: params.interpretedOutput,\n evaluationFunction: params.evaluationFunction,\n evaluationOutput: params.evaluationOutput,\n directory: params.directory,\n logDir: params.logDir,\n qlref: params.qlref\n }\n : {\n _positional: params._positional || [],\n files: params.files,\n file: params.file,\n dir: params.dir,\n packDir: params.packDir,\n tests: params.tests,\n query: params.query,\n queryName: params.queryName,\n queryLanguage: params.queryLanguage,\n queryPack: params.queryPack, \n sourceFiles: params.sourceFiles,\n sourceFunction: params.sourceFunction,\n targetFunction: params.targetFunction,\n format: params.format,\n interpretedOutput: params.interpretedOutput,\n evaluationFunction: params.evaluationFunction,\n evaluationOutput: params.evaluationOutput,\n directory: params.directory,\n logDir: params.logDir,\n qlref: params.qlref\n };\n\n const {\n _positional = [],\n files,\n file,\n dir,\n packDir,\n tests,\n query,\n queryName,\n queryLanguage: _queryLanguage,\n queryPack: _queryPack,\n sourceFiles,\n sourceFunction,\n targetFunction,\n format: _format,\n interpretedOutput: _interpretedOutput,\n evaluationFunction: _evaluationFunction,\n evaluationOutput: _evaluationOutput,\n directory,\n logDir: customLogDir,\n qlref,\n } = extractedParams;\n\n // Get remaining options (everything not extracted above)\n const options = {...params};\n Object.keys(extractedParams).forEach(key => delete options[key]);\n let positionalArgs = Array.isArray(_positional) ? _positional as string[] : [_positional as string];\n\n // Handle files parameter as positional arguments for certain tools\n if (files && Array.isArray(files)) {\n positionalArgs = [...positionalArgs, ...files as string[]];\n }\n\n // Handle file parameter as positional argument for BQRS tools\n if (file && name.startsWith('codeql_bqrs_')) {\n positionalArgs = [...positionalArgs, file as string];\n }\n\n // Handle qlref parameter as positional argument for resolve qlref tool\n if (qlref && name === 'codeql_resolve_qlref') {\n positionalArgs = [...positionalArgs, qlref as string];\n }\n\n // Handle database parameter as positional argument for resolve database tool\n if (options.database && name === 'codeql_resolve_database') {\n positionalArgs = [...positionalArgs, options.database as string];\n delete options.database;\n }\n\n // Handle database parameter as positional argument for database create tool\n if (options.database && name === 'codeql_database_create') {\n positionalArgs = [...positionalArgs, options.database as string];\n delete options.database;\n }\n\n // Handle database and queries parameters as positional arguments for database analyze tool\n if (name === 'codeql_database_analyze') {\n if (options.database) {\n positionalArgs = [...positionalArgs, options.database as string];\n delete options.database;\n }\n if (options.queries) {\n positionalArgs = [...positionalArgs, options.queries as string];\n delete options.queries;\n }\n }\n\n // Handle query parameter as positional argument for generate query-help tool\n if (query && name === 'codeql_generate_query-help') {\n positionalArgs = [...positionalArgs, query as string];\n }\n\n // Handle dir parameter as positional argument for pack tools\n if (dir && (name === 'codeql_pack_ls')) {\n positionalArgs = [...positionalArgs, dir as string];\n }\n \n // Handle tool-specific parameters using switch pattern for better maintainability\n switch (name) {\n case 'codeql_test_accept':\n case 'codeql_test_extract':\n case 'codeql_test_run':\n case 'codeql_resolve_tests':\n // Handle tests parameter as positional arguments for test tools.\n // Resolve relative paths against the user's effective workspace\n // directory. In monorepo layouts this is the repo root; in npm-\n // installed layouts it falls back to process.cwd().\n if (tests && Array.isArray(tests)) {\n const userDir = getUserWorkspaceDir();\n positionalArgs = [...positionalArgs, ...(tests as string[]).map(\n t => isAbsolute(t) ? t : resolve(userDir, t)\n )];\n }\n break;\n \n case 'codeql_query_run': {\n // Resolve database path to absolute path if it's relative\n if (options.database && typeof options.database === 'string' && !isAbsolute(options.database)) {\n options.database = resolve(getUserWorkspaceDir(), options.database);\n logger.info(`Resolved database path to: ${options.database}`);\n }\n \n // Implement query resolution logic with enhanced results processing\n const resolvedQuery = await resolveQueryPath(params, logger);\n if (resolvedQuery) {\n positionalArgs = [...positionalArgs, resolvedQuery];\n } else if (query) {\n positionalArgs = [...positionalArgs, query as string];\n }\n \n // Handle external predicates for tool queries\n if (queryName === 'PrintAST' && sourceFiles) {\n // Create a CSV file with the source file paths for the external predicate\n // The external predicate expects a CSV file with one column containing file paths\n const filePaths = (sourceFiles as string).split(',').map((f: string) => f.trim());\n let tempDir: string;\n let csvPath: string;\n try {\n tempDir = createProjectTempDir('codeql-external-');\n tempDirsToCleanup.push(tempDir); // Track for cleanup\n csvPath = join(tempDir, 'selectedSourceFiles.csv');\n\n // Create CSV content\n const csvContent = filePaths.join('\\n') + '\\n';\n\n writeFileSync(csvPath, csvContent, 'utf8');\n } catch (err) {\n logger.error(`Failed to create external predicate CSV for PrintAST query at path ${csvPath || '[unknown]'}: ${err instanceof Error ? err.message : String(err)}`);\n throw err;\n }\n \n // Add the external predicate with the CSV file path\n const currentExternal = options.external || [];\n const externalArray = Array.isArray(currentExternal) ? currentExternal : [currentExternal];\n externalArray.push(`selectedSourceFiles=${csvPath}`);\n options.external = externalArray;\n \n logger.info(`Created external predicate CSV at ${csvPath} for files: ${filePaths.join(', ')}`);\n }\n \n // Handle external predicates for CallGraphFrom queries\n if (queryName === 'CallGraphFrom' && sourceFunction) {\n const functionNames = (sourceFunction as string).split(',').map((f: string) => f.trim());\n let tempDir: string;\n let csvPath: string;\n try {\n tempDir = createProjectTempDir('codeql-external-');\n tempDirsToCleanup.push(tempDir);\n csvPath = join(tempDir, 'sourceFunction.csv');\n\n // Create CSV content\n const csvContent = functionNames.join('\\n') + '\\n';\n\n writeFileSync(csvPath, csvContent, 'utf8');\n } catch (err) {\n logger.error(`Failed to create external predicate CSV for CallGraphFrom query at path ${csvPath || '[unknown]'}: ${err instanceof Error ? err.message : String(err)}`);\n throw err;\n }\n \n // Add the external predicate with the CSV file path\n const currentExternal = options.external || [];\n const externalArray = Array.isArray(currentExternal) ? currentExternal : [currentExternal];\n externalArray.push(`sourceFunction=${csvPath}`);\n options.external = externalArray;\n \n logger.info(`Created external predicate CSV at ${csvPath} for functions: ${functionNames.join(', ')}`);\n }\n \n // Handle external predicates for CallGraphTo queries\n if (queryName === 'CallGraphTo' && targetFunction) {\n const functionNames = (targetFunction as string).split(',').map((f: string) => f.trim());\n let tempDir: string;\n let csvPath: string;\n try {\n tempDir = createProjectTempDir('codeql-external-');\n tempDirsToCleanup.push(tempDir);\n csvPath = join(tempDir, 'targetFunction.csv');\n\n // Create CSV content\n const csvContent = functionNames.join('\\n') + '\\n';\n\n writeFileSync(csvPath, csvContent, 'utf8');\n } catch (err) {\n logger.error(`Failed to create external predicate CSV for CallGraphTo query at path ${csvPath || '[unknown]'}: ${err instanceof Error ? err.message : String(err)}`);\n throw err;\n }\n \n // Add the external predicate with the CSV file path\n const currentExternal = options.external || [];\n const externalArray = Array.isArray(currentExternal) ? currentExternal : [currentExternal];\n externalArray.push(`targetFunction=${csvPath}`);\n options.external = externalArray;\n \n logger.info(`Created external predicate CSV at ${csvPath} for functions: ${functionNames.join(', ')}`);\n }\n break;\n }\n \n case 'codeql_query_compile':\n case 'codeql_resolve_metadata':\n // Handle query parameter as positional argument for query compilation and metadata tools\n if (query) {\n positionalArgs = [...positionalArgs, query as string];\n }\n break;\n \n case 'codeql_resolve_queries':\n // Handle directory parameter as positional argument\n if (directory) {\n positionalArgs = [...positionalArgs, directory as string];\n }\n break;\n \n default:\n // No special parameter handling needed for other tools\n break;\n }\n\n // Set up logging directory for query/test runs\n let queryLogDir: string | undefined;\n if (name === 'codeql_query_run' || name === 'codeql_test_run') {\n queryLogDir = getOrCreateLogDirectory(customLogDir as string | undefined);\n logger.info(`Using log directory for ${name}: ${queryLogDir}`);\n \n // Create timestamp file to track when query/test run started\n const timestampPath = join(queryLogDir, 'timestamp');\n writeFileSync(timestampPath, Date.now().toString(), 'utf8');\n \n // Set the --logdir option for CodeQL CLI\n options.logdir = queryLogDir;\n \n // Set verbosity to progress+ to generate detailed query.log/test.log\n if (!options.verbosity) {\n options.verbosity = 'progress+';\n }\n \n // For query run, also handle the deprecated evaluator-log parameter and default output\n if (name === 'codeql_query_run') {\n // If evaluator-log was explicitly provided (deprecated), use it\n // Otherwise, set it to the log directory\n if (!options['evaluator-log']) {\n options['evaluator-log'] = join(queryLogDir, 'evaluator-log.jsonl');\n }\n \n // If output was not explicitly provided, set it to the log directory\n if (!options.output) {\n options.output = join(queryLogDir, 'results.bqrs');\n }\n }\n }\n\n let result: CLIExecutionResult;\n \n if (command === 'codeql') {\n // For pack commands, set the working directory to where qlpack.yml is located.\n // Resolve to absolute path since the MCP server's cwd may differ from\n // the workspace root (especially when launched by VS Code).\n let cwd: string | undefined;\n if ((name === 'codeql_pack_install' || name === 'codeql_pack_ls') && (dir || packDir)) {\n const rawCwd = (dir || packDir) as string;\n // Resolve relative paths against the user's effective workspace\n // directory rather than a potentially read-only package root.\n cwd = isAbsolute(rawCwd) ? rawCwd : resolve(getUserWorkspaceDir(), rawCwd);\n }\n \n // Add --additional-packs for commands that need to access local test packs.\n // Only set the default examples path when it actually exists on disk\n // (it may be absent in npm-installed layouts where ql/javascript/examples/\n // is not included in the published package).\n const defaultExamplesPath = resolve(packageRootDir, 'ql', 'javascript', 'examples');\n const additionalPacksPath = process.env.CODEQL_ADDITIONAL_PACKS\n || (existsSync(defaultExamplesPath) ? defaultExamplesPath : undefined);\n if (additionalPacksPath && (name === 'codeql_test_run' || name === 'codeql_query_run' || name === 'codeql_query_compile')) {\n options['additional-packs'] = additionalPacksPath;\n }\n \n // Keep test databases for codeql_test_run to allow subsequent query runs\n if (name === 'codeql_test_run') {\n options['keep-databases'] = true;\n }\n \n result = await executeCodeQLCommand(subcommand, options, positionalArgs, cwd);\n } else if (command === 'qlt') {\n result = await executeQLTCommand(subcommand, options, positionalArgs);\n } else {\n throw new Error(`Unsupported command: ${command}`);\n }\n\n // Post-execution processing for codeql_query_run\n if (name === 'codeql_query_run' && result.success && queryLogDir) {\n // Generate SARIF interpretation if results.bqrs exists\n const bqrsPath = options.output as string;\n const sarifPath = join(queryLogDir, 'results.sarif');\n \n if (existsSync(bqrsPath)) {\n try {\n const sarifResult = await executeCodeQLCommand(\n 'bqrs interpret',\n { format: 'sarif-latest', output: sarifPath },\n [bqrsPath]\n );\n \n if (sarifResult.success) {\n logger.info(`Generated SARIF interpretation at ${sarifPath}`);\n }\n } catch (error) {\n logger.warn(`Failed to generate SARIF interpretation: ${error}`);\n }\n }\n \n // Process evaluation results\n result = await processQueryRunResults(result, params, logger);\n }\n\n // Process the result\n const processedResult = resultProcessor(result, params);\n\n return {\n content: [{\n type: 'text' as const,\n text: processedResult\n }],\n isError: !result.success\n };\n\n } catch (error) {\n logger.error(`Error in CLI tool ${name}:`, error);\n \n return {\n content: [{\n type: 'text' as const,\n text: `Failed to execute CLI tool: ${error instanceof Error ? error.message : String(error)}`\n }],\n isError: true\n };\n } finally {\n // Clean up temporary directories\n for (const tempDir of tempDirsToCleanup) {\n try {\n rmSync(tempDir, { recursive: true, force: true });\n logger.info(`Cleaned up temporary directory: ${tempDir}`);\n } catch (cleanupError) {\n logger.error(`Failed to clean up temporary directory ${tempDir}:`, cleanupError);\n }\n }\n }\n }\n );\n}\n\n/**\n * Helper function to create common CodeQL input schemas\n */\nexport const createCodeQLSchemas = {\n database: () => z.string().describe('Path to the CodeQL database'),\n \n query: () => z.string().describe('Path to the CodeQL query file (.ql)'),\n \n output: () => z.string().optional().describe('Output file path'),\n \n outputFormat: () => z.enum(['csv', 'json', 'bqrs', 'sarif-latest', 'sarifv2.1.0']).optional()\n .describe('Output format for results'),\n \n language: () => z.string().optional().describe('Programming language'),\n \n threads: () => z.number().optional().describe('Number of threads to use'),\n \n ram: () => z.number().optional().describe('Amount of RAM to use (MB)'),\n \n timeout: () => z.number().optional().describe('Timeout in seconds'),\n \n verbose: () => z.boolean().optional().describe('Enable verbose output'),\n \n additionalArgs: () => z.array(z.string()).optional().describe('Additional command-line arguments'),\n \n positionalArgs: () => z.array(z.string()).optional().describe('Positional arguments')\n .transform((val) => ({ _positional: val }))\n};\n\n/**\n * Helper function to create common QLT input schemas\n */\nexport const createQLTSchemas = {\n language: () => z.string().describe('Programming language'),\n \n output: () => z.string().optional().describe('Output directory or file path'),\n \n template: () => z.string().optional().describe('Template to use'),\n \n name: () => z.string().optional().describe('Name for generated query'),\n \n description: () => z.string().optional().describe('Description for generated query'),\n \n verbose: () => z.boolean().optional().describe('Enable verbose output'),\n \n force: () => z.boolean().optional().describe('Force overwrite existing files'),\n \n additionalArgs: () => z.array(z.string()).optional().describe('Additional command-line arguments')\n};\n\n/**\n * Create a result processor that formats BQRS output specially\n */\nexport const createBQRSResultProcessor = () => (\n result: CLIExecutionResult, \n params: Record\n): string => {\n if (!result.success) {\n return defaultCLIResultProcessor(result, params);\n }\n \n // For BQRS commands, provide more context about the output\n let output = result.stdout;\n \n if (params.output) {\n output += `\\n\\nResults saved to: ${params.output}`;\n }\n \n if (result.stderr) {\n output += `\\n\\nAdditional information:\\n${result.stderr}`;\n }\n \n return output;\n};\n\n/**\n * Create a result processor that formats database creation output\n */\nexport const createDatabaseResultProcessor = () => (\n result: CLIExecutionResult, \n params: Record\n): string => {\n if (!result.success) {\n return defaultCLIResultProcessor(result, params);\n }\n \n let output = 'Database creation completed successfully';\n \n if (params.database || params._positional) {\n const dbPath = params.database || (Array.isArray(params._positional) ? params._positional[0] : params._positional);\n output += `\\n\\nDatabase location: ${dbPath}`;\n }\n \n if (result.stdout) {\n output += `\\n\\nOutput:\\n${result.stdout}`;\n }\n \n if (result.stderr) {\n output += `\\n\\nAdditional information:\\n${result.stderr}`;\n }\n \n return output;\n};\n\n/**\n * Resolve query path for codeql_query_run tool\n * If queryName and queryLanguage are provided, resolve using codeql resolve queries\n */\nasync function resolveQueryPath(\n params: Record, \n logger: { info: (_message: string, ..._args: unknown[]) => void; error: (_message: string, ..._args: unknown[]) => void }\n): Promise {\n const { queryName, queryLanguage, queryPack, query } = params;\n \n // Validate parameter usage - queryName and query are mutually exclusive\n if (queryName && query) {\n logger.error('Cannot use both \"query\" and \"queryName\" parameters simultaneously. Use either \"query\" for direct file path OR \"queryName\" + \"queryLanguage\" for tool queries.');\n throw new Error('Cannot use both \"query\" and \"queryName\" parameters simultaneously. Use either \"query\" for direct file path OR \"queryName\" + \"queryLanguage\" for tool queries.');\n }\n \n // If no queryName provided, fall back to direct query parameter\n if (!queryName) {\n return query as string || null;\n }\n \n // If queryName provided but no language, we can't resolve\n if (!queryLanguage) {\n logger.error('queryLanguage is required when using queryName parameter. Supported languages: actions, cpp, csharp, go, java, javascript, python, ruby, swift');\n throw new Error('queryLanguage is required when using queryName parameter. Supported languages: actions, cpp, csharp, go, java, javascript, python, ruby, swift');\n }\n \n try {\n // Determine the query pack path - use absolute path to ensure it works regardless of cwd\n const defaultPackPath = resolveToolQueryPackPath(queryLanguage as string);\n const packPath = queryPack as string || defaultPackPath;\n \n logger.info(`Resolving query: ${queryName} for language: ${queryLanguage} in pack: ${packPath}`);\n \n // Execute codeql resolve queries to get available queries\n const { executeCodeQLCommand } = await import('./cli-executor');\n const resolveResult = await executeCodeQLCommand(\n 'resolve queries',\n { format: 'json' },\n [packPath]\n );\n \n if (!resolveResult.success) {\n logger.error('Failed to resolve queries:', resolveResult.stderr || resolveResult.error);\n throw new Error(`Failed to resolve queries: ${resolveResult.stderr || resolveResult.error}`);\n }\n \n // Parse the JSON output to find matching queries\n let resolvedQueries: string[];\n try {\n resolvedQueries = JSON.parse(resolveResult.stdout);\n } catch (parseError) {\n logger.error('Failed to parse resolve queries output:', parseError);\n throw new Error('Failed to parse resolve queries output');\n }\n \n // Find the query that matches the requested name exactly\n const matchingQuery = resolvedQueries.find(queryPath => {\n const fileName = basename(queryPath);\n // Match exact query name: \"PrintAST\" should match \"PrintAST.ql\" only\n return fileName === `${queryName}.ql`;\n });\n\n if (!matchingQuery) {\n logger.error(`Query \"${queryName}.ql\" not found in pack \"${packPath}\". Available queries:`, resolvedQueries.map(q => basename(q)));\n throw new Error(`Query \"${queryName}.ql\" not found in pack \"${packPath}\"`);\n }\n \n logger.info(`Resolved query \"${queryName}\" to: ${matchingQuery}`);\n return matchingQuery;\n \n } catch (error) {\n logger.error('Error resolving query path:', error);\n throw error;\n }\n}\n\n/**\n * Interpret BQRS file using codeql bqrs interpret\n */\nasync function interpretBQRSFile(\n bqrsPath: string,\n queryPath: string,\n format: string,\n outputPath: string,\n logger: { info: (_message: string, ..._args: unknown[]) => void; error: (_message: string, ..._args: unknown[]) => void }\n): Promise {\n try {\n // Extract query metadata to get id and kind\n const metadata = await extractQueryMetadata(queryPath);\n \n // Validate required metadata fields\n const missingFields = [];\n if (!metadata.id) missingFields.push('id');\n if (!metadata.kind) missingFields.push('kind');\n \n if (missingFields.length > 0) {\n return {\n success: false,\n exitCode: 1,\n stdout: '',\n stderr: '',\n error: `Query metadata is incomplete. Missing required field(s): ${missingFields.join(', ')}. Ensure the query file contains @id and @kind metadata.`\n };\n }\n \n // Sanitize metadata values to prevent command injection\n const sanitizedKind = (metadata.kind || '').replace(/[^a-zA-Z0-9_-]/g, '');\n const sanitizedId = (metadata.id || '').replace(/[^a-zA-Z0-9_/:-]/g, '');\n \n // Validate format for query kind\n const graphFormats = ['graphtext', 'dgml', 'dot'];\n if (graphFormats.includes(format) && metadata.kind !== 'graph') {\n return {\n success: false,\n exitCode: 1,\n stdout: '',\n stderr: '',\n error: `Format '${format}' is only compatible with @kind graph queries, but this query has @kind ${metadata.kind}`\n };\n }\n \n // Ensure output directory exists\n mkdirSync(dirname(outputPath), { recursive: true });\n \n // Build the codeql bqrs interpret command\n const params: Record = {\n format: format,\n output: outputPath,\n t: [`kind=${sanitizedKind}`, `id=${sanitizedId}`]\n };\n \n logger.info(`Interpreting BQRS file ${bqrsPath} with format ${format} to ${outputPath}`);\n \n // Execute codeql bqrs interpret\n const result = await executeCodeQLCommand(\n 'bqrs interpret',\n params,\n [bqrsPath]\n );\n \n return result;\n } catch (error) {\n return {\n success: false,\n exitCode: 1,\n stdout: '',\n stderr: '',\n error: `Failed to interpret BQRS file: ${error}`\n };\n }\n}\n\n/**\n * Get default output extension based on format\n */\nfunction getDefaultExtension(format: string): string {\n switch (format) {\n case 'sarif-latest':\n case 'sarifv2.1.0':\n return '.sarif';\n case 'csv':\n return '.csv';\n case 'graphtext':\n return '.txt';\n case 'dgml':\n return '.dgml';\n case 'dot':\n return '.dot';\n default:\n return '.txt';\n }\n}\n\n/**\n * Process query run results with optional interpretation or evaluation\n */\nasync function processQueryRunResults(\n result: CLIExecutionResult,\n params: Record,\n logger: { info: (_message: string, ..._args: unknown[]) => void; error: (_message: string, ..._args: unknown[]) => void }\n): Promise {\n try {\n const { format, interpretedOutput, evaluationFunction, evaluationOutput, output, query, queryName, queryLanguage } = params;\n \n // If no format or evaluationFunction specified, return as-is\n if (!format && !evaluationFunction) {\n return result;\n }\n \n // Ensure output (bqrs file) was generated\n if (!output) {\n return result;\n }\n \n const bqrsPath = output as string;\n \n // Determine the query path for metadata extraction\n let queryPath: string | null = null;\n \n if (query) {\n queryPath = query as string;\n } else if (queryName && queryLanguage) {\n // Try to resolve the query path again for evaluation\n queryPath = await resolveQueryPath(params, logger);\n }\n \n if (!queryPath) {\n logger.error('Cannot determine query path for interpretation/evaluation');\n return {\n ...result,\n stdout: result.stdout + '\\n\\nWarning: Query interpretation skipped - could not determine query path'\n };\n }\n \n // Handle new format parameter (preferred approach)\n if (format) {\n const outputFormat = format as string;\n \n // Determine output path\n let outputFilePath = interpretedOutput as string | undefined;\n if (!outputFilePath) {\n const ext = getDefaultExtension(outputFormat);\n outputFilePath = bqrsPath.replace('.bqrs', ext);\n }\n \n logger.info(`Interpreting query results from ${bqrsPath} with format: ${outputFormat}`);\n \n // Interpret the BQRS file\n const interpretResult = await interpretBQRSFile(\n bqrsPath,\n queryPath,\n outputFormat,\n outputFilePath,\n logger\n );\n \n if (interpretResult.success) {\n let enhancedOutput = result.stdout;\n enhancedOutput += `\\n\\nQuery results interpreted successfully with format: ${outputFormat}`;\n enhancedOutput += `\\nInterpreted output saved to: ${outputFilePath}`;\n \n return {\n ...result,\n stdout: enhancedOutput\n };\n } else {\n logger.error('Query interpretation failed:', interpretResult.error);\n return {\n ...result,\n stdout: result.stdout + `\\n\\nWarning: Query interpretation failed - ${interpretResult.error || interpretResult.stderr}`\n };\n }\n }\n \n // Handle legacy evaluationFunction parameter (deprecated)\n if (evaluationFunction) {\n logger.info(`Using deprecated evaluationFunction parameter. Consider using format parameter instead.`);\n logger.info(`Evaluating query results from ${bqrsPath} using function: ${evaluationFunction}`);\n \n // Perform the evaluation\n const evaluationResult: QueryEvaluationResult = await evaluateQueryResults(\n bqrsPath,\n queryPath,\n evaluationFunction as string,\n evaluationOutput as string | undefined\n );\n \n if (evaluationResult.success) {\n // Append evaluation results to the command output\n let enhancedOutput = result.stdout;\n \n if (evaluationResult.outputPath) {\n enhancedOutput += `\\n\\nQuery evaluation completed successfully.`;\n enhancedOutput += `\\nEvaluation output saved to: ${evaluationResult.outputPath}`;\n }\n \n if (evaluationResult.content) {\n enhancedOutput += `\\n\\n=== Query Results Evaluation ===\\n`;\n enhancedOutput += evaluationResult.content;\n }\n \n return {\n ...result,\n stdout: enhancedOutput\n };\n } else {\n // Evaluation failed, but don't fail the whole operation\n logger.error('Query evaluation failed:', evaluationResult.error);\n return {\n ...result,\n stdout: result.stdout + `\\n\\nWarning: Query evaluation failed - ${evaluationResult.error}`\n };\n }\n }\n \n return result;\n } catch (error) {\n logger.error('Error in query results processing:', error);\n return {\n ...result,\n stdout: result.stdout + `\\n\\nWarning: Query processing error - ${error}`\n };\n }\n}", "/**\n * Query results evaluation functions for processing .bqrs files\n */\n\nimport { executeCodeQLCommand } from './cli-executor';\nimport { logger } from '../utils/logger';\nimport { writeFileSync, readFileSync } from 'fs';\nimport { dirname, isAbsolute } from 'path';\nimport { mkdirSync } from 'fs';\n\nexport interface QueryEvaluationResult {\n success: boolean;\n outputPath?: string;\n content?: string;\n error?: string;\n}\n\nexport interface QueryMetadata {\n kind?: string;\n name?: string;\n description?: string;\n id?: string;\n tags?: string[];\n}\n\n/**\n * Built-in evaluation functions\n */\nexport const BUILT_IN_EVALUATORS = {\n 'json-decode': 'JSON format decoder for query results',\n 'csv-decode': 'CSV format decoder for query results', \n 'mermaid-graph': 'Mermaid diagram generator for @kind graph queries (like PrintAST)',\n} as const;\n\nexport type BuiltInEvaluator = keyof typeof BUILT_IN_EVALUATORS;\n\n/**\n * Extract metadata from a CodeQL query file\n */\nexport async function extractQueryMetadata(queryPath: string): Promise {\n try {\n const queryContent = readFileSync(queryPath, 'utf-8');\n const metadata: QueryMetadata = {};\n \n // Extract metadata from comments using regex patterns\n const kindMatch = queryContent.match(/@kind\\s+([^\\s]+)/);\n if (kindMatch) metadata.kind = kindMatch[1];\n \n const nameMatch = queryContent.match(/@name\\s+(.+)/);\n if (nameMatch) metadata.name = nameMatch[1].trim();\n \n const descMatch = queryContent.match(/@description\\s+(.+)/);\n if (descMatch) metadata.description = descMatch[1].trim();\n \n const idMatch = queryContent.match(/@id\\s+(.+)/);\n if (idMatch) metadata.id = idMatch[1].trim();\n \n const tagsMatch = queryContent.match(/@tags\\s+(.+)/);\n if (tagsMatch) {\n metadata.tags = tagsMatch[1].split(/\\s+/).filter(t => t.length > 0);\n }\n \n return metadata;\n } catch (error) {\n logger.error('Failed to extract query metadata:', error);\n return {};\n }\n}\n\n/**\n * JSON decoder - converts .bqrs to JSON format\n */\nexport async function evaluateWithJsonDecoder(\n bqrsPath: string, \n outputPath?: string\n): Promise {\n try {\n const result = await executeCodeQLCommand(\n 'bqrs decode',\n { format: 'json' },\n [bqrsPath]\n );\n \n if (!result.success) {\n return {\n success: false,\n error: `Failed to decode BQRS file: ${result.stderr || result.error}`\n };\n }\n \n const defaultOutputPath = outputPath || bqrsPath.replace('.bqrs', '.json');\n \n // Ensure output directory exists\n mkdirSync(dirname(defaultOutputPath), { recursive: true });\n \n // Write JSON results\n writeFileSync(defaultOutputPath, result.stdout);\n \n return {\n success: true,\n outputPath: defaultOutputPath,\n content: result.stdout\n };\n } catch (error) {\n return {\n success: false,\n error: `JSON evaluation failed: ${error}`\n };\n }\n}\n\n/**\n * CSV decoder - converts .bqrs to CSV format\n */\nexport async function evaluateWithCsvDecoder(\n bqrsPath: string,\n outputPath?: string\n): Promise {\n try {\n const result = await executeCodeQLCommand(\n 'bqrs decode',\n { format: 'csv' },\n [bqrsPath]\n );\n \n if (!result.success) {\n return {\n success: false,\n error: `Failed to decode BQRS file: ${result.stderr || result.error}`\n };\n }\n \n const defaultOutputPath = outputPath || bqrsPath.replace('.bqrs', '.csv');\n \n // Ensure output directory exists\n mkdirSync(dirname(defaultOutputPath), { recursive: true });\n \n // Write CSV results\n writeFileSync(defaultOutputPath, result.stdout);\n \n return {\n success: true,\n outputPath: defaultOutputPath,\n content: result.stdout\n };\n } catch (error) {\n return {\n success: false,\n error: `CSV evaluation failed: ${error}`\n };\n }\n}\n\n/**\n * Mermaid graph generator - converts @kind graph query results to mermaid diagrams\n */\nexport async function evaluateWithMermaidGraph(\n bqrsPath: string,\n queryPath: string,\n outputPath?: string\n): Promise {\n try {\n // First extract query metadata to confirm this is a graph query\n const metadata = await extractQueryMetadata(queryPath);\n \n if (metadata.kind !== 'graph') {\n logger.error(`Query is not a graph query (kind: ${metadata.kind}), mermaid-graph evaluation is only for @kind graph queries`);\n return {\n success: false,\n error: `Query is not a graph query (kind: ${metadata.kind}), mermaid-graph evaluation is only for @kind graph queries`\n };\n }\n \n // Decode the BQRS file to JSON first\n const jsonResult = await executeCodeQLCommand(\n 'bqrs decode',\n { format: 'json' },\n [bqrsPath]\n );\n \n if (!jsonResult.success) {\n return {\n success: false,\n error: `Failed to decode BQRS file: ${jsonResult.stderr || jsonResult.error}`\n };\n }\n \n // Parse the JSON results\n let queryResults;\n try {\n queryResults = JSON.parse(jsonResult.stdout);\n } catch (parseError) {\n return {\n success: false,\n error: `Failed to parse query results JSON: ${parseError}`\n };\n }\n \n // Generate mermaid diagram from graph results\n const mermaidContent = generateMermaidFromGraphResults(queryResults, metadata);\n \n const defaultOutputPath = outputPath || bqrsPath.replace('.bqrs', '.md');\n \n // Ensure output directory exists\n mkdirSync(dirname(defaultOutputPath), { recursive: true });\n \n // Write markdown file with mermaid diagram\n writeFileSync(defaultOutputPath, mermaidContent);\n \n return {\n success: true,\n outputPath: defaultOutputPath,\n content: mermaidContent\n };\n } catch (error) {\n return {\n success: false,\n error: `Mermaid graph evaluation failed: ${error}`\n };\n }\n}\n\n/**\n * Generate mermaid diagram from CodeQL graph query results\n */\nfunction generateMermaidFromGraphResults(queryResults: unknown, metadata: QueryMetadata): string {\n const queryName = sanitizeMarkdown(metadata.name || 'CodeQL Query Results');\n const queryDesc = sanitizeMarkdown(metadata.description || 'Graph visualization of CodeQL query results');\n \n let mermaidContent = `# ${queryName}\\n\\n${queryDesc}\\n\\n`;\n \n // Handle different result structures that CodeQL graph queries can produce\n if (!queryResults || typeof queryResults !== 'object') {\n mermaidContent += '```mermaid\\ngraph TD\\n A[No Results]\\n```\\n';\n return mermaidContent;\n }\n \n // Check if results have the expected structure for graph queries\n const tuples = queryResults.tuples || queryResults;\n \n if (!Array.isArray(tuples) || tuples.length === 0) {\n mermaidContent += '```mermaid\\ngraph TD\\n A[No Graph Data]\\n```\\n';\n return mermaidContent;\n }\n \n mermaidContent += '```mermaid\\ngraph TD\\n';\n \n // Track nodes and edges to avoid duplicates\n const nodes = new Set();\n const edges = new Set();\n \n // Process each tuple in the results\n tuples.forEach((tuple: unknown, index: number) => {\n if (Array.isArray(tuple) && tuple.length >= 2) {\n // Extract source and target from tuple\n const source = sanitizeNodeId(tuple[0]?.toString() || `node_${index}_0`);\n const target = sanitizeNodeId(tuple[1]?.toString() || `node_${index}_1`);\n const label = tuple[2]?.toString() || '';\n \n // Add nodes\n nodes.add(source);\n nodes.add(target);\n \n // Add edge\n const edgeId = `${source}_${target}`;\n if (!edges.has(edgeId)) {\n if (label) {\n mermaidContent += ` ${source} -->|${sanitizeLabel(label)}| ${target}\\n`;\n } else {\n mermaidContent += ` ${source} --> ${target}\\n`;\n }\n edges.add(edgeId);\n }\n } else if (typeof tuple === 'object' && tuple !== null) {\n // Handle object-based results\n const source = sanitizeNodeId(tuple.source?.toString() || tuple.from?.toString() || `node_${index}_src`);\n const target = sanitizeNodeId(tuple.target?.toString() || tuple.to?.toString() || `node_${index}_tgt`);\n const label = tuple.label?.toString() || tuple.relation?.toString() || '';\n \n nodes.add(source);\n nodes.add(target);\n \n const edgeId = `${source}_${target}`;\n if (!edges.has(edgeId)) {\n if (label) {\n mermaidContent += ` ${source} -->|${sanitizeLabel(label)}| ${target}\\n`;\n } else {\n mermaidContent += ` ${source} --> ${target}\\n`;\n }\n edges.add(edgeId);\n }\n }\n });\n \n // If no edges were created, create a simple diagram showing the first few nodes\n if (edges.size === 0 && nodes.size > 0) {\n const nodeArray = Array.from(nodes).slice(0, 10); // Limit to avoid clutter\n nodeArray.forEach((node, index) => {\n if (index === 0) {\n mermaidContent += ` ${node}[${sanitizeLabel(node)}]\\n`;\n } else {\n mermaidContent += ` ${nodeArray[0]} --> ${node}\\n`;\n }\n });\n }\n \n mermaidContent += '```\\n\\n';\n \n // Add statistics\n mermaidContent += `## Query Statistics\\n\\n`;\n mermaidContent += `- Total nodes: ${nodes.size}\\n`;\n mermaidContent += `- Total edges: ${edges.size}\\n`;\n mermaidContent += `- Total tuples processed: ${tuples.length}\\n`;\n \n return mermaidContent;\n}\n\n/**\n * Sanitize node IDs for mermaid compatibility\n */\nfunction sanitizeNodeId(id: string): string {\n return id\n .replace(/[^a-zA-Z0-9_]/g, '_')\n .replace(/^(\\d)/, 'n$1') // Prefix with 'n' if starts with number\n .substring(0, 50); // Limit length\n}\n\n/**\n * Sanitize labels for mermaid compatibility\n */\nfunction sanitizeLabel(label: string): string {\n return label\n .replace(/[|\"`<>\\n\\r\\t]/g, '') // Remove problematic characters including backticks, newlines, angle brackets\n .replace(/\\s+/g, ' ') // Normalize whitespace\n .trim()\n .substring(0, 30); // Limit length for readability\n}\n\n/**\n * Sanitize markdown content to prevent injection\n */\nfunction sanitizeMarkdown(content: string): string {\n return content\n .replace(/[<>\"`]/g, '') // Remove potentially dangerous characters\n .replace(/\\n/g, ' ') // Convert newlines to spaces\n .replace(/\\s+/g, ' ') // Normalize whitespace\n .trim()\n .substring(0, 100); // Limit length\n}\n\n/**\n * Main evaluation function that determines which evaluator to use\n */\nexport async function evaluateQueryResults(\n bqrsPath: string,\n queryPath: string,\n evaluationFunction?: string,\n outputPath?: string\n): Promise {\n try {\n // If no evaluation function specified, default to json-decode\n const evalFunc = evaluationFunction || 'json-decode';\n \n logger.info(`Evaluating query results with function: ${evalFunc}`);\n \n // Handle built-in evaluation functions\n switch (evalFunc) {\n case 'json-decode':\n return await evaluateWithJsonDecoder(bqrsPath, outputPath);\n \n case 'csv-decode':\n return await evaluateWithCsvDecoder(bqrsPath, outputPath);\n \n case 'mermaid-graph':\n return await evaluateWithMermaidGraph(bqrsPath, queryPath, outputPath);\n \n default:\n // Check if it's a path to a custom evaluation script\n if (isAbsolute(evalFunc)) {\n return await evaluateWithCustomScript(bqrsPath, queryPath, evalFunc, outputPath);\n } else {\n return {\n success: false,\n error: `Unknown evaluation function: ${evalFunc}. Available built-in functions: ${Object.keys(BUILT_IN_EVALUATORS).join(', ')}`\n };\n }\n }\n } catch (error) {\n return {\n success: false,\n error: `Query evaluation failed: ${error}`\n };\n }\n}\n\n/**\n * Execute custom evaluation script\n */\nasync function evaluateWithCustomScript(\n _bqrsPath: string,\n _queryPath: string,\n _scriptPath: string,\n _outputPath?: string\n): Promise {\n // TODO: Implement custom script execution\n // This would need to execute the script with bqrsPath and queryPath as arguments\n // and capture the output\n return {\n success: false,\n error: 'Custom evaluation scripts are not yet implemented'\n };\n}", "/**\n * Log directory management utilities for CodeQL query and test runs\n */\n\nimport { mkdirSync, existsSync } from 'fs';\nimport { join, resolve } from 'path';\nimport { randomBytes } from 'crypto';\nimport { getProjectTmpDir } from '../utils/temp-dir';\n\n/**\n * Ensure that a given path is within a base directory.\n * Throws an error if the path is outside the base directory.\n */\nfunction ensurePathWithinBase(baseDir: string, targetPath: string): string {\n const absBase = resolve(baseDir);\n const absTarget = resolve(targetPath);\n if (!absTarget.startsWith(absBase + '/') && absTarget !== absBase) {\n throw new Error(`Provided log directory is outside the allowed base directory: ${absBase}`);\n }\n return absTarget;\n}\n\n/**\n * Get or create a unique log directory for query/test runs\n * \n * @param logDir - Optional custom log directory from parameters\n * @returns Absolute path to the log directory\n */\nexport function getOrCreateLogDirectory(logDir?: string): string {\n // Use CODEQL_QUERY_LOG_DIR env var or default base\n const baseLogDir = process.env.CODEQL_QUERY_LOG_DIR || getProjectTmpDir('query-logs');\n\n // If logDir is explicitly provided, validate it is within baseLogDir\n if (logDir) {\n const absLogDir = ensurePathWithinBase(baseLogDir, logDir);\n if (!existsSync(absLogDir)) {\n mkdirSync(absLogDir, { recursive: true });\n }\n return absLogDir;\n }\n \n // Otherwise, use baseLogDir and create a unique subdirectory\n \n // Create base directory if it doesn't exist\n if (!existsSync(baseLogDir)) {\n mkdirSync(baseLogDir, { recursive: true });\n }\n \n // Generate unique subdirectory with timestamp and random ID\n const timestamp = new Date().toISOString().replace(/[:.]/g, '-');\n const uniqueId = randomBytes(4).toString('hex');\n const uniqueLogDir = join(baseLogDir, `query-run-${timestamp}-${uniqueId}`);\n \n mkdirSync(uniqueLogDir, { recursive: true });\n \n return uniqueLogDir;\n}\n", "/**\n * CodeQL BQRS info tool\n */\n\nimport { z } from 'zod';\nimport { CLIToolDefinition, createCodeQLSchemas, createBQRSResultProcessor } from '../../lib/cli-tool-registry';\n\nexport const codeqlBqrsInfoTool: CLIToolDefinition = {\n name: 'codeql_bqrs_info',\n description: 'Get metadata and information about BQRS result files',\n command: 'codeql',\n subcommand: 'bqrs info',\n inputSchema: {\n files: z.array(z.string()).describe('BQRS file(s) to examine'),\n verbose: createCodeQLSchemas.verbose(),\n additionalArgs: createCodeQLSchemas.additionalArgs()\n },\n examples: [\n 'codeql bqrs info results.bqrs',\n 'codeql bqrs info --verbose results.bqrs'\n ],\n resultProcessor: createBQRSResultProcessor()\n};", "/**\n * CodeQL BQRS interpret tool\n */\n\nimport { z } from 'zod';\nimport { CLIToolDefinition, createCodeQLSchemas, createBQRSResultProcessor } from '../../lib/cli-tool-registry';\n\nexport const codeqlBqrsInterpretTool: CLIToolDefinition = {\n name: 'codeql_bqrs_interpret',\n description: 'Interpret BQRS result files according to query metadata and generate output in specified formats (CSV, SARIF, graph formats)',\n command: 'codeql',\n subcommand: 'bqrs interpret',\n inputSchema: {\n file: z.string().describe('The BQRS file to interpret'),\n format: z.enum(['csv', 'sarif-latest', 'sarifv2.1.0', 'graphtext', 'dgml', 'dot'])\n .describe('Output format: csv (comma-separated), sarif-latest/sarifv2.1.0 (SARIF), graphtext/dgml/dot (graph formats, only for @kind graph queries)'),\n output: createCodeQLSchemas.output(),\n t: z.array(z.string())\n .describe('Query metadata key=value pairs. At least \"kind\" and \"id\" must be specified (e.g., [\"kind=graph\", \"id=js/print-ast\"])'),\n 'max-paths': z.number().optional()\n .describe('Maximum number of paths to produce for each alert with paths (default: 4)'),\n 'sarif-add-file-contents': z.boolean().optional()\n .describe('[SARIF only] Include full file contents for all files referenced in results'),\n 'sarif-add-snippets': z.boolean().optional()\n .describe('[SARIF only] Include code snippets for each location with context'),\n 'sarif-group-rules-by-pack': z.boolean().optional()\n .describe('[SARIF only] Place rule objects under their QL pack in tool.extensions property'),\n 'sarif-multicause-markdown': z.boolean().optional()\n .describe('[SARIF only] Include multi-cause alerts as Markdown-formatted lists'),\n 'sarif-category': z.string().optional()\n .describe('[SARIF only] Category for this analysis (distinguishes multiple analyses on same code)'),\n 'csv-location-format': z.enum(['uri', 'line-column', 'offset-length']).optional()\n .describe('[CSV only] Format for locations in CSV output (default: line-column)'),\n 'dot-location-url-format': z.string().optional()\n .describe('[DOT only] Format string for file location URLs (placeholders: {path}, {start:line}, {start:column}, {end:line}, {end:column}, {offset}, {length})'),\n threads: z.number().optional()\n .describe('Number of threads for computing paths (0 = one per core, -N = leave N cores unused)'),\n 'column-kind': z.enum(['utf8', 'utf16', 'utf32', 'bytes']).optional()\n .describe('[SARIF only] Column kind for interpreting location columns'),\n 'unicode-new-lines': z.boolean().optional()\n .describe('[SARIF only] Whether unicode newlines (U+2028, U+2029) are considered as newlines'),\n verbose: createCodeQLSchemas.verbose(),\n additionalArgs: createCodeQLSchemas.additionalArgs()\n },\n examples: [\n 'codeql bqrs interpret --format=sarif-latest --output=results.sarif -t kind=problem -t id=js/sql-injection results.bqrs',\n 'codeql bqrs interpret --format=graphtext --output=ast.txt -t kind=graph -t id=js/print-ast results.bqrs',\n 'codeql bqrs interpret --format=csv --csv-location-format=line-column --output=results.csv -t kind=problem -t id=js/xss results.bqrs',\n 'codeql bqrs interpret --format=dot --output=graph.dot -t kind=graph -t id=java/call-graph results.bqrs',\n 'codeql bqrs interpret --format=sarif-latest --sarif-add-snippets --sarif-category=security --output=results.sarif -t kind=path-problem -t id=go/path-injection results.bqrs'\n ],\n resultProcessor: createBQRSResultProcessor()\n};\n", "/**\n * CodeQL database analyze tool\n */\n\nimport { z } from 'zod';\nimport { CLIToolDefinition } from '../../lib/cli-tool-registry';\n\nexport const codeqlDatabaseAnalyzeTool: CLIToolDefinition = {\n name: 'codeql_database_analyze',\n description: 'Run queries or query suites against CodeQL databases',\n command: 'codeql',\n subcommand: 'database analyze',\n inputSchema: {\n database: z.string().describe('Path to the CodeQL database'),\n queries: z.string().describe('Queries or query suite to run'),\n output: z.string().optional().describe('Output file path'),\n format: z.enum(['csv', 'json', 'sarif-latest', 'sarifv2.1.0']).optional()\n .describe('Output format for results'),\n 'download-location': z.string().optional()\n .describe('Location to download missing dependencies'),\n threads: z.number().optional().describe('Number of threads to use'),\n ram: z.number().optional().describe('Amount of RAM to use (MB)'),\n timeout: z.number().optional().describe('Timeout in seconds'),\n verbose: z.boolean().optional().describe('Enable verbose output'),\n additionalArgs: z.array(z.string()).optional().describe('Additional command-line arguments')\n },\n examples: [\n 'codeql database analyze mydb queries.qls --format=sarif-latest --output=results.sarif',\n 'codeql database analyze mydb codeql/java-queries --format=csv'\n ]\n};", "/**\n * CodeQL database create tool\n */\n\nimport { z } from 'zod';\nimport { CLIToolDefinition, createDatabaseResultProcessor } from '../../lib/cli-tool-registry';\n\nexport const codeqlDatabaseCreateTool: CLIToolDefinition = {\n name: 'codeql_database_create',\n description: 'Create a CodeQL database from source code',\n command: 'codeql',\n subcommand: 'database create',\n inputSchema: {\n database: z.string().describe('Database path/name to create'),\n language: z.string().optional().describe('Programming language(s) to extract'),\n 'source-root': z.string().optional().describe('Root directory of source code'),\n command: z.string().optional().describe('Build command for compiled languages'),\n 'build-mode': z.enum(['none', 'autobuild', 'manual']).optional()\n .describe('Build mode: none (interpreted langs), autobuild, or manual'),\n threads: z.number().optional().describe('Number of threads to use'),\n ram: z.number().optional().describe('Amount of RAM to use (MB)'),\n verbose: z.boolean().optional().describe('Enable verbose output'),\n overwrite: z.boolean().optional().describe('Overwrite existing database if it exists'),\n 'no-cleanup': z.boolean().optional().describe('Skip database cleanup after finalization'),\n additionalArgs: z.array(z.string()).optional().describe('Additional command-line arguments')\n },\n examples: [\n 'codeql database create --language=java --source-root=/path/to/project mydb',\n 'codeql database create --language=cpp --command=\"make all\" mydb',\n 'codeql database create --language=python,javascript mydb'\n ],\n resultProcessor: createDatabaseResultProcessor()\n};", "/**\n * CodeQL find class position tool\n * \n * Inspired by JordyZomer/codeql-mcp repository:\n * - https://github.com/JordyZomer/codeql-mcp/blob/main/server.py\n * - https://github.com/JordyZomer/codeql-mcp/blob/main/codeqlclient.py\n */\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport { readFile } from 'fs/promises';\nimport { logger } from '../../utils/logger';\n\nexport interface ClassPosition {\n start_line: number;\n start_col: number;\n end_line: number;\n end_col: number;\n}\n\n/**\n * Find the 1-based position of a class name identifier in a QL file.\n * Returns: { start_line, start_col, end_line, end_col }\n */\nexport async function findClassPosition(filepath: string, className: string): Promise {\n try {\n const content = await readFile(filepath, 'utf-8');\n const lines = content.split('\\n');\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n // Match class definition with the specific class name\n const classNameRegex = new RegExp(`\\\\bclass\\\\s+(${className.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')})\\\\b`);\n const match = classNameRegex.exec(line);\n \n if (match) {\n const start_line = i + 1; // 1-based line numbering\n // The class name is in capture group 1\n const classNameStart = match.index + match[0].indexOf(match[1]);\n const start_col = classNameStart + 1; // 1-based column numbering\n const end_col = start_col + className.length - 1; // end_col is inclusive\n \n return {\n start_line,\n start_col,\n end_line: start_line,\n end_col\n };\n }\n }\n\n throw new Error(`Class name '${className}' not found in file: ${filepath}`);\n } catch (error) {\n if (error instanceof Error && error.message.includes('not found in file')) {\n throw error;\n }\n throw new Error(`Failed to read or parse file ${filepath}: ${error instanceof Error ? error.message : 'Unknown error'}`);\n }\n}\n\n/**\n * Register the find class position tool with the MCP server\n */\nexport function registerFindClassPositionTool(server: McpServer): void {\n server.tool(\n 'find_class_position',\n 'Finds startline, startcol, endline endcol of a class for quickeval',\n {\n file: z.string().describe('Path to the .ql file to search'),\n name: z.string().describe('Name of the class to find'),\n },\n async ({ file, name }) => {\n try {\n const position = await findClassPosition(file, name);\n return {\n content: [{ type: 'text', text: JSON.stringify(position, null, 2) }],\n };\n } catch (error) {\n logger.error('Error finding class position:', error);\n return {\n content: [\n {\n type: 'text',\n text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`,\n },\n ],\n isError: true,\n };\n }\n }\n );\n}", "/**\n * CodeQL find predicate position tool\n * \n * Inspired by JordyZomer/codeql-mcp repository:\n * - https://github.com/JordyZomer/codeql-mcp/blob/main/server.py\n * - https://github.com/JordyZomer/codeql-mcp/blob/main/codeqlclient.py\n */\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport { readFile } from 'fs/promises';\nimport { logger } from '../../utils/logger';\n\nexport interface PredicatePosition {\n start_line: number;\n start_col: number;\n end_line: number;\n end_col: number;\n}\n\n/**\n * Find the 1-based position of a predicate name in a QL file.\n * Supports: \n * - predicate name(...) - predicates with no return type\n * - Type name(...) - predicates with return type (e.g., string foo())\n * - name(...) (inside class) - member predicates\n * Returns: { start_line, start_col, end_line, end_col }\n */\nexport async function findPredicatePosition(filepath: string, predicateName: string): Promise {\n try {\n const content = await readFile(filepath, 'utf-8');\n const lines = content.split('\\n');\n const escapedName = predicateName.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n \n // Match predicate definition with the specific predicate name\n // Pattern 1: predicate name(...)\n const predicateKeywordRegex = new RegExp(`\\\\bpredicate\\\\s+(${escapedName})\\\\s*\\\\(`);\n let match = predicateKeywordRegex.exec(line);\n \n // Pattern 2: Type name(...) - predicates with return type\n // Matches: string foo(), int bar(), MyClass baz(), etc.\n // Must start at beginning of line (with optional whitespace) or after certain keywords\n if (!match) {\n const returnTypeRegex = new RegExp(`(?:^|\\\\s)(?:abstract\\\\s+)?(?:cached\\\\s+)?(?:private\\\\s+)?(?:deprecated\\\\s+)?(?:\\\\w+)\\\\s+(${escapedName})\\\\s*\\\\(`);\n match = returnTypeRegex.exec(line);\n }\n \n if (match) {\n const start_line = i + 1; // 1-based line numbering\n // The predicate name is in capture group 1\n const predicateNameStart = match.index + match[0].indexOf(match[1]);\n const start_col = predicateNameStart + 1; // 1-based column numbering\n const end_col = start_col + predicateName.length - 1; // end_col is inclusive\n \n return {\n start_line,\n start_col,\n end_line: start_line,\n end_col\n };\n }\n }\n\n throw new Error(`Predicate name '${predicateName}' not found in file: ${filepath}`);\n } catch (error) {\n if (error instanceof Error && error.message.includes('not found in file')) {\n throw error;\n }\n throw new Error(`Failed to read or parse file ${filepath}: ${error instanceof Error ? error.message : 'Unknown error'}`);\n }\n}\n\n/**\n * Register the find predicate position tool with the MCP server\n */\nexport function registerFindPredicatePositionTool(server: McpServer): void {\n server.tool(\n 'find_predicate_position',\n 'Finds startline, startcol, endline endcol of a predicate for quickeval',\n {\n file: z.string().describe('Path to the .ql file to search'),\n name: z.string().describe('Name of the predicate to find'),\n },\n async ({ file, name }) => {\n try {\n const position = await findPredicatePosition(file, name);\n return {\n content: [{ type: 'text', text: JSON.stringify(position, null, 2) }],\n };\n } catch (error) {\n logger.error('Error finding predicate position:', error);\n return {\n content: [\n {\n type: 'text',\n text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`,\n },\n ],\n isError: true,\n };\n }\n }\n );\n}", "/**\n * CodeQL find query files tool\n * Discovers and tracks all files related to a CodeQL query\n */\n\nimport { z } from 'zod';\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { findCodeQLQueryFiles } from '../../lib/query-file-finder';\nimport { logger } from '../../utils/logger';\n\n/**\n * Register the find_codeql_query_files tool\n */\nexport function registerFindCodeQLQueryFilesTool(server: McpServer): void {\n server.tool(\n 'find_codeql_query_files',\n 'Find and track all files and directories related to a CodeQL query, including resolved metadata',\n {\n queryPath: z.string().describe('Path to the CodeQL query file (.ql)'),\n language: z.string().optional().describe('Programming language (optional, will be inferred if not provided)'),\n resolveMetadata: z.boolean().optional().describe('Whether to resolve query metadata (default: true)')\n },\n async ({ queryPath, language, resolveMetadata }) => {\n try {\n const result = await findCodeQLQueryFiles(\n queryPath,\n language,\n resolveMetadata !== false // Default to true if not specified\n );\n\n return {\n content: [{ type: 'text', text: JSON.stringify(result, null, 2) }]\n };\n } catch (error) {\n logger.error('Error finding CodeQL query files:', error);\n return {\n content: [\n {\n type: 'text',\n text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`\n }\n ],\n isError: true\n };\n }\n }\n );\n}\n", "/**\n * CodeQL query file finder utilities\n * Handles discovery and tracking of query-related files and directories\n */\n\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport * as yaml from 'js-yaml';\nimport { QueryFileInfo, QueryFilesResult } from '../types/codeql';\nimport { resolveQueryMetadata } from './metadata-resolver';\n\n// Supported CodeQL languages and their file extensions (alphabetically ordered)\nconst LANGUAGE_EXTENSIONS: Record = {\n actions: 'yml',\n cpp: 'cpp',\n csharp: 'cs',\n go: 'go',\n java: 'java',\n javascript: 'js',\n python: 'py',\n ruby: 'rb',\n swift: 'swift',\n typescript: 'ts'\n};\n\nconst KNOWN_LANGUAGES = Object.keys(LANGUAGE_EXTENSIONS);\n\n/**\n * Get the file extension for test code based on language\n */\nfunction getLanguageExtension(language: string): string {\n return LANGUAGE_EXTENSIONS[language.toLowerCase()] || 'txt';\n}\n\n/**\n * Infer the language from the query file's directory structure\n */\nfunction inferLanguageFromPath(queryPath: string): string {\n const parts = queryPath.split(path.sep);\n\n for (const part of parts) {\n if (KNOWN_LANGUAGES.includes(part.toLowerCase())) {\n return part.toLowerCase();\n }\n }\n\n // Default fallback\n return 'unknown';\n}\n\n/**\n * Find the nearest qlpack.yml or codeql-pack.yml file by walking up the directory tree\n */\nfunction findNearestQlpack(startPath: string): string | null {\n let currentPath = startPath;\n const root = path.parse(currentPath).root;\n\n while (currentPath !== root) {\n // Check for codeql-pack.yml first (newer format), then qlpack.yml\n for (const packFile of ['codeql-pack.yml', 'qlpack.yml']) {\n const packPath = path.join(currentPath, packFile);\n if (fs.existsSync(packPath) && fs.statSync(packPath).isFile()) {\n return packPath;\n }\n }\n currentPath = path.dirname(currentPath);\n }\n\n return null;\n}\n\n/**\n * Read and parse qlpack.yml file\n */\nfunction readQlpackMetadata(qlpackPath: string): Record | null {\n try {\n const content = fs.readFileSync(qlpackPath, 'utf-8');\n const parsed = yaml.load(content) as Record;\n return parsed;\n } catch (_error) {\n // If we can't parse it, return null rather than failing\n return null;\n }\n}\n\n/**\n * Check if a file exists and return QueryFileInfo\n */\nfunction checkFile(filePath: string): QueryFileInfo {\n try {\n const exists = fs.existsSync(filePath) && fs.statSync(filePath).isFile();\n return {\n exists,\n path: filePath // Always return path, whether it exists or not\n };\n } catch {\n return {\n exists: false,\n path: filePath // Return the path even on error\n };\n }\n}\n\n/**\n * Check if a directory exists and return QueryFileInfo\n */\nfunction checkDirectory(dirPath: string): QueryFileInfo {\n try {\n const exists = fs.existsSync(dirPath) && fs.statSync(dirPath).isDirectory();\n return {\n exists,\n path: dirPath // Always return path, whether it exists or not\n };\n } catch {\n return {\n exists: false,\n path: dirPath // Return the path even on error\n };\n }\n}\n\n/**\n * Find all test code files in the test directory\n */\nfunction findTestCodeFiles(testDir: string, queryName: string, language: string): string[] {\n if (!fs.existsSync(testDir)) {\n return [];\n }\n\n try {\n const files = fs.readdirSync(testDir);\n const languageExt = getLanguageExtension(language);\n const testFiles: string[] = [];\n\n // Look for files matching the query name or other test code files\n const allValidExtensions = [...new Set([...Object.values(LANGUAGE_EXTENSIONS), 'yaml'])]; // Include yaml as alias for yml\n\n for (const file of files) {\n const filePath = path.join(testDir, file);\n const stat = fs.statSync(filePath);\n\n if (stat.isFile()) {\n // Include the primary test file matching query name\n if (file === `${queryName}.${languageExt}`) {\n testFiles.push(filePath);\n }\n // Include other code files (but not .qlref, .expected, .actual files)\n else if (!file.endsWith('.qlref') && !file.endsWith('.expected') && !file.endsWith('.actual')) {\n const ext = path.extname(file).slice(1);\n if (ext === languageExt || allValidExtensions.includes(ext)) {\n testFiles.push(filePath);\n }\n }\n }\n }\n\n return testFiles;\n } catch {\n return [];\n }\n}\n\n/**\n * Find CodeQL query files and directories based on a query file path\n * Optionally resolves metadata if the query file exists\n */\nexport async function findCodeQLQueryFiles(\n queryFilePath: string,\n language?: string,\n resolveMetadata: boolean = true\n): Promise {\n // Resolve absolute path\n const absoluteQueryPath = path.resolve(queryFilePath);\n\n // Extract query name and directory\n const queryName = path.basename(absoluteQueryPath, '.ql');\n const queryDir = path.dirname(absoluteQueryPath);\n\n // Infer language if not provided\n const detectedLanguage = language || inferLanguageFromPath(absoluteQueryPath);\n\n // Check query file itself\n const queryPath = checkFile(absoluteQueryPath);\n const queryDirectory = checkDirectory(queryDir);\n\n // Check for documentation files\n const mdPath = path.join(queryDir, `${queryName}.md`);\n const qhelpPath = path.join(queryDir, `${queryName}.qhelp`);\n const mdInfo = checkFile(mdPath);\n const qhelpInfo = checkFile(qhelpPath);\n\n const documentationPath: QueryFileInfo = mdInfo.exists ? mdInfo : (qhelpInfo.exists ? qhelpInfo : {\n exists: false,\n path: mdPath // Suggest .md as the default\n });\n\n // Check for specification file\n const qspecPath = path.join(queryDir, `${queryName}.qspec`);\n const specificationPath = checkFile(qspecPath);\n\n // Determine test directory\n // Pattern: if query is in .../src/QueryName/, test should be in .../test/QueryName/\n let testDir: string;\n if (queryDir.includes(`${path.sep}src${path.sep}`)) {\n // Find the last occurrence of /src/ and replace it with /test/\n const parts = queryDir.split(path.sep);\n const srcIndex = parts.lastIndexOf('src');\n if (srcIndex !== -1) {\n parts[srcIndex] = 'test';\n testDir = parts.join(path.sep);\n } else {\n // Fallback in case split didn't find it\n testDir = path.join(path.dirname(queryDir), 'test', queryName);\n }\n } else {\n // Fallback: if not in src directory, construct test dir based on parent\n testDir = path.join(path.dirname(queryDir), 'test', queryName);\n }\n const testDirectory = checkDirectory(testDir);\n\n // Check for .qlref file\n const qlrefPath = path.join(testDir, `${queryName}.qlref`);\n const qlrefInfo = checkFile(qlrefPath);\n\n // Find test code files\n const testCodePaths = findTestCodeFiles(testDir, queryName, detectedLanguage);\n\n // Check for expected results\n const expectedPath = path.join(testDir, `${queryName}.expected`);\n const expectedResultsPath = checkFile(expectedPath);\n\n // Check for actual results\n const actualPath = path.join(testDir, `${queryName}.actual`);\n const actualResultsPath = checkFile(actualPath);\n\n // Check for test database\n const testprojPath = path.join(testDir, `${queryName}.testproj`);\n const testDatabasePath = checkDirectory(testprojPath);\n\n // Determine missing files\n const missingFiles: string[] = [];\n if (!queryPath.exists) missingFiles.push(queryPath.path);\n if (!documentationPath.exists) missingFiles.push(documentationPath.path);\n if (!specificationPath.exists) missingFiles.push(specificationPath.path);\n if (!testDirectory.exists) missingFiles.push(testDirectory.path);\n if (!qlrefInfo.exists) missingFiles.push(qlrefInfo.path);\n if (testCodePaths.length === 0) missingFiles.push(path.join(testDir, `${queryName}.${getLanguageExtension(detectedLanguage)}`));\n if (!expectedResultsPath.exists) missingFiles.push(expectedResultsPath.path);\n\n const allFilesExist = missingFiles.length === 0;\n\n // Resolve metadata if requested and query file exists\n let metadata: { [key: string]: string | string[] } | undefined;\n if (resolveMetadata && queryPath.exists) {\n const resolvedMetadata = await resolveQueryMetadata(absoluteQueryPath);\n if (resolvedMetadata) {\n metadata = resolvedMetadata;\n }\n }\n\n // Resolve pack metadata and directories by finding the nearest qlpack.yml files\n let packMetadata: Record | undefined;\n const queryPackPath = findNearestQlpack(queryDir);\n const queryPackDir = queryPackPath ? path.dirname(queryPackPath) : queryDir;\n if (queryPackPath) {\n const parsed = readQlpackMetadata(queryPackPath);\n if (parsed) {\n packMetadata = parsed;\n }\n }\n\n // Find pack directory for test files\n const testPackPath = findNearestQlpack(testDir);\n const testPackDir = testPackPath ? path.dirname(testPackPath) : testDir;\n\n return {\n queryName,\n language: detectedLanguage,\n\n allFilesExist,\n\n files: {\n query: {\n dir: queryDirectory.path,\n doc: path.basename(documentationPath.path),\n packDir: queryPackDir,\n query: path.basename(queryPath.path),\n spec: path.basename(specificationPath.path)\n },\n test: {\n actual: path.basename(actualResultsPath.path),\n dir: testDirectory.path,\n expected: path.basename(expectedResultsPath.path),\n packDir: testPackDir,\n qlref: path.basename(qlrefInfo.path),\n testCode: testCodePaths,\n testDatabaseDir: testDatabasePath.path\n }\n },\n\n metadata,\n\n missingFiles,\n\n packMetadata,\n\n status: {\n actualResultsExist: actualResultsPath.exists,\n documentationExists: documentationPath.exists,\n expectedResultsExist: expectedResultsPath.exists,\n hasTestCode: testCodePaths.length > 0,\n qlrefExists: qlrefInfo.exists,\n queryExists: queryPath.exists,\n specificationExists: specificationPath.exists,\n testDatabaseDirExists: testDatabasePath.exists,\n testDirectoryExists: testDirectory.exists\n }\n };\n}\n", "\n/*! js-yaml 4.1.1 https://github.com/nodeca/js-yaml @license MIT */\nfunction isNothing(subject) {\n return (typeof subject === 'undefined') || (subject === null);\n}\n\n\nfunction isObject(subject) {\n return (typeof subject === 'object') && (subject !== null);\n}\n\n\nfunction toArray(sequence) {\n if (Array.isArray(sequence)) return sequence;\n else if (isNothing(sequence)) return [];\n\n return [ sequence ];\n}\n\n\nfunction extend(target, source) {\n var index, length, key, sourceKeys;\n\n if (source) {\n sourceKeys = Object.keys(source);\n\n for (index = 0, length = sourceKeys.length; index < length; index += 1) {\n key = sourceKeys[index];\n target[key] = source[key];\n }\n }\n\n return target;\n}\n\n\nfunction repeat(string, count) {\n var result = '', cycle;\n\n for (cycle = 0; cycle < count; cycle += 1) {\n result += string;\n }\n\n return result;\n}\n\n\nfunction isNegativeZero(number) {\n return (number === 0) && (Number.NEGATIVE_INFINITY === 1 / number);\n}\n\n\nvar isNothing_1 = isNothing;\nvar isObject_1 = isObject;\nvar toArray_1 = toArray;\nvar repeat_1 = repeat;\nvar isNegativeZero_1 = isNegativeZero;\nvar extend_1 = extend;\n\nvar common = {\n\tisNothing: isNothing_1,\n\tisObject: isObject_1,\n\ttoArray: toArray_1,\n\trepeat: repeat_1,\n\tisNegativeZero: isNegativeZero_1,\n\textend: extend_1\n};\n\n// YAML error class. http://stackoverflow.com/questions/8458984\n\n\nfunction formatError(exception, compact) {\n var where = '', message = exception.reason || '(unknown reason)';\n\n if (!exception.mark) return message;\n\n if (exception.mark.name) {\n where += 'in \"' + exception.mark.name + '\" ';\n }\n\n where += '(' + (exception.mark.line + 1) + ':' + (exception.mark.column + 1) + ')';\n\n if (!compact && exception.mark.snippet) {\n where += '\\n\\n' + exception.mark.snippet;\n }\n\n return message + ' ' + where;\n}\n\n\nfunction YAMLException$1(reason, mark) {\n // Super constructor\n Error.call(this);\n\n this.name = 'YAMLException';\n this.reason = reason;\n this.mark = mark;\n this.message = formatError(this, false);\n\n // Include stack trace in error object\n if (Error.captureStackTrace) {\n // Chrome and NodeJS\n Error.captureStackTrace(this, this.constructor);\n } else {\n // FF, IE 10+ and Safari 6+. Fallback for others\n this.stack = (new Error()).stack || '';\n }\n}\n\n\n// Inherit from Error\nYAMLException$1.prototype = Object.create(Error.prototype);\nYAMLException$1.prototype.constructor = YAMLException$1;\n\n\nYAMLException$1.prototype.toString = function toString(compact) {\n return this.name + ': ' + formatError(this, compact);\n};\n\n\nvar exception = YAMLException$1;\n\n// get snippet for a single line, respecting maxLength\nfunction getLine(buffer, lineStart, lineEnd, position, maxLineLength) {\n var head = '';\n var tail = '';\n var maxHalfLength = Math.floor(maxLineLength / 2) - 1;\n\n if (position - lineStart > maxHalfLength) {\n head = ' ... ';\n lineStart = position - maxHalfLength + head.length;\n }\n\n if (lineEnd - position > maxHalfLength) {\n tail = ' ...';\n lineEnd = position + maxHalfLength - tail.length;\n }\n\n return {\n str: head + buffer.slice(lineStart, lineEnd).replace(/\\t/g, '\u2192') + tail,\n pos: position - lineStart + head.length // relative position\n };\n}\n\n\nfunction padStart(string, max) {\n return common.repeat(' ', max - string.length) + string;\n}\n\n\nfunction makeSnippet(mark, options) {\n options = Object.create(options || null);\n\n if (!mark.buffer) return null;\n\n if (!options.maxLength) options.maxLength = 79;\n if (typeof options.indent !== 'number') options.indent = 1;\n if (typeof options.linesBefore !== 'number') options.linesBefore = 3;\n if (typeof options.linesAfter !== 'number') options.linesAfter = 2;\n\n var re = /\\r?\\n|\\r|\\0/g;\n var lineStarts = [ 0 ];\n var lineEnds = [];\n var match;\n var foundLineNo = -1;\n\n while ((match = re.exec(mark.buffer))) {\n lineEnds.push(match.index);\n lineStarts.push(match.index + match[0].length);\n\n if (mark.position <= match.index && foundLineNo < 0) {\n foundLineNo = lineStarts.length - 2;\n }\n }\n\n if (foundLineNo < 0) foundLineNo = lineStarts.length - 1;\n\n var result = '', i, line;\n var lineNoLength = Math.min(mark.line + options.linesAfter, lineEnds.length).toString().length;\n var maxLineLength = options.maxLength - (options.indent + lineNoLength + 3);\n\n for (i = 1; i <= options.linesBefore; i++) {\n if (foundLineNo - i < 0) break;\n line = getLine(\n mark.buffer,\n lineStarts[foundLineNo - i],\n lineEnds[foundLineNo - i],\n mark.position - (lineStarts[foundLineNo] - lineStarts[foundLineNo - i]),\n maxLineLength\n );\n result = common.repeat(' ', options.indent) + padStart((mark.line - i + 1).toString(), lineNoLength) +\n ' | ' + line.str + '\\n' + result;\n }\n\n line = getLine(mark.buffer, lineStarts[foundLineNo], lineEnds[foundLineNo], mark.position, maxLineLength);\n result += common.repeat(' ', options.indent) + padStart((mark.line + 1).toString(), lineNoLength) +\n ' | ' + line.str + '\\n';\n result += common.repeat('-', options.indent + lineNoLength + 3 + line.pos) + '^' + '\\n';\n\n for (i = 1; i <= options.linesAfter; i++) {\n if (foundLineNo + i >= lineEnds.length) break;\n line = getLine(\n mark.buffer,\n lineStarts[foundLineNo + i],\n lineEnds[foundLineNo + i],\n mark.position - (lineStarts[foundLineNo] - lineStarts[foundLineNo + i]),\n maxLineLength\n );\n result += common.repeat(' ', options.indent) + padStart((mark.line + i + 1).toString(), lineNoLength) +\n ' | ' + line.str + '\\n';\n }\n\n return result.replace(/\\n$/, '');\n}\n\n\nvar snippet = makeSnippet;\n\nvar TYPE_CONSTRUCTOR_OPTIONS = [\n 'kind',\n 'multi',\n 'resolve',\n 'construct',\n 'instanceOf',\n 'predicate',\n 'represent',\n 'representName',\n 'defaultStyle',\n 'styleAliases'\n];\n\nvar YAML_NODE_KINDS = [\n 'scalar',\n 'sequence',\n 'mapping'\n];\n\nfunction compileStyleAliases(map) {\n var result = {};\n\n if (map !== null) {\n Object.keys(map).forEach(function (style) {\n map[style].forEach(function (alias) {\n result[String(alias)] = style;\n });\n });\n }\n\n return result;\n}\n\nfunction Type$1(tag, options) {\n options = options || {};\n\n Object.keys(options).forEach(function (name) {\n if (TYPE_CONSTRUCTOR_OPTIONS.indexOf(name) === -1) {\n throw new exception('Unknown option \"' + name + '\" is met in definition of \"' + tag + '\" YAML type.');\n }\n });\n\n // TODO: Add tag format check.\n this.options = options; // keep original options in case user wants to extend this type later\n this.tag = tag;\n this.kind = options['kind'] || null;\n this.resolve = options['resolve'] || function () { return true; };\n this.construct = options['construct'] || function (data) { return data; };\n this.instanceOf = options['instanceOf'] || null;\n this.predicate = options['predicate'] || null;\n this.represent = options['represent'] || null;\n this.representName = options['representName'] || null;\n this.defaultStyle = options['defaultStyle'] || null;\n this.multi = options['multi'] || false;\n this.styleAliases = compileStyleAliases(options['styleAliases'] || null);\n\n if (YAML_NODE_KINDS.indexOf(this.kind) === -1) {\n throw new exception('Unknown kind \"' + this.kind + '\" is specified for \"' + tag + '\" YAML type.');\n }\n}\n\nvar type = Type$1;\n\n/*eslint-disable max-len*/\n\n\n\n\n\nfunction compileList(schema, name) {\n var result = [];\n\n schema[name].forEach(function (currentType) {\n var newIndex = result.length;\n\n result.forEach(function (previousType, previousIndex) {\n if (previousType.tag === currentType.tag &&\n previousType.kind === currentType.kind &&\n previousType.multi === currentType.multi) {\n\n newIndex = previousIndex;\n }\n });\n\n result[newIndex] = currentType;\n });\n\n return result;\n}\n\n\nfunction compileMap(/* lists... */) {\n var result = {\n scalar: {},\n sequence: {},\n mapping: {},\n fallback: {},\n multi: {\n scalar: [],\n sequence: [],\n mapping: [],\n fallback: []\n }\n }, index, length;\n\n function collectType(type) {\n if (type.multi) {\n result.multi[type.kind].push(type);\n result.multi['fallback'].push(type);\n } else {\n result[type.kind][type.tag] = result['fallback'][type.tag] = type;\n }\n }\n\n for (index = 0, length = arguments.length; index < length; index += 1) {\n arguments[index].forEach(collectType);\n }\n return result;\n}\n\n\nfunction Schema$1(definition) {\n return this.extend(definition);\n}\n\n\nSchema$1.prototype.extend = function extend(definition) {\n var implicit = [];\n var explicit = [];\n\n if (definition instanceof type) {\n // Schema.extend(type)\n explicit.push(definition);\n\n } else if (Array.isArray(definition)) {\n // Schema.extend([ type1, type2, ... ])\n explicit = explicit.concat(definition);\n\n } else if (definition && (Array.isArray(definition.implicit) || Array.isArray(definition.explicit))) {\n // Schema.extend({ explicit: [ type1, type2, ... ], implicit: [ type1, type2, ... ] })\n if (definition.implicit) implicit = implicit.concat(definition.implicit);\n if (definition.explicit) explicit = explicit.concat(definition.explicit);\n\n } else {\n throw new exception('Schema.extend argument should be a Type, [ Type ], ' +\n 'or a schema definition ({ implicit: [...], explicit: [...] })');\n }\n\n implicit.forEach(function (type$1) {\n if (!(type$1 instanceof type)) {\n throw new exception('Specified list of YAML types (or a single Type object) contains a non-Type object.');\n }\n\n if (type$1.loadKind && type$1.loadKind !== 'scalar') {\n throw new exception('There is a non-scalar type in the implicit list of a schema. Implicit resolving of such types is not supported.');\n }\n\n if (type$1.multi) {\n throw new exception('There is a multi type in the implicit list of a schema. Multi tags can only be listed as explicit.');\n }\n });\n\n explicit.forEach(function (type$1) {\n if (!(type$1 instanceof type)) {\n throw new exception('Specified list of YAML types (or a single Type object) contains a non-Type object.');\n }\n });\n\n var result = Object.create(Schema$1.prototype);\n\n result.implicit = (this.implicit || []).concat(implicit);\n result.explicit = (this.explicit || []).concat(explicit);\n\n result.compiledImplicit = compileList(result, 'implicit');\n result.compiledExplicit = compileList(result, 'explicit');\n result.compiledTypeMap = compileMap(result.compiledImplicit, result.compiledExplicit);\n\n return result;\n};\n\n\nvar schema = Schema$1;\n\nvar str = new type('tag:yaml.org,2002:str', {\n kind: 'scalar',\n construct: function (data) { return data !== null ? data : ''; }\n});\n\nvar seq = new type('tag:yaml.org,2002:seq', {\n kind: 'sequence',\n construct: function (data) { return data !== null ? data : []; }\n});\n\nvar map = new type('tag:yaml.org,2002:map', {\n kind: 'mapping',\n construct: function (data) { return data !== null ? data : {}; }\n});\n\nvar failsafe = new schema({\n explicit: [\n str,\n seq,\n map\n ]\n});\n\nfunction resolveYamlNull(data) {\n if (data === null) return true;\n\n var max = data.length;\n\n return (max === 1 && data === '~') ||\n (max === 4 && (data === 'null' || data === 'Null' || data === 'NULL'));\n}\n\nfunction constructYamlNull() {\n return null;\n}\n\nfunction isNull(object) {\n return object === null;\n}\n\nvar _null = new type('tag:yaml.org,2002:null', {\n kind: 'scalar',\n resolve: resolveYamlNull,\n construct: constructYamlNull,\n predicate: isNull,\n represent: {\n canonical: function () { return '~'; },\n lowercase: function () { return 'null'; },\n uppercase: function () { return 'NULL'; },\n camelcase: function () { return 'Null'; },\n empty: function () { return ''; }\n },\n defaultStyle: 'lowercase'\n});\n\nfunction resolveYamlBoolean(data) {\n if (data === null) return false;\n\n var max = data.length;\n\n return (max === 4 && (data === 'true' || data === 'True' || data === 'TRUE')) ||\n (max === 5 && (data === 'false' || data === 'False' || data === 'FALSE'));\n}\n\nfunction constructYamlBoolean(data) {\n return data === 'true' ||\n data === 'True' ||\n data === 'TRUE';\n}\n\nfunction isBoolean(object) {\n return Object.prototype.toString.call(object) === '[object Boolean]';\n}\n\nvar bool = new type('tag:yaml.org,2002:bool', {\n kind: 'scalar',\n resolve: resolveYamlBoolean,\n construct: constructYamlBoolean,\n predicate: isBoolean,\n represent: {\n lowercase: function (object) { return object ? 'true' : 'false'; },\n uppercase: function (object) { return object ? 'TRUE' : 'FALSE'; },\n camelcase: function (object) { return object ? 'True' : 'False'; }\n },\n defaultStyle: 'lowercase'\n});\n\nfunction isHexCode(c) {\n return ((0x30/* 0 */ <= c) && (c <= 0x39/* 9 */)) ||\n ((0x41/* A */ <= c) && (c <= 0x46/* F */)) ||\n ((0x61/* a */ <= c) && (c <= 0x66/* f */));\n}\n\nfunction isOctCode(c) {\n return ((0x30/* 0 */ <= c) && (c <= 0x37/* 7 */));\n}\n\nfunction isDecCode(c) {\n return ((0x30/* 0 */ <= c) && (c <= 0x39/* 9 */));\n}\n\nfunction resolveYamlInteger(data) {\n if (data === null) return false;\n\n var max = data.length,\n index = 0,\n hasDigits = false,\n ch;\n\n if (!max) return false;\n\n ch = data[index];\n\n // sign\n if (ch === '-' || ch === '+') {\n ch = data[++index];\n }\n\n if (ch === '0') {\n // 0\n if (index + 1 === max) return true;\n ch = data[++index];\n\n // base 2, base 8, base 16\n\n if (ch === 'b') {\n // base 2\n index++;\n\n for (; index < max; index++) {\n ch = data[index];\n if (ch === '_') continue;\n if (ch !== '0' && ch !== '1') return false;\n hasDigits = true;\n }\n return hasDigits && ch !== '_';\n }\n\n\n if (ch === 'x') {\n // base 16\n index++;\n\n for (; index < max; index++) {\n ch = data[index];\n if (ch === '_') continue;\n if (!isHexCode(data.charCodeAt(index))) return false;\n hasDigits = true;\n }\n return hasDigits && ch !== '_';\n }\n\n\n if (ch === 'o') {\n // base 8\n index++;\n\n for (; index < max; index++) {\n ch = data[index];\n if (ch === '_') continue;\n if (!isOctCode(data.charCodeAt(index))) return false;\n hasDigits = true;\n }\n return hasDigits && ch !== '_';\n }\n }\n\n // base 10 (except 0)\n\n // value should not start with `_`;\n if (ch === '_') return false;\n\n for (; index < max; index++) {\n ch = data[index];\n if (ch === '_') continue;\n if (!isDecCode(data.charCodeAt(index))) {\n return false;\n }\n hasDigits = true;\n }\n\n // Should have digits and should not end with `_`\n if (!hasDigits || ch === '_') return false;\n\n return true;\n}\n\nfunction constructYamlInteger(data) {\n var value = data, sign = 1, ch;\n\n if (value.indexOf('_') !== -1) {\n value = value.replace(/_/g, '');\n }\n\n ch = value[0];\n\n if (ch === '-' || ch === '+') {\n if (ch === '-') sign = -1;\n value = value.slice(1);\n ch = value[0];\n }\n\n if (value === '0') return 0;\n\n if (ch === '0') {\n if (value[1] === 'b') return sign * parseInt(value.slice(2), 2);\n if (value[1] === 'x') return sign * parseInt(value.slice(2), 16);\n if (value[1] === 'o') return sign * parseInt(value.slice(2), 8);\n }\n\n return sign * parseInt(value, 10);\n}\n\nfunction isInteger(object) {\n return (Object.prototype.toString.call(object)) === '[object Number]' &&\n (object % 1 === 0 && !common.isNegativeZero(object));\n}\n\nvar int = new type('tag:yaml.org,2002:int', {\n kind: 'scalar',\n resolve: resolveYamlInteger,\n construct: constructYamlInteger,\n predicate: isInteger,\n represent: {\n binary: function (obj) { return obj >= 0 ? '0b' + obj.toString(2) : '-0b' + obj.toString(2).slice(1); },\n octal: function (obj) { return obj >= 0 ? '0o' + obj.toString(8) : '-0o' + obj.toString(8).slice(1); },\n decimal: function (obj) { return obj.toString(10); },\n /* eslint-disable max-len */\n hexadecimal: function (obj) { return obj >= 0 ? '0x' + obj.toString(16).toUpperCase() : '-0x' + obj.toString(16).toUpperCase().slice(1); }\n },\n defaultStyle: 'decimal',\n styleAliases: {\n binary: [ 2, 'bin' ],\n octal: [ 8, 'oct' ],\n decimal: [ 10, 'dec' ],\n hexadecimal: [ 16, 'hex' ]\n }\n});\n\nvar YAML_FLOAT_PATTERN = new RegExp(\n // 2.5e4, 2.5 and integers\n '^(?:[-+]?(?:[0-9][0-9_]*)(?:\\\\.[0-9_]*)?(?:[eE][-+]?[0-9]+)?' +\n // .2e4, .2\n // special case, seems not from spec\n '|\\\\.[0-9_]+(?:[eE][-+]?[0-9]+)?' +\n // .inf\n '|[-+]?\\\\.(?:inf|Inf|INF)' +\n // .nan\n '|\\\\.(?:nan|NaN|NAN))$');\n\nfunction resolveYamlFloat(data) {\n if (data === null) return false;\n\n if (!YAML_FLOAT_PATTERN.test(data) ||\n // Quick hack to not allow integers end with `_`\n // Probably should update regexp & check speed\n data[data.length - 1] === '_') {\n return false;\n }\n\n return true;\n}\n\nfunction constructYamlFloat(data) {\n var value, sign;\n\n value = data.replace(/_/g, '').toLowerCase();\n sign = value[0] === '-' ? -1 : 1;\n\n if ('+-'.indexOf(value[0]) >= 0) {\n value = value.slice(1);\n }\n\n if (value === '.inf') {\n return (sign === 1) ? Number.POSITIVE_INFINITY : Number.NEGATIVE_INFINITY;\n\n } else if (value === '.nan') {\n return NaN;\n }\n return sign * parseFloat(value, 10);\n}\n\n\nvar SCIENTIFIC_WITHOUT_DOT = /^[-+]?[0-9]+e/;\n\nfunction representYamlFloat(object, style) {\n var res;\n\n if (isNaN(object)) {\n switch (style) {\n case 'lowercase': return '.nan';\n case 'uppercase': return '.NAN';\n case 'camelcase': return '.NaN';\n }\n } else if (Number.POSITIVE_INFINITY === object) {\n switch (style) {\n case 'lowercase': return '.inf';\n case 'uppercase': return '.INF';\n case 'camelcase': return '.Inf';\n }\n } else if (Number.NEGATIVE_INFINITY === object) {\n switch (style) {\n case 'lowercase': return '-.inf';\n case 'uppercase': return '-.INF';\n case 'camelcase': return '-.Inf';\n }\n } else if (common.isNegativeZero(object)) {\n return '-0.0';\n }\n\n res = object.toString(10);\n\n // JS stringifier can build scientific format without dots: 5e-100,\n // while YAML requres dot: 5.e-100. Fix it with simple hack\n\n return SCIENTIFIC_WITHOUT_DOT.test(res) ? res.replace('e', '.e') : res;\n}\n\nfunction isFloat(object) {\n return (Object.prototype.toString.call(object) === '[object Number]') &&\n (object % 1 !== 0 || common.isNegativeZero(object));\n}\n\nvar float = new type('tag:yaml.org,2002:float', {\n kind: 'scalar',\n resolve: resolveYamlFloat,\n construct: constructYamlFloat,\n predicate: isFloat,\n represent: representYamlFloat,\n defaultStyle: 'lowercase'\n});\n\nvar json = failsafe.extend({\n implicit: [\n _null,\n bool,\n int,\n float\n ]\n});\n\nvar core = json;\n\nvar YAML_DATE_REGEXP = new RegExp(\n '^([0-9][0-9][0-9][0-9])' + // [1] year\n '-([0-9][0-9])' + // [2] month\n '-([0-9][0-9])$'); // [3] day\n\nvar YAML_TIMESTAMP_REGEXP = new RegExp(\n '^([0-9][0-9][0-9][0-9])' + // [1] year\n '-([0-9][0-9]?)' + // [2] month\n '-([0-9][0-9]?)' + // [3] day\n '(?:[Tt]|[ \\\\t]+)' + // ...\n '([0-9][0-9]?)' + // [4] hour\n ':([0-9][0-9])' + // [5] minute\n ':([0-9][0-9])' + // [6] second\n '(?:\\\\.([0-9]*))?' + // [7] fraction\n '(?:[ \\\\t]*(Z|([-+])([0-9][0-9]?)' + // [8] tz [9] tz_sign [10] tz_hour\n '(?::([0-9][0-9]))?))?$'); // [11] tz_minute\n\nfunction resolveYamlTimestamp(data) {\n if (data === null) return false;\n if (YAML_DATE_REGEXP.exec(data) !== null) return true;\n if (YAML_TIMESTAMP_REGEXP.exec(data) !== null) return true;\n return false;\n}\n\nfunction constructYamlTimestamp(data) {\n var match, year, month, day, hour, minute, second, fraction = 0,\n delta = null, tz_hour, tz_minute, date;\n\n match = YAML_DATE_REGEXP.exec(data);\n if (match === null) match = YAML_TIMESTAMP_REGEXP.exec(data);\n\n if (match === null) throw new Error('Date resolve error');\n\n // match: [1] year [2] month [3] day\n\n year = +(match[1]);\n month = +(match[2]) - 1; // JS month starts with 0\n day = +(match[3]);\n\n if (!match[4]) { // no hour\n return new Date(Date.UTC(year, month, day));\n }\n\n // match: [4] hour [5] minute [6] second [7] fraction\n\n hour = +(match[4]);\n minute = +(match[5]);\n second = +(match[6]);\n\n if (match[7]) {\n fraction = match[7].slice(0, 3);\n while (fraction.length < 3) { // milli-seconds\n fraction += '0';\n }\n fraction = +fraction;\n }\n\n // match: [8] tz [9] tz_sign [10] tz_hour [11] tz_minute\n\n if (match[9]) {\n tz_hour = +(match[10]);\n tz_minute = +(match[11] || 0);\n delta = (tz_hour * 60 + tz_minute) * 60000; // delta in mili-seconds\n if (match[9] === '-') delta = -delta;\n }\n\n date = new Date(Date.UTC(year, month, day, hour, minute, second, fraction));\n\n if (delta) date.setTime(date.getTime() - delta);\n\n return date;\n}\n\nfunction representYamlTimestamp(object /*, style*/) {\n return object.toISOString();\n}\n\nvar timestamp = new type('tag:yaml.org,2002:timestamp', {\n kind: 'scalar',\n resolve: resolveYamlTimestamp,\n construct: constructYamlTimestamp,\n instanceOf: Date,\n represent: representYamlTimestamp\n});\n\nfunction resolveYamlMerge(data) {\n return data === '<<' || data === null;\n}\n\nvar merge = new type('tag:yaml.org,2002:merge', {\n kind: 'scalar',\n resolve: resolveYamlMerge\n});\n\n/*eslint-disable no-bitwise*/\n\n\n\n\n\n// [ 64, 65, 66 ] -> [ padding, CR, LF ]\nvar BASE64_MAP = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\\n\\r';\n\n\nfunction resolveYamlBinary(data) {\n if (data === null) return false;\n\n var code, idx, bitlen = 0, max = data.length, map = BASE64_MAP;\n\n // Convert one by one.\n for (idx = 0; idx < max; idx++) {\n code = map.indexOf(data.charAt(idx));\n\n // Skip CR/LF\n if (code > 64) continue;\n\n // Fail on illegal characters\n if (code < 0) return false;\n\n bitlen += 6;\n }\n\n // If there are any bits left, source was corrupted\n return (bitlen % 8) === 0;\n}\n\nfunction constructYamlBinary(data) {\n var idx, tailbits,\n input = data.replace(/[\\r\\n=]/g, ''), // remove CR/LF & padding to simplify scan\n max = input.length,\n map = BASE64_MAP,\n bits = 0,\n result = [];\n\n // Collect by 6*4 bits (3 bytes)\n\n for (idx = 0; idx < max; idx++) {\n if ((idx % 4 === 0) && idx) {\n result.push((bits >> 16) & 0xFF);\n result.push((bits >> 8) & 0xFF);\n result.push(bits & 0xFF);\n }\n\n bits = (bits << 6) | map.indexOf(input.charAt(idx));\n }\n\n // Dump tail\n\n tailbits = (max % 4) * 6;\n\n if (tailbits === 0) {\n result.push((bits >> 16) & 0xFF);\n result.push((bits >> 8) & 0xFF);\n result.push(bits & 0xFF);\n } else if (tailbits === 18) {\n result.push((bits >> 10) & 0xFF);\n result.push((bits >> 2) & 0xFF);\n } else if (tailbits === 12) {\n result.push((bits >> 4) & 0xFF);\n }\n\n return new Uint8Array(result);\n}\n\nfunction representYamlBinary(object /*, style*/) {\n var result = '', bits = 0, idx, tail,\n max = object.length,\n map = BASE64_MAP;\n\n // Convert every three bytes to 4 ASCII characters.\n\n for (idx = 0; idx < max; idx++) {\n if ((idx % 3 === 0) && idx) {\n result += map[(bits >> 18) & 0x3F];\n result += map[(bits >> 12) & 0x3F];\n result += map[(bits >> 6) & 0x3F];\n result += map[bits & 0x3F];\n }\n\n bits = (bits << 8) + object[idx];\n }\n\n // Dump tail\n\n tail = max % 3;\n\n if (tail === 0) {\n result += map[(bits >> 18) & 0x3F];\n result += map[(bits >> 12) & 0x3F];\n result += map[(bits >> 6) & 0x3F];\n result += map[bits & 0x3F];\n } else if (tail === 2) {\n result += map[(bits >> 10) & 0x3F];\n result += map[(bits >> 4) & 0x3F];\n result += map[(bits << 2) & 0x3F];\n result += map[64];\n } else if (tail === 1) {\n result += map[(bits >> 2) & 0x3F];\n result += map[(bits << 4) & 0x3F];\n result += map[64];\n result += map[64];\n }\n\n return result;\n}\n\nfunction isBinary(obj) {\n return Object.prototype.toString.call(obj) === '[object Uint8Array]';\n}\n\nvar binary = new type('tag:yaml.org,2002:binary', {\n kind: 'scalar',\n resolve: resolveYamlBinary,\n construct: constructYamlBinary,\n predicate: isBinary,\n represent: representYamlBinary\n});\n\nvar _hasOwnProperty$3 = Object.prototype.hasOwnProperty;\nvar _toString$2 = Object.prototype.toString;\n\nfunction resolveYamlOmap(data) {\n if (data === null) return true;\n\n var objectKeys = [], index, length, pair, pairKey, pairHasKey,\n object = data;\n\n for (index = 0, length = object.length; index < length; index += 1) {\n pair = object[index];\n pairHasKey = false;\n\n if (_toString$2.call(pair) !== '[object Object]') return false;\n\n for (pairKey in pair) {\n if (_hasOwnProperty$3.call(pair, pairKey)) {\n if (!pairHasKey) pairHasKey = true;\n else return false;\n }\n }\n\n if (!pairHasKey) return false;\n\n if (objectKeys.indexOf(pairKey) === -1) objectKeys.push(pairKey);\n else return false;\n }\n\n return true;\n}\n\nfunction constructYamlOmap(data) {\n return data !== null ? data : [];\n}\n\nvar omap = new type('tag:yaml.org,2002:omap', {\n kind: 'sequence',\n resolve: resolveYamlOmap,\n construct: constructYamlOmap\n});\n\nvar _toString$1 = Object.prototype.toString;\n\nfunction resolveYamlPairs(data) {\n if (data === null) return true;\n\n var index, length, pair, keys, result,\n object = data;\n\n result = new Array(object.length);\n\n for (index = 0, length = object.length; index < length; index += 1) {\n pair = object[index];\n\n if (_toString$1.call(pair) !== '[object Object]') return false;\n\n keys = Object.keys(pair);\n\n if (keys.length !== 1) return false;\n\n result[index] = [ keys[0], pair[keys[0]] ];\n }\n\n return true;\n}\n\nfunction constructYamlPairs(data) {\n if (data === null) return [];\n\n var index, length, pair, keys, result,\n object = data;\n\n result = new Array(object.length);\n\n for (index = 0, length = object.length; index < length; index += 1) {\n pair = object[index];\n\n keys = Object.keys(pair);\n\n result[index] = [ keys[0], pair[keys[0]] ];\n }\n\n return result;\n}\n\nvar pairs = new type('tag:yaml.org,2002:pairs', {\n kind: 'sequence',\n resolve: resolveYamlPairs,\n construct: constructYamlPairs\n});\n\nvar _hasOwnProperty$2 = Object.prototype.hasOwnProperty;\n\nfunction resolveYamlSet(data) {\n if (data === null) return true;\n\n var key, object = data;\n\n for (key in object) {\n if (_hasOwnProperty$2.call(object, key)) {\n if (object[key] !== null) return false;\n }\n }\n\n return true;\n}\n\nfunction constructYamlSet(data) {\n return data !== null ? data : {};\n}\n\nvar set = new type('tag:yaml.org,2002:set', {\n kind: 'mapping',\n resolve: resolveYamlSet,\n construct: constructYamlSet\n});\n\nvar _default = core.extend({\n implicit: [\n timestamp,\n merge\n ],\n explicit: [\n binary,\n omap,\n pairs,\n set\n ]\n});\n\n/*eslint-disable max-len,no-use-before-define*/\n\n\n\n\n\n\n\nvar _hasOwnProperty$1 = Object.prototype.hasOwnProperty;\n\n\nvar CONTEXT_FLOW_IN = 1;\nvar CONTEXT_FLOW_OUT = 2;\nvar CONTEXT_BLOCK_IN = 3;\nvar CONTEXT_BLOCK_OUT = 4;\n\n\nvar CHOMPING_CLIP = 1;\nvar CHOMPING_STRIP = 2;\nvar CHOMPING_KEEP = 3;\n\n\nvar PATTERN_NON_PRINTABLE = /[\\x00-\\x08\\x0B\\x0C\\x0E-\\x1F\\x7F-\\x84\\x86-\\x9F\\uFFFE\\uFFFF]|[\\uD800-\\uDBFF](?![\\uDC00-\\uDFFF])|(?:[^\\uD800-\\uDBFF]|^)[\\uDC00-\\uDFFF]/;\nvar PATTERN_NON_ASCII_LINE_BREAKS = /[\\x85\\u2028\\u2029]/;\nvar PATTERN_FLOW_INDICATORS = /[,\\[\\]\\{\\}]/;\nvar PATTERN_TAG_HANDLE = /^(?:!|!!|![a-z\\-]+!)$/i;\nvar PATTERN_TAG_URI = /^(?:!|[^,\\[\\]\\{\\}])(?:%[0-9a-f]{2}|[0-9a-z\\-#;\\/\\?:@&=\\+\\$,_\\.!~\\*'\\(\\)\\[\\]])*$/i;\n\n\nfunction _class(obj) { return Object.prototype.toString.call(obj); }\n\nfunction is_EOL(c) {\n return (c === 0x0A/* LF */) || (c === 0x0D/* CR */);\n}\n\nfunction is_WHITE_SPACE(c) {\n return (c === 0x09/* Tab */) || (c === 0x20/* Space */);\n}\n\nfunction is_WS_OR_EOL(c) {\n return (c === 0x09/* Tab */) ||\n (c === 0x20/* Space */) ||\n (c === 0x0A/* LF */) ||\n (c === 0x0D/* CR */);\n}\n\nfunction is_FLOW_INDICATOR(c) {\n return c === 0x2C/* , */ ||\n c === 0x5B/* [ */ ||\n c === 0x5D/* ] */ ||\n c === 0x7B/* { */ ||\n c === 0x7D/* } */;\n}\n\nfunction fromHexCode(c) {\n var lc;\n\n if ((0x30/* 0 */ <= c) && (c <= 0x39/* 9 */)) {\n return c - 0x30;\n }\n\n /*eslint-disable no-bitwise*/\n lc = c | 0x20;\n\n if ((0x61/* a */ <= lc) && (lc <= 0x66/* f */)) {\n return lc - 0x61 + 10;\n }\n\n return -1;\n}\n\nfunction escapedHexLen(c) {\n if (c === 0x78/* x */) { return 2; }\n if (c === 0x75/* u */) { return 4; }\n if (c === 0x55/* U */) { return 8; }\n return 0;\n}\n\nfunction fromDecimalCode(c) {\n if ((0x30/* 0 */ <= c) && (c <= 0x39/* 9 */)) {\n return c - 0x30;\n }\n\n return -1;\n}\n\nfunction simpleEscapeSequence(c) {\n /* eslint-disable indent */\n return (c === 0x30/* 0 */) ? '\\x00' :\n (c === 0x61/* a */) ? '\\x07' :\n (c === 0x62/* b */) ? '\\x08' :\n (c === 0x74/* t */) ? '\\x09' :\n (c === 0x09/* Tab */) ? '\\x09' :\n (c === 0x6E/* n */) ? '\\x0A' :\n (c === 0x76/* v */) ? '\\x0B' :\n (c === 0x66/* f */) ? '\\x0C' :\n (c === 0x72/* r */) ? '\\x0D' :\n (c === 0x65/* e */) ? '\\x1B' :\n (c === 0x20/* Space */) ? ' ' :\n (c === 0x22/* \" */) ? '\\x22' :\n (c === 0x2F/* / */) ? '/' :\n (c === 0x5C/* \\ */) ? '\\x5C' :\n (c === 0x4E/* N */) ? '\\x85' :\n (c === 0x5F/* _ */) ? '\\xA0' :\n (c === 0x4C/* L */) ? '\\u2028' :\n (c === 0x50/* P */) ? '\\u2029' : '';\n}\n\nfunction charFromCodepoint(c) {\n if (c <= 0xFFFF) {\n return String.fromCharCode(c);\n }\n // Encode UTF-16 surrogate pair\n // https://en.wikipedia.org/wiki/UTF-16#Code_points_U.2B010000_to_U.2B10FFFF\n return String.fromCharCode(\n ((c - 0x010000) >> 10) + 0xD800,\n ((c - 0x010000) & 0x03FF) + 0xDC00\n );\n}\n\n// set a property of a literal object, while protecting against prototype pollution,\n// see https://github.com/nodeca/js-yaml/issues/164 for more details\nfunction setProperty(object, key, value) {\n // used for this specific key only because Object.defineProperty is slow\n if (key === '__proto__') {\n Object.defineProperty(object, key, {\n configurable: true,\n enumerable: true,\n writable: true,\n value: value\n });\n } else {\n object[key] = value;\n }\n}\n\nvar simpleEscapeCheck = new Array(256); // integer, for fast access\nvar simpleEscapeMap = new Array(256);\nfor (var i = 0; i < 256; i++) {\n simpleEscapeCheck[i] = simpleEscapeSequence(i) ? 1 : 0;\n simpleEscapeMap[i] = simpleEscapeSequence(i);\n}\n\n\nfunction State$1(input, options) {\n this.input = input;\n\n this.filename = options['filename'] || null;\n this.schema = options['schema'] || _default;\n this.onWarning = options['onWarning'] || null;\n // (Hidden) Remove? makes the loader to expect YAML 1.1 documents\n // if such documents have no explicit %YAML directive\n this.legacy = options['legacy'] || false;\n\n this.json = options['json'] || false;\n this.listener = options['listener'] || null;\n\n this.implicitTypes = this.schema.compiledImplicit;\n this.typeMap = this.schema.compiledTypeMap;\n\n this.length = input.length;\n this.position = 0;\n this.line = 0;\n this.lineStart = 0;\n this.lineIndent = 0;\n\n // position of first leading tab in the current line,\n // used to make sure there are no tabs in the indentation\n this.firstTabInLine = -1;\n\n this.documents = [];\n\n /*\n this.version;\n this.checkLineBreaks;\n this.tagMap;\n this.anchorMap;\n this.tag;\n this.anchor;\n this.kind;\n this.result;*/\n\n}\n\n\nfunction generateError(state, message) {\n var mark = {\n name: state.filename,\n buffer: state.input.slice(0, -1), // omit trailing \\0\n position: state.position,\n line: state.line,\n column: state.position - state.lineStart\n };\n\n mark.snippet = snippet(mark);\n\n return new exception(message, mark);\n}\n\nfunction throwError(state, message) {\n throw generateError(state, message);\n}\n\nfunction throwWarning(state, message) {\n if (state.onWarning) {\n state.onWarning.call(null, generateError(state, message));\n }\n}\n\n\nvar directiveHandlers = {\n\n YAML: function handleYamlDirective(state, name, args) {\n\n var match, major, minor;\n\n if (state.version !== null) {\n throwError(state, 'duplication of %YAML directive');\n }\n\n if (args.length !== 1) {\n throwError(state, 'YAML directive accepts exactly one argument');\n }\n\n match = /^([0-9]+)\\.([0-9]+)$/.exec(args[0]);\n\n if (match === null) {\n throwError(state, 'ill-formed argument of the YAML directive');\n }\n\n major = parseInt(match[1], 10);\n minor = parseInt(match[2], 10);\n\n if (major !== 1) {\n throwError(state, 'unacceptable YAML version of the document');\n }\n\n state.version = args[0];\n state.checkLineBreaks = (minor < 2);\n\n if (minor !== 1 && minor !== 2) {\n throwWarning(state, 'unsupported YAML version of the document');\n }\n },\n\n TAG: function handleTagDirective(state, name, args) {\n\n var handle, prefix;\n\n if (args.length !== 2) {\n throwError(state, 'TAG directive accepts exactly two arguments');\n }\n\n handle = args[0];\n prefix = args[1];\n\n if (!PATTERN_TAG_HANDLE.test(handle)) {\n throwError(state, 'ill-formed tag handle (first argument) of the TAG directive');\n }\n\n if (_hasOwnProperty$1.call(state.tagMap, handle)) {\n throwError(state, 'there is a previously declared suffix for \"' + handle + '\" tag handle');\n }\n\n if (!PATTERN_TAG_URI.test(prefix)) {\n throwError(state, 'ill-formed tag prefix (second argument) of the TAG directive');\n }\n\n try {\n prefix = decodeURIComponent(prefix);\n } catch (err) {\n throwError(state, 'tag prefix is malformed: ' + prefix);\n }\n\n state.tagMap[handle] = prefix;\n }\n};\n\n\nfunction captureSegment(state, start, end, checkJson) {\n var _position, _length, _character, _result;\n\n if (start < end) {\n _result = state.input.slice(start, end);\n\n if (checkJson) {\n for (_position = 0, _length = _result.length; _position < _length; _position += 1) {\n _character = _result.charCodeAt(_position);\n if (!(_character === 0x09 ||\n (0x20 <= _character && _character <= 0x10FFFF))) {\n throwError(state, 'expected valid JSON character');\n }\n }\n } else if (PATTERN_NON_PRINTABLE.test(_result)) {\n throwError(state, 'the stream contains non-printable characters');\n }\n\n state.result += _result;\n }\n}\n\nfunction mergeMappings(state, destination, source, overridableKeys) {\n var sourceKeys, key, index, quantity;\n\n if (!common.isObject(source)) {\n throwError(state, 'cannot merge mappings; the provided source object is unacceptable');\n }\n\n sourceKeys = Object.keys(source);\n\n for (index = 0, quantity = sourceKeys.length; index < quantity; index += 1) {\n key = sourceKeys[index];\n\n if (!_hasOwnProperty$1.call(destination, key)) {\n setProperty(destination, key, source[key]);\n overridableKeys[key] = true;\n }\n }\n}\n\nfunction storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, valueNode,\n startLine, startLineStart, startPos) {\n\n var index, quantity;\n\n // The output is a plain object here, so keys can only be strings.\n // We need to convert keyNode to a string, but doing so can hang the process\n // (deeply nested arrays that explode exponentially using aliases).\n if (Array.isArray(keyNode)) {\n keyNode = Array.prototype.slice.call(keyNode);\n\n for (index = 0, quantity = keyNode.length; index < quantity; index += 1) {\n if (Array.isArray(keyNode[index])) {\n throwError(state, 'nested arrays are not supported inside keys');\n }\n\n if (typeof keyNode === 'object' && _class(keyNode[index]) === '[object Object]') {\n keyNode[index] = '[object Object]';\n }\n }\n }\n\n // Avoid code execution in load() via toString property\n // (still use its own toString for arrays, timestamps,\n // and whatever user schema extensions happen to have @@toStringTag)\n if (typeof keyNode === 'object' && _class(keyNode) === '[object Object]') {\n keyNode = '[object Object]';\n }\n\n\n keyNode = String(keyNode);\n\n if (_result === null) {\n _result = {};\n }\n\n if (keyTag === 'tag:yaml.org,2002:merge') {\n if (Array.isArray(valueNode)) {\n for (index = 0, quantity = valueNode.length; index < quantity; index += 1) {\n mergeMappings(state, _result, valueNode[index], overridableKeys);\n }\n } else {\n mergeMappings(state, _result, valueNode, overridableKeys);\n }\n } else {\n if (!state.json &&\n !_hasOwnProperty$1.call(overridableKeys, keyNode) &&\n _hasOwnProperty$1.call(_result, keyNode)) {\n state.line = startLine || state.line;\n state.lineStart = startLineStart || state.lineStart;\n state.position = startPos || state.position;\n throwError(state, 'duplicated mapping key');\n }\n\n setProperty(_result, keyNode, valueNode);\n delete overridableKeys[keyNode];\n }\n\n return _result;\n}\n\nfunction readLineBreak(state) {\n var ch;\n\n ch = state.input.charCodeAt(state.position);\n\n if (ch === 0x0A/* LF */) {\n state.position++;\n } else if (ch === 0x0D/* CR */) {\n state.position++;\n if (state.input.charCodeAt(state.position) === 0x0A/* LF */) {\n state.position++;\n }\n } else {\n throwError(state, 'a line break is expected');\n }\n\n state.line += 1;\n state.lineStart = state.position;\n state.firstTabInLine = -1;\n}\n\nfunction skipSeparationSpace(state, allowComments, checkIndent) {\n var lineBreaks = 0,\n ch = state.input.charCodeAt(state.position);\n\n while (ch !== 0) {\n while (is_WHITE_SPACE(ch)) {\n if (ch === 0x09/* Tab */ && state.firstTabInLine === -1) {\n state.firstTabInLine = state.position;\n }\n ch = state.input.charCodeAt(++state.position);\n }\n\n if (allowComments && ch === 0x23/* # */) {\n do {\n ch = state.input.charCodeAt(++state.position);\n } while (ch !== 0x0A/* LF */ && ch !== 0x0D/* CR */ && ch !== 0);\n }\n\n if (is_EOL(ch)) {\n readLineBreak(state);\n\n ch = state.input.charCodeAt(state.position);\n lineBreaks++;\n state.lineIndent = 0;\n\n while (ch === 0x20/* Space */) {\n state.lineIndent++;\n ch = state.input.charCodeAt(++state.position);\n }\n } else {\n break;\n }\n }\n\n if (checkIndent !== -1 && lineBreaks !== 0 && state.lineIndent < checkIndent) {\n throwWarning(state, 'deficient indentation');\n }\n\n return lineBreaks;\n}\n\nfunction testDocumentSeparator(state) {\n var _position = state.position,\n ch;\n\n ch = state.input.charCodeAt(_position);\n\n // Condition state.position === state.lineStart is tested\n // in parent on each call, for efficiency. No needs to test here again.\n if ((ch === 0x2D/* - */ || ch === 0x2E/* . */) &&\n ch === state.input.charCodeAt(_position + 1) &&\n ch === state.input.charCodeAt(_position + 2)) {\n\n _position += 3;\n\n ch = state.input.charCodeAt(_position);\n\n if (ch === 0 || is_WS_OR_EOL(ch)) {\n return true;\n }\n }\n\n return false;\n}\n\nfunction writeFoldedLines(state, count) {\n if (count === 1) {\n state.result += ' ';\n } else if (count > 1) {\n state.result += common.repeat('\\n', count - 1);\n }\n}\n\n\nfunction readPlainScalar(state, nodeIndent, withinFlowCollection) {\n var preceding,\n following,\n captureStart,\n captureEnd,\n hasPendingContent,\n _line,\n _lineStart,\n _lineIndent,\n _kind = state.kind,\n _result = state.result,\n ch;\n\n ch = state.input.charCodeAt(state.position);\n\n if (is_WS_OR_EOL(ch) ||\n is_FLOW_INDICATOR(ch) ||\n ch === 0x23/* # */ ||\n ch === 0x26/* & */ ||\n ch === 0x2A/* * */ ||\n ch === 0x21/* ! */ ||\n ch === 0x7C/* | */ ||\n ch === 0x3E/* > */ ||\n ch === 0x27/* ' */ ||\n ch === 0x22/* \" */ ||\n ch === 0x25/* % */ ||\n ch === 0x40/* @ */ ||\n ch === 0x60/* ` */) {\n return false;\n }\n\n if (ch === 0x3F/* ? */ || ch === 0x2D/* - */) {\n following = state.input.charCodeAt(state.position + 1);\n\n if (is_WS_OR_EOL(following) ||\n withinFlowCollection && is_FLOW_INDICATOR(following)) {\n return false;\n }\n }\n\n state.kind = 'scalar';\n state.result = '';\n captureStart = captureEnd = state.position;\n hasPendingContent = false;\n\n while (ch !== 0) {\n if (ch === 0x3A/* : */) {\n following = state.input.charCodeAt(state.position + 1);\n\n if (is_WS_OR_EOL(following) ||\n withinFlowCollection && is_FLOW_INDICATOR(following)) {\n break;\n }\n\n } else if (ch === 0x23/* # */) {\n preceding = state.input.charCodeAt(state.position - 1);\n\n if (is_WS_OR_EOL(preceding)) {\n break;\n }\n\n } else if ((state.position === state.lineStart && testDocumentSeparator(state)) ||\n withinFlowCollection && is_FLOW_INDICATOR(ch)) {\n break;\n\n } else if (is_EOL(ch)) {\n _line = state.line;\n _lineStart = state.lineStart;\n _lineIndent = state.lineIndent;\n skipSeparationSpace(state, false, -1);\n\n if (state.lineIndent >= nodeIndent) {\n hasPendingContent = true;\n ch = state.input.charCodeAt(state.position);\n continue;\n } else {\n state.position = captureEnd;\n state.line = _line;\n state.lineStart = _lineStart;\n state.lineIndent = _lineIndent;\n break;\n }\n }\n\n if (hasPendingContent) {\n captureSegment(state, captureStart, captureEnd, false);\n writeFoldedLines(state, state.line - _line);\n captureStart = captureEnd = state.position;\n hasPendingContent = false;\n }\n\n if (!is_WHITE_SPACE(ch)) {\n captureEnd = state.position + 1;\n }\n\n ch = state.input.charCodeAt(++state.position);\n }\n\n captureSegment(state, captureStart, captureEnd, false);\n\n if (state.result) {\n return true;\n }\n\n state.kind = _kind;\n state.result = _result;\n return false;\n}\n\nfunction readSingleQuotedScalar(state, nodeIndent) {\n var ch,\n captureStart, captureEnd;\n\n ch = state.input.charCodeAt(state.position);\n\n if (ch !== 0x27/* ' */) {\n return false;\n }\n\n state.kind = 'scalar';\n state.result = '';\n state.position++;\n captureStart = captureEnd = state.position;\n\n while ((ch = state.input.charCodeAt(state.position)) !== 0) {\n if (ch === 0x27/* ' */) {\n captureSegment(state, captureStart, state.position, true);\n ch = state.input.charCodeAt(++state.position);\n\n if (ch === 0x27/* ' */) {\n captureStart = state.position;\n state.position++;\n captureEnd = state.position;\n } else {\n return true;\n }\n\n } else if (is_EOL(ch)) {\n captureSegment(state, captureStart, captureEnd, true);\n writeFoldedLines(state, skipSeparationSpace(state, false, nodeIndent));\n captureStart = captureEnd = state.position;\n\n } else if (state.position === state.lineStart && testDocumentSeparator(state)) {\n throwError(state, 'unexpected end of the document within a single quoted scalar');\n\n } else {\n state.position++;\n captureEnd = state.position;\n }\n }\n\n throwError(state, 'unexpected end of the stream within a single quoted scalar');\n}\n\nfunction readDoubleQuotedScalar(state, nodeIndent) {\n var captureStart,\n captureEnd,\n hexLength,\n hexResult,\n tmp,\n ch;\n\n ch = state.input.charCodeAt(state.position);\n\n if (ch !== 0x22/* \" */) {\n return false;\n }\n\n state.kind = 'scalar';\n state.result = '';\n state.position++;\n captureStart = captureEnd = state.position;\n\n while ((ch = state.input.charCodeAt(state.position)) !== 0) {\n if (ch === 0x22/* \" */) {\n captureSegment(state, captureStart, state.position, true);\n state.position++;\n return true;\n\n } else if (ch === 0x5C/* \\ */) {\n captureSegment(state, captureStart, state.position, true);\n ch = state.input.charCodeAt(++state.position);\n\n if (is_EOL(ch)) {\n skipSeparationSpace(state, false, nodeIndent);\n\n // TODO: rework to inline fn with no type cast?\n } else if (ch < 256 && simpleEscapeCheck[ch]) {\n state.result += simpleEscapeMap[ch];\n state.position++;\n\n } else if ((tmp = escapedHexLen(ch)) > 0) {\n hexLength = tmp;\n hexResult = 0;\n\n for (; hexLength > 0; hexLength--) {\n ch = state.input.charCodeAt(++state.position);\n\n if ((tmp = fromHexCode(ch)) >= 0) {\n hexResult = (hexResult << 4) + tmp;\n\n } else {\n throwError(state, 'expected hexadecimal character');\n }\n }\n\n state.result += charFromCodepoint(hexResult);\n\n state.position++;\n\n } else {\n throwError(state, 'unknown escape sequence');\n }\n\n captureStart = captureEnd = state.position;\n\n } else if (is_EOL(ch)) {\n captureSegment(state, captureStart, captureEnd, true);\n writeFoldedLines(state, skipSeparationSpace(state, false, nodeIndent));\n captureStart = captureEnd = state.position;\n\n } else if (state.position === state.lineStart && testDocumentSeparator(state)) {\n throwError(state, 'unexpected end of the document within a double quoted scalar');\n\n } else {\n state.position++;\n captureEnd = state.position;\n }\n }\n\n throwError(state, 'unexpected end of the stream within a double quoted scalar');\n}\n\nfunction readFlowCollection(state, nodeIndent) {\n var readNext = true,\n _line,\n _lineStart,\n _pos,\n _tag = state.tag,\n _result,\n _anchor = state.anchor,\n following,\n terminator,\n isPair,\n isExplicitPair,\n isMapping,\n overridableKeys = Object.create(null),\n keyNode,\n keyTag,\n valueNode,\n ch;\n\n ch = state.input.charCodeAt(state.position);\n\n if (ch === 0x5B/* [ */) {\n terminator = 0x5D;/* ] */\n isMapping = false;\n _result = [];\n } else if (ch === 0x7B/* { */) {\n terminator = 0x7D;/* } */\n isMapping = true;\n _result = {};\n } else {\n return false;\n }\n\n if (state.anchor !== null) {\n state.anchorMap[state.anchor] = _result;\n }\n\n ch = state.input.charCodeAt(++state.position);\n\n while (ch !== 0) {\n skipSeparationSpace(state, true, nodeIndent);\n\n ch = state.input.charCodeAt(state.position);\n\n if (ch === terminator) {\n state.position++;\n state.tag = _tag;\n state.anchor = _anchor;\n state.kind = isMapping ? 'mapping' : 'sequence';\n state.result = _result;\n return true;\n } else if (!readNext) {\n throwError(state, 'missed comma between flow collection entries');\n } else if (ch === 0x2C/* , */) {\n // \"flow collection entries can never be completely empty\", as per YAML 1.2, section 7.4\n throwError(state, \"expected the node content, but found ','\");\n }\n\n keyTag = keyNode = valueNode = null;\n isPair = isExplicitPair = false;\n\n if (ch === 0x3F/* ? */) {\n following = state.input.charCodeAt(state.position + 1);\n\n if (is_WS_OR_EOL(following)) {\n isPair = isExplicitPair = true;\n state.position++;\n skipSeparationSpace(state, true, nodeIndent);\n }\n }\n\n _line = state.line; // Save the current line.\n _lineStart = state.lineStart;\n _pos = state.position;\n composeNode(state, nodeIndent, CONTEXT_FLOW_IN, false, true);\n keyTag = state.tag;\n keyNode = state.result;\n skipSeparationSpace(state, true, nodeIndent);\n\n ch = state.input.charCodeAt(state.position);\n\n if ((isExplicitPair || state.line === _line) && ch === 0x3A/* : */) {\n isPair = true;\n ch = state.input.charCodeAt(++state.position);\n skipSeparationSpace(state, true, nodeIndent);\n composeNode(state, nodeIndent, CONTEXT_FLOW_IN, false, true);\n valueNode = state.result;\n }\n\n if (isMapping) {\n storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, valueNode, _line, _lineStart, _pos);\n } else if (isPair) {\n _result.push(storeMappingPair(state, null, overridableKeys, keyTag, keyNode, valueNode, _line, _lineStart, _pos));\n } else {\n _result.push(keyNode);\n }\n\n skipSeparationSpace(state, true, nodeIndent);\n\n ch = state.input.charCodeAt(state.position);\n\n if (ch === 0x2C/* , */) {\n readNext = true;\n ch = state.input.charCodeAt(++state.position);\n } else {\n readNext = false;\n }\n }\n\n throwError(state, 'unexpected end of the stream within a flow collection');\n}\n\nfunction readBlockScalar(state, nodeIndent) {\n var captureStart,\n folding,\n chomping = CHOMPING_CLIP,\n didReadContent = false,\n detectedIndent = false,\n textIndent = nodeIndent,\n emptyLines = 0,\n atMoreIndented = false,\n tmp,\n ch;\n\n ch = state.input.charCodeAt(state.position);\n\n if (ch === 0x7C/* | */) {\n folding = false;\n } else if (ch === 0x3E/* > */) {\n folding = true;\n } else {\n return false;\n }\n\n state.kind = 'scalar';\n state.result = '';\n\n while (ch !== 0) {\n ch = state.input.charCodeAt(++state.position);\n\n if (ch === 0x2B/* + */ || ch === 0x2D/* - */) {\n if (CHOMPING_CLIP === chomping) {\n chomping = (ch === 0x2B/* + */) ? CHOMPING_KEEP : CHOMPING_STRIP;\n } else {\n throwError(state, 'repeat of a chomping mode identifier');\n }\n\n } else if ((tmp = fromDecimalCode(ch)) >= 0) {\n if (tmp === 0) {\n throwError(state, 'bad explicit indentation width of a block scalar; it cannot be less than one');\n } else if (!detectedIndent) {\n textIndent = nodeIndent + tmp - 1;\n detectedIndent = true;\n } else {\n throwError(state, 'repeat of an indentation width identifier');\n }\n\n } else {\n break;\n }\n }\n\n if (is_WHITE_SPACE(ch)) {\n do { ch = state.input.charCodeAt(++state.position); }\n while (is_WHITE_SPACE(ch));\n\n if (ch === 0x23/* # */) {\n do { ch = state.input.charCodeAt(++state.position); }\n while (!is_EOL(ch) && (ch !== 0));\n }\n }\n\n while (ch !== 0) {\n readLineBreak(state);\n state.lineIndent = 0;\n\n ch = state.input.charCodeAt(state.position);\n\n while ((!detectedIndent || state.lineIndent < textIndent) &&\n (ch === 0x20/* Space */)) {\n state.lineIndent++;\n ch = state.input.charCodeAt(++state.position);\n }\n\n if (!detectedIndent && state.lineIndent > textIndent) {\n textIndent = state.lineIndent;\n }\n\n if (is_EOL(ch)) {\n emptyLines++;\n continue;\n }\n\n // End of the scalar.\n if (state.lineIndent < textIndent) {\n\n // Perform the chomping.\n if (chomping === CHOMPING_KEEP) {\n state.result += common.repeat('\\n', didReadContent ? 1 + emptyLines : emptyLines);\n } else if (chomping === CHOMPING_CLIP) {\n if (didReadContent) { // i.e. only if the scalar is not empty.\n state.result += '\\n';\n }\n }\n\n // Break this `while` cycle and go to the funciton's epilogue.\n break;\n }\n\n // Folded style: use fancy rules to handle line breaks.\n if (folding) {\n\n // Lines starting with white space characters (more-indented lines) are not folded.\n if (is_WHITE_SPACE(ch)) {\n atMoreIndented = true;\n // except for the first content line (cf. Example 8.1)\n state.result += common.repeat('\\n', didReadContent ? 1 + emptyLines : emptyLines);\n\n // End of more-indented block.\n } else if (atMoreIndented) {\n atMoreIndented = false;\n state.result += common.repeat('\\n', emptyLines + 1);\n\n // Just one line break - perceive as the same line.\n } else if (emptyLines === 0) {\n if (didReadContent) { // i.e. only if we have already read some scalar content.\n state.result += ' ';\n }\n\n // Several line breaks - perceive as different lines.\n } else {\n state.result += common.repeat('\\n', emptyLines);\n }\n\n // Literal style: just add exact number of line breaks between content lines.\n } else {\n // Keep all line breaks except the header line break.\n state.result += common.repeat('\\n', didReadContent ? 1 + emptyLines : emptyLines);\n }\n\n didReadContent = true;\n detectedIndent = true;\n emptyLines = 0;\n captureStart = state.position;\n\n while (!is_EOL(ch) && (ch !== 0)) {\n ch = state.input.charCodeAt(++state.position);\n }\n\n captureSegment(state, captureStart, state.position, false);\n }\n\n return true;\n}\n\nfunction readBlockSequence(state, nodeIndent) {\n var _line,\n _tag = state.tag,\n _anchor = state.anchor,\n _result = [],\n following,\n detected = false,\n ch;\n\n // there is a leading tab before this token, so it can't be a block sequence/mapping;\n // it can still be flow sequence/mapping or a scalar\n if (state.firstTabInLine !== -1) return false;\n\n if (state.anchor !== null) {\n state.anchorMap[state.anchor] = _result;\n }\n\n ch = state.input.charCodeAt(state.position);\n\n while (ch !== 0) {\n if (state.firstTabInLine !== -1) {\n state.position = state.firstTabInLine;\n throwError(state, 'tab characters must not be used in indentation');\n }\n\n if (ch !== 0x2D/* - */) {\n break;\n }\n\n following = state.input.charCodeAt(state.position + 1);\n\n if (!is_WS_OR_EOL(following)) {\n break;\n }\n\n detected = true;\n state.position++;\n\n if (skipSeparationSpace(state, true, -1)) {\n if (state.lineIndent <= nodeIndent) {\n _result.push(null);\n ch = state.input.charCodeAt(state.position);\n continue;\n }\n }\n\n _line = state.line;\n composeNode(state, nodeIndent, CONTEXT_BLOCK_IN, false, true);\n _result.push(state.result);\n skipSeparationSpace(state, true, -1);\n\n ch = state.input.charCodeAt(state.position);\n\n if ((state.line === _line || state.lineIndent > nodeIndent) && (ch !== 0)) {\n throwError(state, 'bad indentation of a sequence entry');\n } else if (state.lineIndent < nodeIndent) {\n break;\n }\n }\n\n if (detected) {\n state.tag = _tag;\n state.anchor = _anchor;\n state.kind = 'sequence';\n state.result = _result;\n return true;\n }\n return false;\n}\n\nfunction readBlockMapping(state, nodeIndent, flowIndent) {\n var following,\n allowCompact,\n _line,\n _keyLine,\n _keyLineStart,\n _keyPos,\n _tag = state.tag,\n _anchor = state.anchor,\n _result = {},\n overridableKeys = Object.create(null),\n keyTag = null,\n keyNode = null,\n valueNode = null,\n atExplicitKey = false,\n detected = false,\n ch;\n\n // there is a leading tab before this token, so it can't be a block sequence/mapping;\n // it can still be flow sequence/mapping or a scalar\n if (state.firstTabInLine !== -1) return false;\n\n if (state.anchor !== null) {\n state.anchorMap[state.anchor] = _result;\n }\n\n ch = state.input.charCodeAt(state.position);\n\n while (ch !== 0) {\n if (!atExplicitKey && state.firstTabInLine !== -1) {\n state.position = state.firstTabInLine;\n throwError(state, 'tab characters must not be used in indentation');\n }\n\n following = state.input.charCodeAt(state.position + 1);\n _line = state.line; // Save the current line.\n\n //\n // Explicit notation case. There are two separate blocks:\n // first for the key (denoted by \"?\") and second for the value (denoted by \":\")\n //\n if ((ch === 0x3F/* ? */ || ch === 0x3A/* : */) && is_WS_OR_EOL(following)) {\n\n if (ch === 0x3F/* ? */) {\n if (atExplicitKey) {\n storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, null, _keyLine, _keyLineStart, _keyPos);\n keyTag = keyNode = valueNode = null;\n }\n\n detected = true;\n atExplicitKey = true;\n allowCompact = true;\n\n } else if (atExplicitKey) {\n // i.e. 0x3A/* : */ === character after the explicit key.\n atExplicitKey = false;\n allowCompact = true;\n\n } else {\n throwError(state, 'incomplete explicit mapping pair; a key node is missed; or followed by a non-tabulated empty line');\n }\n\n state.position += 1;\n ch = following;\n\n //\n // Implicit notation case. Flow-style node as the key first, then \":\", and the value.\n //\n } else {\n _keyLine = state.line;\n _keyLineStart = state.lineStart;\n _keyPos = state.position;\n\n if (!composeNode(state, flowIndent, CONTEXT_FLOW_OUT, false, true)) {\n // Neither implicit nor explicit notation.\n // Reading is done. Go to the epilogue.\n break;\n }\n\n if (state.line === _line) {\n ch = state.input.charCodeAt(state.position);\n\n while (is_WHITE_SPACE(ch)) {\n ch = state.input.charCodeAt(++state.position);\n }\n\n if (ch === 0x3A/* : */) {\n ch = state.input.charCodeAt(++state.position);\n\n if (!is_WS_OR_EOL(ch)) {\n throwError(state, 'a whitespace character is expected after the key-value separator within a block mapping');\n }\n\n if (atExplicitKey) {\n storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, null, _keyLine, _keyLineStart, _keyPos);\n keyTag = keyNode = valueNode = null;\n }\n\n detected = true;\n atExplicitKey = false;\n allowCompact = false;\n keyTag = state.tag;\n keyNode = state.result;\n\n } else if (detected) {\n throwError(state, 'can not read an implicit mapping pair; a colon is missed');\n\n } else {\n state.tag = _tag;\n state.anchor = _anchor;\n return true; // Keep the result of `composeNode`.\n }\n\n } else if (detected) {\n throwError(state, 'can not read a block mapping entry; a multiline key may not be an implicit key');\n\n } else {\n state.tag = _tag;\n state.anchor = _anchor;\n return true; // Keep the result of `composeNode`.\n }\n }\n\n //\n // Common reading code for both explicit and implicit notations.\n //\n if (state.line === _line || state.lineIndent > nodeIndent) {\n if (atExplicitKey) {\n _keyLine = state.line;\n _keyLineStart = state.lineStart;\n _keyPos = state.position;\n }\n\n if (composeNode(state, nodeIndent, CONTEXT_BLOCK_OUT, true, allowCompact)) {\n if (atExplicitKey) {\n keyNode = state.result;\n } else {\n valueNode = state.result;\n }\n }\n\n if (!atExplicitKey) {\n storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, valueNode, _keyLine, _keyLineStart, _keyPos);\n keyTag = keyNode = valueNode = null;\n }\n\n skipSeparationSpace(state, true, -1);\n ch = state.input.charCodeAt(state.position);\n }\n\n if ((state.line === _line || state.lineIndent > nodeIndent) && (ch !== 0)) {\n throwError(state, 'bad indentation of a mapping entry');\n } else if (state.lineIndent < nodeIndent) {\n break;\n }\n }\n\n //\n // Epilogue.\n //\n\n // Special case: last mapping's node contains only the key in explicit notation.\n if (atExplicitKey) {\n storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, null, _keyLine, _keyLineStart, _keyPos);\n }\n\n // Expose the resulting mapping.\n if (detected) {\n state.tag = _tag;\n state.anchor = _anchor;\n state.kind = 'mapping';\n state.result = _result;\n }\n\n return detected;\n}\n\nfunction readTagProperty(state) {\n var _position,\n isVerbatim = false,\n isNamed = false,\n tagHandle,\n tagName,\n ch;\n\n ch = state.input.charCodeAt(state.position);\n\n if (ch !== 0x21/* ! */) return false;\n\n if (state.tag !== null) {\n throwError(state, 'duplication of a tag property');\n }\n\n ch = state.input.charCodeAt(++state.position);\n\n if (ch === 0x3C/* < */) {\n isVerbatim = true;\n ch = state.input.charCodeAt(++state.position);\n\n } else if (ch === 0x21/* ! */) {\n isNamed = true;\n tagHandle = '!!';\n ch = state.input.charCodeAt(++state.position);\n\n } else {\n tagHandle = '!';\n }\n\n _position = state.position;\n\n if (isVerbatim) {\n do { ch = state.input.charCodeAt(++state.position); }\n while (ch !== 0 && ch !== 0x3E/* > */);\n\n if (state.position < state.length) {\n tagName = state.input.slice(_position, state.position);\n ch = state.input.charCodeAt(++state.position);\n } else {\n throwError(state, 'unexpected end of the stream within a verbatim tag');\n }\n } else {\n while (ch !== 0 && !is_WS_OR_EOL(ch)) {\n\n if (ch === 0x21/* ! */) {\n if (!isNamed) {\n tagHandle = state.input.slice(_position - 1, state.position + 1);\n\n if (!PATTERN_TAG_HANDLE.test(tagHandle)) {\n throwError(state, 'named tag handle cannot contain such characters');\n }\n\n isNamed = true;\n _position = state.position + 1;\n } else {\n throwError(state, 'tag suffix cannot contain exclamation marks');\n }\n }\n\n ch = state.input.charCodeAt(++state.position);\n }\n\n tagName = state.input.slice(_position, state.position);\n\n if (PATTERN_FLOW_INDICATORS.test(tagName)) {\n throwError(state, 'tag suffix cannot contain flow indicator characters');\n }\n }\n\n if (tagName && !PATTERN_TAG_URI.test(tagName)) {\n throwError(state, 'tag name cannot contain such characters: ' + tagName);\n }\n\n try {\n tagName = decodeURIComponent(tagName);\n } catch (err) {\n throwError(state, 'tag name is malformed: ' + tagName);\n }\n\n if (isVerbatim) {\n state.tag = tagName;\n\n } else if (_hasOwnProperty$1.call(state.tagMap, tagHandle)) {\n state.tag = state.tagMap[tagHandle] + tagName;\n\n } else if (tagHandle === '!') {\n state.tag = '!' + tagName;\n\n } else if (tagHandle === '!!') {\n state.tag = 'tag:yaml.org,2002:' + tagName;\n\n } else {\n throwError(state, 'undeclared tag handle \"' + tagHandle + '\"');\n }\n\n return true;\n}\n\nfunction readAnchorProperty(state) {\n var _position,\n ch;\n\n ch = state.input.charCodeAt(state.position);\n\n if (ch !== 0x26/* & */) return false;\n\n if (state.anchor !== null) {\n throwError(state, 'duplication of an anchor property');\n }\n\n ch = state.input.charCodeAt(++state.position);\n _position = state.position;\n\n while (ch !== 0 && !is_WS_OR_EOL(ch) && !is_FLOW_INDICATOR(ch)) {\n ch = state.input.charCodeAt(++state.position);\n }\n\n if (state.position === _position) {\n throwError(state, 'name of an anchor node must contain at least one character');\n }\n\n state.anchor = state.input.slice(_position, state.position);\n return true;\n}\n\nfunction readAlias(state) {\n var _position, alias,\n ch;\n\n ch = state.input.charCodeAt(state.position);\n\n if (ch !== 0x2A/* * */) return false;\n\n ch = state.input.charCodeAt(++state.position);\n _position = state.position;\n\n while (ch !== 0 && !is_WS_OR_EOL(ch) && !is_FLOW_INDICATOR(ch)) {\n ch = state.input.charCodeAt(++state.position);\n }\n\n if (state.position === _position) {\n throwError(state, 'name of an alias node must contain at least one character');\n }\n\n alias = state.input.slice(_position, state.position);\n\n if (!_hasOwnProperty$1.call(state.anchorMap, alias)) {\n throwError(state, 'unidentified alias \"' + alias + '\"');\n }\n\n state.result = state.anchorMap[alias];\n skipSeparationSpace(state, true, -1);\n return true;\n}\n\nfunction composeNode(state, parentIndent, nodeContext, allowToSeek, allowCompact) {\n var allowBlockStyles,\n allowBlockScalars,\n allowBlockCollections,\n indentStatus = 1, // 1: this>parent, 0: this=parent, -1: this parentIndent) {\n indentStatus = 1;\n } else if (state.lineIndent === parentIndent) {\n indentStatus = 0;\n } else if (state.lineIndent < parentIndent) {\n indentStatus = -1;\n }\n }\n }\n\n if (indentStatus === 1) {\n while (readTagProperty(state) || readAnchorProperty(state)) {\n if (skipSeparationSpace(state, true, -1)) {\n atNewLine = true;\n allowBlockCollections = allowBlockStyles;\n\n if (state.lineIndent > parentIndent) {\n indentStatus = 1;\n } else if (state.lineIndent === parentIndent) {\n indentStatus = 0;\n } else if (state.lineIndent < parentIndent) {\n indentStatus = -1;\n }\n } else {\n allowBlockCollections = false;\n }\n }\n }\n\n if (allowBlockCollections) {\n allowBlockCollections = atNewLine || allowCompact;\n }\n\n if (indentStatus === 1 || CONTEXT_BLOCK_OUT === nodeContext) {\n if (CONTEXT_FLOW_IN === nodeContext || CONTEXT_FLOW_OUT === nodeContext) {\n flowIndent = parentIndent;\n } else {\n flowIndent = parentIndent + 1;\n }\n\n blockIndent = state.position - state.lineStart;\n\n if (indentStatus === 1) {\n if (allowBlockCollections &&\n (readBlockSequence(state, blockIndent) ||\n readBlockMapping(state, blockIndent, flowIndent)) ||\n readFlowCollection(state, flowIndent)) {\n hasContent = true;\n } else {\n if ((allowBlockScalars && readBlockScalar(state, flowIndent)) ||\n readSingleQuotedScalar(state, flowIndent) ||\n readDoubleQuotedScalar(state, flowIndent)) {\n hasContent = true;\n\n } else if (readAlias(state)) {\n hasContent = true;\n\n if (state.tag !== null || state.anchor !== null) {\n throwError(state, 'alias node should not have any properties');\n }\n\n } else if (readPlainScalar(state, flowIndent, CONTEXT_FLOW_IN === nodeContext)) {\n hasContent = true;\n\n if (state.tag === null) {\n state.tag = '?';\n }\n }\n\n if (state.anchor !== null) {\n state.anchorMap[state.anchor] = state.result;\n }\n }\n } else if (indentStatus === 0) {\n // Special case: block sequences are allowed to have same indentation level as the parent.\n // http://www.yaml.org/spec/1.2/spec.html#id2799784\n hasContent = allowBlockCollections && readBlockSequence(state, blockIndent);\n }\n }\n\n if (state.tag === null) {\n if (state.anchor !== null) {\n state.anchorMap[state.anchor] = state.result;\n }\n\n } else if (state.tag === '?') {\n // Implicit resolving is not allowed for non-scalar types, and '?'\n // non-specific tag is only automatically assigned to plain scalars.\n //\n // We only need to check kind conformity in case user explicitly assigns '?'\n // tag, for example like this: \"! [0]\"\n //\n if (state.result !== null && state.kind !== 'scalar') {\n throwError(state, 'unacceptable node kind for ! tag; it should be \"scalar\", not \"' + state.kind + '\"');\n }\n\n for (typeIndex = 0, typeQuantity = state.implicitTypes.length; typeIndex < typeQuantity; typeIndex += 1) {\n type = state.implicitTypes[typeIndex];\n\n if (type.resolve(state.result)) { // `state.result` updated in resolver if matched\n state.result = type.construct(state.result);\n state.tag = type.tag;\n if (state.anchor !== null) {\n state.anchorMap[state.anchor] = state.result;\n }\n break;\n }\n }\n } else if (state.tag !== '!') {\n if (_hasOwnProperty$1.call(state.typeMap[state.kind || 'fallback'], state.tag)) {\n type = state.typeMap[state.kind || 'fallback'][state.tag];\n } else {\n // looking for multi type\n type = null;\n typeList = state.typeMap.multi[state.kind || 'fallback'];\n\n for (typeIndex = 0, typeQuantity = typeList.length; typeIndex < typeQuantity; typeIndex += 1) {\n if (state.tag.slice(0, typeList[typeIndex].tag.length) === typeList[typeIndex].tag) {\n type = typeList[typeIndex];\n break;\n }\n }\n }\n\n if (!type) {\n throwError(state, 'unknown tag !<' + state.tag + '>');\n }\n\n if (state.result !== null && type.kind !== state.kind) {\n throwError(state, 'unacceptable node kind for !<' + state.tag + '> tag; it should be \"' + type.kind + '\", not \"' + state.kind + '\"');\n }\n\n if (!type.resolve(state.result, state.tag)) { // `state.result` updated in resolver if matched\n throwError(state, 'cannot resolve a node with !<' + state.tag + '> explicit tag');\n } else {\n state.result = type.construct(state.result, state.tag);\n if (state.anchor !== null) {\n state.anchorMap[state.anchor] = state.result;\n }\n }\n }\n\n if (state.listener !== null) {\n state.listener('close', state);\n }\n return state.tag !== null || state.anchor !== null || hasContent;\n}\n\nfunction readDocument(state) {\n var documentStart = state.position,\n _position,\n directiveName,\n directiveArgs,\n hasDirectives = false,\n ch;\n\n state.version = null;\n state.checkLineBreaks = state.legacy;\n state.tagMap = Object.create(null);\n state.anchorMap = Object.create(null);\n\n while ((ch = state.input.charCodeAt(state.position)) !== 0) {\n skipSeparationSpace(state, true, -1);\n\n ch = state.input.charCodeAt(state.position);\n\n if (state.lineIndent > 0 || ch !== 0x25/* % */) {\n break;\n }\n\n hasDirectives = true;\n ch = state.input.charCodeAt(++state.position);\n _position = state.position;\n\n while (ch !== 0 && !is_WS_OR_EOL(ch)) {\n ch = state.input.charCodeAt(++state.position);\n }\n\n directiveName = state.input.slice(_position, state.position);\n directiveArgs = [];\n\n if (directiveName.length < 1) {\n throwError(state, 'directive name must not be less than one character in length');\n }\n\n while (ch !== 0) {\n while (is_WHITE_SPACE(ch)) {\n ch = state.input.charCodeAt(++state.position);\n }\n\n if (ch === 0x23/* # */) {\n do { ch = state.input.charCodeAt(++state.position); }\n while (ch !== 0 && !is_EOL(ch));\n break;\n }\n\n if (is_EOL(ch)) break;\n\n _position = state.position;\n\n while (ch !== 0 && !is_WS_OR_EOL(ch)) {\n ch = state.input.charCodeAt(++state.position);\n }\n\n directiveArgs.push(state.input.slice(_position, state.position));\n }\n\n if (ch !== 0) readLineBreak(state);\n\n if (_hasOwnProperty$1.call(directiveHandlers, directiveName)) {\n directiveHandlers[directiveName](state, directiveName, directiveArgs);\n } else {\n throwWarning(state, 'unknown document directive \"' + directiveName + '\"');\n }\n }\n\n skipSeparationSpace(state, true, -1);\n\n if (state.lineIndent === 0 &&\n state.input.charCodeAt(state.position) === 0x2D/* - */ &&\n state.input.charCodeAt(state.position + 1) === 0x2D/* - */ &&\n state.input.charCodeAt(state.position + 2) === 0x2D/* - */) {\n state.position += 3;\n skipSeparationSpace(state, true, -1);\n\n } else if (hasDirectives) {\n throwError(state, 'directives end mark is expected');\n }\n\n composeNode(state, state.lineIndent - 1, CONTEXT_BLOCK_OUT, false, true);\n skipSeparationSpace(state, true, -1);\n\n if (state.checkLineBreaks &&\n PATTERN_NON_ASCII_LINE_BREAKS.test(state.input.slice(documentStart, state.position))) {\n throwWarning(state, 'non-ASCII line breaks are interpreted as content');\n }\n\n state.documents.push(state.result);\n\n if (state.position === state.lineStart && testDocumentSeparator(state)) {\n\n if (state.input.charCodeAt(state.position) === 0x2E/* . */) {\n state.position += 3;\n skipSeparationSpace(state, true, -1);\n }\n return;\n }\n\n if (state.position < (state.length - 1)) {\n throwError(state, 'end of the stream or a document separator is expected');\n } else {\n return;\n }\n}\n\n\nfunction loadDocuments(input, options) {\n input = String(input);\n options = options || {};\n\n if (input.length !== 0) {\n\n // Add tailing `\\n` if not exists\n if (input.charCodeAt(input.length - 1) !== 0x0A/* LF */ &&\n input.charCodeAt(input.length - 1) !== 0x0D/* CR */) {\n input += '\\n';\n }\n\n // Strip BOM\n if (input.charCodeAt(0) === 0xFEFF) {\n input = input.slice(1);\n }\n }\n\n var state = new State$1(input, options);\n\n var nullpos = input.indexOf('\\0');\n\n if (nullpos !== -1) {\n state.position = nullpos;\n throwError(state, 'null byte is not allowed in input');\n }\n\n // Use 0 as string terminator. That significantly simplifies bounds check.\n state.input += '\\0';\n\n while (state.input.charCodeAt(state.position) === 0x20/* Space */) {\n state.lineIndent += 1;\n state.position += 1;\n }\n\n while (state.position < (state.length - 1)) {\n readDocument(state);\n }\n\n return state.documents;\n}\n\n\nfunction loadAll$1(input, iterator, options) {\n if (iterator !== null && typeof iterator === 'object' && typeof options === 'undefined') {\n options = iterator;\n iterator = null;\n }\n\n var documents = loadDocuments(input, options);\n\n if (typeof iterator !== 'function') {\n return documents;\n }\n\n for (var index = 0, length = documents.length; index < length; index += 1) {\n iterator(documents[index]);\n }\n}\n\n\nfunction load$1(input, options) {\n var documents = loadDocuments(input, options);\n\n if (documents.length === 0) {\n /*eslint-disable no-undefined*/\n return undefined;\n } else if (documents.length === 1) {\n return documents[0];\n }\n throw new exception('expected a single document in the stream, but found more');\n}\n\n\nvar loadAll_1 = loadAll$1;\nvar load_1 = load$1;\n\nvar loader = {\n\tloadAll: loadAll_1,\n\tload: load_1\n};\n\n/*eslint-disable no-use-before-define*/\n\n\n\n\n\nvar _toString = Object.prototype.toString;\nvar _hasOwnProperty = Object.prototype.hasOwnProperty;\n\nvar CHAR_BOM = 0xFEFF;\nvar CHAR_TAB = 0x09; /* Tab */\nvar CHAR_LINE_FEED = 0x0A; /* LF */\nvar CHAR_CARRIAGE_RETURN = 0x0D; /* CR */\nvar CHAR_SPACE = 0x20; /* Space */\nvar CHAR_EXCLAMATION = 0x21; /* ! */\nvar CHAR_DOUBLE_QUOTE = 0x22; /* \" */\nvar CHAR_SHARP = 0x23; /* # */\nvar CHAR_PERCENT = 0x25; /* % */\nvar CHAR_AMPERSAND = 0x26; /* & */\nvar CHAR_SINGLE_QUOTE = 0x27; /* ' */\nvar CHAR_ASTERISK = 0x2A; /* * */\nvar CHAR_COMMA = 0x2C; /* , */\nvar CHAR_MINUS = 0x2D; /* - */\nvar CHAR_COLON = 0x3A; /* : */\nvar CHAR_EQUALS = 0x3D; /* = */\nvar CHAR_GREATER_THAN = 0x3E; /* > */\nvar CHAR_QUESTION = 0x3F; /* ? */\nvar CHAR_COMMERCIAL_AT = 0x40; /* @ */\nvar CHAR_LEFT_SQUARE_BRACKET = 0x5B; /* [ */\nvar CHAR_RIGHT_SQUARE_BRACKET = 0x5D; /* ] */\nvar CHAR_GRAVE_ACCENT = 0x60; /* ` */\nvar CHAR_LEFT_CURLY_BRACKET = 0x7B; /* { */\nvar CHAR_VERTICAL_LINE = 0x7C; /* | */\nvar CHAR_RIGHT_CURLY_BRACKET = 0x7D; /* } */\n\nvar ESCAPE_SEQUENCES = {};\n\nESCAPE_SEQUENCES[0x00] = '\\\\0';\nESCAPE_SEQUENCES[0x07] = '\\\\a';\nESCAPE_SEQUENCES[0x08] = '\\\\b';\nESCAPE_SEQUENCES[0x09] = '\\\\t';\nESCAPE_SEQUENCES[0x0A] = '\\\\n';\nESCAPE_SEQUENCES[0x0B] = '\\\\v';\nESCAPE_SEQUENCES[0x0C] = '\\\\f';\nESCAPE_SEQUENCES[0x0D] = '\\\\r';\nESCAPE_SEQUENCES[0x1B] = '\\\\e';\nESCAPE_SEQUENCES[0x22] = '\\\\\"';\nESCAPE_SEQUENCES[0x5C] = '\\\\\\\\';\nESCAPE_SEQUENCES[0x85] = '\\\\N';\nESCAPE_SEQUENCES[0xA0] = '\\\\_';\nESCAPE_SEQUENCES[0x2028] = '\\\\L';\nESCAPE_SEQUENCES[0x2029] = '\\\\P';\n\nvar DEPRECATED_BOOLEANS_SYNTAX = [\n 'y', 'Y', 'yes', 'Yes', 'YES', 'on', 'On', 'ON',\n 'n', 'N', 'no', 'No', 'NO', 'off', 'Off', 'OFF'\n];\n\nvar DEPRECATED_BASE60_SYNTAX = /^[-+]?[0-9_]+(?::[0-9_]+)+(?:\\.[0-9_]*)?$/;\n\nfunction compileStyleMap(schema, map) {\n var result, keys, index, length, tag, style, type;\n\n if (map === null) return {};\n\n result = {};\n keys = Object.keys(map);\n\n for (index = 0, length = keys.length; index < length; index += 1) {\n tag = keys[index];\n style = String(map[tag]);\n\n if (tag.slice(0, 2) === '!!') {\n tag = 'tag:yaml.org,2002:' + tag.slice(2);\n }\n type = schema.compiledTypeMap['fallback'][tag];\n\n if (type && _hasOwnProperty.call(type.styleAliases, style)) {\n style = type.styleAliases[style];\n }\n\n result[tag] = style;\n }\n\n return result;\n}\n\nfunction encodeHex(character) {\n var string, handle, length;\n\n string = character.toString(16).toUpperCase();\n\n if (character <= 0xFF) {\n handle = 'x';\n length = 2;\n } else if (character <= 0xFFFF) {\n handle = 'u';\n length = 4;\n } else if (character <= 0xFFFFFFFF) {\n handle = 'U';\n length = 8;\n } else {\n throw new exception('code point within a string may not be greater than 0xFFFFFFFF');\n }\n\n return '\\\\' + handle + common.repeat('0', length - string.length) + string;\n}\n\n\nvar QUOTING_TYPE_SINGLE = 1,\n QUOTING_TYPE_DOUBLE = 2;\n\nfunction State(options) {\n this.schema = options['schema'] || _default;\n this.indent = Math.max(1, (options['indent'] || 2));\n this.noArrayIndent = options['noArrayIndent'] || false;\n this.skipInvalid = options['skipInvalid'] || false;\n this.flowLevel = (common.isNothing(options['flowLevel']) ? -1 : options['flowLevel']);\n this.styleMap = compileStyleMap(this.schema, options['styles'] || null);\n this.sortKeys = options['sortKeys'] || false;\n this.lineWidth = options['lineWidth'] || 80;\n this.noRefs = options['noRefs'] || false;\n this.noCompatMode = options['noCompatMode'] || false;\n this.condenseFlow = options['condenseFlow'] || false;\n this.quotingType = options['quotingType'] === '\"' ? QUOTING_TYPE_DOUBLE : QUOTING_TYPE_SINGLE;\n this.forceQuotes = options['forceQuotes'] || false;\n this.replacer = typeof options['replacer'] === 'function' ? options['replacer'] : null;\n\n this.implicitTypes = this.schema.compiledImplicit;\n this.explicitTypes = this.schema.compiledExplicit;\n\n this.tag = null;\n this.result = '';\n\n this.duplicates = [];\n this.usedDuplicates = null;\n}\n\n// Indents every line in a string. Empty lines (\\n only) are not indented.\nfunction indentString(string, spaces) {\n var ind = common.repeat(' ', spaces),\n position = 0,\n next = -1,\n result = '',\n line,\n length = string.length;\n\n while (position < length) {\n next = string.indexOf('\\n', position);\n if (next === -1) {\n line = string.slice(position);\n position = length;\n } else {\n line = string.slice(position, next + 1);\n position = next + 1;\n }\n\n if (line.length && line !== '\\n') result += ind;\n\n result += line;\n }\n\n return result;\n}\n\nfunction generateNextLine(state, level) {\n return '\\n' + common.repeat(' ', state.indent * level);\n}\n\nfunction testImplicitResolving(state, str) {\n var index, length, type;\n\n for (index = 0, length = state.implicitTypes.length; index < length; index += 1) {\n type = state.implicitTypes[index];\n\n if (type.resolve(str)) {\n return true;\n }\n }\n\n return false;\n}\n\n// [33] s-white ::= s-space | s-tab\nfunction isWhitespace(c) {\n return c === CHAR_SPACE || c === CHAR_TAB;\n}\n\n// Returns true if the character can be printed without escaping.\n// From YAML 1.2: \"any allowed characters known to be non-printable\n// should also be escaped. [However,] This isn\u2019t mandatory\"\n// Derived from nb-char - \\t - #x85 - #xA0 - #x2028 - #x2029.\nfunction isPrintable(c) {\n return (0x00020 <= c && c <= 0x00007E)\n || ((0x000A1 <= c && c <= 0x00D7FF) && c !== 0x2028 && c !== 0x2029)\n || ((0x0E000 <= c && c <= 0x00FFFD) && c !== CHAR_BOM)\n || (0x10000 <= c && c <= 0x10FFFF);\n}\n\n// [34] ns-char ::= nb-char - s-white\n// [27] nb-char ::= c-printable - b-char - c-byte-order-mark\n// [26] b-char ::= b-line-feed | b-carriage-return\n// Including s-white (for some reason, examples doesn't match specs in this aspect)\n// ns-char ::= c-printable - b-line-feed - b-carriage-return - c-byte-order-mark\nfunction isNsCharOrWhitespace(c) {\n return isPrintable(c)\n && c !== CHAR_BOM\n // - b-char\n && c !== CHAR_CARRIAGE_RETURN\n && c !== CHAR_LINE_FEED;\n}\n\n// [127] ns-plain-safe(c) ::= c = flow-out \u21D2 ns-plain-safe-out\n// c = flow-in \u21D2 ns-plain-safe-in\n// c = block-key \u21D2 ns-plain-safe-out\n// c = flow-key \u21D2 ns-plain-safe-in\n// [128] ns-plain-safe-out ::= ns-char\n// [129] ns-plain-safe-in ::= ns-char - c-flow-indicator\n// [130] ns-plain-char(c) ::= ( ns-plain-safe(c) - \u201C:\u201D - \u201C#\u201D )\n// | ( /* An ns-char preceding */ \u201C#\u201D )\n// | ( \u201C:\u201D /* Followed by an ns-plain-safe(c) */ )\nfunction isPlainSafe(c, prev, inblock) {\n var cIsNsCharOrWhitespace = isNsCharOrWhitespace(c);\n var cIsNsChar = cIsNsCharOrWhitespace && !isWhitespace(c);\n return (\n // ns-plain-safe\n inblock ? // c = flow-in\n cIsNsCharOrWhitespace\n : cIsNsCharOrWhitespace\n // - c-flow-indicator\n && c !== CHAR_COMMA\n && c !== CHAR_LEFT_SQUARE_BRACKET\n && c !== CHAR_RIGHT_SQUARE_BRACKET\n && c !== CHAR_LEFT_CURLY_BRACKET\n && c !== CHAR_RIGHT_CURLY_BRACKET\n )\n // ns-plain-char\n && c !== CHAR_SHARP // false on '#'\n && !(prev === CHAR_COLON && !cIsNsChar) // false on ': '\n || (isNsCharOrWhitespace(prev) && !isWhitespace(prev) && c === CHAR_SHARP) // change to true on '[^ ]#'\n || (prev === CHAR_COLON && cIsNsChar); // change to true on ':[^ ]'\n}\n\n// Simplified test for values allowed as the first character in plain style.\nfunction isPlainSafeFirst(c) {\n // Uses a subset of ns-char - c-indicator\n // where ns-char = nb-char - s-white.\n // No support of ( ( \u201C?\u201D | \u201C:\u201D | \u201C-\u201D ) /* Followed by an ns-plain-safe(c)) */ ) part\n return isPrintable(c) && c !== CHAR_BOM\n && !isWhitespace(c) // - s-white\n // - (c-indicator ::=\n // \u201C-\u201D | \u201C?\u201D | \u201C:\u201D | \u201C,\u201D | \u201C[\u201D | \u201C]\u201D | \u201C{\u201D | \u201C}\u201D\n && c !== CHAR_MINUS\n && c !== CHAR_QUESTION\n && c !== CHAR_COLON\n && c !== CHAR_COMMA\n && c !== CHAR_LEFT_SQUARE_BRACKET\n && c !== CHAR_RIGHT_SQUARE_BRACKET\n && c !== CHAR_LEFT_CURLY_BRACKET\n && c !== CHAR_RIGHT_CURLY_BRACKET\n // | \u201C#\u201D | \u201C&\u201D | \u201C*\u201D | \u201C!\u201D | \u201C|\u201D | \u201C=\u201D | \u201C>\u201D | \u201C'\u201D | \u201C\"\u201D\n && c !== CHAR_SHARP\n && c !== CHAR_AMPERSAND\n && c !== CHAR_ASTERISK\n && c !== CHAR_EXCLAMATION\n && c !== CHAR_VERTICAL_LINE\n && c !== CHAR_EQUALS\n && c !== CHAR_GREATER_THAN\n && c !== CHAR_SINGLE_QUOTE\n && c !== CHAR_DOUBLE_QUOTE\n // | \u201C%\u201D | \u201C@\u201D | \u201C`\u201D)\n && c !== CHAR_PERCENT\n && c !== CHAR_COMMERCIAL_AT\n && c !== CHAR_GRAVE_ACCENT;\n}\n\n// Simplified test for values allowed as the last character in plain style.\nfunction isPlainSafeLast(c) {\n // just not whitespace or colon, it will be checked to be plain character later\n return !isWhitespace(c) && c !== CHAR_COLON;\n}\n\n// Same as 'string'.codePointAt(pos), but works in older browsers.\nfunction codePointAt(string, pos) {\n var first = string.charCodeAt(pos), second;\n if (first >= 0xD800 && first <= 0xDBFF && pos + 1 < string.length) {\n second = string.charCodeAt(pos + 1);\n if (second >= 0xDC00 && second <= 0xDFFF) {\n // https://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae\n return (first - 0xD800) * 0x400 + second - 0xDC00 + 0x10000;\n }\n }\n return first;\n}\n\n// Determines whether block indentation indicator is required.\nfunction needIndentIndicator(string) {\n var leadingSpaceRe = /^\\n* /;\n return leadingSpaceRe.test(string);\n}\n\nvar STYLE_PLAIN = 1,\n STYLE_SINGLE = 2,\n STYLE_LITERAL = 3,\n STYLE_FOLDED = 4,\n STYLE_DOUBLE = 5;\n\n// Determines which scalar styles are possible and returns the preferred style.\n// lineWidth = -1 => no limit.\n// Pre-conditions: str.length > 0.\n// Post-conditions:\n// STYLE_PLAIN or STYLE_SINGLE => no \\n are in the string.\n// STYLE_LITERAL => no lines are suitable for folding (or lineWidth is -1).\n// STYLE_FOLDED => a line > lineWidth and can be folded (and lineWidth != -1).\nfunction chooseScalarStyle(string, singleLineOnly, indentPerLevel, lineWidth,\n testAmbiguousType, quotingType, forceQuotes, inblock) {\n\n var i;\n var char = 0;\n var prevChar = null;\n var hasLineBreak = false;\n var hasFoldableLine = false; // only checked if shouldTrackWidth\n var shouldTrackWidth = lineWidth !== -1;\n var previousLineBreak = -1; // count the first line correctly\n var plain = isPlainSafeFirst(codePointAt(string, 0))\n && isPlainSafeLast(codePointAt(string, string.length - 1));\n\n if (singleLineOnly || forceQuotes) {\n // Case: no block styles.\n // Check for disallowed characters to rule out plain and single.\n for (i = 0; i < string.length; char >= 0x10000 ? i += 2 : i++) {\n char = codePointAt(string, i);\n if (!isPrintable(char)) {\n return STYLE_DOUBLE;\n }\n plain = plain && isPlainSafe(char, prevChar, inblock);\n prevChar = char;\n }\n } else {\n // Case: block styles permitted.\n for (i = 0; i < string.length; char >= 0x10000 ? i += 2 : i++) {\n char = codePointAt(string, i);\n if (char === CHAR_LINE_FEED) {\n hasLineBreak = true;\n // Check if any line can be folded.\n if (shouldTrackWidth) {\n hasFoldableLine = hasFoldableLine ||\n // Foldable line = too long, and not more-indented.\n (i - previousLineBreak - 1 > lineWidth &&\n string[previousLineBreak + 1] !== ' ');\n previousLineBreak = i;\n }\n } else if (!isPrintable(char)) {\n return STYLE_DOUBLE;\n }\n plain = plain && isPlainSafe(char, prevChar, inblock);\n prevChar = char;\n }\n // in case the end is missing a \\n\n hasFoldableLine = hasFoldableLine || (shouldTrackWidth &&\n (i - previousLineBreak - 1 > lineWidth &&\n string[previousLineBreak + 1] !== ' '));\n }\n // Although every style can represent \\n without escaping, prefer block styles\n // for multiline, since they're more readable and they don't add empty lines.\n // Also prefer folding a super-long line.\n if (!hasLineBreak && !hasFoldableLine) {\n // Strings interpretable as another type have to be quoted;\n // e.g. the string 'true' vs. the boolean true.\n if (plain && !forceQuotes && !testAmbiguousType(string)) {\n return STYLE_PLAIN;\n }\n return quotingType === QUOTING_TYPE_DOUBLE ? STYLE_DOUBLE : STYLE_SINGLE;\n }\n // Edge case: block indentation indicator can only have one digit.\n if (indentPerLevel > 9 && needIndentIndicator(string)) {\n return STYLE_DOUBLE;\n }\n // At this point we know block styles are valid.\n // Prefer literal style unless we want to fold.\n if (!forceQuotes) {\n return hasFoldableLine ? STYLE_FOLDED : STYLE_LITERAL;\n }\n return quotingType === QUOTING_TYPE_DOUBLE ? STYLE_DOUBLE : STYLE_SINGLE;\n}\n\n// Note: line breaking/folding is implemented for only the folded style.\n// NB. We drop the last trailing newline (if any) of a returned block scalar\n// since the dumper adds its own newline. This always works:\n// \u2022 No ending newline => unaffected; already using strip \"-\" chomping.\n// \u2022 Ending newline => removed then restored.\n// Importantly, this keeps the \"+\" chomp indicator from gaining an extra line.\nfunction writeScalar(state, string, level, iskey, inblock) {\n state.dump = (function () {\n if (string.length === 0) {\n return state.quotingType === QUOTING_TYPE_DOUBLE ? '\"\"' : \"''\";\n }\n if (!state.noCompatMode) {\n if (DEPRECATED_BOOLEANS_SYNTAX.indexOf(string) !== -1 || DEPRECATED_BASE60_SYNTAX.test(string)) {\n return state.quotingType === QUOTING_TYPE_DOUBLE ? ('\"' + string + '\"') : (\"'\" + string + \"'\");\n }\n }\n\n var indent = state.indent * Math.max(1, level); // no 0-indent scalars\n // As indentation gets deeper, let the width decrease monotonically\n // to the lower bound min(state.lineWidth, 40).\n // Note that this implies\n // state.lineWidth \u2264 40 + state.indent: width is fixed at the lower bound.\n // state.lineWidth > 40 + state.indent: width decreases until the lower bound.\n // This behaves better than a constant minimum width which disallows narrower options,\n // or an indent threshold which causes the width to suddenly increase.\n var lineWidth = state.lineWidth === -1\n ? -1 : Math.max(Math.min(state.lineWidth, 40), state.lineWidth - indent);\n\n // Without knowing if keys are implicit/explicit, assume implicit for safety.\n var singleLineOnly = iskey\n // No block styles in flow mode.\n || (state.flowLevel > -1 && level >= state.flowLevel);\n function testAmbiguity(string) {\n return testImplicitResolving(state, string);\n }\n\n switch (chooseScalarStyle(string, singleLineOnly, state.indent, lineWidth,\n testAmbiguity, state.quotingType, state.forceQuotes && !iskey, inblock)) {\n\n case STYLE_PLAIN:\n return string;\n case STYLE_SINGLE:\n return \"'\" + string.replace(/'/g, \"''\") + \"'\";\n case STYLE_LITERAL:\n return '|' + blockHeader(string, state.indent)\n + dropEndingNewline(indentString(string, indent));\n case STYLE_FOLDED:\n return '>' + blockHeader(string, state.indent)\n + dropEndingNewline(indentString(foldString(string, lineWidth), indent));\n case STYLE_DOUBLE:\n return '\"' + escapeString(string) + '\"';\n default:\n throw new exception('impossible error: invalid scalar style');\n }\n }());\n}\n\n// Pre-conditions: string is valid for a block scalar, 1 <= indentPerLevel <= 9.\nfunction blockHeader(string, indentPerLevel) {\n var indentIndicator = needIndentIndicator(string) ? String(indentPerLevel) : '';\n\n // note the special case: the string '\\n' counts as a \"trailing\" empty line.\n var clip = string[string.length - 1] === '\\n';\n var keep = clip && (string[string.length - 2] === '\\n' || string === '\\n');\n var chomp = keep ? '+' : (clip ? '' : '-');\n\n return indentIndicator + chomp + '\\n';\n}\n\n// (See the note for writeScalar.)\nfunction dropEndingNewline(string) {\n return string[string.length - 1] === '\\n' ? string.slice(0, -1) : string;\n}\n\n// Note: a long line without a suitable break point will exceed the width limit.\n// Pre-conditions: every char in str isPrintable, str.length > 0, width > 0.\nfunction foldString(string, width) {\n // In folded style, $k$ consecutive newlines output as $k+1$ newlines\u2014\n // unless they're before or after a more-indented line, or at the very\n // beginning or end, in which case $k$ maps to $k$.\n // Therefore, parse each chunk as newline(s) followed by a content line.\n var lineRe = /(\\n+)([^\\n]*)/g;\n\n // first line (possibly an empty line)\n var result = (function () {\n var nextLF = string.indexOf('\\n');\n nextLF = nextLF !== -1 ? nextLF : string.length;\n lineRe.lastIndex = nextLF;\n return foldLine(string.slice(0, nextLF), width);\n }());\n // If we haven't reached the first content line yet, don't add an extra \\n.\n var prevMoreIndented = string[0] === '\\n' || string[0] === ' ';\n var moreIndented;\n\n // rest of the lines\n var match;\n while ((match = lineRe.exec(string))) {\n var prefix = match[1], line = match[2];\n moreIndented = (line[0] === ' ');\n result += prefix\n + (!prevMoreIndented && !moreIndented && line !== ''\n ? '\\n' : '')\n + foldLine(line, width);\n prevMoreIndented = moreIndented;\n }\n\n return result;\n}\n\n// Greedy line breaking.\n// Picks the longest line under the limit each time,\n// otherwise settles for the shortest line over the limit.\n// NB. More-indented lines *cannot* be folded, as that would add an extra \\n.\nfunction foldLine(line, width) {\n if (line === '' || line[0] === ' ') return line;\n\n // Since a more-indented line adds a \\n, breaks can't be followed by a space.\n var breakRe = / [^ ]/g; // note: the match index will always be <= length-2.\n var match;\n // start is an inclusive index. end, curr, and next are exclusive.\n var start = 0, end, curr = 0, next = 0;\n var result = '';\n\n // Invariants: 0 <= start <= length-1.\n // 0 <= curr <= next <= max(0, length-2). curr - start <= width.\n // Inside the loop:\n // A match implies length >= 2, so curr and next are <= length-2.\n while ((match = breakRe.exec(line))) {\n next = match.index;\n // maintain invariant: curr - start <= width\n if (next - start > width) {\n end = (curr > start) ? curr : next; // derive end <= length-2\n result += '\\n' + line.slice(start, end);\n // skip the space that was output as \\n\n start = end + 1; // derive start <= length-1\n }\n curr = next;\n }\n\n // By the invariants, start <= length-1, so there is something left over.\n // It is either the whole string or a part starting from non-whitespace.\n result += '\\n';\n // Insert a break if the remainder is too long and there is a break available.\n if (line.length - start > width && curr > start) {\n result += line.slice(start, curr) + '\\n' + line.slice(curr + 1);\n } else {\n result += line.slice(start);\n }\n\n return result.slice(1); // drop extra \\n joiner\n}\n\n// Escapes a double-quoted string.\nfunction escapeString(string) {\n var result = '';\n var char = 0;\n var escapeSeq;\n\n for (var i = 0; i < string.length; char >= 0x10000 ? i += 2 : i++) {\n char = codePointAt(string, i);\n escapeSeq = ESCAPE_SEQUENCES[char];\n\n if (!escapeSeq && isPrintable(char)) {\n result += string[i];\n if (char >= 0x10000) result += string[i + 1];\n } else {\n result += escapeSeq || encodeHex(char);\n }\n }\n\n return result;\n}\n\nfunction writeFlowSequence(state, level, object) {\n var _result = '',\n _tag = state.tag,\n index,\n length,\n value;\n\n for (index = 0, length = object.length; index < length; index += 1) {\n value = object[index];\n\n if (state.replacer) {\n value = state.replacer.call(object, String(index), value);\n }\n\n // Write only valid elements, put null instead of invalid elements.\n if (writeNode(state, level, value, false, false) ||\n (typeof value === 'undefined' &&\n writeNode(state, level, null, false, false))) {\n\n if (_result !== '') _result += ',' + (!state.condenseFlow ? ' ' : '');\n _result += state.dump;\n }\n }\n\n state.tag = _tag;\n state.dump = '[' + _result + ']';\n}\n\nfunction writeBlockSequence(state, level, object, compact) {\n var _result = '',\n _tag = state.tag,\n index,\n length,\n value;\n\n for (index = 0, length = object.length; index < length; index += 1) {\n value = object[index];\n\n if (state.replacer) {\n value = state.replacer.call(object, String(index), value);\n }\n\n // Write only valid elements, put null instead of invalid elements.\n if (writeNode(state, level + 1, value, true, true, false, true) ||\n (typeof value === 'undefined' &&\n writeNode(state, level + 1, null, true, true, false, true))) {\n\n if (!compact || _result !== '') {\n _result += generateNextLine(state, level);\n }\n\n if (state.dump && CHAR_LINE_FEED === state.dump.charCodeAt(0)) {\n _result += '-';\n } else {\n _result += '- ';\n }\n\n _result += state.dump;\n }\n }\n\n state.tag = _tag;\n state.dump = _result || '[]'; // Empty sequence if no valid values.\n}\n\nfunction writeFlowMapping(state, level, object) {\n var _result = '',\n _tag = state.tag,\n objectKeyList = Object.keys(object),\n index,\n length,\n objectKey,\n objectValue,\n pairBuffer;\n\n for (index = 0, length = objectKeyList.length; index < length; index += 1) {\n\n pairBuffer = '';\n if (_result !== '') pairBuffer += ', ';\n\n if (state.condenseFlow) pairBuffer += '\"';\n\n objectKey = objectKeyList[index];\n objectValue = object[objectKey];\n\n if (state.replacer) {\n objectValue = state.replacer.call(object, objectKey, objectValue);\n }\n\n if (!writeNode(state, level, objectKey, false, false)) {\n continue; // Skip this pair because of invalid key;\n }\n\n if (state.dump.length > 1024) pairBuffer += '? ';\n\n pairBuffer += state.dump + (state.condenseFlow ? '\"' : '') + ':' + (state.condenseFlow ? '' : ' ');\n\n if (!writeNode(state, level, objectValue, false, false)) {\n continue; // Skip this pair because of invalid value.\n }\n\n pairBuffer += state.dump;\n\n // Both key and value are valid.\n _result += pairBuffer;\n }\n\n state.tag = _tag;\n state.dump = '{' + _result + '}';\n}\n\nfunction writeBlockMapping(state, level, object, compact) {\n var _result = '',\n _tag = state.tag,\n objectKeyList = Object.keys(object),\n index,\n length,\n objectKey,\n objectValue,\n explicitPair,\n pairBuffer;\n\n // Allow sorting keys so that the output file is deterministic\n if (state.sortKeys === true) {\n // Default sorting\n objectKeyList.sort();\n } else if (typeof state.sortKeys === 'function') {\n // Custom sort function\n objectKeyList.sort(state.sortKeys);\n } else if (state.sortKeys) {\n // Something is wrong\n throw new exception('sortKeys must be a boolean or a function');\n }\n\n for (index = 0, length = objectKeyList.length; index < length; index += 1) {\n pairBuffer = '';\n\n if (!compact || _result !== '') {\n pairBuffer += generateNextLine(state, level);\n }\n\n objectKey = objectKeyList[index];\n objectValue = object[objectKey];\n\n if (state.replacer) {\n objectValue = state.replacer.call(object, objectKey, objectValue);\n }\n\n if (!writeNode(state, level + 1, objectKey, true, true, true)) {\n continue; // Skip this pair because of invalid key.\n }\n\n explicitPair = (state.tag !== null && state.tag !== '?') ||\n (state.dump && state.dump.length > 1024);\n\n if (explicitPair) {\n if (state.dump && CHAR_LINE_FEED === state.dump.charCodeAt(0)) {\n pairBuffer += '?';\n } else {\n pairBuffer += '? ';\n }\n }\n\n pairBuffer += state.dump;\n\n if (explicitPair) {\n pairBuffer += generateNextLine(state, level);\n }\n\n if (!writeNode(state, level + 1, objectValue, true, explicitPair)) {\n continue; // Skip this pair because of invalid value.\n }\n\n if (state.dump && CHAR_LINE_FEED === state.dump.charCodeAt(0)) {\n pairBuffer += ':';\n } else {\n pairBuffer += ': ';\n }\n\n pairBuffer += state.dump;\n\n // Both key and value are valid.\n _result += pairBuffer;\n }\n\n state.tag = _tag;\n state.dump = _result || '{}'; // Empty mapping if no valid pairs.\n}\n\nfunction detectType(state, object, explicit) {\n var _result, typeList, index, length, type, style;\n\n typeList = explicit ? state.explicitTypes : state.implicitTypes;\n\n for (index = 0, length = typeList.length; index < length; index += 1) {\n type = typeList[index];\n\n if ((type.instanceOf || type.predicate) &&\n (!type.instanceOf || ((typeof object === 'object') && (object instanceof type.instanceOf))) &&\n (!type.predicate || type.predicate(object))) {\n\n if (explicit) {\n if (type.multi && type.representName) {\n state.tag = type.representName(object);\n } else {\n state.tag = type.tag;\n }\n } else {\n state.tag = '?';\n }\n\n if (type.represent) {\n style = state.styleMap[type.tag] || type.defaultStyle;\n\n if (_toString.call(type.represent) === '[object Function]') {\n _result = type.represent(object, style);\n } else if (_hasOwnProperty.call(type.represent, style)) {\n _result = type.represent[style](object, style);\n } else {\n throw new exception('!<' + type.tag + '> tag resolver accepts not \"' + style + '\" style');\n }\n\n state.dump = _result;\n }\n\n return true;\n }\n }\n\n return false;\n}\n\n// Serializes `object` and writes it to global `result`.\n// Returns true on success, or false on invalid object.\n//\nfunction writeNode(state, level, object, block, compact, iskey, isblockseq) {\n state.tag = null;\n state.dump = object;\n\n if (!detectType(state, object, false)) {\n detectType(state, object, true);\n }\n\n var type = _toString.call(state.dump);\n var inblock = block;\n var tagStr;\n\n if (block) {\n block = (state.flowLevel < 0 || state.flowLevel > level);\n }\n\n var objectOrArray = type === '[object Object]' || type === '[object Array]',\n duplicateIndex,\n duplicate;\n\n if (objectOrArray) {\n duplicateIndex = state.duplicates.indexOf(object);\n duplicate = duplicateIndex !== -1;\n }\n\n if ((state.tag !== null && state.tag !== '?') || duplicate || (state.indent !== 2 && level > 0)) {\n compact = false;\n }\n\n if (duplicate && state.usedDuplicates[duplicateIndex]) {\n state.dump = '*ref_' + duplicateIndex;\n } else {\n if (objectOrArray && duplicate && !state.usedDuplicates[duplicateIndex]) {\n state.usedDuplicates[duplicateIndex] = true;\n }\n if (type === '[object Object]') {\n if (block && (Object.keys(state.dump).length !== 0)) {\n writeBlockMapping(state, level, state.dump, compact);\n if (duplicate) {\n state.dump = '&ref_' + duplicateIndex + state.dump;\n }\n } else {\n writeFlowMapping(state, level, state.dump);\n if (duplicate) {\n state.dump = '&ref_' + duplicateIndex + ' ' + state.dump;\n }\n }\n } else if (type === '[object Array]') {\n if (block && (state.dump.length !== 0)) {\n if (state.noArrayIndent && !isblockseq && level > 0) {\n writeBlockSequence(state, level - 1, state.dump, compact);\n } else {\n writeBlockSequence(state, level, state.dump, compact);\n }\n if (duplicate) {\n state.dump = '&ref_' + duplicateIndex + state.dump;\n }\n } else {\n writeFlowSequence(state, level, state.dump);\n if (duplicate) {\n state.dump = '&ref_' + duplicateIndex + ' ' + state.dump;\n }\n }\n } else if (type === '[object String]') {\n if (state.tag !== '?') {\n writeScalar(state, state.dump, level, iskey, inblock);\n }\n } else if (type === '[object Undefined]') {\n return false;\n } else {\n if (state.skipInvalid) return false;\n throw new exception('unacceptable kind of an object to dump ' + type);\n }\n\n if (state.tag !== null && state.tag !== '?') {\n // Need to encode all characters except those allowed by the spec:\n //\n // [35] ns-dec-digit ::= [#x30-#x39] /* 0-9 */\n // [36] ns-hex-digit ::= ns-dec-digit\n // | [#x41-#x46] /* A-F */ | [#x61-#x66] /* a-f */\n // [37] ns-ascii-letter ::= [#x41-#x5A] /* A-Z */ | [#x61-#x7A] /* a-z */\n // [38] ns-word-char ::= ns-dec-digit | ns-ascii-letter | \u201C-\u201D\n // [39] ns-uri-char ::= \u201C%\u201D ns-hex-digit ns-hex-digit | ns-word-char | \u201C#\u201D\n // | \u201C;\u201D | \u201C/\u201D | \u201C?\u201D | \u201C:\u201D | \u201C@\u201D | \u201C&\u201D | \u201C=\u201D | \u201C+\u201D | \u201C$\u201D | \u201C,\u201D\n // | \u201C_\u201D | \u201C.\u201D | \u201C!\u201D | \u201C~\u201D | \u201C*\u201D | \u201C'\u201D | \u201C(\u201D | \u201C)\u201D | \u201C[\u201D | \u201C]\u201D\n //\n // Also need to encode '!' because it has special meaning (end of tag prefix).\n //\n tagStr = encodeURI(\n state.tag[0] === '!' ? state.tag.slice(1) : state.tag\n ).replace(/!/g, '%21');\n\n if (state.tag[0] === '!') {\n tagStr = '!' + tagStr;\n } else if (tagStr.slice(0, 18) === 'tag:yaml.org,2002:') {\n tagStr = '!!' + tagStr.slice(18);\n } else {\n tagStr = '!<' + tagStr + '>';\n }\n\n state.dump = tagStr + ' ' + state.dump;\n }\n }\n\n return true;\n}\n\nfunction getDuplicateReferences(object, state) {\n var objects = [],\n duplicatesIndexes = [],\n index,\n length;\n\n inspectNode(object, objects, duplicatesIndexes);\n\n for (index = 0, length = duplicatesIndexes.length; index < length; index += 1) {\n state.duplicates.push(objects[duplicatesIndexes[index]]);\n }\n state.usedDuplicates = new Array(length);\n}\n\nfunction inspectNode(object, objects, duplicatesIndexes) {\n var objectKeyList,\n index,\n length;\n\n if (object !== null && typeof object === 'object') {\n index = objects.indexOf(object);\n if (index !== -1) {\n if (duplicatesIndexes.indexOf(index) === -1) {\n duplicatesIndexes.push(index);\n }\n } else {\n objects.push(object);\n\n if (Array.isArray(object)) {\n for (index = 0, length = object.length; index < length; index += 1) {\n inspectNode(object[index], objects, duplicatesIndexes);\n }\n } else {\n objectKeyList = Object.keys(object);\n\n for (index = 0, length = objectKeyList.length; index < length; index += 1) {\n inspectNode(object[objectKeyList[index]], objects, duplicatesIndexes);\n }\n }\n }\n }\n}\n\nfunction dump$1(input, options) {\n options = options || {};\n\n var state = new State(options);\n\n if (!state.noRefs) getDuplicateReferences(input, state);\n\n var value = input;\n\n if (state.replacer) {\n value = state.replacer.call({ '': value }, '', value);\n }\n\n if (writeNode(state, 0, value, true, true)) return state.dump + '\\n';\n\n return '';\n}\n\nvar dump_1 = dump$1;\n\nvar dumper = {\n\tdump: dump_1\n};\n\nfunction renamed(from, to) {\n return function () {\n throw new Error('Function yaml.' + from + ' is removed in js-yaml 4. ' +\n 'Use yaml.' + to + ' instead, which is now safe by default.');\n };\n}\n\n\nvar Type = type;\nvar Schema = schema;\nvar FAILSAFE_SCHEMA = failsafe;\nvar JSON_SCHEMA = json;\nvar CORE_SCHEMA = core;\nvar DEFAULT_SCHEMA = _default;\nvar load = loader.load;\nvar loadAll = loader.loadAll;\nvar dump = dumper.dump;\nvar YAMLException = exception;\n\n// Re-export all types in case user wants to create custom schema\nvar types = {\n binary: binary,\n float: float,\n map: map,\n null: _null,\n pairs: pairs,\n set: set,\n timestamp: timestamp,\n bool: bool,\n int: int,\n merge: merge,\n omap: omap,\n seq: seq,\n str: str\n};\n\n// Removed functions from JS-YAML 3.0.x\nvar safeLoad = renamed('safeLoad', 'load');\nvar safeLoadAll = renamed('safeLoadAll', 'loadAll');\nvar safeDump = renamed('safeDump', 'dump');\n\nvar jsYaml = {\n\tType: Type,\n\tSchema: Schema,\n\tFAILSAFE_SCHEMA: FAILSAFE_SCHEMA,\n\tJSON_SCHEMA: JSON_SCHEMA,\n\tCORE_SCHEMA: CORE_SCHEMA,\n\tDEFAULT_SCHEMA: DEFAULT_SCHEMA,\n\tload: load,\n\tloadAll: loadAll,\n\tdump: dump,\n\tYAMLException: YAMLException,\n\ttypes: types,\n\tsafeLoad: safeLoad,\n\tsafeLoadAll: safeLoadAll,\n\tsafeDump: safeDump\n};\n\nexport { CORE_SCHEMA, DEFAULT_SCHEMA, FAILSAFE_SCHEMA, JSON_SCHEMA, Schema, Type, YAMLException, jsYaml as default, dump, load, loadAll, safeDump, safeLoad, safeLoadAll, types };\n", "/**\n * CodeQL metadata resolver utilities\n * Handles resolution of query metadata using the CodeQL CLI\n */\n\nimport { executeCodeQLCommand } from './cli-executor';\nimport { logger } from '../utils/logger';\n\n/**\n * Query metadata structure returned by codeql resolve metadata\n */\nexport interface QueryMetadata {\n [key: string]: string | string[];\n}\n\n/**\n * Resolve metadata for a CodeQL query file\n * @param queryPath - Absolute or relative path to the .ql query file\n * @returns Parsed metadata object or null if resolution fails\n */\nexport async function resolveQueryMetadata(queryPath: string): Promise {\n try {\n logger.info(`Resolving metadata for query: ${queryPath}`);\n\n const result = await executeCodeQLCommand(\n 'resolve metadata',\n { format: 'json' },\n [queryPath]\n );\n\n if (!result.success) {\n logger.error(`Failed to resolve metadata for ${queryPath}:`, result.stderr || result.error);\n return null;\n }\n\n // Parse the JSON output\n try {\n const metadata = JSON.parse(result.stdout);\n return metadata;\n } catch (parseError) {\n logger.error(`Failed to parse metadata JSON for ${queryPath}:`, parseError);\n return null;\n }\n } catch (error) {\n logger.error(`Error resolving metadata for ${queryPath}:`, error);\n return null;\n }\n}\n", "/**\n * CodeQL generate log-summary tool\n */\n\nimport { z } from 'zod';\nimport { CLIToolDefinition, createCodeQLSchemas, defaultCLIResultProcessor } from '../../lib/cli-tool-registry';\n\nexport const codeqlGenerateLogSummaryTool: CLIToolDefinition = {\n name: 'codeql_generate_log-summary',\n description: 'Create a summary of a structured JSON evaluator event log file',\n command: 'codeql',\n subcommand: 'generate log-summary',\n inputSchema: {\n inputLog: z.string().describe('Path to the evaluator log file to summarize'),\n outputFile: z.string().optional().describe('Path to write the summary (optional, defaults to stdout)'),\n format: z.enum(['text', 'predicates', 'overall']).optional()\n .describe('Output format: text (human-readable), predicates (JSON), or overall (stats)'),\n 'minify-output': z.boolean().optional().describe('Minify JSON output'),\n utc: z.boolean().optional().describe('Force UTC timestamps'),\n 'deduplicate-stage-summaries': z.boolean().optional().describe('Deduplicate stage summaries'),\n verbose: createCodeQLSchemas.verbose(),\n additionalArgs: createCodeQLSchemas.additionalArgs()\n },\n examples: [\n 'codeql generate log-summary --format=text -- evaluator-log.json.txt summary.txt',\n 'codeql generate log-summary --format=predicates --minify-output -- evaluator-log.json.txt',\n 'codeql generate log-summary --format=overall -- evaluator-log.json.txt overall-stats.json'\n ],\n resultProcessor: defaultCLIResultProcessor\n};", "/**\n * CodeQL generate query-help tool\n */\n\nimport { z } from 'zod';\nimport { CLIToolDefinition, createCodeQLSchemas, defaultCLIResultProcessor } from '../../lib/cli-tool-registry';\n\nexport const codeqlGenerateQueryHelpTool: CLIToolDefinition = {\n name: 'codeql_generate_query-help',\n description: 'Generate query help documentation from QLDoc comments',\n command: 'codeql',\n subcommand: 'generate query-help',\n inputSchema: {\n query: z.string().describe('Path to the query file to generate help for'),\n outputFile: z.string().optional().describe('Path to write the help documentation'),\n format: z.enum(['markdown', 'text', 'html']).optional()\n .describe('Output format for the help documentation'),\n verbose: createCodeQLSchemas.verbose(),\n additionalArgs: createCodeQLSchemas.additionalArgs()\n },\n examples: [\n 'codeql generate query-help -- MyQuery.ql',\n 'codeql generate query-help --format=markdown -- MyQuery.ql help.md',\n 'codeql generate query-help --format=html -- MyQuery.ql help.html'\n ],\n resultProcessor: defaultCLIResultProcessor\n};", "/**\n * CodeQL pack install tool\n */\n\nimport { z } from 'zod';\nimport { CLIToolDefinition, createCodeQLSchemas } from '../../lib/cli-tool-registry';\n\nexport const codeqlPackInstallTool: CLIToolDefinition = {\n name: 'codeql_pack_install',\n description: 'Install CodeQL pack dependencies',\n command: 'codeql',\n subcommand: 'pack install',\n inputSchema: {\n packDir: z.string().optional().describe('Directory containing qlpack.yml (default: current)'),\n force: z.boolean().optional().describe('Force reinstall of dependencies'),\n 'no-strict-mode': z.boolean().optional()\n .describe('Allow non-strict dependency resolution'),\n verbose: createCodeQLSchemas.verbose(),\n additionalArgs: createCodeQLSchemas.additionalArgs()\n },\n examples: [\n 'codeql pack install',\n 'codeql pack install --force /path/to/pack',\n 'codeql pack install --no-strict-mode'\n ]\n};", "/**\n * CodeQL pack ls tool\n */\n\nimport { z } from 'zod';\nimport { CLIToolDefinition, createCodeQLSchemas, defaultCLIResultProcessor } from '../../lib/cli-tool-registry';\n\nexport const codeqlPackLsTool: CLIToolDefinition = {\n name: 'codeql_pack_ls',\n description: 'List CodeQL packs under some local directory path',\n command: 'codeql',\n subcommand: 'pack ls',\n inputSchema: {\n dir: z.string().optional().describe('The root directory of the package or workspace, defaults to the current working directory'),\n format: z.enum(['text', 'json']).optional()\n .describe('Output format: text (default) or json'),\n groups: z.string().optional()\n .describe('List of CodeQL pack groups to include or exclude'),\n verbose: createCodeQLSchemas.verbose(),\n additionalArgs: createCodeQLSchemas.additionalArgs()\n },\n examples: [\n 'codeql pack ls -- .',\n 'codeql pack ls --format=json -- /path/to/pack-directory',\n 'codeql pack ls --format=json --groups=queries,tests -- .'\n ],\n resultProcessor: defaultCLIResultProcessor\n};", "/**\n * CodeQL query profiling tool\n */\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport { executeCodeQLCommand } from '../../lib/cli-executor';\nimport { logger } from '../../utils/logger';\nimport { writeFileSync, readFileSync, existsSync } from 'fs';\nimport { join, dirname, basename } from 'path';\nimport { mkdirSync } from 'fs';\n\ninterface EvaluatorLogEvent {\n time: string;\n type: string;\n eventId: number;\n nanoTime: number;\n [key: string]: unknown;\n}\n\ninterface PipelineNode {\n eventId: number;\n name: string;\n position?: string;\n type?: string;\n startTime: number;\n endTime: number;\n duration: number;\n resultSize?: number;\n tupleCount?: number;\n dependencies: string[];\n dependencyEventIds: number[];\n}\n\ninterface ProfileData {\n queryName: string;\n totalDuration: number;\n totalEvents: number;\n pipelines: PipelineNode[];\n}\n\n/**\n * Parse evaluator log and create profile data\n */\nfunction parseEvaluatorLog(logPath: string): ProfileData {\n const logContent = readFileSync(logPath, 'utf-8');\n \n // Split by empty lines to get each JSON object (handles both JSONL and pretty-printed JSON)\n const jsonObjects = logContent.split('\\n\\n').filter((s) => s.trim());\n const events: EvaluatorLogEvent[] = jsonObjects\n .map((obj) => {\n try {\n return JSON.parse(obj);\n } catch (_error) {\n logger.warn(`Failed to parse evaluator log object: ${obj.substring(0, 100)}...`);\n return null;\n }\n })\n .filter((event): event is EvaluatorLogEvent => event !== null);\n\n // Map to track pipeline nodes by their start event ID\n const pipelineMap = new Map>();\n // Map to track dependency event IDs by predicate name\n const predicateNameToEventId = new Map();\n \n let queryName = '';\n let queryStartTime = 0;\n let queryEndTime = 0;\n\n for (const event of events) {\n switch (event.type) {\n case 'QUERY_STARTED':\n queryName = (event.queryName as string) || '';\n queryStartTime = event.nanoTime;\n break;\n\n case 'QUERY_COMPLETED':\n queryEndTime = event.nanoTime;\n break;\n\n case 'PREDICATE_STARTED': {\n const predicateName = event.predicateName as string;\n const position = event.position as string | undefined;\n const predicateType = event.predicateType as string | undefined;\n const dependencies = event.dependencies as Record | undefined;\n \n // Track this predicate's event ID by name for dependency resolution\n predicateNameToEventId.set(predicateName, event.eventId);\n \n // Get dependency event IDs\n const dependencyEventIds: number[] = [];\n const dependencyNames: string[] = [];\n if (dependencies) {\n for (const depName of Object.keys(dependencies)) {\n dependencyNames.push(depName);\n const depEventId = predicateNameToEventId.get(depName);\n if (depEventId !== undefined) {\n dependencyEventIds.push(depEventId);\n }\n }\n }\n\n pipelineMap.set(event.eventId, {\n eventId: event.eventId,\n name: predicateName,\n position,\n type: predicateType,\n startTime: event.nanoTime,\n dependencies: dependencyNames,\n dependencyEventIds,\n });\n break;\n }\n\n case 'PREDICATE_COMPLETED': {\n const startEventId = event.startEvent as number;\n const pipelineInfo = pipelineMap.get(startEventId);\n if (pipelineInfo) {\n const startEvent = events.find((e) => e.eventId === startEventId);\n if (startEvent) {\n const duration = (event.nanoTime - startEvent.nanoTime) / 1_000_000; // Convert to ms\n pipelineInfo.endTime = event.nanoTime;\n pipelineInfo.duration = duration;\n pipelineInfo.resultSize = event.resultSize as number | undefined;\n pipelineInfo.tupleCount = event.tupleCount as number | undefined;\n }\n }\n break;\n }\n }\n }\n\n // Convert to array and maintain original evaluation order (by eventId)\n const pipelines: PipelineNode[] = Array.from(pipelineMap.values())\n .filter((p): p is PipelineNode => p.duration !== undefined)\n .sort((a, b) => a.eventId - b.eventId);\n\n const totalDuration = queryEndTime > 0 ? (queryEndTime - queryStartTime) / 1_000_000 : 0;\n\n return {\n queryName,\n totalDuration,\n totalEvents: events.length,\n pipelines,\n };\n}\n\n/**\n * Format profile data as JSON\n */\nfunction formatAsJson(profile: ProfileData): string {\n return JSON.stringify(profile, null, 2);\n}\n\n/**\n * Format profile data as Mermaid diagram\n * Creates a graph showing the query evaluation pipelines in order of execution\n * with dependencies as edges, annotated with duration and result sizes\n */\nfunction formatAsMermaid(profile: ProfileData): string {\n const lines: string[] = [];\n\n lines.push('```mermaid');\n lines.push('graph TD');\n lines.push('');\n \n // Add query root node\n lines.push(` QUERY[\"${basename(profile.queryName)}
Total: ${profile.totalDuration.toFixed(2)}ms\"]`);\n lines.push('');\n \n // Create nodes for each pipeline in evaluation order (already sorted by eventId)\n profile.pipelines.forEach((pipeline) => {\n const nodeId = `P${pipeline.eventId}`;\n const duration = pipeline.duration.toFixed(2);\n const resultSize = pipeline.resultSize !== undefined ? pipeline.resultSize : '?';\n // Sanitize predicate name for Mermaid\n const name = pipeline.name.replace(/[<>]/g, '').substring(0, 40);\n lines.push(` ${nodeId}[\"${name}
${duration}ms | ${resultSize} results\"]`);\n });\n \n lines.push('');\n \n // Add edges from QUERY to root pipelines (those with no dependencies)\n const rootPipelines = profile.pipelines.filter((p) => p.dependencies.length === 0);\n \n rootPipelines.forEach((pipeline) => {\n lines.push(` QUERY --> P${pipeline.eventId}`);\n });\n \n lines.push('');\n \n // Add edges between pipelines based on dependencies (using eventIds to preserve links)\n profile.pipelines.forEach((pipeline) => {\n pipeline.dependencyEventIds.forEach((depEventId) => {\n const duration = pipeline.duration.toFixed(2);\n lines.push(` P${depEventId} -->|\"${duration}ms\"| P${pipeline.eventId}`);\n });\n });\n \n lines.push('');\n lines.push(' classDef default fill:#e1f5ff,stroke:#333,stroke-width:2px');\n lines.push(' classDef query fill:#ffe1e1,stroke:#333,stroke-width:3px');\n lines.push(' class QUERY query');\n lines.push('```');\n\n return lines.join('\\n');\n}\n\n/**\n * Register the profile_codeql_query tool\n */\nexport function registerProfileCodeQLQueryTool(server: McpServer): void {\n server.tool(\n 'profile_codeql_query',\n 'Profile the performance of a CodeQL query run against a specific database by analyzing the evaluator log JSON file',\n {\n query: z.string().describe('Path to the .ql query file'),\n database: z.string().describe('Path to the CodeQL database directory'),\n evaluatorLog: z\n .string()\n .optional()\n .describe(\n 'Path to an existing structured JSON log (e.g., evaluator-log.jsonl) file. If not provided, the tool will run the query to generate one.'\n ),\n outputDir: z\n .string()\n .optional()\n .describe('Directory to write profiling data files (defaults to same directory as evaluator log)'),\n },\n async (params) => {\n try {\n const { query, database, evaluatorLog, outputDir } = params;\n let logPath = evaluatorLog;\n let bqrsPath: string | undefined;\n let sarifPath: string | undefined;\n\n // If evaluator log not provided, run the query to generate one\n if (!logPath) {\n logger.info('No evaluator log provided, running query to generate one');\n\n // Determine output directory\n const defaultOutputDir = outputDir || join(dirname(query as string), 'profile-output');\n mkdirSync(defaultOutputDir, { recursive: true });\n\n logPath = join(defaultOutputDir, 'evaluator-log.jsonl');\n bqrsPath = join(defaultOutputDir, 'query-results.bqrs');\n sarifPath = join(defaultOutputDir, 'query-results.sarif');\n\n // Run query with evaluator logging and tuple counting\n const queryResult = await executeCodeQLCommand(\n 'query run',\n {\n database: database as string,\n output: bqrsPath,\n 'evaluator-log': logPath,\n 'tuple-counting': true,\n 'evaluator-log-level': 5,\n },\n [query as string]\n );\n\n if (!queryResult.success) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Failed to run query: ${queryResult.stderr || queryResult.error}`,\n },\n ],\n isError: true,\n };\n }\n\n // Generate SARIF interpretation\n if (existsSync(bqrsPath)) {\n try {\n const sarifResult = await executeCodeQLCommand(\n 'bqrs interpret',\n { format: 'sarif-latest', output: sarifPath },\n [bqrsPath]\n );\n\n if (sarifResult.success) {\n logger.info(`Generated SARIF interpretation at ${sarifPath}`);\n }\n } catch (error) {\n logger.warn(`Failed to generate SARIF interpretation: ${error}`);\n }\n }\n }\n\n // Verify evaluator log exists\n if (!existsSync(logPath)) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Evaluator log not found at: ${logPath}`,\n },\n ],\n isError: true,\n };\n }\n\n // Parse the evaluator log\n logger.info(`Parsing evaluator log from: ${logPath}`);\n const profile = parseEvaluatorLog(logPath);\n\n // Determine output directory for profile\n const profileOutputDir = outputDir || dirname(logPath);\n mkdirSync(profileOutputDir, { recursive: true });\n\n // Write profile JSON file\n const jsonPath = join(profileOutputDir, 'query-evaluation-profile.json');\n const jsonContent = formatAsJson(profile);\n writeFileSync(jsonPath, jsonContent);\n logger.info(`Profile JSON written to: ${jsonPath}`);\n\n // Write profile Mermaid diagram file\n const mdPath = join(profileOutputDir, 'query-evaluation-profile.md');\n const mdContent = formatAsMermaid(profile);\n writeFileSync(mdPath, mdContent);\n logger.info(`Profile Mermaid diagram written to: ${mdPath}`);\n\n // Build response message\n const outputFiles: string[] = [\n `Profile JSON: ${jsonPath}`,\n `Profile Mermaid: ${mdPath}`,\n `Evaluator Log: ${logPath}`,\n ];\n\n if (bqrsPath) {\n outputFiles.push(`Query Results (BQRS): ${bqrsPath}`);\n }\n\n if (sarifPath && existsSync(sarifPath)) {\n outputFiles.push(`Query Results (SARIF): ${sarifPath}`);\n }\n\n const responseText = [\n 'Query profiling completed successfully!',\n '',\n 'Output Files:',\n ...outputFiles.map((f) => ` - ${f}`),\n '',\n 'Profile Summary:',\n ` - Query: ${basename(profile.queryName)}`,\n ` - Total Duration: ${profile.totalDuration.toFixed(2)} ms`,\n ` - Total Pipelines: ${profile.pipelines.length}`,\n ` - Total Events: ${profile.totalEvents}`,\n '',\n 'First 5 Pipeline Nodes (in evaluation order):',\n ...profile.pipelines.slice(0, 5).map((p, idx) => {\n return ` ${idx + 1}. ${p.name} (${p.duration.toFixed(2)} ms, ${p.resultSize || '?'} results)`;\n }),\n ].join('\\n');\n\n return {\n content: [\n {\n type: 'text' as const,\n text: responseText,\n },\n ],\n };\n } catch (error) {\n logger.error('Error profiling CodeQL query:', error);\n\n return {\n content: [\n {\n type: 'text' as const,\n text: `Failed to profile query: ${error instanceof Error ? error.message : String(error)}`,\n },\n ],\n isError: true,\n };\n }\n }\n );\n}\n", "/**\n * CodeQL query compile tool\n */\n\nimport { z } from 'zod';\nimport { CLIToolDefinition } from '../../lib/cli-tool-registry';\n\nexport const codeqlQueryCompileTool: CLIToolDefinition = {\n name: 'codeql_query_compile',\n description: 'Compile and validate CodeQL queries',\n command: 'codeql',\n subcommand: 'query compile',\n inputSchema: {\n query: z.string().describe('Path to the CodeQL query file (.ql)'),\n database: z.string().optional().describe('Path to the CodeQL database'),\n library: z.string().optional().describe('Path to query library'),\n output: z.string().optional().describe('Output file path'),\n warnings: z.enum(['hide', 'show', 'error']).optional()\n .describe('How to handle compilation warnings'),\n verbose: z.boolean().optional().describe('Enable verbose output'),\n additionalArgs: z.array(z.string()).optional().describe('Additional command-line arguments')\n },\n examples: [\n 'codeql query compile --database=/path/to/db MyQuery.ql',\n 'codeql query compile --library=/path/to/lib --output=compiled.qlo MyQuery.ql'\n ]\n};", "/**\n * CodeQL query format tool\n */\n\nimport { z } from 'zod';\nimport { CLIToolDefinition, createCodeQLSchemas, defaultCLIResultProcessor, CLIExecutionResult } from '../../lib/cli-tool-registry';\n\n/**\n * Custom result processor for codeql query format tool\n * Exit code 1 with --check-only means \"file would change\" - this is success for format checking\n */\nfunction formatResultProcessor(result: CLIExecutionResult, params: Record): string {\n const isCheckOnly = params['check-only'];\n const hasFormatChanges = result.exitCode === 1;\n \n if (isCheckOnly && hasFormatChanges) {\n // Mark as success for the CLI tool registry since detecting format changes is the intended behavior\n result.success = true;\n return result.stdout || result.stderr || 'File would change by autoformatting.';\n }\n \n return defaultCLIResultProcessor(result, params);\n}\n\nexport const codeqlQueryFormatTool: CLIToolDefinition = {\n name: 'codeql_query_format',\n description: 'Automatically format CodeQL source code files',\n command: 'codeql',\n subcommand: 'query format',\n inputSchema: {\n files: z.array(z.string()).describe('One or more .ql or .qll source files to format'),\n output: z.string().optional().describe('Write formatted code to this file instead of stdout'),\n 'in-place': z.boolean().optional().describe('Overwrite each input file with formatted version'),\n 'check-only': z.boolean().optional().describe('Check formatting without writing output'),\n backup: z.string().optional().describe('Backup extension when overwriting existing files'),\n 'no-syntax-errors': z.boolean().optional().describe('Ignore syntax errors and pretend file is formatted'),\n verbose: createCodeQLSchemas.verbose(),\n additionalArgs: createCodeQLSchemas.additionalArgs()\n },\n examples: [\n 'codeql query format -i -- ExampleQuery.ql',\n 'codeql query format --in-place -- queries/*.ql',\n 'codeql query format --check-only -- queries/*.ql'\n ],\n resultProcessor: formatResultProcessor\n};", "/**\n * CodeQL query run tool\n */\n\nimport { z } from 'zod';\nimport { CLIToolDefinition, createCodeQLSchemas } from '../../lib/cli-tool-registry';\n\nexport const codeqlQueryRunTool: CLIToolDefinition = {\n name: 'codeql_query_run',\n description: 'Execute a CodeQL query against a database. Use either \"query\" parameter for direct file path OR \"queryName\" + \"queryLanguage\" for pre-defined tool queries.',\n command: 'codeql',\n subcommand: 'query run',\n inputSchema: {\n query: z.string().optional().describe('Path to the CodeQL query file (.ql) - cannot be used with queryName'),\n queryName: z.string().optional().describe('Name of pre-defined query to run (e.g., \"PrintAST\", \"CallGraphFrom\", \"CallGraphTo\") - requires queryLanguage'),\n queryLanguage: z.string().optional().describe('Programming language for tools queries (e.g., \"javascript\", \"java\", \"python\") - required when using queryName'),\n queryPack: z.string().optional().describe('Query pack path (defaults to server/ql//tools/src/ for tool queries)'),\n sourceFiles: z.string().optional().describe('Comma-separated list of source file paths for PrintAST queries (e.g., \"src/main.js,src/utils.js\" or just \"main.js\")'),\n sourceFunction: z.string().optional().describe('Comma-separated list of source function names for CallGraphFrom queries (e.g., \"main,processData\")'),\n targetFunction: z.string().optional().describe('Comma-separated list of target function names for CallGraphTo queries (e.g., \"helper,validateInput\")'),\n database: createCodeQLSchemas.database(),\n output: createCodeQLSchemas.output(),\n external: z.array(z.string()).optional()\n .describe('External predicate data: predicate=file.csv'),\n timeout: createCodeQLSchemas.timeout(),\n logDir: z.string().optional()\n .describe('Custom directory for query execution logs (overrides CODEQL_QUERY_LOG_DIR environment variable). If not provided, uses CODEQL_QUERY_LOG_DIR or defaults to .tmp/query-logs/'),\n 'evaluator-log': z.string().optional().describe('Path to save evaluator log (deprecated: use logDir instead)'),\n 'evaluator-log-minify': z.boolean().optional()\n .describe('Minimize evaluator log for smaller size'),\n 'evaluator-log-level': z.number().min(1).max(5).optional()\n .describe('Evaluator log verbosity level (1-5, default 5)'),\n 'tuple-counting': z.boolean().optional()\n .describe('Display tuple counts for each evaluation step in evaluator logs'),\n format: z.enum(['sarif-latest', 'sarifv2.1.0', 'csv', 'graphtext', 'dgml', 'dot']).optional()\n .describe('Output format for query results via codeql bqrs interpret. Defaults to sarif-latest for @kind problem/path-problem queries, graphtext for @kind graph queries. Graph formats (graphtext, dgml, dot) only work with @kind graph queries.'),\n interpretedOutput: z.string().optional()\n .describe('Output file for interpreted results (e.g., results.sarif, results.txt). If not provided, defaults based on format: .sarif for SARIF, .txt for graphtext/csv, .dgml for dgml, .dot for dot'),\n evaluationFunction: z.string().optional()\n .describe('[DEPRECATED - use format parameter instead] Built-in function for query results evaluation (e.g., \"mermaid-graph\", \"json-decode\", \"csv-decode\") or path to custom evaluation script'),\n evaluationOutput: z.string().optional()\n .describe('[DEPRECATED - use interpretedOutput parameter instead] Output file for evaluation results'),\n verbose: createCodeQLSchemas.verbose(),\n additionalArgs: createCodeQLSchemas.additionalArgs()\n },\n examples: [\n 'codeql query run --database=mydb --output=results.bqrs MyQuery.ql',\n 'codeql query run --database=mydb --query-name=PrintAST --query-language=javascript --source-files=src/index.js --output=results.bqrs --format=graphtext --interpreted-output=results.txt',\n 'codeql query run --database=mydb --external=data=input.csv --output=results.bqrs MyQuery.ql --format=sarif-latest --interpreted-output=results.sarif',\n 'codeql query run --database=mydb --evaluator-log=eval.log --tuple-counting --evaluator-log-level=5 --output=results.bqrs MyQuery.ql',\n 'codeql query run --database=mydb --query-name=PrintAST --query-language=javascript --source-files=\"main.js,utils.js\" --format=graphtext',\n 'codeql query run --database=mydb --log-dir=/custom/log/path --tuple-counting --output=results.bqrs MyQuery.ql'\n ]\n};", "/**\n * CodeQL quick evaluate tool\n * \n * Inspired by JordyZomer/codeql-mcp repository:\n * - https://github.com/JordyZomer/codeql-mcp/blob/main/server.py\n * - https://github.com/JordyZomer/codeql-mcp/blob/main/codeqlclient.py\n */\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport { join, resolve } from 'path';\nimport { findClassPosition } from './find-class-position';\nimport { findPredicatePosition } from './find-predicate-position';\nimport { logger } from '../../utils/logger';\nimport { getProjectTmpDir } from '../../utils/temp-dir';\n\nexport interface QuickEvaluateParams {\n file: string;\n db: string;\n symbol: string;\n output_path?: string;\n}\n\n/**\n * Quick evaluate either a class or a predicate in a CodeQL query.\n * This allows debugging a select portion of QL code without running the whole query.\n */\nexport async function quickEvaluate({\n file,\n db: _db,\n symbol,\n output_path\n}: QuickEvaluateParams): Promise {\n try {\n // Try to find as a class first, then as a predicate\n try {\n await findClassPosition(file, symbol);\n } catch {\n try {\n await findPredicatePosition(file, symbol);\n } catch {\n throw new Error(`Symbol '${symbol}' not found as class or predicate in file: ${file}`);\n }\n }\n \n const resolvedOutput = resolve(output_path || join(getProjectTmpDir('quickeval'), 'quickeval.bqrs'));\n \n // For now, return the resolved output path\n // In a full implementation, this would use the CodeQL CLI or query server\n // to perform the actual quick evaluation with the position parameters\n return resolvedOutput;\n } catch (error) {\n throw new Error(`CodeQL evaluation failed: ${error instanceof Error ? error.message : 'Unknown error'}`);\n }\n}\n\n/**\n * Register the quick evaluate tool with the MCP server\n */\nexport function registerQuickEvaluateTool(server: McpServer): void {\n server.tool(\n 'quick_evaluate',\n 'Quick evaluate either a class or a predicate in a CodeQL query for debugging',\n {\n file: z.string().describe('Path to the .ql file containing the symbol'),\n db: z.string().describe('Path to the CodeQL database'),\n symbol: z.string().describe('Name of the class or predicate to evaluate'),\n output_path: z.string().optional().describe('Output path for results (defaults to project-local .tmp/quickeval/)'),\n },\n async ({ file, db, symbol, output_path }) => {\n try {\n const result = await quickEvaluate({ file, db, symbol, output_path });\n return {\n content: [{ type: 'text', text: result }],\n };\n } catch (error) {\n logger.error('Error in quick evaluate:', error);\n return {\n content: [\n {\n type: 'text',\n text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`,\n },\n ],\n isError: true,\n };\n }\n }\n );\n}", "/**\n * CodeQL register database tool\n * \n * Inspired by JordyZomer/codeql-mcp repository:\n * - https://github.com/JordyZomer/codeql-mcp/blob/main/server.py\n * - https://github.com/JordyZomer/codeql-mcp/blob/main/codeqlclient.py\n */\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport { access, constants } from 'fs/promises';\nimport { resolve } from 'path';\nimport { logger } from '../../utils/logger';\n\nexport interface DatabaseRegistration {\n uri: string;\n content: {\n sourceArchiveZip: string;\n dbDir: string;\n };\n}\n\n/**\n * Register a CodeQL database given a local path to the database directory.\n * Validates that the database exists and has required structure.\n * Supports both full databases (with src.zip) and test databases (with src/ folder).\n */\nexport async function registerDatabase(dbPath: string): Promise {\n try {\n const resolvedPath = resolve(dbPath);\n \n // Check if database directory exists\n await access(resolvedPath, constants.F_OK);\n \n // Check for codeql-database.yml (required for all CodeQL databases)\n const dbYmlPath = resolve(resolvedPath, 'codeql-database.yml');\n await access(dbYmlPath, constants.F_OK);\n \n // Check if src.zip exists (for full databases) OR src/ directory exists (for test databases)\n const srcZipPath = resolve(resolvedPath, 'src.zip');\n const srcDirPath = resolve(resolvedPath, 'src');\n \n let hasSrcZip = false;\n let hasSrcDir = false;\n \n try {\n await access(srcZipPath, constants.F_OK);\n hasSrcZip = true;\n } catch {\n // src.zip not found, check for src/ directory\n }\n \n if (!hasSrcZip) {\n try {\n await access(srcDirPath, constants.F_OK);\n hasSrcDir = true;\n } catch {\n // src directory not found either\n }\n }\n \n if (!hasSrcZip && !hasSrcDir) {\n throw new Error(`Missing required source archive (src.zip) or source directory (src/) in: ${dbPath}`);\n }\n \n // For now, we just validate and return success message\n // In a full implementation, this would register with a query server\n const sourceType = hasSrcZip ? 'src.zip' : 'src/';\n return `Database registered: ${dbPath} (source: ${sourceType})`;\n } catch (error) {\n if (error instanceof Error) {\n const errorCode = (error as { code?: string }).code;\n if (errorCode === 'ENOENT') {\n if (error.message.includes('codeql-database.yml')) {\n throw new Error(`Missing required codeql-database.yml in: ${dbPath}`);\n }\n throw new Error(`Database path does not exist: ${dbPath}`);\n }\n if (errorCode === 'EACCES') {\n throw new Error(`Database path does not exist: ${dbPath}`);\n }\n }\n throw new Error(`Failed to register database: ${error instanceof Error ? error.message : 'Unknown error'}`);\n }\n}\n\n/**\n * Register the register database tool with the MCP server\n */\nexport function registerRegisterDatabaseTool(server: McpServer): void {\n server.tool(\n 'register_database',\n 'Register a CodeQL database given a local path to the database directory',\n {\n db_path: z.string().describe('Path to the CodeQL database directory'),\n },\n async ({ db_path }) => {\n try {\n const result = await registerDatabase(db_path);\n return {\n content: [{ type: 'text', text: result }],\n };\n } catch (error) {\n logger.error('Error registering database:', error);\n return {\n content: [\n {\n type: 'text',\n text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`,\n },\n ],\n isError: true,\n };\n }\n }\n );\n}", "/**\n * CodeQL resolve database tool\n */\n\nimport { z } from 'zod';\nimport { CLIToolDefinition, createCodeQLSchemas, defaultCLIResultProcessor } from '../../lib/cli-tool-registry';\n\nexport const codeqlResolveDatabaseTool: CLIToolDefinition = {\n name: 'codeql_resolve_database',\n description: 'Resolve database path and validate database structure',\n command: 'codeql',\n subcommand: 'resolve database',\n inputSchema: {\n database: z.string().describe('Database path to resolve'),\n format: z.enum(['text', 'json', 'betterjson']).optional()\n .describe('Output format for database information'),\n verbose: createCodeQLSchemas.verbose(),\n additionalArgs: createCodeQLSchemas.additionalArgs()\n },\n examples: [\n 'codeql resolve database -- /path/to/database',\n 'codeql resolve database --format=json -- my-database',\n 'codeql resolve database --format=betterjson -- database-dir'\n ],\n resultProcessor: defaultCLIResultProcessor\n};", "/**\n * CodeQL resolve languages tool\n */\n\nimport { z } from 'zod';\nimport { CLIToolDefinition, defaultCLIResultProcessor } from '../../lib/cli-tool-registry';\n\nexport const codeqlResolveLanguagesTool: CLIToolDefinition = {\n name: 'codeql_resolve_languages',\n description: 'List installed CodeQL extractor packs',\n command: 'codeql',\n subcommand: 'resolve languages',\n inputSchema: {\n format: z.enum(['text', 'json', 'betterjson']).optional()\n .describe('Output format for language information'),\n verbose: z.boolean().optional().describe('Enable verbose output'),\n additionalArgs: z.array(z.string()).optional().describe('Additional command-line arguments')\n },\n examples: [\n 'codeql resolve languages --format=text',\n 'codeql resolve languages --format=json',\n 'codeql resolve languages --format=betterjson'\n ],\n resultProcessor: defaultCLIResultProcessor\n};", "/**\n * CodeQL resolve library-path tool\n */\n\nimport { z } from 'zod';\nimport { CLIToolDefinition, defaultCLIResultProcessor } from '../../lib/cli-tool-registry';\n\nexport const codeqlResolveLibraryPathTool: CLIToolDefinition = {\n name: 'codeql_resolve_library-path',\n description: 'Resolve library path for CodeQL queries and libraries',\n command: 'codeql',\n subcommand: 'resolve library-path',\n inputSchema: {\n language: z.string().optional().describe('Programming language to resolve library path for'),\n format: z.enum(['text', 'json', 'betterjson']).optional()\n .describe('Output format for library path information'),\n verbose: z.boolean().optional().describe('Enable verbose output'),\n additionalArgs: z.array(z.string()).optional().describe('Additional command-line arguments')\n },\n examples: [\n 'codeql resolve library-path --language=java',\n 'codeql resolve library-path --format=json --language=python',\n 'codeql resolve library-path --format=betterjson'\n ],\n resultProcessor: defaultCLIResultProcessor\n};", "/**\n * CodeQL resolve metadata tool\n */\n\nimport { z } from 'zod';\nimport { CLIToolDefinition, defaultCLIResultProcessor } from '../../lib/cli-tool-registry';\n\nexport const codeqlResolveMetadataTool: CLIToolDefinition = {\n name: 'codeql_resolve_metadata',\n description: 'Resolve and return the key-value metadata pairs from a CodeQL query source file.',\n command: 'codeql',\n subcommand: 'resolve metadata',\n inputSchema: {\n query: z.string().describe('Query file to resolve metadata for'),\n format: z.enum(['json']).optional()\n .describe('Output format for metadata information (always JSON, optional for future compatibility)'),\n verbose: z.boolean().optional().describe('Enable verbose output'),\n additionalArgs: z.array(z.string()).optional().describe('Additional command-line arguments')\n },\n examples: [\n 'codeql resolve metadata -- relative-path/2/MyQuery.ql',\n 'codeql resolve metadata --format=json -- /absolute-plus/relative-path/2/MyQuery.ql'\n ],\n resultProcessor: defaultCLIResultProcessor\n};", "/**\n * CodeQL resolve qlref tool\n */\n\nimport { z } from 'zod';\nimport { CLIToolDefinition, createCodeQLSchemas, defaultCLIResultProcessor } from '../../lib/cli-tool-registry';\n\nexport const codeqlResolveQlrefTool: CLIToolDefinition = {\n name: 'codeql_resolve_qlref',\n description: 'Resolve qlref files to their corresponding query files',\n command: 'codeql',\n subcommand: 'resolve qlref',\n inputSchema: {\n qlref: z.string().describe('Path to the .qlref file to resolve'),\n format: z.enum(['text', 'json', 'betterjson']).optional()\n .describe('Output format for qlref resolution'),\n verbose: createCodeQLSchemas.verbose(),\n additionalArgs: createCodeQLSchemas.additionalArgs()\n },\n examples: [\n 'codeql resolve qlref -- test/MyQuery.qlref',\n 'codeql resolve qlref --format=json -- test/MyQuery.qlref',\n 'codeql resolve qlref --format=betterjson -- test/MyQuery.qlref'\n ],\n resultProcessor: defaultCLIResultProcessor\n};", "/**\n * CodeQL resolve queries tool\n */\n\nimport { z } from 'zod';\nimport { CLIToolDefinition, createCodeQLSchemas, CLIExecutionResult } from '../../lib/cli-tool-registry';\n\n/**\n * Result processor that only returns stdout for JSON formats\n * This prevents warnings/info from stderr from corrupting JSON output\n */\nconst jsonOnlyResultProcessor = (\n result: CLIExecutionResult,\n params: Record\n): string => {\n if (!result.success) {\n return `Command failed (exit code ${result.exitCode || 'unknown'}):\\n${result.error || result.stderr}`;\n }\n\n // For JSON formats (including bylanguage), only return stdout to avoid mixing warnings with JSON\n if (params.format === 'json' || params.format === 'betterjson' || params.format === 'bylanguage') {\n return result.stdout || '[]';\n }\n\n // For text format, include warnings\n let output = '';\n\n if (result.stdout) {\n output += result.stdout;\n }\n\n if (result.stderr) {\n if (output) {\n output += '\\n\\nWarnings/Info:\\n';\n }\n output += result.stderr;\n }\n\n if (!output) {\n output = 'Command executed successfully (no output)';\n }\n\n return output;\n};\n\nexport const codeqlResolveQueriesTool: CLIToolDefinition = {\n name: 'codeql_resolve_queries',\n description: 'List available CodeQL queries found on the local filesystem',\n command: 'codeql',\n subcommand: 'resolve queries',\n inputSchema: {\n directory: z.string().optional().describe('Directory to search for queries'),\n language: z.string().optional().describe('Filter queries by programming language'),\n format: z.enum(['text', 'json', 'betterjson', 'bylanguage']).optional()\n .describe('Output format for query list'),\n 'additional-packs': z.union([z.string(), z.array(z.string())]).optional()\n .describe('Additional pack directories to search for CodeQL packs'),\n verbose: createCodeQLSchemas.verbose(),\n additionalArgs: createCodeQLSchemas.additionalArgs()\n },\n examples: [\n 'codeql resolve queries',\n 'codeql resolve queries --language=java --format=json',\n 'codeql resolve queries --format=betterjson -- /path/to/queries',\n 'codeql resolve queries --additional-packs=/path/to/packs codeql/java-queries'\n ],\n resultProcessor: jsonOnlyResultProcessor\n};", "/**\n * CodeQL resolve tests tool\n */\n\nimport { z } from 'zod';\nimport { CLIToolDefinition, createCodeQLSchemas, defaultCLIResultProcessor } from '../../lib/cli-tool-registry';\n\nexport const codeqlResolveTestsTool: CLIToolDefinition = {\n name: 'codeql_resolve_tests',\n description: 'Resolve the local filesystem paths of unit tests and/or queries under some base directory',\n command: 'codeql',\n subcommand: 'resolve tests',\n inputSchema: {\n tests: z.array(z.string()).optional().describe('One or more tests (.ql, .qlref files, or test directories)'),\n format: z.enum(['text', 'json']).optional()\n .describe('Output format for test list'),\n verbose: createCodeQLSchemas.verbose(),\n additionalArgs: createCodeQLSchemas.additionalArgs()\n },\n examples: [\n 'codeql resolve tests',\n 'codeql resolve tests --format=json -- test-directory',\n 'codeql resolve tests --format=json -- test1.ql test2.ql'\n ],\n resultProcessor: defaultCLIResultProcessor\n};", "/**\n * CodeQL test accept tool\n */\n\nimport { z } from 'zod';\nimport { CLIToolDefinition, createCodeQLSchemas, defaultCLIResultProcessor } from '../../lib/cli-tool-registry';\n\nexport const codeqlTestAcceptTool: CLIToolDefinition = {\n name: 'codeql_test_accept',\n description: 'Accept new test results as the expected baseline',\n command: 'codeql',\n subcommand: 'test accept',\n inputSchema: {\n tests: z.array(z.string()).describe('One or more tests (.ql, .qlref files, or test directories)'),\n verbose: createCodeQLSchemas.verbose(),\n additionalArgs: createCodeQLSchemas.additionalArgs()\n },\n examples: [\n 'codeql test accept -- languages/java/test/MyQuery/MyQuery.qlref',\n 'codeql test accept -- languages/java/test/MyQuery/',\n 'codeql test accept -- languages/java/src/MyQuery/MyQuery.ql'\n ],\n resultProcessor: defaultCLIResultProcessor\n};", "/**\n * CodeQL test extract tool\n */\n\nimport { z } from 'zod';\nimport { CLIToolDefinition, createCodeQLSchemas, defaultCLIResultProcessor } from '../../lib/cli-tool-registry';\n\nexport const codeqlTestExtractTool: CLIToolDefinition = {\n name: 'codeql_test_extract',\n description: 'Extract test databases for CodeQL query tests',\n command: 'codeql',\n subcommand: 'test extract',\n inputSchema: {\n tests: z.array(z.string()).describe('One or more test directories or files'),\n language: z.string().optional().describe('Programming language for extraction'),\n threads: createCodeQLSchemas.threads(),\n ram: createCodeQLSchemas.ram(),\n verbose: createCodeQLSchemas.verbose(),\n additionalArgs: createCodeQLSchemas.additionalArgs()\n },\n examples: [\n 'codeql test extract -- languages/java/test/MyQuery/',\n 'codeql test extract --language=java --threads=4 -- test-directory',\n 'codeql test extract --threads=2 --ram=2048 -- multiple/test/directories'\n ],\n resultProcessor: defaultCLIResultProcessor\n};", "/**\n * CodeQL test run tool\n */\n\nimport { CLIToolDefinition, createCodeQLSchemas } from '../../lib/cli-tool-registry';\nimport { z } from 'zod';\n\nexport const codeqlTestRunTool: CLIToolDefinition = {\n name: 'codeql_test_run',\n description: 'Run CodeQL query tests',\n command: 'codeql',\n subcommand: 'test run',\n inputSchema: {\n tests: z.array(z.string()).describe('One or more tests (.ql, .qlref files, or test directories)'),\n 'show-extractor-output': z.boolean().optional()\n .describe('Show output from extractors during test execution'),\n 'keep-databases': z.boolean().optional()\n .describe('Keep test databases after running tests'),\n 'learn': z.boolean().optional()\n .describe('Accept current output as expected for failing tests'),\n logDir: z.string().optional()\n .describe('Custom directory for test execution logs (overrides CODEQL_QUERY_LOG_DIR environment variable). If not provided, uses CODEQL_QUERY_LOG_DIR or defaults to .tmp/query-logs/'),\n threads: createCodeQLSchemas.threads(),\n ram: createCodeQLSchemas.ram(),\n verbose: createCodeQLSchemas.verbose(),\n additionalArgs: createCodeQLSchemas.additionalArgs()\n },\n examples: [\n 'codeql test run /path/to/tests',\n 'codeql test run --learn /path/to/failing/tests',\n 'codeql test run --threads=4 --keep-databases /path/to/tests',\n 'codeql test run --log-dir=/custom/log/path /path/to/tests'\n ]\n};", "/**\n * CodeQL tools registration for MCP server\n * Includes both high-level helpers and CLI command wrappers\n */\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport { validateCodeQLSyntax } from '../lib/validation';\nimport { createCodeQLQuery } from '../lib/query-scaffolding';\nimport { registerCLITool } from '../lib/cli-tool-registry';\nimport {\n codeqlBqrsDecodeTool,\n codeqlBqrsInfoTool,\n codeqlBqrsInterpretTool,\n codeqlDatabaseAnalyzeTool,\n codeqlDatabaseCreateTool,\n codeqlGenerateLogSummaryTool,\n codeqlGenerateQueryHelpTool,\n codeqlPackInstallTool,\n codeqlPackLsTool,\n codeqlQueryCompileTool,\n codeqlQueryFormatTool,\n codeqlQueryRunTool,\n codeqlResolveDatabaseTool,\n codeqlResolveLanguagesTool,\n codeqlResolveLibraryPathTool,\n codeqlResolveMetadataTool,\n codeqlResolveQlrefTool,\n codeqlResolveQueriesTool,\n codeqlResolveTestsTool,\n codeqlTestAcceptTool,\n codeqlTestExtractTool,\n codeqlTestRunTool,\n registerFindClassPositionTool,\n registerFindCodeQLQueryFilesTool,\n registerFindPredicatePositionTool,\n registerProfileCodeQLQueryTool,\n registerQuickEvaluateTool,\n registerRegisterDatabaseTool\n} from './codeql';\nimport { logger } from '../utils/logger';\n\n/**\n * Register all CodeQL tools with the MCP server\n */\nexport function registerCodeQLTools(server: McpServer): void {\n // Register high-level helper tools\n \n // Tool: Validate CodeQL Query (heuristic-based)\n server.tool(\n 'validate_codeql_query',\n 'Quick heuristic validation for CodeQL query structure - checks for common patterns like from/where/select clauses and metadata presence. Does NOT compile the query. For authoritative validation with actual compilation, use codeql_lsp_diagnostics instead.',\n {\n query: z.string().describe('The CodeQL query to validate'),\n language: z.string().optional().describe('Target programming language'),\n },\n async ({ query, language }) => {\n try {\n const validation = validateCodeQLSyntax(query, language);\n return {\n content: [{ type: 'text', text: JSON.stringify(validation, null, 2) }],\n };\n } catch (error) {\n logger.error('Error validating CodeQL query:', error);\n return {\n content: [\n {\n type: 'text',\n text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`,\n },\n ],\n isError: true,\n };\n }\n }\n );\n\n // Tool: Create CodeQL Query\n server.tool(\n 'create_codeql_query',\n 'Create directory structure and files for a new CodeQL query with tests',\n {\n basePath: z.string().describe('Base path where src/ and test/ directories will be created'),\n queryName: z.string().describe('Name of the query (e.g., MySecurityQuery)'),\n language: z.string().describe('Target programming language (e.g., javascript, python, java)'),\n description: z.string().optional().describe('Description of what the query does'),\n queryId: z.string().optional().describe('Custom query ID (defaults to language/example/queryname)'),\n },\n async ({ basePath, queryName, language, description, queryId }) => {\n try {\n const result = createCodeQLQuery({\n basePath,\n queryName,\n language,\n description,\n queryId\n });\n \n const summary = {\n success: true,\n queryPath: result.queryPath,\n testPath: result.testPath,\n qlrefPath: result.qlrefPath,\n testCodePath: result.testCodePath,\n filesCreated: result.filesCreated,\n nextSteps: [\n 'Review and customize the generated query in: ' + result.queryPath,\n 'Add test cases to: ' + result.testCodePath,\n 'Run codeql_pack_install to install dependencies',\n 'Run codeql_test_extract to create test database',\n 'Run codeql_test_run to execute tests'\n ]\n };\n \n return {\n content: [{ type: 'text', text: JSON.stringify(summary, null, 2) }],\n };\n } catch (error) {\n logger.error('Error creating CodeQL query:', error);\n return {\n content: [\n {\n type: 'text',\n text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`,\n },\n ],\n isError: true,\n };\n }\n }\n );\n\n // Register CLI tools (alphabetically by tool name)\n registerCLITool(server, codeqlBqrsDecodeTool);\n registerCLITool(server, codeqlBqrsInfoTool);\n registerCLITool(server, codeqlBqrsInterpretTool);\n registerCLITool(server, codeqlDatabaseAnalyzeTool);\n registerCLITool(server, codeqlDatabaseCreateTool);\n registerCLITool(server, codeqlGenerateLogSummaryTool);\n registerCLITool(server, codeqlGenerateQueryHelpTool);\n registerCLITool(server, codeqlPackInstallTool);\n registerCLITool(server, codeqlPackLsTool);\n registerCLITool(server, codeqlQueryCompileTool);\n registerCLITool(server, codeqlQueryFormatTool);\n registerCLITool(server, codeqlQueryRunTool);\n registerCLITool(server, codeqlResolveDatabaseTool);\n registerCLITool(server, codeqlResolveLanguagesTool);\n registerCLITool(server, codeqlResolveLibraryPathTool);\n registerCLITool(server, codeqlResolveMetadataTool);\n registerCLITool(server, codeqlResolveQlrefTool);\n registerCLITool(server, codeqlResolveQueriesTool);\n registerCLITool(server, codeqlResolveTestsTool);\n registerCLITool(server, codeqlTestAcceptTool);\n registerCLITool(server, codeqlTestExtractTool);\n registerCLITool(server, codeqlTestRunTool);\n\n // Register new MCP tools (inspired by JordyZomer/codeql-mcp repository)\n registerFindClassPositionTool(server);\n registerFindCodeQLQueryFilesTool(server);\n registerFindPredicatePositionTool(server);\n registerProfileCodeQLQueryTool(server);\n registerQuickEvaluateTool(server);\n registerRegisterDatabaseTool(server);\n}\n", "/**\n * CodeQL query validation utilities\n */\n\nimport { resolve, normalize, isAbsolute, relative } from 'path';\n\nexport interface CodeQLValidationResult {\n isValid: boolean;\n errors: string[];\n warnings: string[];\n suggestions: string[];\n}\n\n/**\n * Validates CodeQL query syntax and structure\n */\nexport function validateCodeQLSyntax(query: string, _language?: string): CodeQLValidationResult {\n const validation: CodeQLValidationResult = {\n isValid: true,\n errors: [],\n warnings: [],\n suggestions: [],\n };\n\n if (!query.trim()) {\n validation.isValid = false;\n validation.errors.push('Query cannot be empty');\n return validation;\n }\n\n if (!query.includes('from') && !query.includes('select')) {\n validation.warnings.push('Query should typically include \"from\" and \"select\" clauses');\n }\n\n if (!query.includes('@name') && !query.includes('@description')) {\n validation.suggestions.push('Consider adding @name and @description metadata');\n }\n\n return validation;\n}\n\n/**\n * Validates a file path to prevent path traversal attacks\n * @param filePath - The file path to validate\n * @param workspaceRoot - Optional workspace root directory. If not provided, allows any absolute path but still blocks traversal attempts\n * @returns The validated absolute path\n * @throws Error if the path contains path traversal sequences\n */\nexport function validateFilePath(filePath: string, workspaceRoot?: string): string {\n // Normalize the path to resolve any . or .. segments\n const normalizedPath = normalize(filePath);\n \n // Check for path traversal attempts in the normalized path\n // This blocks paths like \"../../../etc/passwd\" even after normalization\n if (normalizedPath.includes('..')) {\n throw new Error(`Invalid file path: path traversal detected in \"${filePath}\"`);\n }\n \n // Resolve to absolute path\n const absolutePath = isAbsolute(normalizedPath) \n ? normalizedPath \n : resolve(workspaceRoot || process.cwd(), normalizedPath);\n \n // If workspace root is specified, ensure the resolved path is within it\n if (workspaceRoot) {\n const relativePath = relative(workspaceRoot, absolutePath);\n \n // If relative path starts with .. or is absolute, it's outside workspace\n if (relativePath.startsWith('..') || isAbsolute(relativePath)) {\n throw new Error(`Invalid file path: \"${filePath}\" is outside the workspace root`);\n }\n }\n \n return absolutePath;\n}", "/**\n * CodeQL query scaffolding utilities\n * Handles creation of query directory structure and files\n */\n\nimport * as fs from 'fs';\nimport * as path from 'path';\n\nexport interface QueryScaffoldingOptions {\n basePath: string;\n queryName: string;\n language: string;\n description?: string;\n queryId?: string;\n}\n\nexport interface QueryScaffoldingResult {\n queryPath: string;\n testPath: string;\n qlrefPath: string;\n testCodePath: string;\n filesCreated: string[];\n}\n\n/**\n * Get the file extension for test code based on language\n */\nfunction getLanguageExtension(language: string): string {\n const extensions: Record = {\n javascript: 'js',\n typescript: 'ts',\n python: 'py',\n java: 'java',\n csharp: 'cs',\n cpp: 'cpp',\n go: 'go',\n ruby: 'rb',\n actions: 'yml'\n };\n return extensions[language.toLowerCase()] || 'txt';\n}\n\n/**\n * Generate query template content\n */\nfunction generateQueryTemplate(\n queryName: string,\n language: string,\n description?: string,\n queryId?: string\n): string {\n const desc = description || `${queryName} query`;\n const id = queryId || `${language}/example/${queryName.toLowerCase()}`;\n \n return `/**\n * @id ${id}\n * @name ${queryName}\n * @description ${desc}\n * @kind problem\n * @precision medium\n * @problem.severity warning\n */\n\nimport ${language}\n\n// TODO: Implement query logic\nfrom File f\nwhere f.getBaseName() = \"${queryName}.${getLanguageExtension(language)}\"\nselect f, \"TODO: Add query logic\"\n`;\n}\n\n/**\n * Create the directory structure and files for a new CodeQL query\n */\nexport function createCodeQLQuery(options: QueryScaffoldingOptions): QueryScaffoldingResult {\n const { basePath, queryName, language, description, queryId } = options;\n \n // Resolve absolute paths\n const absoluteBasePath = path.resolve(basePath);\n \n // Define paths with intermediate directory\n const srcDir = path.join(absoluteBasePath, 'src', queryName);\n const testDir = path.join(absoluteBasePath, 'test', queryName);\n \n const queryPath = path.join(srcDir, `${queryName}.ql`);\n const qlrefPath = path.join(testDir, `${queryName}.qlref`);\n const testCodePath = path.join(testDir, `${queryName}.${getLanguageExtension(language)}`);\n \n const filesCreated: string[] = [];\n \n try {\n // Create directories (recursive: true is a no-op if they already exist)\n fs.mkdirSync(srcDir, { recursive: true });\n fs.mkdirSync(testDir, { recursive: true });\n \n // Create files atomically using 'wx' flag (exclusive create) to avoid\n // TOCTOU race between existsSync check and writeFileSync (CWE-367).\n // The 'wx' flag fails with EEXIST if the file already exists.\n try {\n const queryContent = generateQueryTemplate(queryName, language, description, queryId);\n fs.writeFileSync(queryPath, queryContent, { encoding: 'utf8', flag: 'wx' });\n filesCreated.push(queryPath);\n } catch (e: unknown) {\n const err = e as { code?: string };\n if (err.code !== 'EEXIST') throw e;\n }\n \n try {\n const qlrefContent = `${queryName}/${queryName}.ql\\n`;\n fs.writeFileSync(qlrefPath, qlrefContent, { encoding: 'utf8', flag: 'wx' });\n filesCreated.push(qlrefPath);\n } catch (e: unknown) {\n const err = e as { code?: string };\n if (err.code !== 'EEXIST') throw e;\n }\n \n try {\n const testCodeContent = `// Test code for ${queryName}\\n// TODO: Add test cases\\n`;\n fs.writeFileSync(testCodePath, testCodeContent, { encoding: 'utf8', flag: 'wx' });\n filesCreated.push(testCodePath);\n } catch (e: unknown) {\n const err = e as { code?: string };\n if (err.code !== 'EEXIST') throw e;\n }\n \n return {\n queryPath,\n testPath: testDir,\n qlrefPath,\n testCodePath,\n filesCreated\n };\n } catch (error) {\n throw new Error(`Failed to create query scaffolding: ${error instanceof Error ? error.message : 'Unknown error'}`);\n }\n}\n", "/**\n * CodeQL learning resources utilities\n */\n\nimport { readFileSync } from 'fs';\nimport { join, dirname } from 'path';\nimport { fileURLToPath } from 'url';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\n\n/**\n * Get the getting started guide content\n */\nexport function getGettingStartedGuide(): string {\n try {\n return readFileSync(join(__dirname, '../resources/getting-started.md'), 'utf-8');\n } catch {\n return 'Getting started guide not available';\n }\n}\n\n/**\n * Get the query basics guide content\n */\nexport function getQueryBasicsGuide(): string {\n try {\n return readFileSync(join(__dirname, '../resources/query-basics.md'), 'utf-8');\n } catch {\n return 'Query basics guide not available';\n }\n}\n\n/**\n * Get the security templates content\n */\nexport function getSecurityTemplates(): string {\n try {\n return readFileSync(join(__dirname, '../resources/security-templates.md'), 'utf-8');\n } catch {\n return 'Security templates not available';\n }\n}\n\n/**\n * Get the performance patterns content\n */\nexport function getPerformancePatterns(): string {\n try {\n return readFileSync(join(__dirname, '../resources/performance-patterns.md'), 'utf-8');\n } catch {\n return 'Performance patterns not available';\n }\n}", "/**\n * CodeQL resources registration for MCP server\n */\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport {\n getGettingStartedGuide,\n getQueryBasicsGuide,\n getSecurityTemplates,\n getPerformancePatterns,\n} from '../lib/resources';\n\n/**\n * Register all CodeQL resources with the MCP server\n */\nexport function registerCodeQLResources(server: McpServer): void {\n // Getting Started Guide\n server.resource(\n 'CodeQL Getting Started',\n 'codeql://learning/getting-started',\n {\n description: 'Comprehensive introduction to CodeQL for beginners',\n mimeType: 'text/markdown',\n },\n async () => {\n return {\n contents: [\n {\n uri: 'codeql://learning/getting-started',\n mimeType: 'text/markdown',\n text: getGettingStartedGuide(),\n },\n ],\n };\n }\n );\n\n // Query Basics Guide\n server.resource(\n 'CodeQL Query Basics',\n 'codeql://learning/query-basics',\n {\n description: 'Learn the fundamentals of writing CodeQL queries',\n mimeType: 'text/markdown',\n },\n async () => {\n return {\n contents: [\n {\n uri: 'codeql://learning/query-basics',\n mimeType: 'text/markdown',\n text: getQueryBasicsGuide(),\n },\n ],\n };\n }\n );\n\n // Security Templates\n server.resource(\n 'CodeQL Security Templates',\n 'codeql://templates/security',\n {\n description: 'Ready-to-use security query templates',\n mimeType: 'text/markdown',\n },\n async () => {\n return {\n contents: [\n {\n uri: 'codeql://templates/security',\n mimeType: 'text/markdown',\n text: getSecurityTemplates(),\n },\n ],\n };\n }\n );\n\n // Performance Patterns\n server.resource(\n 'CodeQL Performance Patterns',\n 'codeql://patterns/performance',\n {\n description: 'Best practices for writing efficient CodeQL queries',\n mimeType: 'text/markdown',\n },\n async () => {\n return {\n contents: [\n {\n uri: 'codeql://patterns/performance',\n mimeType: 'text/markdown',\n text: getPerformancePatterns(),\n },\n ],\n };\n }\n );\n}\n", "/**\n * CodeQL LSP Diagnostics tool for MCP server.\n *\n * Provides real-time QL code validation through LSP communication.\n * Renamed from `codeql_language_server_eval` to `codeql_lsp_diagnostics`\n * for consistency with the `codeql_lsp_*` tool naming convention.\n */\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport { Diagnostic, LanguageServerOptions } from '../../lib/language-server';\nimport { logger } from '../../utils/logger';\nimport { getProjectTmpDir } from '../../utils/temp-dir';\nimport { join } from 'path';\nimport { pathToFileURL } from 'url';\nimport { getInitializedLanguageServer } from './lsp-server-helper';\n\nexport interface LspDiagnosticsParams {\n qlCode: string;\n serverOptions?: LanguageServerOptions;\n workspaceUri?: string;\n}\n\nexport interface LspDiagnosticsResult {\n diagnostics: Diagnostic[];\n formattedOutput: string;\n isValid: boolean;\n summary: {\n errorCount: number;\n hintCount: number;\n infoCount: number;\n warningCount: number;\n };\n}\n\n/**\n * Format diagnostics for human-readable output.\n */\nfunction formatDiagnostics(diagnostics: Diagnostic[]): string {\n if (diagnostics.length === 0) {\n return '\u2705 No issues found in QL code';\n }\n\n const lines: string[] = [];\n lines.push(`Found ${diagnostics.length} issue(s):\\n`);\n\n diagnostics.forEach((diagnostic, index) => {\n const severityIcon = getSeverityIcon(diagnostic.severity);\n const severityName = getSeverityName(diagnostic.severity);\n const location = `Line ${diagnostic.range.start.line + 1}, Column ${diagnostic.range.start.character + 1}`;\n\n lines.push(`${index + 1}. ${severityIcon} ${severityName} at ${location}`);\n lines.push(` ${diagnostic.message}`);\n if (diagnostic.source) {\n lines.push(` Source: ${diagnostic.source}`);\n }\n if (diagnostic.code) {\n lines.push(` Code: ${diagnostic.code}`);\n }\n lines.push('');\n });\n\n return lines.join('\\n');\n}\n\nfunction getSeverityIcon(severity: number): string {\n switch (severity) {\n case 1: return '\u274C'; // Error\n case 2: return '\u26A0\uFE0F'; // Warning\n case 3: return '\u2139\uFE0F'; // Information\n case 4: return '\uD83D\uDCA1'; // Hint\n default: return '\u2753';\n }\n}\n\nfunction getSeverityName(severity: number): string {\n switch (severity) {\n case 1: return 'Error';\n case 2: return 'Warning';\n case 3: return 'Information';\n case 4: return 'Hint';\n default: return 'Unknown';\n }\n}\n\n/**\n * Evaluate QL code using the CodeQL Language Server and return diagnostics.\n */\nexport async function lspDiagnostics({\n qlCode,\n workspaceUri,\n serverOptions = {}\n}: LspDiagnosticsParams): Promise {\n try {\n logger.info('Evaluating QL code via Language Server...');\n\n const languageServer = await getInitializedLanguageServer({\n serverOptions,\n workspaceUri,\n });\n\n // Generate unique URI for this evaluation\n const evalUri = pathToFileURL(join(getProjectTmpDir('lsp-eval'), `eval_${Date.now()}.ql`)).href;\n\n const diagnostics = await languageServer.evaluateQL(qlCode, evalUri);\n\n // Count diagnostics by severity\n const summary = {\n errorCount: diagnostics.filter(d => d.severity === 1).length,\n hintCount: diagnostics.filter(d => d.severity === 4).length,\n infoCount: diagnostics.filter(d => d.severity === 3).length,\n warningCount: diagnostics.filter(d => d.severity === 2).length,\n };\n\n const isValid = summary.errorCount === 0;\n const formattedOutput = formatDiagnostics(diagnostics);\n\n logger.info(`QL evaluation complete. Valid: ${isValid}, Issues: ${diagnostics.length}`);\n\n return {\n diagnostics,\n formattedOutput,\n isValid,\n summary,\n };\n\n } catch (error) {\n logger.error('Error evaluating QL code:', error);\n throw new Error(`QL evaluation failed: ${error instanceof Error ? error.message : 'Unknown error'}`);\n }\n}\n\n/**\n * Shutdown the language server via the server manager.\n */\nexport async function shutdownDiagnosticsServer(): Promise {\n const { getServerManager } = await import('../../lib/server-manager');\n const manager = getServerManager();\n await manager.shutdownServer('language');\n}\n\n/**\n * Register the codeql_lsp_diagnostics tool with the MCP server.\n */\nexport function registerLspDiagnosticsTool(server: McpServer): void {\n server.tool(\n 'codeql_lsp_diagnostics',\n 'Authoritative syntax and semantic validation of CodeQL (QL) code via the CodeQL Language Server. Compiles the query and provides real-time diagnostics with precise error locations. Use this for accurate validation; for quick heuristic checks without compilation, use validate_codeql_query instead. Note: inline ql_code is evaluated as a virtual document and cannot resolve pack imports (e.g. `import javascript`). For validating queries with imports, use codeql_query_compile on the actual file instead.',\n {\n log_level: z.enum(['OFF', 'ERROR', 'WARN', 'INFO', 'DEBUG', 'TRACE', 'ALL']).optional().describe('Language server log level'),\n ql_code: z.string().describe('The CodeQL (QL) code to evaluate for syntax and semantic errors'),\n search_path: z.string().optional().describe('Optional search path for CodeQL libraries'),\n workspace_uri: z.string().optional().describe('Optional workspace URI for context (defaults to ./ql directory)'),\n },\n async ({ ql_code, workspace_uri, search_path, log_level }) => {\n try {\n const serverOptions: LanguageServerOptions = {};\n\n if (search_path) {\n serverOptions.searchPath = search_path;\n }\n if (log_level) {\n serverOptions.loglevel = log_level;\n }\n\n const result = await lspDiagnostics({\n qlCode: ql_code,\n serverOptions,\n workspaceUri: workspace_uri,\n });\n\n // Return structured result\n const responseContent = {\n diagnostics: result.diagnostics.map(d => ({\n code: d.code,\n column: d.range.start.character + 1, // Convert to 1-based column numbers\n line: d.range.start.line + 1, // Convert to 1-based line numbers\n message: d.message,\n severity: getSeverityName(d.severity),\n source: d.source,\n })),\n formattedOutput: result.formattedOutput,\n isValid: result.isValid,\n summary: result.summary,\n };\n\n return {\n content: [\n {\n text: JSON.stringify(responseContent, null, 2),\n type: 'text',\n }\n ],\n };\n\n } catch (error) {\n logger.error('Error in codeql_lsp_diagnostics tool:', error);\n return {\n content: [\n {\n text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`,\n type: 'text',\n },\n ],\n isError: true,\n };\n }\n }\n );\n\n // NOTE: Cleanup is handled centrally by shutdownServerManager() in\n // codeql-development-mcp-server.ts (setupGracefulShutdown). Registering\n // additional process.on('SIGINT'/'SIGTERM') handlers here would\n // accumulate on repeated calls and is unnecessary.\n}\n", "/**\n * Shared helper for obtaining a running, initialized CodeQL Language Server.\n *\n * Both `lsp-diagnostics.ts` and `lsp-handlers.ts` need to:\n * 1. Build a `LanguageServerConfig` with sensible defaults\n * 2. Obtain a server instance from the `CodeQLServerManager`\n * 3. Resolve a workspace URI (relative \u2192 absolute \u2192 `file://`)\n * 4. Initialize the server with the resolved workspace\n *\n * Centralizing this logic avoids duplication and ensures consistent\n * default behaviour across all LSP tools.\n */\n\nimport { isAbsolute, resolve } from 'path';\nimport { pathToFileURL } from 'url';\n\nimport { CodeQLLanguageServer, LanguageServerOptions } from '../../lib/language-server';\nimport { LanguageServerConfig } from '../../lib/server-config';\nimport { getServerManager } from '../../lib/server-manager';\nimport { logger } from '../../utils/logger';\n\n/**\n * Options accepted by {@link getInitializedLanguageServer}.\n */\nexport interface InitializedServerOptions {\n /** Language-server-level options (loglevel, searchPath, etc.). */\n serverOptions?: LanguageServerOptions;\n /** Workspace URI \u2014 may be a `file://` URI, absolute path, or relative path. */\n workspaceUri?: string;\n}\n\n/**\n * Return a running, initialized `CodeQLLanguageServer`.\n *\n * - Resolves `searchPath` to the bundled `ql` directory when not provided.\n * - Converts relative / bare-directory `workspaceUri` paths to `file://` URIs\n * resolved against `getUserWorkspaceDir()` (respects `CODEQL_MCP_WORKSPACE`).\n * - Falls back to the bundled `ql` directory when no workspace is given.\n * - Delegates lifecycle management to the global `CodeQLServerManager`.\n */\nexport async function getInitializedLanguageServer(\n opts: InitializedServerOptions = {},\n): Promise {\n const { packageRootDir: pkgRoot, getUserWorkspaceDir } = await import('../../utils/package-paths');\n const options = opts.serverOptions ?? {};\n\n const config: LanguageServerConfig = {\n checkErrors: 'ON_CHANGE',\n loglevel: options.loglevel ?? 'WARN',\n searchPath: options.searchPath ?? resolve(pkgRoot, 'ql'),\n synchronous: options.synchronous,\n verbosity: options.verbosity,\n };\n\n const manager = getServerManager();\n const server = await manager.getLanguageServer(config);\n\n // Normalize workspace URI: convert relative / bare directory paths to\n // file:// URIs against getUserWorkspaceDir() (respects CODEQL_MCP_WORKSPACE).\n let effectiveUri = opts.workspaceUri;\n if (effectiveUri && !effectiveUri.startsWith('file://')) {\n const absWorkspace = isAbsolute(effectiveUri)\n ? effectiveUri\n : resolve(getUserWorkspaceDir(), effectiveUri);\n effectiveUri = pathToFileURL(absWorkspace).href;\n }\n effectiveUri = effectiveUri ?? pathToFileURL(resolve(pkgRoot, 'ql')).href;\n\n await server.initialize(effectiveUri);\n logger.debug(`Language server initialized with workspace: ${effectiveUri}`);\n\n return server;\n}\n", "/**\n * CodeQL LSP tool handlers.\n *\n * Bridges MCP tool invocations to LSP requests on the CodeQL Language Server.\n * Each handler acquires a language server via the CodeQLServerManager,\n * opens the requested document, sends the LSP request, and returns the result.\n */\n\nimport { readFile } from 'fs/promises';\nimport { isAbsolute, resolve } from 'path';\nimport { pathToFileURL } from 'url';\n\nimport {\n CompletionItem,\n LSPLocation,\n TextDocumentPositionParams,\n} from '../../lib/language-server';\nimport { logger } from '../../utils/logger';\nimport { getUserWorkspaceDir } from '../../utils/package-paths';\nimport { getInitializedLanguageServer } from './lsp-server-helper';\n\n/**\n * Common parameters for LSP tool invocations.\n */\nexport interface LSPToolParams {\n /** 0-based character offset within the line. */\n character: number;\n /** Optional override for the file content (if not reading from disk). */\n fileContent?: string;\n /**\n * Path to the QL file. May be absolute or relative.\n * Relative paths are resolved against `getUserWorkspaceDir()`\n * (respects the `CODEQL_MCP_WORKSPACE` environment variable).\n */\n filePath: string;\n /** 0-based line number in the document. */\n line: number;\n /** Optional search path for CodeQL libraries. */\n searchPath?: string;\n /** Optional workspace URI for context. */\n workspaceUri?: string;\n}\n\n/**\n * Get a running, initialized language server for the given parameters.\n */\nasync function getInitializedServer(params: LSPToolParams) {\n return getInitializedLanguageServer({\n serverOptions: { searchPath: params.searchPath },\n workspaceUri: params.workspaceUri,\n });\n}\n\n/**\n * Resolve the file path to an absolute path and file:// URI.\n */\nfunction prepareDocumentPosition(\n params: LSPToolParams,\n): { absPath: string; docUri: string } {\n // Resolve relative paths against getUserWorkspaceDir() so that\n // CODEQL_MCP_WORKSPACE is respected and behaviour is consistent across tools.\n const absPath = isAbsolute(params.filePath)\n ? params.filePath\n : resolve(getUserWorkspaceDir(), params.filePath);\n const docUri = pathToFileURL(absPath).href;\n\n return { absPath, docUri };\n}\n\n/**\n * Read file content and open the document in the language server.\n */\nasync function openDocumentForPosition(\n server: Awaited>,\n params: LSPToolParams,\n absPath: string,\n docUri: string,\n): Promise {\n // Read file content from disk or use provided content\n let text: string;\n if (params.fileContent) {\n text = params.fileContent;\n } else {\n try {\n text = await readFile(absPath, 'utf-8');\n } catch (error) {\n throw new Error(`Cannot read file: ${absPath}: ${error instanceof Error ? error.message : error}`);\n }\n }\n\n // Open the document so the language server knows about it\n server.openDocument(docUri, text);\n\n return {\n position: { character: params.character, line: params.line },\n textDocument: { uri: docUri },\n };\n}\n\n/**\n * Get code completions at a position.\n */\nexport async function lspCompletion(params: LSPToolParams): Promise {\n logger.info(`LSP completion at ${params.filePath}:${params.line}:${params.character}`);\n const server = await getInitializedServer(params);\n const { absPath, docUri } = prepareDocumentPosition(params);\n const positionParams = await openDocumentForPosition(server, params, absPath, docUri);\n\n try {\n return await server.getCompletions(positionParams);\n } finally {\n server.closeDocument(docUri);\n }\n}\n\n/**\n * Go to definition of a symbol at a position.\n */\nexport async function lspDefinition(params: LSPToolParams): Promise {\n logger.info(`LSP definition at ${params.filePath}:${params.line}:${params.character}`);\n const server = await getInitializedServer(params);\n const { absPath, docUri } = prepareDocumentPosition(params);\n const positionParams = await openDocumentForPosition(server, params, absPath, docUri);\n\n try {\n return await server.getDefinition(positionParams);\n } finally {\n server.closeDocument(docUri);\n }\n}\n\n/**\n * Find all references to a symbol at a position.\n */\nexport async function lspReferences(params: LSPToolParams): Promise {\n logger.info(`LSP references at ${params.filePath}:${params.line}:${params.character}`);\n const server = await getInitializedServer(params);\n const { absPath, docUri } = prepareDocumentPosition(params);\n const positionParams = await openDocumentForPosition(server, params, absPath, docUri);\n\n try {\n return await server.getReferences({\n ...positionParams,\n context: { includeDeclaration: true },\n });\n } finally {\n server.closeDocument(docUri);\n }\n}\n", "/**\n * CodeQL LSP MCP tool definitions.\n *\n * Registers four LSP-based tools:\n * - codeql_lsp_completion \u2013 code completions at cursor position\n * - codeql_lsp_definition \u2013 go to definition\n * - codeql_lsp_diagnostics \u2013 QL code validation via LSP diagnostics\n * - codeql_lsp_references \u2013 find all references\n */\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport { registerLspDiagnosticsTool } from './lsp-diagnostics';\nimport {\n lspCompletion,\n lspDefinition,\n lspReferences,\n} from './lsp-handlers';\nimport { logger } from '../../utils/logger';\n\n/**\n * Shared Zod schema for the common LSP tool parameters.\n */\nconst lspParamsSchema = {\n character: z.number().int().min(0).describe('0-based character offset within the line'),\n file_content: z.string().optional().describe('Optional file content override (reads from disk if omitted)'),\n file_path: z.string().describe('Path to the CodeQL (.ql/.qll) file. Relative paths are resolved against the user workspace directory (see CODEQL_MCP_WORKSPACE).'),\n line: z.number().int().min(0).describe('0-based line number in the document'),\n search_path: z.string().optional().describe('Optional search path for CodeQL libraries'),\n workspace_uri: z.string().optional().describe('Optional workspace URI for context (defaults to ./ql directory)'),\n};\n\n/**\n * Helper to build the handler params from the raw MCP tool input.\n */\nfunction toHandlerParams(input: {\n character: number;\n file_content?: string;\n file_path: string;\n line: number;\n search_path?: string;\n workspace_uri?: string;\n}) {\n return {\n character: input.character,\n fileContent: input.file_content,\n filePath: input.file_path,\n line: input.line,\n searchPath: input.search_path,\n workspaceUri: input.workspace_uri,\n };\n}\n\n/**\n * Register all LSP-based tools with the MCP server.\n */\nexport function registerLSPTools(server: McpServer): void {\n // --- codeql_lsp_diagnostics (relocated from codeql_language_server_eval) ---\n registerLspDiagnosticsTool(server);\n\n // --- codeql_lsp_completion ---\n server.tool(\n 'codeql_lsp_completion',\n 'Get code completions at a cursor position in a CodeQL file. Returns completion items with labels, documentation, and insert text. The file must be a .ql or .qll file. IMPORTANT: Set workspace_uri to the pack or workspace root directory for dependency resolution; without it, completions for imported libraries will be empty.',\n lspParamsSchema,\n async (input) => {\n try {\n const items = await lspCompletion(toHandlerParams(input));\n return {\n content: [{\n text: JSON.stringify({\n completionCount: items.length,\n items: items.map((item) => ({\n detail: item.detail,\n documentation: item.documentation,\n insertText: item.insertText,\n kind: item.kind,\n label: item.label,\n })),\n }, null, 2),\n type: 'text' as const,\n }],\n };\n } catch (error) {\n logger.error('codeql_lsp_completion error:', error);\n return {\n content: [{ text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`, type: 'text' as const }],\n isError: true,\n };\n }\n },\n );\n\n // --- codeql_lsp_definition ---\n server.tool(\n 'codeql_lsp_definition',\n 'Go to the definition of a CodeQL symbol at a given position. Returns one or more file locations where the symbol is defined. Set workspace_uri to the pack root for dependency resolution.',\n lspParamsSchema,\n async (input) => {\n try {\n const locations = await lspDefinition(toHandlerParams(input));\n return {\n content: [{\n text: JSON.stringify({\n definitionCount: locations.length,\n locations: locations.map((loc) => ({\n endCharacter: loc.range.end.character,\n endLine: loc.range.end.line + 1,\n startCharacter: loc.range.start.character,\n startLine: loc.range.start.line + 1,\n uri: loc.uri,\n })),\n }, null, 2),\n type: 'text' as const,\n }],\n };\n } catch (error) {\n logger.error('codeql_lsp_definition error:', error);\n return {\n content: [{ text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`, type: 'text' as const }],\n isError: true,\n };\n }\n },\n );\n\n // --- codeql_lsp_references ---\n server.tool(\n 'codeql_lsp_references',\n 'Find all references to a CodeQL symbol at a given position. Returns file locations of all usages, including the declaration. Set workspace_uri to the pack root for dependency resolution.',\n lspParamsSchema,\n async (input) => {\n try {\n const locations = await lspReferences(toHandlerParams(input));\n return {\n content: [{\n text: JSON.stringify({\n locations: locations.map((loc) => ({\n endCharacter: loc.range.end.character,\n endLine: loc.range.end.line + 1,\n startCharacter: loc.range.start.character,\n startLine: loc.range.start.line + 1,\n uri: loc.uri,\n })),\n referenceCount: locations.length,\n }, null, 2),\n type: 'text' as const,\n }],\n };\n } catch (error) {\n logger.error('codeql_lsp_references error:', error);\n return {\n content: [{ text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`, type: 'text' as const }],\n isError: true,\n };\n }\n },\n );\n}\n", "/**\n * Language-specific resources implementation\n * Dynamically loads and serves language-specific AST references and security patterns\n */\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { readFileSync, existsSync } from 'fs';\nimport { join } from 'path';\nimport { LANGUAGE_RESOURCES } from '../types/language-types';\nimport { workspaceRootDir } from '../utils/package-paths';\nimport { logger } from '../utils/logger';\n\n/**\n * Get the base path for ql resources.\n * Uses the workspace root (monorepo root or package root) so that\n * resource files are found regardless of the server's process.cwd().\n */\nfunction getQLBasePath(): string {\n return workspaceRootDir;\n}\n\n/**\n * Load content from a resource file\n */\nfunction loadResourceContent(relativePath: string): string | null {\n try {\n const fullPath = join(getQLBasePath(), relativePath);\n \n if (!existsSync(fullPath)) {\n logger.warn(`Resource file not found: ${fullPath}`);\n return null;\n }\n \n return readFileSync(fullPath, 'utf-8');\n } catch (error) {\n logger.error(`Error loading resource file ${relativePath}:`, error);\n return null;\n }\n}\n\n/**\n * Register language-specific AST resources\n */\nexport function registerLanguageASTResources(server: McpServer): void {\n for (const langResource of LANGUAGE_RESOURCES) {\n if (!langResource.astFile) continue;\n \n const resourceUri = `codeql://languages/${langResource.language}/ast`;\n \n server.resource(\n `${langResource.language.toUpperCase()} AST Reference`,\n resourceUri,\n {\n description: `CodeQL AST class reference for ${langResource.language} programs`,\n mimeType: 'text/markdown'\n },\n async () => {\n const content = loadResourceContent(langResource.astFile!);\n \n if (!content) {\n return {\n contents: [{\n uri: resourceUri,\n mimeType: 'text/markdown',\n text: `# ${langResource.language.toUpperCase()} AST Reference\\n\\nResource file not found or could not be loaded.`\n }]\n };\n }\n \n return {\n contents: [{\n uri: resourceUri,\n mimeType: 'text/markdown',\n text: content\n }]\n };\n }\n );\n }\n}\n\n/**\n * Register language-specific security pattern resources\n */\nexport function registerLanguageSecurityResources(server: McpServer): void {\n for (const langResource of LANGUAGE_RESOURCES) {\n if (!langResource.securityFile) continue;\n \n const resourceUri = `codeql://languages/${langResource.language}/security`;\n \n server.resource(\n `${langResource.language.toUpperCase()} Security Patterns`,\n resourceUri,\n {\n description: `CodeQL security query patterns and framework modeling for ${langResource.language}`,\n mimeType: 'text/markdown'\n },\n async () => {\n const content = loadResourceContent(langResource.securityFile!);\n \n if (!content) {\n return {\n contents: [{\n uri: resourceUri,\n mimeType: 'text/markdown',\n text: `# ${langResource.language.toUpperCase()} Security Patterns\\n\\nResource file not found or could not be loaded.`\n }]\n };\n }\n \n return {\n contents: [{\n uri: resourceUri,\n mimeType: 'text/markdown',\n text: content\n }]\n };\n }\n );\n }\n}\n\n/**\n * Register additional language-specific resources (like Go's dataflow patterns)\n */\nexport function registerLanguageAdditionalResources(server: McpServer): void {\n for (const langResource of LANGUAGE_RESOURCES) {\n if (!langResource.additionalFiles) continue;\n \n for (const [resourceType, filePath] of Object.entries(langResource.additionalFiles)) {\n const resourceUri = `codeql://languages/${langResource.language}/${resourceType}`;\n \n server.resource(\n `${langResource.language.toUpperCase()} ${resourceType.replace('-', ' ').replace(/\\b\\w/g, l => l.toUpperCase())}`,\n resourceUri,\n {\n description: `CodeQL ${resourceType.replace('-', ' ')} guide for ${langResource.language}`,\n mimeType: 'text/markdown'\n },\n async () => {\n const content = loadResourceContent(filePath);\n \n if (!content) {\n return {\n contents: [{\n uri: resourceUri,\n mimeType: 'text/markdown',\n text: `# ${langResource.language.toUpperCase()} ${resourceType.replace('-', ' ').replace(/\\b\\w/g, l => l.toUpperCase())}\\n\\nResource file not found or could not be loaded.`\n }]\n };\n }\n \n return {\n contents: [{\n uri: resourceUri,\n mimeType: 'text/markdown',\n text: content\n }]\n };\n }\n );\n }\n }\n}\n\n/**\n * Register all language-specific resources\n */\nexport function registerLanguageResources(server: McpServer): void {\n logger.info('Registering language-specific resources...');\n \n // Register AST references for all languages\n registerLanguageASTResources(server);\n \n // Register security patterns for languages that have them\n registerLanguageSecurityResources(server);\n \n // Register additional resources (like Go's dataflow patterns)\n registerLanguageAdditionalResources(server);\n \n logger.info(`Registered resources for ${LANGUAGE_RESOURCES.length} languages`);\n}", "/**\n * Type definitions and constants for language-specific resources\n */\n\n// Language mappings to resource files\nexport interface LanguageResource {\n language: string;\n astFile?: string;\n securityFile?: string;\n additionalFiles?: Record;\n}\n\nexport const LANGUAGE_RESOURCES: LanguageResource[] = [\n {\n language: 'actions',\n astFile: 'ql/languages/actions/tools/dev/actions_ast.prompt.md'\n },\n {\n language: 'cpp',\n astFile: 'ql/languages/cpp/tools/dev/cpp_ast.prompt.md',\n securityFile: 'ql/languages/cpp/tools/dev/cpp_security_query_guide.prompt.md'\n },\n {\n language: 'csharp',\n astFile: 'ql/languages/csharp/tools/dev/csharp_ast.prompt.md',\n securityFile: 'ql/languages/csharp/tools/dev/csharp_security_query_guide.prompt.md'\n },\n {\n language: 'go',\n astFile: 'ql/languages/go/tools/dev/go_ast.prompt.md',\n securityFile: 'ql/languages/go/tools/dev/go_security_query_guide.prompt.md',\n additionalFiles: {\n 'dataflow': 'ql/languages/go/tools/dev/go_dataflow.prompt.md',\n 'library-modeling': 'ql/languages/go/tools/dev/go_library_modeling.prompt.md',\n 'basic-queries': 'ql/languages/go/tools/dev/go_basic_queries.prompt.md'\n }\n },\n {\n language: 'java',\n astFile: 'ql/languages/java/tools/dev/java_ast.prompt.md'\n },\n {\n language: 'javascript',\n astFile: 'ql/languages/javascript/tools/dev/javascript_ast.prompt.md',\n securityFile: 'ql/languages/javascript/tools/dev/javascript_security_query_guide.prompt.md'\n },\n {\n language: 'python',\n astFile: 'ql/languages/python/tools/dev/python_ast.prompt.md',\n securityFile: 'ql/languages/python/tools/dev/python_security_query_guide.prompt.md'\n },\n {\n language: 'ql',\n astFile: 'ql/languages/ql/tools/dev/ql_ast.prompt.md'\n },\n {\n language: 'ruby',\n astFile: 'ql/languages/ruby/tools/dev/ruby_ast.prompt.md'\n }\n];", "/**\n * MCP Server workflow prompts for CodeQL development\n *\n * All prompt content is loaded from .prompt.md files in this directory.\n * This file only handles prompt registration and parameter processing.\n */\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport { basename } from 'path';\nimport { loadPromptTemplate, processPromptTemplate } from './prompt-loader';\nimport { logger } from '../utils/logger';\n\n/** Supported CodeQL languages for tools queries */\nexport const SUPPORTED_LANGUAGES = [\n 'actions',\n 'cpp',\n 'csharp',\n 'go',\n 'java',\n 'javascript',\n 'python',\n 'ruby',\n 'swift'\n] as const;\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Exported parameter schemas for each workflow prompt.\n//\n// Extracting the schemas makes it easy to unit-test required vs optional\n// validation independently of the MCP server registration.\n//\n// **Convention for VS Code UX consistency**:\n// Every prompt MUST expose at least one parameter \u2013 even if all parameters\n// are optional \u2013 so that VS Code always displays the parameter input dialog\n// and allows the user to customize the prompt before Copilot Chat processes\n// it. The `description` field on each Zod schema member doubles as the\n// placeholder text shown in the VS Code input box.\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Schema for test_driven_development prompt parameters.\n *\n * - `language` is **required** \u2013 the TDD workflow is language-specific.\n * - `queryName` is optional \u2013 defaults to '[QueryName]' if omitted.\n */\nexport const testDrivenDevelopmentSchema = z.object({\n language: z\n .enum(SUPPORTED_LANGUAGES)\n .describe('Programming language for the query'),\n queryName: z\n .string()\n .optional()\n .describe('Name of the query to develop'),\n});\n\n/**\n * Schema for tools_query_workflow prompt parameters.\n *\n * - `language` and `database` are **required**.\n * - `sourceFiles`, `sourceFunction`, `targetFunction` are optional context.\n */\nexport const toolsQueryWorkflowSchema = z.object({\n database: z\n .string()\n .describe('Path to the CodeQL database'),\n language: z\n .enum(SUPPORTED_LANGUAGES)\n .describe('Programming language for the tools queries'),\n sourceFiles: z\n .string()\n .optional()\n .describe('Comma-separated source file names for PrintAST (e.g., \"main.js,utils.js\")'),\n sourceFunction: z\n .string()\n .optional()\n .describe('Function name for PrintCFG or CallGraphFrom (e.g., \"processData\")'),\n targetFunction: z\n .string()\n .optional()\n .describe('Function name for CallGraphTo (e.g., \"validate\")'),\n});\n\n/**\n * Schema for workshop_creation_workflow prompt parameters.\n * Uses z.coerce.number() for numStages to handle string inputs from VSCode slash commands.\n *\n * - `queryPath` and `language` are **required**.\n * - `workshopName` and `numStages` are optional.\n */\nexport const workshopCreationWorkflowSchema = z.object({\n queryPath: z\n .string()\n .describe('Path to the production-grade CodeQL query (.ql or .qlref)'),\n language: z\n .enum(SUPPORTED_LANGUAGES)\n .describe('Programming language of the query'),\n workshopName: z\n .string()\n .optional()\n .describe('Name for the workshop directory'),\n numStages: z\n .coerce.number()\n .optional()\n .describe('Number of incremental stages (default: 4-8)'),\n});\n\n/**\n * Schema for ql_tdd_basic prompt parameters.\n *\n * All parameters are optional \u2013 but at least one should be present so the\n * VS Code quick-pick dialog appears.\n */\nexport const qlTddBasicSchema = z.object({\n language: z\n .enum(SUPPORTED_LANGUAGES)\n .optional()\n .describe('Programming language for the query (optional)'),\n queryName: z\n .string()\n .optional()\n .describe('Name of the query to develop'),\n});\n\n/**\n * Schema for ql_tdd_advanced prompt parameters.\n *\n * All parameters are optional.\n */\nexport const qlTddAdvancedSchema = z.object({\n database: z\n .string()\n .optional()\n .describe('Path to the CodeQL database for analysis'),\n language: z\n .enum(SUPPORTED_LANGUAGES)\n .optional()\n .describe('Programming language for the query (optional)'),\n queryName: z\n .string()\n .optional()\n .describe('Name of the query to develop'),\n});\n\n/**\n * Schema for sarif_rank_false_positives / sarif_rank_true_positives.\n *\n * Both parameters are optional.\n */\nexport const sarifRankSchema = z.object({\n queryId: z\n .string()\n .optional()\n .describe('CodeQL query/rule identifier'),\n sarifPath: z\n .string()\n .optional()\n .describe('Path to the SARIF file to analyze'),\n});\n\n/**\n * Schema for explain_codeql_query prompt parameters.\n *\n * - `queryPath` and `language` are **required**.\n * - `databasePath` is optional.\n */\nexport const explainCodeqlQuerySchema = z.object({\n databasePath: z\n .string()\n .optional()\n .describe('Optional path to a real CodeQL database for profiling'),\n language: z\n .enum(SUPPORTED_LANGUAGES)\n .describe('Programming language of the query'),\n queryPath: z\n .string()\n .describe('Path to the CodeQL query file (.ql or .qlref)'),\n});\n\n/**\n * Schema for document_codeql_query prompt parameters.\n *\n * - `queryPath` and `language` are **required**.\n */\nexport const documentCodeqlQuerySchema = z.object({\n language: z\n .enum(SUPPORTED_LANGUAGES)\n .describe('Programming language of the query'),\n queryPath: z\n .string()\n .describe('Path to the CodeQL query file (.ql or .qlref)'),\n});\n\n/**\n * Schema for ql_lsp_iterative_development prompt parameters.\n *\n * All parameters are optional.\n */\nexport const qlLspIterativeDevelopmentSchema = z.object({\n language: z\n .enum(SUPPORTED_LANGUAGES)\n .optional()\n .describe('Programming language for the query'),\n queryPath: z\n .string()\n .optional()\n .describe('Path to the query file being developed'),\n workspaceUri: z\n .string()\n .optional()\n .describe('Workspace URI for LSP dependency resolution'),\n});\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Prompt names (exported for testing)\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** Names of every workflow prompt registered with the MCP server. */\nexport const WORKFLOW_PROMPT_NAMES = [\n 'document_codeql_query',\n 'explain_codeql_query',\n 'ql_lsp_iterative_development',\n 'ql_tdd_advanced',\n 'ql_tdd_basic',\n 'sarif_rank_false_positives',\n 'sarif_rank_true_positives',\n 'test_driven_development',\n 'tools_query_workflow',\n 'workshop_creation_workflow',\n] as const;\n\n/**\n * Register MCP workflow prompts\n *\n * Each prompt loads its content from a corresponding .prompt.md file\n * and processes any parameter substitutions.\n *\n * **UX note**: Every prompt schema is passed to `server.prompt()` so that\n * VS Code always displays the parameter-input quick-pick before the prompt\n * is sent to Copilot Chat. This lets users review and customise the values.\n */\nexport function registerWorkflowPrompts(server: McpServer): void {\n // Test-Driven Development Prompt\n server.prompt(\n 'test_driven_development',\n 'Test-driven development workflow for CodeQL queries using MCP tools',\n testDrivenDevelopmentSchema.shape,\n async ({ language, queryName }) => {\n const template = loadPromptTemplate('ql-tdd-basic.prompt.md');\n const content = processPromptTemplate(template, {\n language,\n queryName: queryName || '[QueryName]'\n });\n\n return {\n messages: [\n {\n role: 'user',\n content: {\n type: 'text',\n text: `## Context\\n\\n- **Language**: ${language}\\n${queryName ? `- **Query Name**: ${queryName}\\n` : ''}\\n${content}`\n }\n }\n ]\n };\n }\n );\n\n // Tools Query Workflow Prompt\n server.prompt(\n 'tools_query_workflow',\n 'Guide for using built-in tools queries (PrintAST, PrintCFG, CallGraphFrom, CallGraphTo) to understand code structure',\n toolsQueryWorkflowSchema.shape,\n async ({\n language,\n database,\n sourceFiles,\n sourceFunction,\n targetFunction\n }) => {\n const template = loadPromptTemplate('tools-query-workflow.prompt.md');\n const content = processPromptTemplate(template, {\n language,\n database\n });\n\n const contextSection = buildToolsQueryContext(\n language,\n database,\n sourceFiles,\n sourceFunction,\n targetFunction\n );\n\n return {\n messages: [\n {\n role: 'user',\n content: {\n type: 'text',\n text: contextSection + content\n }\n }\n ]\n };\n }\n );\n\n // Workshop Creation Workflow Prompt\n server.prompt(\n 'workshop_creation_workflow',\n 'Guide for creating CodeQL query development workshops from production-grade queries',\n workshopCreationWorkflowSchema.shape,\n async ({ queryPath, language, workshopName, numStages }) => {\n const template = loadPromptTemplate('workshop-creation-workflow.prompt.md');\n\n // Derive workshop name from query path if not provided\n const derivedName =\n workshopName ||\n basename(queryPath)\n .replace(/\\.(ql|qlref)$/, '')\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-') ||\n 'codeql-workshop';\n\n const contextSection = buildWorkshopContext(\n queryPath,\n language,\n derivedName,\n numStages\n );\n\n return {\n messages: [\n {\n role: 'user',\n content: {\n type: 'text',\n text: contextSection + template\n }\n }\n ]\n };\n }\n );\n\n // TDD Basic Prompt - Test-Driven Development Checklist\n server.prompt(\n 'ql_tdd_basic',\n 'Test-driven CodeQL query development checklist - write tests first, implement query, iterate until tests pass',\n qlTddBasicSchema.shape,\n async ({ language, queryName }) => {\n const template = loadPromptTemplate('ql-tdd-basic.prompt.md');\n\n let contextSection = '## Your Development Context\\n\\n';\n if (language) {\n contextSection += `- **Language**: ${language}\\n`;\n }\n if (queryName) {\n contextSection += `- **Query Name**: ${queryName}\\n`;\n }\n if (language || queryName) {\n contextSection += '\\n';\n }\n\n return {\n messages: [\n {\n role: 'user',\n content: {\n type: 'text',\n text: contextSection + template\n }\n }\n ]\n };\n }\n );\n\n // TDD Advanced Prompt - Advanced Techniques with AST/CFG/CallGraph\n server.prompt(\n 'ql_tdd_advanced',\n 'Advanced test-driven CodeQL development with AST visualization, control flow, and call graph analysis',\n qlTddAdvancedSchema.shape,\n async ({ language, queryName, database }) => {\n const template = loadPromptTemplate('ql-tdd-advanced.prompt.md');\n\n let contextSection = '## Your Development Context\\n\\n';\n if (language) {\n contextSection += `- **Language**: ${language}\\n`;\n }\n if (queryName) {\n contextSection += `- **Query Name**: ${queryName}\\n`;\n }\n if (database) {\n contextSection += `- **Database**: ${database}\\n`;\n }\n if (language || queryName || database) {\n contextSection += '\\n';\n }\n\n return {\n messages: [\n {\n role: 'user',\n content: {\n type: 'text',\n text: contextSection + template\n }\n }\n ]\n };\n }\n );\n\n // SARIF Rank False Positives Prompt\n server.prompt(\n 'sarif_rank_false_positives',\n 'Analyze SARIF results to identify likely false positives in CodeQL query results',\n sarifRankSchema.shape,\n async ({ queryId, sarifPath }) => {\n const template = loadPromptTemplate('sarif-rank-false-positives.prompt.md');\n\n let contextSection = '## Analysis Context\\n\\n';\n if (queryId) {\n contextSection += `- **Query ID**: ${queryId}\\n`;\n }\n if (sarifPath) {\n contextSection += `- **SARIF File**: ${sarifPath}\\n`;\n }\n if (queryId || sarifPath) {\n contextSection += '\\n';\n }\n\n return {\n messages: [\n {\n role: 'user',\n content: {\n type: 'text',\n text: contextSection + template\n }\n }\n ]\n };\n }\n );\n\n // SARIF Rank True Positives Prompt\n server.prompt(\n 'sarif_rank_true_positives',\n 'Analyze SARIF results to identify likely true positives in CodeQL query results',\n sarifRankSchema.shape,\n async ({ queryId, sarifPath }) => {\n const template = loadPromptTemplate('sarif-rank-true-positives.prompt.md');\n\n let contextSection = '## Analysis Context\\n\\n';\n if (queryId) {\n contextSection += `- **Query ID**: ${queryId}\\n`;\n }\n if (sarifPath) {\n contextSection += `- **SARIF File**: ${sarifPath}\\n`;\n }\n if (queryId || sarifPath) {\n contextSection += '\\n';\n }\n\n return {\n messages: [\n {\n role: 'user',\n content: {\n type: 'text',\n text: contextSection + template\n }\n }\n ]\n };\n }\n );\n\n // Explain CodeQL Query Prompt (for workshop learning content)\n server.prompt(\n 'explain_codeql_query',\n 'Generate detailed explanation of a CodeQL query for workshop learning content - uses MCP tools to gather context and produces both verbal explanations and mermaid evaluation diagrams',\n explainCodeqlQuerySchema.shape,\n async ({ queryPath, language, databasePath }) => {\n const template = loadPromptTemplate('explain-codeql-query.prompt.md');\n\n let contextSection = '## Query to Explain\\n\\n';\n contextSection += `- **Query Path**: ${queryPath}\\n`;\n contextSection += `- **Language**: ${language}\\n`;\n if (databasePath) {\n contextSection += `- **Database Path**: ${databasePath}\\n`;\n }\n contextSection += '\\n';\n\n return {\n messages: [\n {\n role: 'user',\n content: {\n type: 'text',\n text: contextSection + template\n }\n }\n ]\n };\n }\n );\n\n // Document CodeQL Query Prompt\n server.prompt(\n 'document_codeql_query',\n 'Create or update documentation for a CodeQL query - generates standardized markdown documentation as a sibling file to the query',\n documentCodeqlQuerySchema.shape,\n async ({ queryPath, language }) => {\n const template = loadPromptTemplate('document-codeql-query.prompt.md');\n\n const contextSection = `## Query to Document\n\n- **Query Path**: ${queryPath}\n- **Language**: ${language}\n\n`;\n\n return {\n messages: [\n {\n role: 'user',\n content: {\n type: 'text',\n text: contextSection + template\n }\n }\n ]\n };\n }\n );\n\n // LSP-powered Iterative Development Prompt\n server.prompt(\n 'ql_lsp_iterative_development',\n 'Iterative CodeQL query development using LSP tools for completion, navigation, and validation',\n qlLspIterativeDevelopmentSchema.shape,\n async ({ language, queryPath, workspaceUri }) => {\n const template = loadPromptTemplate('ql-lsp-iterative-development.prompt.md');\n\n let contextSection = '## Your Development Context\\n\\n';\n if (language) {\n contextSection += `- **Language**: ${language}\\n`;\n }\n if (queryPath) {\n contextSection += `- **Query Path**: ${queryPath}\\n`;\n }\n if (workspaceUri) {\n contextSection += `- **Workspace URI**: ${workspaceUri}\\n`;\n }\n if (language || queryPath || workspaceUri) {\n contextSection += '\\n';\n }\n\n return {\n messages: [\n {\n role: 'user',\n content: {\n type: 'text',\n text: contextSection + template,\n },\n },\n ],\n };\n }\n );\n\n logger.info(`Registered ${WORKFLOW_PROMPT_NAMES.length} workflow prompts`);\n}\n\n/**\n * Build context section for tools query workflow\n */\nexport function buildToolsQueryContext(\n language: string,\n database: string,\n sourceFiles?: string,\n sourceFunction?: string,\n targetFunction?: string\n): string {\n const lines = [\n '## Your Context',\n '',\n `- **Language**: ${language}`,\n `- **Database**: ${database}`\n ];\n\n if (sourceFiles) {\n lines.push(`- **Source Files**: ${sourceFiles}`);\n }\n if (sourceFunction) {\n lines.push(`- **Source Function**: ${sourceFunction}`);\n }\n if (targetFunction) {\n lines.push(`- **Target Function**: ${targetFunction}`);\n }\n\n lines.push('', '## Recommended Next Steps', '');\n\n if (sourceFiles) {\n lines.push(\n `1. Run \\`codeql_query_run\\` with queryName=\"PrintAST\", sourceFiles=\"${sourceFiles}\"`\n );\n } else {\n lines.push('1. Identify source files to analyze with PrintAST');\n }\n\n if (sourceFunction) {\n lines.push(\n `2. Run \\`codeql_query_run\\` with queryName=\"PrintCFG\" or \"CallGraphFrom\", sourceFunction=\"${sourceFunction}\"`\n );\n } else {\n lines.push(\n '2. Identify key functions for CFG or call graph analysis'\n );\n }\n\n if (targetFunction) {\n lines.push(\n `3. Run \\`codeql_query_run\\` with queryName=\"CallGraphTo\", targetFunction=\"${targetFunction}\"`\n );\n } else {\n lines.push('3. Identify target functions to find callers');\n }\n\n lines.push('', '');\n return lines.join('\\n');\n}\n\n/**\n * Build context section for workshop creation workflow\n */\nexport function buildWorkshopContext(\n queryPath: string,\n language: string,\n workshopName: string,\n numStages?: number\n): string {\n return `## Your Workshop Context\n\n- **Target Query**: ${queryPath}\n- **Language**: ${language}\n- **Workshop Name**: ${workshopName}\n- **Suggested Stages**: ${numStages || '4-8 (auto-detect based on query complexity)'}\n\n## Immediate Actions\n\n1. **Locate query files**: Use \\`find_codeql_query_files\\` with queryPath=\"${queryPath}\"\n2. **Understand query for learning content**: Use the \\`explain_codeql_query\\` prompt with queryPath=\"${queryPath}\" and language=\"${language}\"\n3. **Document each workshop stage**: Use the \\`document_codeql_query\\` prompt to create/update documentation for each solution query\n4. **Verify tests pass**: Use \\`codeql_test_run\\` on existing tests\n5. **Run tools queries**: Generate AST/CFG understanding for workshop materials\n\n`;\n}\n", "/**\n * Utility functions for loading prompt template files\n */\n\nimport { readFileSync } from 'fs';\nimport { join, dirname } from 'path';\nimport { fileURLToPath } from 'url';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\n\n/**\n * Load a prompt template from a .prompt.md file\n */\nexport function loadPromptTemplate(promptFileName: string): string {\n try {\n const promptPath = join(__dirname, promptFileName);\n return readFileSync(promptPath, 'utf-8');\n } catch (error) {\n return `Prompt template '${promptFileName}' not available: ${error instanceof Error ? error.message : 'Unknown error'}`;\n }\n}\n\n/**\n * Process prompt template by replacing placeholders with actual values\n */\nexport function processPromptTemplate(template: string, variables: Record): string {\n let processed = template;\n \n // Replace variables in the format {{variable}} or {variable}\n for (const [key, value] of Object.entries(variables)) {\n const patterns = [\n new RegExp(`\\\\{\\\\{${key}\\\\}\\\\}`, 'g'),\n new RegExp(`\\\\{${key}\\\\}`, 'g')\n ];\n \n for (const pattern of patterns) {\n processed = processed.replace(pattern, value);\n }\n }\n \n return processed;\n}", "/**\n * Monitoring Tools - MCP tool implementations for session management and reporting\n * Provides the MCP Tool APIs specified in the monitoring specification\n */\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport { randomUUID } from 'crypto';\nimport { sessionDataManager } from '../lib/session-data-manager';\nimport {\n QueryDevelopmentSession,\n SessionFilter,\n QualityScoreRecord,\n ComparisonReport,\n AggregateReport,\n ExportResult,\n FunctionalTestResult,\n} from '../types/monitoring';\nimport { logger } from '../utils/logger';\n\n/**\n * Register all monitoring and reporting tools with the MCP server\n * Note: These tools are opt-in and disabled by default for end-users.\n * Set enableMonitoringTools: true in monitoring config to enable them.\n */\nexport function registerMonitoringTools(server: McpServer): void {\n const config = sessionDataManager.getConfig();\n \n // Check if monitoring tools are enabled (opt-in, disabled by default)\n if (!config.enableMonitoringTools) {\n logger.info('Monitoring tools are disabled (opt-in). Set enableMonitoringTools: true to enable session_* tools.');\n return;\n }\n\n // Session Management Tools - session_start removed per feedback (auto-creation instead)\n registerSessionEndTool(server);\n registerSessionGetTool(server);\n registerSessionListTool(server);\n registerSessionUpdateStateTool(server);\n\n // Session Analytics Tools\n registerSessionGetCallHistoryTool(server);\n registerSessionGetTestHistoryTool(server);\n registerSessionGetScoreHistoryTool(server);\n registerSessionCalculateCurrentScoreTool(server);\n\n // Batch Operations Tools\n registerSessionsCompareTool(server);\n registerSessionsAggregateTool(server);\n registerSessionsExportTool(server);\n\n // Note: Functional Testing Support Tools are internal only, not exposed as MCP tools\n\n logger.info('Registered monitoring and reporting tools');\n}\n\n/**\n * Session Management Tools\n */\n\n// session_start tool removed - sessions are now auto-created when needed\n// Sessions are automatically created when MCP tools are called with queryPath\n// If explicit session creation is needed, provide sessionId=null and it will auto-create\n\nfunction registerSessionEndTool(server: McpServer): void {\n server.tool(\n 'session_end',\n 'End a query development session with final status',\n {\n sessionId: z.string().describe('ID of the session to end'),\n status: z.enum(['completed', 'failed', 'abandoned']).describe('Final status of the session'),\n },\n async ({ sessionId, status }) => {\n try {\n const session = await sessionDataManager.endSession(sessionId, status);\n \n if (!session) {\n return {\n content: [\n {\n type: 'text',\n text: `Session not found: ${sessionId}`,\n },\n ],\n isError: true,\n };\n }\n\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify(session, null, 2),\n },\n ],\n };\n } catch (error) {\n logger.error('Error ending session:', error);\n return {\n content: [\n {\n type: 'text',\n text: `Error ending session: ${error instanceof Error ? error.message : 'Unknown error'}`,\n },\n ],\n isError: true,\n };\n }\n }\n );\n}\n\nfunction registerSessionGetTool(server: McpServer): void {\n server.tool(\n 'session_get',\n 'Get complete details of a specific query development session',\n {\n sessionId: z.string().describe('ID of the session to retrieve'),\n },\n async ({ sessionId }) => {\n try {\n const session = await sessionDataManager.getSession(sessionId);\n \n if (!session) {\n return {\n content: [\n {\n type: 'text',\n text: `Session not found: ${sessionId}`,\n },\n ],\n isError: true,\n };\n }\n\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify(session, null, 2),\n },\n ],\n };\n } catch (error) {\n logger.error('Error getting session:', error);\n return {\n content: [\n {\n type: 'text',\n text: `Error getting session: ${error instanceof Error ? error.message : 'Unknown error'}`,\n },\n ],\n isError: true,\n };\n }\n }\n );\n}\n\nfunction registerSessionListTool(server: McpServer): void {\n server.tool(\n 'session_list',\n 'List query development sessions with optional filtering',\n {\n queryPath: z.string().optional().describe('Filter by query path (partial match)'),\n status: z.string().optional().describe('Filter by session status'),\n dateRange: z.array(z.string()).length(2).optional().describe('Filter by date range [start, end] (ISO timestamps)'),\n language: z.string().optional().describe('Filter by programming language'),\n queryType: z.string().optional().describe('Filter by query type'),\n },\n async ({ queryPath, status, dateRange, language, queryType }) => {\n try {\n const filters: SessionFilter = {};\n if (queryPath) filters.queryPath = queryPath;\n if (status) filters.status = status;\n if (dateRange) filters.dateRange = [dateRange[0], dateRange[1]];\n if (language) filters.language = language;\n if (queryType) filters.queryType = queryType;\n\n const sessions = await sessionDataManager.listSessions(\n Object.keys(filters).length > 0 ? filters : undefined\n );\n\n const sessionList = {\n totalSessions: sessions.length,\n sessions: sessions.map(s => ({\n sessionId: s.sessionId,\n queryPath: s.queryPath,\n language: s.language,\n status: s.status,\n startTime: s.startTime,\n endTime: s.endTime,\n mcpCallsCount: s.mcpCalls.length,\n testExecutionsCount: s.testExecutions.length,\n currentScore: s.qualityScores.length > 0 \n ? s.qualityScores[s.qualityScores.length - 1].overallScore \n : null,\n })),\n };\n\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify(sessionList, null, 2),\n },\n ],\n recommendations: generateListRecommendations(sessions),\n };\n } catch (error) {\n logger.error('Error listing sessions:', error);\n return {\n content: [\n {\n type: 'text',\n text: `Error listing sessions: ${error instanceof Error ? error.message : 'Unknown error'}`,\n },\n ],\n isError: true,\n };\n }\n }\n );\n}\n\nfunction registerSessionUpdateStateTool(server: McpServer): void {\n server.tool(\n 'session_update_state',\n 'Update the current state of a query development session',\n {\n sessionId: z.string().describe('ID of the session to update'),\n filesPresent: z.array(z.string()).optional().describe('List of files present in the query development'),\n compilationStatus: z.enum(['unknown', 'success', 'failed']).optional().describe('Current compilation status'),\n testStatus: z.enum(['unknown', 'passing', 'failing', 'no_tests']).optional().describe('Current test status'),\n documentationStatus: z.enum(['unknown', 'present', 'missing', 'incomplete']).optional().describe('Documentation status'),\n },\n async ({ sessionId, filesPresent, compilationStatus, testStatus, documentationStatus }) => {\n try {\n const stateUpdate: Record = {};\n if (filesPresent !== undefined) stateUpdate.filesPresent = filesPresent;\n if (compilationStatus !== undefined) stateUpdate.compilationStatus = compilationStatus;\n if (testStatus !== undefined) stateUpdate.testStatus = testStatus;\n if (documentationStatus !== undefined) stateUpdate.documentationStatus = documentationStatus;\n\n const session = await sessionDataManager.updateSessionState(sessionId, stateUpdate);\n \n if (!session) {\n return {\n content: [\n {\n type: 'text',\n text: `Session not found: ${sessionId}`,\n },\n ],\n isError: true,\n };\n }\n\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify(session, null, 2),\n },\n ],\n recommendations: generateRecommendations(session, 'session_update_state'),\n };\n } catch (error) {\n logger.error('Error updating session state:', error);\n return {\n content: [\n {\n type: 'text',\n text: `Error updating session state: ${error instanceof Error ? error.message : 'Unknown error'}`,\n },\n ],\n isError: true,\n };\n }\n }\n );\n}\n\n/**\n * Session Analytics Tools\n */\n\nfunction registerSessionGetCallHistoryTool(server: McpServer): void {\n server.tool(\n 'session_get_call_history',\n 'Get MCP call history for a specific session',\n {\n sessionId: z.string().describe('ID of the session'),\n limit: z.number().optional().describe('Maximum number of calls to return (most recent first)'),\n },\n async ({ sessionId, limit }) => {\n try {\n const session = await sessionDataManager.getSession(sessionId);\n \n if (!session) {\n return {\n content: [\n {\n type: 'text',\n text: `Session not found: ${sessionId}`,\n },\n ],\n isError: true,\n };\n }\n\n let calls = [...session.mcpCalls].reverse(); // Most recent first\n if (limit && limit > 0) {\n calls = calls.slice(0, limit);\n }\n\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify({\n sessionId,\n totalCalls: session.mcpCalls.length,\n callHistory: calls,\n }, null, 2),\n },\n ],\n };\n } catch (error) {\n logger.error('Error getting call history:', error);\n return {\n content: [\n {\n type: 'text',\n text: `Error getting call history: ${error instanceof Error ? error.message : 'Unknown error'}`,\n },\n ],\n isError: true,\n };\n }\n }\n );\n}\n\nfunction registerSessionGetTestHistoryTool(server: McpServer): void {\n server.tool(\n 'session_get_test_history',\n 'Get test execution history for a specific session',\n {\n sessionId: z.string().describe('ID of the session'),\n limit: z.number().optional().describe('Maximum number of test executions to return (most recent first)'),\n },\n async ({ sessionId, limit }) => {\n try {\n const session = await sessionDataManager.getSession(sessionId);\n \n if (!session) {\n return {\n content: [\n {\n type: 'text',\n text: `Session not found: ${sessionId}`,\n },\n ],\n isError: true,\n };\n }\n\n let tests = [...session.testExecutions].reverse(); // Most recent first\n if (limit && limit > 0) {\n tests = tests.slice(0, limit);\n }\n\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify({\n sessionId,\n totalTests: session.testExecutions.length,\n testHistory: tests,\n }, null, 2),\n },\n ],\n };\n } catch (error) {\n logger.error('Error getting test history:', error);\n return {\n content: [\n {\n type: 'text',\n text: `Error getting test history: ${error instanceof Error ? error.message : 'Unknown error'}`,\n },\n ],\n isError: true,\n };\n }\n }\n );\n}\n\nfunction registerSessionGetScoreHistoryTool(server: McpServer): void {\n server.tool(\n 'session_get_score_history',\n 'Get quality score history for a specific session',\n {\n sessionId: z.string().describe('ID of the session'),\n limit: z.number().optional().describe('Maximum number of scores to return (most recent first)'),\n },\n async ({ sessionId, limit }) => {\n try {\n const session = await sessionDataManager.getSession(sessionId);\n \n if (!session) {\n return {\n content: [\n {\n type: 'text',\n text: `Session not found: ${sessionId}`,\n },\n ],\n isError: true,\n };\n }\n\n let scores = [...session.qualityScores].reverse(); // Most recent first\n if (limit && limit > 0) {\n scores = scores.slice(0, limit);\n }\n\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify({\n sessionId,\n totalScores: session.qualityScores.length,\n scoreHistory: scores,\n }, null, 2),\n },\n ],\n };\n } catch (error) {\n logger.error('Error getting score history:', error);\n return {\n content: [\n {\n type: 'text',\n text: `Error getting score history: ${error instanceof Error ? error.message : 'Unknown error'}`,\n },\n ],\n isError: true,\n };\n }\n }\n );\n}\n\nfunction registerSessionCalculateCurrentScoreTool(server: McpServer): void {\n server.tool(\n 'session_calculate_current_score',\n 'Calculate current quality score for a session based on its state',\n {\n sessionId: z.string().describe('ID of the session'),\n },\n async ({ sessionId }) => {\n try {\n const session = await sessionDataManager.getSession(sessionId);\n \n if (!session) {\n return {\n content: [\n {\n type: 'text',\n text: `Session not found: ${sessionId}`,\n },\n ],\n isError: true,\n };\n }\n\n // Calculate quality score based on current state\n const scoreRecord = calculateQualityScore(session);\n \n // Add the score to the session\n await sessionDataManager.addQualityScore(sessionId, scoreRecord);\n \n // Get updated session with new score\n const updatedSession = await sessionDataManager.getSession(sessionId);\n\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify(scoreRecord, null, 2),\n },\n ],\n recommendations: generateRecommendations(updatedSession, 'session_calculate_current_score'),\n };\n } catch (error) {\n logger.error('Error calculating current score:', error);\n return {\n content: [\n {\n type: 'text',\n text: `Error calculating current score: ${error instanceof Error ? error.message : 'Unknown error'}`,\n },\n ],\n isError: true,\n };\n }\n }\n );\n}\n\n/**\n * Batch Operations Tools\n */\n\nfunction registerSessionsCompareTool(server: McpServer): void {\n server.tool(\n 'sessions_compare',\n 'Compare multiple query development sessions across specified dimensions',\n {\n sessionIds: z.array(z.string()).describe('Array of session IDs to compare'),\n dimensions: z.array(z.string()).optional().describe('Specific dimensions to compare (default: all)'),\n },\n async ({ sessionIds, dimensions }) => {\n try {\n const sessions = await Promise.all(\n sessionIds.map(id => sessionDataManager.getSession(id))\n );\n\n const validSessions = sessions.filter(s => s !== null) as QueryDevelopmentSession[];\n \n if (validSessions.length === 0) {\n return {\n content: [\n {\n type: 'text',\n text: 'No valid sessions found for comparison',\n },\n ],\n isError: true,\n };\n }\n\n const comparison = await compareSessions(validSessions, dimensions);\n\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify(comparison, null, 2),\n },\n ],\n };\n } catch (error) {\n logger.error('Error comparing sessions:', error);\n return {\n content: [\n {\n type: 'text',\n text: `Error comparing sessions: ${error instanceof Error ? error.message : 'Unknown error'}`,\n },\n ],\n isError: true,\n };\n }\n }\n );\n}\n\nfunction registerSessionsAggregateTool(server: McpServer): void {\n server.tool(\n 'sessions_aggregate',\n 'Generate aggregate insights from multiple sessions based on filters',\n {\n queryPath: z.string().optional().describe('Filter by query path (partial match)'),\n status: z.string().optional().describe('Filter by session status'),\n dateRange: z.array(z.string()).length(2).optional().describe('Filter by date range [start, end] (ISO timestamps)'),\n language: z.string().optional().describe('Filter by programming language'),\n queryType: z.string().optional().describe('Filter by query type'),\n },\n async ({ queryPath, status, dateRange, language, queryType }) => {\n try {\n const filters: SessionFilter = {};\n if (queryPath) filters.queryPath = queryPath;\n if (status) filters.status = status;\n if (dateRange) filters.dateRange = [dateRange[0], dateRange[1]];\n if (language) filters.language = language;\n if (queryType) filters.queryType = queryType;\n\n const sessions = await sessionDataManager.listSessions(\n Object.keys(filters).length > 0 ? filters : undefined\n );\n\n const aggregate = await aggregateSessions(sessions, filters);\n\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify(aggregate, null, 2),\n },\n ],\n };\n } catch (error) {\n logger.error('Error aggregating sessions:', error);\n return {\n content: [\n {\n type: 'text',\n text: `Error aggregating sessions: ${error instanceof Error ? error.message : 'Unknown error'}`,\n },\n ],\n isError: true,\n };\n }\n }\n );\n}\n\nfunction registerSessionsExportTool(server: McpServer): void {\n server.tool(\n 'sessions_export',\n 'Export session data in specified format for external analysis',\n {\n sessionIds: z.array(z.string()).describe('Array of session IDs to export'),\n format: z.enum(['json', 'html', 'markdown']).optional().default('json').describe('Export format'),\n },\n async ({ sessionIds, format = 'json' }) => {\n try {\n const sessions = await Promise.all(\n sessionIds.map(id => sessionDataManager.getSession(id))\n );\n\n const validSessions = sessions.filter(s => s !== null) as QueryDevelopmentSession[];\n \n if (validSessions.length === 0) {\n return {\n content: [\n {\n type: 'text',\n text: 'No valid sessions found for export',\n },\n ],\n isError: true,\n };\n }\n\n const exportResult = await exportSessions(validSessions, format);\n\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify(exportResult, null, 2),\n },\n ],\n };\n } catch (error) {\n logger.error('Error exporting sessions:', error);\n return {\n content: [\n {\n type: 'text',\n text: `Error exporting sessions: ${error instanceof Error ? error.message : 'Unknown error'}`,\n },\n ],\n isError: true,\n };\n }\n }\n );\n}\n\n/**\n * Helper Functions\n */\n\n/**\n * Calculate quality score for a session based on current state and history\n */\nfunction calculateQualityScore(session: QueryDevelopmentSession): QualityScoreRecord {\n const timestamp = new Date().toISOString();\n \n // Calculate syntactic correctness (25%)\n const syntacticCorrectness = session.currentState.compilationStatus === 'success' ? 100 :\n session.currentState.compilationStatus === 'failed' ? 0 : 50;\n\n // Calculate test coverage and results (30%)\n const testCoverageResults = session.currentState.testStatus === 'passing' ? 100 :\n session.currentState.testStatus === 'failing' ? 25 :\n session.currentState.testStatus === 'no_tests' ? 0 : 50;\n\n // Calculate documentation quality (20%)\n const documentationQuality = session.currentState.documentationStatus === 'present' ? 100 :\n session.currentState.documentationStatus === 'incomplete' ? 60 :\n session.currentState.documentationStatus === 'missing' ? 0 : 50;\n\n // Calculate functional correctness (25%) - based on successful test runs\n const successfulTests = session.testExecutions.filter(t => t.success && t.type === 'test_run').length;\n const totalTests = session.testExecutions.filter(t => t.type === 'test_run').length;\n const functionalCorrectness = totalTests > 0 ? (successfulTests / totalTests) * 100 : 50;\n\n // Calculate overall score\n const overallScore = Math.round(\n (syntacticCorrectness * 0.25) +\n (testCoverageResults * 0.30) +\n (documentationQuality * 0.20) +\n (functionalCorrectness * 0.25)\n );\n\n // Determine grade\n const grade = overallScore >= 90 ? 'A' :\n overallScore >= 80 ? 'B' :\n overallScore >= 70 ? 'C' :\n overallScore >= 60 ? 'D' : 'F';\n\n // Generate recommendations\n const recommendations: string[] = [];\n if (syntacticCorrectness < 100) {\n recommendations.push('Fix compilation errors to improve syntactic correctness');\n }\n if (testCoverageResults < 70) {\n recommendations.push('Add comprehensive tests and ensure they pass');\n }\n if (documentationQuality < 80) {\n recommendations.push('Add or improve query documentation with examples');\n }\n if (functionalCorrectness < 80) {\n recommendations.push('Improve test pass rate and verify query logic');\n }\n\n return {\n scoreId: randomUUID(),\n timestamp,\n overallScore,\n dimensions: {\n syntacticCorrectness,\n testCoverageResults,\n documentationQuality,\n functionalCorrectness,\n },\n grade,\n recommendations,\n };\n}\n\n/**\n * Compare multiple sessions\n */\nasync function compareSessions(\n sessions: QueryDevelopmentSession[],\n dimensions?: string[]\n): Promise {\n const timestamp = new Date().toISOString();\n const sessionIds = sessions.map(s => s.sessionId);\n \n const results: Record = {\n sessionCount: sessions.length,\n sessionOverview: sessions.map(s => ({\n sessionId: s.sessionId,\n queryPath: s.queryPath,\n status: s.status,\n mcpCallsCount: s.mcpCalls.length,\n duration: s.endTime ? \n new Date(s.endTime).getTime() - new Date(s.startTime).getTime() : \n new Date().getTime() - new Date(s.startTime).getTime(),\n currentScore: s.qualityScores.length > 0 ? \n s.qualityScores[s.qualityScores.length - 1].overallScore : null,\n })),\n };\n\n if (!dimensions || dimensions.includes('quality')) {\n const qualityScores = sessions.map(s => \n s.qualityScores.length > 0 ? s.qualityScores[s.qualityScores.length - 1] : null\n );\n results.qualityComparison = {\n averageScore: qualityScores\n .filter(q => q !== null)\n .reduce((sum, q) => sum + q!.overallScore, 0) / qualityScores.filter(q => q !== null).length,\n scoreRange: {\n min: Math.min(...qualityScores.filter(q => q !== null).map(q => q!.overallScore)),\n max: Math.max(...qualityScores.filter(q => q !== null).map(q => q!.overallScore)),\n },\n };\n }\n\n if (!dimensions || dimensions.includes('activity')) {\n results.activityComparison = {\n totalMCPCalls: sessions.reduce((sum, s) => sum + s.mcpCalls.length, 0),\n averageCallsPerSession: sessions.reduce((sum, s) => sum + s.mcpCalls.length, 0) / sessions.length,\n mostActiveTool: getMostUsedTool(sessions),\n };\n }\n\n return {\n sessionIds,\n dimensions: dimensions || ['all'],\n timestamp,\n results,\n };\n}\n\n/**\n * Aggregate insights from multiple sessions\n */\nasync function aggregateSessions(\n sessions: QueryDevelopmentSession[],\n filters: SessionFilter\n): Promise {\n const timestamp = new Date().toISOString();\n \n const completedSessions = sessions.filter(s => s.status === 'completed');\n const successRate = sessions.length > 0 ? completedSessions.length / sessions.length : 0;\n \n const qualityScores = sessions\n .map(s => s.qualityScores.length > 0 ? s.qualityScores[s.qualityScores.length - 1].overallScore : null)\n .filter(score => score !== null) as number[];\n \n const averageQualityScore = qualityScores.length > 0 ? \n qualityScores.reduce((sum, score) => sum + score, 0) / qualityScores.length : 0;\n\n const commonPatterns = identifyCommonPatterns(sessions);\n const recommendations = generateAggregateRecommendations(sessions);\n\n return {\n filters,\n timestamp,\n totalSessions: sessions.length,\n successRate,\n averageQualityScore,\n commonPatterns,\n recommendations,\n };\n}\n\n/**\n * Export sessions in specified format\n */\nasync function exportSessions(\n sessions: QueryDevelopmentSession[],\n format: 'json' | 'html' | 'markdown'\n): Promise {\n const timestamp = new Date().toISOString();\n const filename = `session-export-${timestamp.replace(/[:.]/g, '-')}.${format}`;\n \n let content: string;\n \n switch (format) {\n case 'json':\n content = JSON.stringify(sessions, null, 2);\n break;\n case 'html':\n content = generateHTMLReport(sessions);\n break;\n case 'markdown':\n content = generateMarkdownReport(sessions);\n break;\n }\n\n return {\n format,\n filename,\n content,\n timestamp,\n };\n}\n\n/**\n * Utility functions\n */\n\nfunction getMostUsedTool(sessions: QueryDevelopmentSession[]): string {\n const toolCounts: Record = {};\n \n sessions.forEach(session => {\n session.mcpCalls.forEach(call => {\n toolCounts[call.toolName] = (toolCounts[call.toolName] || 0) + 1;\n });\n });\n\n return Object.entries(toolCounts)\n .sort(([, a], [, b]) => b - a)[0]?.[0] || 'none';\n}\n\nfunction identifyCommonPatterns(sessions: QueryDevelopmentSession[]): string[] {\n const patterns: string[] = [];\n \n const commonTools = getMostUsedTool(sessions);\n if (commonTools && commonTools !== 'none') {\n patterns.push(`Most commonly used tool: ${commonTools}`);\n }\n\n const completionRate = sessions.filter(s => s.status === 'completed').length / sessions.length;\n if (completionRate > 0.8) {\n patterns.push('High completion rate indicates effective workflow');\n } else if (completionRate < 0.5) {\n patterns.push('Low completion rate suggests workflow issues');\n }\n\n return patterns;\n}\n\nfunction generateAggregateRecommendations(sessions: QueryDevelopmentSession[]): string[] {\n const recommendations: string[] = [];\n \n const failedSessions = sessions.filter(s => s.status === 'failed');\n if (failedSessions.length > sessions.length * 0.3) {\n recommendations.push('High failure rate - consider improving error handling and guidance');\n }\n\n const averageCallsPerSession = sessions.reduce((sum, s) => sum + s.mcpCalls.length, 0) / sessions.length;\n if (averageCallsPerSession > 20) {\n recommendations.push('High number of MCP calls per session - consider workflow optimization');\n }\n\n return recommendations;\n}\n\nfunction generateHTMLReport(sessions: QueryDevelopmentSession[]): string {\n const html = `\n\n\n\n Session Export Report\n \n\n\n

Query Development Sessions Report

\n

Generated: ${new Date().toISOString()}

\n

Total Sessions: ${sessions.length}

\n \n ${sessions.map(session => `\n
\n

Session: ${session.sessionId}

\n

Query Path: ${session.queryPath}

\n

Status: ${session.status}

\n

Language: ${session.language}

\n

Start Time: ${session.startTime}

\n

MCP Calls: ${session.mcpCalls.length}

\n

Test Executions: ${session.testExecutions.length}

\n

Quality Scores: ${session.qualityScores.length}

\n
\n `).join('')}\n\n`;\n \n return html;\n}\n\nfunction generateMarkdownReport(sessions: QueryDevelopmentSession[]): string {\n const md = `# Query Development Sessions Report\n\nGenerated: ${new Date().toISOString()}\nTotal Sessions: ${sessions.length}\n\n## Session Summary\n\n| Session ID | Query Path | Status | Language | MCP Calls | Test Executions |\n|------------|-----------|--------|----------|-----------|-----------------|\n${sessions.map(session => \n `| ${session.sessionId} | ${session.queryPath} | ${session.status} | ${session.language} | ${session.mcpCalls.length} | ${session.testExecutions.length} |`\n).join('\\n')}\n\n## Detailed Sessions\n\n${sessions.map(session => `\n### Session: ${session.sessionId}\n\n- **Query Path:** ${session.queryPath}\n- **Status:** ${session.status}\n- **Language:** ${session.language}\n- **Start Time:** ${session.startTime}\n- **End Time:** ${session.endTime || 'N/A'}\n- **MCP Calls:** ${session.mcpCalls.length}\n- **Test Executions:** ${session.testExecutions.length}\n- **Quality Scores:** ${session.qualityScores.length}\n\n${session.recommendations.length > 0 ? `\n**Current Recommendations:**\n${session.recommendations.map(rec => `- ${rec}`).join('\\n')}\n` : ''}\n`).join('\\n')}`;\n\n return md;\n}\n\nfunction _calculateAverageDuration(sessions: QueryDevelopmentSession[]): number {\n const completedSessions = sessions.filter(s => s.endTime);\n if (completedSessions.length === 0) return 0;\n\n const totalDuration = completedSessions.reduce((sum, session) => {\n return sum + (new Date(session.endTime!).getTime() - new Date(session.startTime).getTime());\n }, 0);\n\n return totalDuration / completedSessions.length;\n}\n\nfunction _identifyFailureReasons(results: FunctionalTestResult[]): string[] {\n const failedResults = results.filter(r => !r.passed);\n const reasons: Record = {};\n\n failedResults.forEach(result => {\n Object.entries(result.criteria).forEach(([criterion, passed]) => {\n if (!passed) {\n reasons[criterion] = (reasons[criterion] || 0) + 1;\n }\n });\n });\n\n return Object.entries(reasons)\n .sort(([, a], [, b]) => b - a)\n .map(([reason, count]) => `${reason}: ${count} sessions`);\n}\n\n/**\n * Generate recommendations for MCP tool responses\n * Returns a map of MCP primitive paths to recommendation reasons\n */\nfunction generateRecommendations(\n session: QueryDevelopmentSession | null,\n currentTool: string\n): Record {\n if (!session) {\n return {};\n }\n\n const recommendations: Record = {};\n\n // Session state-based recommendations\n if (session.currentState.compilationStatus === 'failed') {\n recommendations['codeql_query_format'] = 'Format query to fix potential syntax issues';\n recommendations['codeql_query_compile'] = 'Recompile after fixing syntax errors';\n } else if (session.currentState.compilationStatus === 'success') {\n if (session.currentState.testStatus === 'unknown' || session.currentState.testStatus === 'no_tests') {\n recommendations['codeql_test_run'] = 'Run tests to validate query functionality';\n } else if (session.currentState.testStatus === 'failing') {\n recommendations['session_get_test_history'] = 'Review test failures to identify issues';\n recommendations['codeql_query_compile'] = 'Verify query logic matches test expectations';\n } else if (session.currentState.testStatus === 'passing') {\n recommendations['session_calculate_current_score'] = 'Calculate quality score for completed query';\n }\n }\n\n // Tool-specific follow-up recommendations\n switch (currentTool) {\n case 'session_get':\n if (session.mcpCalls.length === 0) {\n recommendations['codeql_query_compile'] = 'Start development by compiling the query';\n }\n break;\n case 'session_end':\n if (session.status === 'completed') {\n recommendations['sessions_export'] = 'Export session data for analysis';\n }\n break;\n case 'session_calculate_current_score': {\n const latestScore = session.qualityScores[session.qualityScores.length - 1];\n if (latestScore && latestScore.overallScore < 80) {\n if (latestScore.dimensions.syntacticCorrectness < 100) {\n recommendations['codeql_query_format'] = 'Improve syntax and formatting';\n }\n if (latestScore.dimensions.testCoverageResults < 70) {\n recommendations['codeql_test_run'] = 'Improve test coverage and results';\n }\n }\n break;\n }\n case 'session_update_state':\n // Recommend next logical step based on updated state\n if (session.currentState.compilationStatus === 'success' && session.currentState.testStatus === 'unknown') {\n recommendations['codeql_test_run'] = 'Run tests now that compilation is successful';\n }\n break;\n }\n\n return recommendations;\n}\n\n/**\n * Generate recommendations for session list results\n */\nfunction generateListRecommendations(sessions: QueryDevelopmentSession[]): Record {\n const recommendations: Record = {};\n\n const activeSessions = sessions.filter(s => s.status === 'active');\n const completedSessions = sessions.filter(s => s.status === 'completed');\n\n if (activeSessions.length > 0) {\n recommendations['session_get'] = `Review details of ${activeSessions.length} active session(s)`;\n }\n\n if (completedSessions.length > 1) {\n recommendations['sessions_compare'] = 'Compare completed sessions to identify patterns';\n recommendations['sessions_aggregate'] = 'Generate aggregate insights from multiple sessions';\n }\n\n if (sessions.length > 5) {\n recommendations['sessions_export'] = 'Export session data for comprehensive analysis';\n }\n\n return recommendations;\n}", "function checkArgs(adapter, defaultData) {\n if (adapter === undefined)\n throw new Error('lowdb: missing adapter');\n if (defaultData === undefined)\n throw new Error('lowdb: missing default data');\n}\nexport class Low {\n adapter;\n data;\n constructor(adapter, defaultData) {\n checkArgs(adapter, defaultData);\n this.adapter = adapter;\n this.data = defaultData;\n }\n async read() {\n const data = await this.adapter.read();\n if (data)\n this.data = data;\n }\n async write() {\n if (this.data)\n await this.adapter.write(this.data);\n }\n async update(fn) {\n fn(this.data);\n await this.write();\n }\n}\nexport class LowSync {\n adapter;\n data;\n constructor(adapter, defaultData) {\n checkArgs(adapter, defaultData);\n this.adapter = adapter;\n this.data = defaultData;\n }\n read() {\n const data = this.adapter.read();\n if (data)\n this.data = data;\n }\n write() {\n if (this.data)\n this.adapter.write(this.data);\n }\n update(fn) {\n fn(this.data);\n this.write();\n }\n}\n", "import { readFileSync, renameSync, writeFileSync } from 'node:fs';\nimport { readFile } from 'node:fs/promises';\nimport path from 'node:path';\nimport { Writer } from 'steno';\nexport class TextFile {\n #filename;\n #writer;\n constructor(filename) {\n this.#filename = filename;\n this.#writer = new Writer(filename);\n }\n async read() {\n let data;\n try {\n data = await readFile(this.#filename, 'utf-8');\n }\n catch (e) {\n if (e.code === 'ENOENT') {\n return null;\n }\n throw e;\n }\n return data;\n }\n write(str) {\n return this.#writer.write(str);\n }\n}\nexport class TextFileSync {\n #tempFilename;\n #filename;\n constructor(filename) {\n this.#filename = filename;\n const f = filename.toString();\n this.#tempFilename = path.join(path.dirname(f), `.${path.basename(f)}.tmp`);\n }\n read() {\n let data;\n try {\n data = readFileSync(this.#filename, 'utf-8');\n }\n catch (e) {\n if (e.code === 'ENOENT') {\n return null;\n }\n throw e;\n }\n return data;\n }\n write(str) {\n writeFileSync(this.#tempFilename, str);\n renameSync(this.#tempFilename, this.#filename);\n }\n}\n", "import { TextFile, TextFileSync } from './TextFile.js';\nexport class DataFile {\n #adapter;\n #parse;\n #stringify;\n constructor(filename, { parse, stringify, }) {\n this.#adapter = new TextFile(filename);\n this.#parse = parse;\n this.#stringify = stringify;\n }\n async read() {\n const data = await this.#adapter.read();\n if (data === null) {\n return null;\n }\n else {\n return this.#parse(data);\n }\n }\n write(obj) {\n return this.#adapter.write(this.#stringify(obj));\n }\n}\nexport class DataFileSync {\n #adapter;\n #parse;\n #stringify;\n constructor(filename, { parse, stringify, }) {\n this.#adapter = new TextFileSync(filename);\n this.#parse = parse;\n this.#stringify = stringify;\n }\n read() {\n const data = this.#adapter.read();\n if (data === null) {\n return null;\n }\n else {\n return this.#parse(data);\n }\n }\n write(obj) {\n this.#adapter.write(this.#stringify(obj));\n }\n}\n", "import { DataFile, DataFileSync } from './DataFile.js';\nexport class JSONFile extends DataFile {\n constructor(filename) {\n super(filename, {\n parse: JSON.parse,\n stringify: (data) => JSON.stringify(data, null, 2),\n });\n }\n}\nexport class JSONFileSync extends DataFileSync {\n constructor(filename) {\n super(filename, {\n parse: JSON.parse,\n stringify: (data) => JSON.stringify(data, null, 2),\n });\n }\n}\n", "/**\n * Session Data Management\n * Provides unified JSON storage and session lifecycle management using lowdb\n */\n\nimport { Low } from 'lowdb';\nimport { JSONFileSync } from 'lowdb/node';\nimport { mkdirSync, writeFileSync } from 'fs';\nimport { join } from 'path';\nimport { randomUUID } from 'crypto';\nimport { getProjectTmpBase } from '../utils/temp-dir';\nimport {\n QueryDevelopmentSession,\n QueryState,\n MCPCallRecord,\n TestExecutionRecord,\n QualityScoreRecord,\n SessionFilter,\n MonitoringConfig,\n MonitoringConfigSchema,\n} from '../types/monitoring';\nimport { logger } from '../utils/logger';\n\n/**\n * Database schema for lowdb - sessions only\n */\ninterface SessionDatabase {\n sessions: QueryDevelopmentSession[];\n}\n\n/**\n * Session Data Manager - handles all session persistence and lifecycle\n */\nexport class SessionDataManager {\n private db: Low;\n private config: MonitoringConfig;\n private storageDir: string;\n\n constructor(configOverrides: Partial = {}) {\n this.config = MonitoringConfigSchema.parse({\n ...MonitoringConfigSchema.parse({}),\n ...configOverrides,\n });\n\n this.storageDir = this.config.storageLocation;\n this.ensureStorageDirectory();\n\n const adapter = new JSONFileSync(join(this.storageDir, 'sessions.json'));\n this.db = new Low(adapter, {\n sessions: [],\n });\n\n this.initializeDatabase();\n }\n\n /**\n * Initialize the database and ensure it's properly set up\n */\n async initialize(): Promise {\n await this.initializeDatabase();\n }\n\n /**\n * Initialize the database and ensure it's properly set up\n */\n private async initializeDatabase(): Promise {\n try {\n await this.db.read();\n \n logger.info(`Session data manager initialized with ${this.db.data.sessions.length} sessions`);\n } catch (error) {\n logger.error('Failed to initialize session database:', error);\n throw error;\n }\n }\n\n /**\n * Ensure storage directory structure exists\n */\n private ensureStorageDirectory(): void {\n try {\n // mkdirSync with recursive: true is a no-op if directories already exist\n mkdirSync(this.storageDir, { recursive: true });\n\n // Create subdirectories\n const subdirs = ['sessions-archive', 'exports'];\n for (const subdir of subdirs) {\n mkdirSync(join(this.storageDir, subdir), { recursive: true });\n }\n\n // Use 'wx' flag (exclusive create) to atomically create config only\n // if it doesn't exist, avoiding TOCTOU race (CWE-367).\n const configPath = join(this.storageDir, 'config.json');\n try {\n writeFileSync(configPath, JSON.stringify(this.config, null, 2), { flag: 'wx' });\n } catch (e: unknown) {\n const err = e as { code?: string };\n if (err.code !== 'EEXIST') throw e;\n }\n\n logger.debug(`Storage directory initialized: ${this.storageDir}`);\n } catch (error) {\n logger.error('Failed to create storage directory:', error);\n throw error;\n }\n }\n\n /**\n * Start a new query development session\n */\n async startSession(\n queryPath: string,\n language?: string,\n queryType?: string,\n description?: string\n ): Promise {\n const sessionId = randomUUID();\n const startTime = new Date().toISOString();\n\n const session: QueryDevelopmentSession = {\n sessionId,\n queryPath,\n language: language || 'unknown',\n queryType,\n description,\n startTime,\n status: 'active',\n mcpCalls: [],\n testExecutions: [],\n qualityScores: [],\n currentState: {\n filesPresent: [],\n compilationStatus: 'unknown',\n testStatus: 'unknown',\n documentationStatus: 'unknown',\n lastActivity: startTime,\n },\n recommendations: [],\n };\n\n await this.db.read();\n this.db.data.sessions.push(session);\n await this.db.write();\n\n logger.info(`Started new session: ${sessionId} for query: ${queryPath}`);\n return sessionId;\n }\n\n /**\n * End a session with final status\n */\n async endSession(\n sessionId: string,\n status: 'completed' | 'failed' | 'abandoned'\n ): Promise {\n await this.db.read();\n \n const session = this.db.data.sessions.find(s => s.sessionId === sessionId);\n if (!session) {\n logger.warn(`Session not found: ${sessionId}`);\n return null;\n }\n\n session.status = status;\n session.endTime = new Date().toISOString();\n session.currentState.lastActivity = session.endTime;\n\n await this.db.write();\n\n // Archive completed session if enabled\n if (this.config.archiveCompletedSessions && status === 'completed') {\n await this.archiveSession(sessionId);\n }\n\n logger.info(`Ended session: ${sessionId} with status: ${status}`);\n return session;\n }\n\n /**\n * Get a specific session by ID\n */\n async getSession(sessionId: string): Promise {\n await this.db.read();\n const session = this.db.data.sessions.find(s => s.sessionId === sessionId);\n return session || null;\n }\n\n /**\n * List sessions with optional filtering\n */\n async listSessions(filters?: SessionFilter): Promise {\n await this.db.read();\n let sessions = [...this.db.data.sessions];\n\n if (filters) {\n if (filters.queryPath) {\n sessions = sessions.filter(s => s.queryPath.includes(filters.queryPath!));\n }\n if (filters.status) {\n sessions = sessions.filter(s => s.status === filters.status);\n }\n if (filters.language) {\n sessions = sessions.filter(s => s.language === filters.language);\n }\n if (filters.queryType) {\n sessions = sessions.filter(s => s.queryType === filters.queryType);\n }\n if (filters.dateRange) {\n const [start, end] = filters.dateRange;\n sessions = sessions.filter(s => \n s.startTime >= start && s.startTime <= end\n );\n }\n }\n\n return sessions;\n }\n\n /**\n * Update session state\n */\n async updateSessionState(\n sessionId: string,\n stateUpdate: Partial\n ): Promise {\n await this.db.read();\n \n const session = this.db.data.sessions.find(s => s.sessionId === sessionId);\n if (!session) {\n logger.warn(`Session not found: ${sessionId}`);\n return null;\n }\n\n session.currentState = {\n ...session.currentState,\n ...stateUpdate,\n lastActivity: new Date().toISOString(),\n };\n\n await this.db.write();\n return session;\n }\n\n /**\n * Add MCP call record to session\n */\n async addMCPCall(sessionId: string, callRecord: MCPCallRecord): Promise {\n await this.db.read();\n \n const session = this.db.data.sessions.find(s => s.sessionId === sessionId);\n if (!session) {\n logger.warn(`Session not found for MCP call: ${sessionId}`);\n return;\n }\n\n session.mcpCalls.push(callRecord);\n session.currentState.lastActivity = callRecord.timestamp;\n\n // Update next suggested tool if provided\n if (callRecord.nextSuggestedTool) {\n session.nextSuggestedTool = callRecord.nextSuggestedTool;\n }\n\n await this.db.write();\n }\n\n /**\n * Add test execution record to session\n */\n async addTestExecution(sessionId: string, testRecord: TestExecutionRecord): Promise {\n await this.db.read();\n \n const session = this.db.data.sessions.find(s => s.sessionId === sessionId);\n if (!session) {\n logger.warn(`Session not found for test execution: ${sessionId}`);\n return;\n }\n\n session.testExecutions.push(testRecord);\n session.currentState.lastActivity = testRecord.timestamp;\n\n // Update compilation/test status based on execution\n if (testRecord.type === 'compilation') {\n session.currentState.compilationStatus = testRecord.success ? 'success' : 'failed';\n } else if (testRecord.type === 'test_run') {\n session.currentState.testStatus = testRecord.success ? 'passing' : 'failing';\n }\n\n await this.db.write();\n }\n\n /**\n * Add quality score record to session\n */\n async addQualityScore(sessionId: string, scoreRecord: QualityScoreRecord): Promise {\n await this.db.read();\n \n const session = this.db.data.sessions.find(s => s.sessionId === sessionId);\n if (!session) {\n logger.warn(`Session not found for quality score: ${sessionId}`);\n return;\n }\n\n session.qualityScores.push(scoreRecord);\n session.currentState.lastActivity = scoreRecord.timestamp;\n session.recommendations = scoreRecord.recommendations;\n\n await this.db.write();\n }\n\n /**\n * Archive a completed session to monthly file\n */\n private async archiveSession(sessionId: string): Promise {\n try {\n const session = await this.getSession(sessionId);\n if (!session) return;\n\n const date = new Date(session.endTime || session.startTime);\n const monthDir = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`;\n const archiveDir = join(this.storageDir, 'sessions-archive', monthDir);\n\n mkdirSync(archiveDir, { recursive: true });\n\n const archiveFile = join(archiveDir, `${sessionId}.json`);\n writeFileSync(archiveFile, JSON.stringify(session, null, 2));\n\n // Remove from active sessions\n await this.db.read();\n this.db.data.sessions = this.db.data.sessions.filter(s => s.sessionId !== sessionId);\n await this.db.write();\n\n logger.info(`Archived session: ${sessionId} to ${archiveFile}`);\n } catch (error) {\n logger.error(`Failed to archive session ${sessionId}:`, error);\n }\n }\n\n /**\n * Get active sessions for a specific query path\n */\n async getActiveSessionsForQuery(queryPath: string): Promise {\n await this.db.read();\n return this.db.data.sessions.filter(s => \n s.queryPath === queryPath && s.status === 'active'\n );\n }\n\n /**\n * Clean up old sessions based on retention policy\n */\n async cleanupOldSessions(): Promise {\n const cutoffDate = new Date();\n cutoffDate.setDate(cutoffDate.getDate() - this.config.retentionDays);\n const cutoffTimestamp = cutoffDate.toISOString();\n\n await this.db.read();\n const sessionsToRemove = this.db.data.sessions.filter(s => \n s.endTime && s.endTime < cutoffTimestamp\n );\n\n if (sessionsToRemove.length > 0) {\n this.db.data.sessions = this.db.data.sessions.filter(s => \n !s.endTime || s.endTime >= cutoffTimestamp\n );\n await this.db.write();\n\n logger.info(`Cleaned up ${sessionsToRemove.length} old sessions`);\n }\n }\n\n /**\n * Get configuration\n */\n getConfig(): MonitoringConfig {\n return this.config;\n }\n\n /**\n * Update configuration\n */\n async updateConfig(configUpdate: Partial): Promise {\n this.config = MonitoringConfigSchema.parse({\n ...this.config,\n ...configUpdate,\n });\n\n // Update config.json file only\n const configPath = join(this.storageDir, 'config.json');\n writeFileSync(configPath, JSON.stringify(this.config, null, 2));\n\n logger.info('Updated monitoring configuration');\n }\n}\n\n/**\n * Parse boolean environment variable\n */\nfunction parseBoolEnv(envVar: string | undefined, defaultValue: boolean): boolean {\n if (envVar === undefined) return defaultValue;\n return envVar.toLowerCase() === 'true' || envVar === '1';\n}\n\n// Export singleton instance with environment variable support\nexport const sessionDataManager = new SessionDataManager({\n storageLocation: process.env.MONITORING_STORAGE_LOCATION || join(getProjectTmpBase(), '.ql-mcp-tracking'),\n enableMonitoringTools: parseBoolEnv(process.env.ENABLE_MONITORING_TOOLS, false),\n});", "import { z } from 'zod';\n\n/**\n * Monitoring and Reporting types for CodeQL Development MCP Server\n * Based on the specification in docs/mcp-server-monitoring-and-reporting.md\n */\n\n/**\n * MCP Call Record - captures individual MCP tool calls\n */\nexport const MCPCallRecordSchema = z.object({\n callId: z.string(),\n timestamp: z.string(), // ISO timestamp\n toolName: z.string(),\n parameters: z.record(z.any()),\n result: z.any(),\n success: z.boolean(),\n duration: z.number(), // milliseconds\n nextSuggestedTool: z.string().optional(),\n});\n\nexport type MCPCallRecord = z.infer;\n\n/**\n * Test Execution Record - captures query compilation and test runs\n */\nexport const TestExecutionRecordSchema = z.object({\n executionId: z.string(),\n timestamp: z.string(),\n type: z.enum(['compilation', 'test_run', 'database_build']),\n success: z.boolean(),\n details: z.record(z.any()),\n metrics: z.object({\n passRate: z.number().optional(),\n coverage: z.number().optional(),\n performance: z.number().optional(),\n }).optional(),\n});\n\nexport type TestExecutionRecord = z.infer;\n\n/**\n * Quality Score Record - multi-dimensional quality assessment\n */\nexport const QualityScoreRecordSchema = z.object({\n scoreId: z.string(),\n timestamp: z.string(),\n overallScore: z.number().min(0).max(100), // 0-100\n dimensions: z.object({\n syntacticCorrectness: z.number().min(0).max(100),\n testCoverageResults: z.number().min(0).max(100),\n documentationQuality: z.number().min(0).max(100),\n functionalCorrectness: z.number().min(0).max(100),\n }),\n grade: z.enum(['A', 'B', 'C', 'D', 'F']),\n recommendations: z.array(z.string()),\n});\n\nexport type QualityScoreRecord = z.infer;\n\n/**\n * Query State - current state of the query development\n */\nexport const QueryStateSchema = z.object({\n filesPresent: z.array(z.string()),\n compilationStatus: z.enum(['unknown', 'success', 'failed']),\n testStatus: z.enum(['unknown', 'passing', 'failing', 'no_tests']),\n documentationStatus: z.enum(['unknown', 'present', 'missing', 'incomplete']),\n lastActivity: z.string(), // ISO timestamp\n});\n\nexport type QueryState = z.infer;\n\n/**\n * Query Development Session - main data structure for tracking\n */\nexport const QueryDevelopmentSessionSchema = z.object({\n // Session Metadata\n sessionId: z.string(),\n queryPath: z.string(),\n language: z.string(),\n queryType: z.string().optional(),\n description: z.string().optional(),\n startTime: z.string(), // ISO timestamp\n endTime: z.string().optional(), // ISO timestamp\n status: z.enum(['active', 'completed', 'failed', 'abandoned']),\n\n // MCP Call History\n mcpCalls: z.array(MCPCallRecordSchema),\n\n // Test Execution Records\n testExecutions: z.array(TestExecutionRecordSchema),\n\n // Quality Metrics\n qualityScores: z.array(QualityScoreRecordSchema),\n\n // Development State\n currentState: QueryStateSchema,\n recommendations: z.array(z.string()),\n nextSuggestedTool: z.string().optional(),\n});\n\nexport type QueryDevelopmentSession = z.infer;\n\n/**\n * Session Filter for listing and searching\n */\nexport const SessionFilterSchema = z.object({\n queryPath: z.string().optional(),\n status: z.string().optional(),\n dateRange: z.tuple([z.string(), z.string()]).optional(),\n language: z.string().optional(),\n queryType: z.string().optional(),\n});\n\nexport type SessionFilter = z.infer;\n\n/**\n * Comparison Report for analyzing multiple sessions\n */\nexport const ComparisonReportSchema = z.object({\n sessionIds: z.array(z.string()),\n dimensions: z.array(z.string()),\n timestamp: z.string(),\n results: z.record(z.any()),\n});\n\nexport type ComparisonReport = z.infer;\n\n/**\n * Aggregate Report for batch analysis\n */\nexport const AggregateReportSchema = z.object({\n filters: SessionFilterSchema,\n timestamp: z.string(),\n totalSessions: z.number(),\n successRate: z.number(),\n averageQualityScore: z.number(),\n commonPatterns: z.array(z.string()),\n recommendations: z.array(z.string()),\n});\n\nexport type AggregateReport = z.infer;\n\n/**\n * Export Result for data export operations\n */\nexport const ExportResultSchema = z.object({\n format: z.enum(['json', 'html', 'markdown']),\n filename: z.string(),\n content: z.string(),\n timestamp: z.string(),\n});\n\nexport type ExportResult = z.infer;\n\n/**\n * Functional Test Result for automated testing\n */\nexport const FunctionalTestResultSchema = z.object({\n sessionId: z.string(),\n queryPath: z.string(),\n passed: z.boolean(),\n criteria: z.record(z.any()),\n details: z.record(z.any()),\n timestamp: z.string(),\n});\n\nexport type FunctionalTestResult = z.infer;\n\n/**\n * Test Report for comprehensive test analysis\n */\nexport const TestReportSchema = z.object({\n sessionIds: z.array(z.string()),\n criteria: z.record(z.any()),\n timestamp: z.string(),\n overallPassRate: z.number(),\n results: z.array(FunctionalTestResultSchema),\n summary: z.record(z.any()),\n});\n\nexport type TestReport = z.infer;\n\n/**\n * Monitoring Configuration\n */\nexport const MonitoringConfigSchema = z.object({\n storageLocation: z.string().default('.ql-mcp-tracking/'),\n autoTrackSessions: z.boolean().default(true),\n retentionDays: z.number().default(90),\n includeCallParameters: z.boolean().default(true),\n includeCallResults: z.boolean().default(true),\n maxActiveSessionsPerQuery: z.number().default(3),\n scoringFrequency: z.enum(['per_call', 'periodic', 'manual']).default('per_call'),\n archiveCompletedSessions: z.boolean().default(true),\n enableRecommendations: z.boolean().default(true),\n enableMonitoringTools: z.boolean().default(false), // Opt-in: session_* tools disabled by default for end-users\n});\n\nexport type MonitoringConfig = z.infer;"], + "sourcesContent": ["/**\n * Simple logger utility.\n *\n * All log output is written to stderr. In stdio transport mode, stdout is\n * reserved exclusively for the MCP JSON-RPC protocol \u2014 any non-protocol\n * bytes on stdout would corrupt the message stream.\n */\nexport const logger = {\n info: (message: string, ...args: unknown[]) => {\n console.error(`[INFO] ${new Date().toISOString()} ${message}`, ...args);\n },\n error: (message: string, ...args: unknown[]) => {\n console.error(`[ERROR] ${new Date().toISOString()} ${message}`, ...args);\n },\n warn: (message: string, ...args: unknown[]) => {\n console.error(`[WARN] ${new Date().toISOString()} ${message}`, ...args);\n },\n debug: (message: string, ...args: unknown[]) => {\n if (process.env.DEBUG) {\n console.error(`[DEBUG] ${new Date().toISOString()} ${message}`, ...args);\n }\n },\n};\n", "/**\n * Configuration types for CodeQL background server processes.\n *\n * CodeQL provides three background server types:\n * 1. language-server \u2013 LSP-based QL validation (JSON-RPC over stdio)\n * 2. query-server2 \u2013 Query evaluation (custom protocol over stdio)\n * 3. cli-server \u2013 JVM reuse for CLI commands (custom protocol over stdio)\n *\n * Each server type has its own configuration options, but they share common\n * settings like searchPath and commonCaches.\n */\n\nimport { createHash } from 'crypto';\n\n/**\n * Server types supported by CodeQL.\n */\nexport type CodeQLServerType = 'cli' | 'language' | 'query';\n\n/**\n * Common configuration shared across all server types.\n */\nexport interface BaseServerConfig {\n /** Path to QL packs (like `codeql query compile --search-path`). */\n searchPath?: string;\n /** Location for cached data (compilation plans, downloaded packs). */\n commonCaches?: string;\n /** Directory for detailed logs. */\n logdir?: string;\n}\n\n/**\n * Configuration for the CodeQL Language Server.\n */\nexport interface LanguageServerConfig extends BaseServerConfig {\n /** Error checking mode. Default: ON_CHANGE */\n checkErrors?: 'EXPLICIT' | 'ON_CHANGE';\n /** Log level for the language server. */\n loglevel?: 'ALL' | 'DEBUG' | 'ERROR' | 'INFO' | 'OFF' | 'TRACE' | 'WARN';\n /** Single-threaded execution. */\n synchronous?: boolean;\n /** Verbosity level for progress. */\n verbosity?: 'errors' | 'progress' | 'progress+' | 'progress++' | 'progress+++' | 'warnings';\n}\n\n/**\n * Configuration for the CodeQL Query Server (query-server2).\n */\nexport interface QueryServerConfig extends BaseServerConfig {\n /** Thread count. 0 = one per core, -N = leave N cores free. */\n threads?: number;\n /** Query evaluation timeout in seconds. */\n timeout?: number;\n /** Maximum disk cache size in MB for intermediate results. */\n maxDiskCache?: number;\n /** Path for structured evaluator performance logs. */\n evaluatorLog?: string;\n /** Include tuple counts in evaluation logs. */\n tupleCounting?: boolean;\n /** Enable debug mode. */\n debug?: boolean;\n}\n\n/**\n * Configuration for the CodeQL CLI Server.\n */\nexport interface CLIServerConfig extends BaseServerConfig {\n // cli-server has fewer options \u2014 just commonCaches and logdir.\n}\n\n/**\n * Union of all server configurations (discriminated by usage context).\n */\nexport type ServerConfig = CLIServerConfig | LanguageServerConfig | QueryServerConfig;\n\n/**\n * Compute a deterministic hash for a server configuration.\n * Used to detect configuration changes that require a server restart.\n *\n * @param type - The server type.\n * @param config - The server configuration.\n * @returns A hex-encoded SHA-256 hash of the canonical JSON representation.\n */\nexport function computeConfigHash(type: CodeQLServerType, config: ServerConfig): string {\n // Deep-sort all keys to ensure deterministic serialization regardless of\n // property insertion order.\n const sortKeys = (_key: string, value: unknown): unknown => {\n if (value && typeof value === 'object' && !Array.isArray(value)) {\n const sorted: Record = {};\n for (const k of Object.keys(value as Record).sort()) {\n sorted[k] = (value as Record)[k];\n }\n return sorted;\n }\n return value;\n };\n const canonical = JSON.stringify({ config, type }, sortKeys);\n return createHash('sha256').update(canonical).digest('hex');\n}\n\n/**\n * Build command-line arguments for a language server from its configuration.\n */\nexport function buildLanguageServerArgs(config: LanguageServerConfig): string[] {\n const args: string[] = [\n 'execute', 'language-server',\n `--check-errors=${config.checkErrors ?? 'ON_CHANGE'}`,\n ];\n\n if (config.searchPath) {\n args.push(`--search-path=${config.searchPath}`);\n }\n if (config.commonCaches) {\n args.push(`--common-caches=${config.commonCaches}`);\n }\n if (config.logdir) {\n args.push(`--logdir=${config.logdir}`);\n }\n if (config.loglevel) {\n args.push(`--loglevel=${config.loglevel}`);\n }\n if (config.synchronous) {\n args.push('--synchronous');\n }\n if (config.verbosity) {\n args.push(`--verbosity=${config.verbosity}`);\n }\n\n return args;\n}\n\n/**\n * Build command-line arguments for a query server from its configuration.\n */\nexport function buildQueryServerArgs(config: QueryServerConfig): string[] {\n const args: string[] = [\n 'execute', 'query-server2',\n ];\n\n if (config.searchPath) {\n args.push(`--search-path=${config.searchPath}`);\n }\n if (config.commonCaches) {\n args.push(`--common-caches=${config.commonCaches}`);\n }\n if (config.logdir) {\n args.push(`--logdir=${config.logdir}`);\n }\n if (config.threads !== undefined) {\n args.push(`--threads=${config.threads}`);\n }\n if (config.timeout !== undefined) {\n args.push(`--timeout=${config.timeout}`);\n }\n if (config.maxDiskCache !== undefined) {\n args.push(`--max-disk-cache=${config.maxDiskCache}`);\n }\n if (config.evaluatorLog) {\n args.push(`--evaluator-log=${config.evaluatorLog}`);\n }\n if (config.debug) {\n args.push('--debug');\n args.push('--tuple-counting');\n } else if (config.tupleCounting) {\n args.push('--tuple-counting');\n }\n\n return args;\n}\n\n/**\n * Build command-line arguments for a CLI server from its configuration.\n */\nexport function buildCLIServerArgs(config: CLIServerConfig): string[] {\n const args: string[] = [\n 'execute', 'cli-server',\n ];\n\n if (config.commonCaches) {\n args.push(`--common-caches=${config.commonCaches}`);\n }\n if (config.logdir) {\n args.push(`--logdir=${config.logdir}`);\n }\n\n return args;\n}\n", "/**\n * Utilities for resolving filesystem paths relative to the server package root.\n *\n * The server can run from three different directory layouts:\n *\n * 1. **Source** (dev): `server/src/lib/` \u2192 packageRoot = `server/`\n * 2. **Bundle in monorepo** (dev/CI): `server/dist/` \u2192 packageRoot = `server/`\n * 3. **Bundle via npm** (production): `/dist/` \u2192 packageRoot = `/`\n *\n * In all three cases, the bundled QL tool query packs live at\n * `/ql//tools/src/`.\n *\n * The \"workspace root\" (monorepo root) is one level above packageRoot when\n * running from the monorepo checkout, and the packageRoot itself when running\n * from an npm install (no parent monorepo).\n */\n\nimport { dirname, resolve } from 'path';\nimport { existsSync, readFileSync } from 'fs';\nimport { fileURLToPath } from 'url';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\n\n/**\n * Detect whether the current __dirname looks like source code (`src/lib` or\n * `src/utils`) vs a bundled flat output directory (`dist/`).\n *\n * Uses a tail-of-path check so that unrelated `/src/` segments earlier in the\n * install path (e.g. `~/src/project/node_modules/.../dist`) don't cause a\n * false positive.\n */\nfunction isRunningFromSource(dir: string): boolean {\n const normalized = dir.replace(/\\\\/g, '/');\n return /\\/src(\\/[^/]+)?$/.test(normalized);\n}\n\n/**\n * Get the server package root directory.\n *\n * - From source (`server/src/utils/`): up 2 levels \u2192 `server/`\n * - From bundle (`server/dist/` or `/dist/`): up 1 level \u2192 package root\n */\nexport function getPackageRootDir(currentDir: string = __dirname): string {\n return isRunningFromSource(currentDir)\n ? resolve(currentDir, '..', '..') // src/utils \u2192 server/\n : resolve(currentDir, '..'); // dist/ \u2192 package root\n}\n\n/**\n * Get the workspace root directory (monorepo root when applicable).\n *\n * If a `package.json` with `workspaces` exists one level above the package\n * root, we're in a monorepo and that parent is the workspace root. Otherwise,\n * the packageRoot itself is the workspace root (npm install scenario).\n */\nexport function getWorkspaceRootDir(packageRoot?: string): string {\n const pkgRoot = packageRoot ?? getPackageRootDir();\n const parentDir = resolve(pkgRoot, '..');\n\n // In the monorepo, the parent directory contains a package.json with workspaces\n try {\n const parentPkgPath = resolve(parentDir, 'package.json');\n if (existsSync(parentPkgPath)) {\n const parentPkg = JSON.parse(readFileSync(parentPkgPath, 'utf8'));\n if (parentPkg.workspaces) {\n return parentDir;\n }\n }\n } catch {\n // Not in a monorepo \u2014 fall through\n }\n\n return pkgRoot;\n}\n\n/**\n * Resolve the path to a tool query pack's source directory.\n *\n * @param language - CodeQL language identifier (e.g., \"javascript\", \"cpp\")\n * @param packageRoot - Override the package root (for testing)\n * @returns Absolute path to `ql//tools/src/`\n */\nexport function resolveToolQueryPackPath(language: string, packageRoot?: string): string {\n const pkgRoot = packageRoot ?? getPackageRootDir();\n return resolve(pkgRoot, 'ql', language, 'tools', 'src');\n}\n\n/**\n * Read the package version from the nearest package.json.\n *\n * Cached at first call so the file is read at most once per process.\n */\nlet _cachedVersion: string | undefined;\nexport function getPackageVersion(): string {\n if (_cachedVersion !== undefined) return _cachedVersion;\n try {\n const pkgPath = resolve(getPackageRootDir(), 'package.json');\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));\n _cachedVersion = pkg.version ?? '0.0.0';\n } catch {\n _cachedVersion = '0.0.0';\n }\n return _cachedVersion as string;\n}\n\n/**\n * Get the effective workspace directory for resolving user-supplied relative\n * paths (test directories, database paths, pack dirs, etc.).\n *\n * In a monorepo checkout the workspace root is the monorepo parent. In an\n * npm-installed layout, `workspaceRootDir` falls back to `packageRootDir`\n * which may be read-only and is not the user's project. In that case we\n * fall back to `process.cwd()` so that relative paths resolve against the\n * directory the user actually invoked the server from.\n *\n * Override with `CODEQL_MCP_WORKSPACE` for deterministic behavior.\n */\nexport function getUserWorkspaceDir(): string {\n if (process.env.CODEQL_MCP_WORKSPACE) {\n return process.env.CODEQL_MCP_WORKSPACE;\n }\n // When workspaceRootDir === packageRootDir we are NOT in a monorepo\n // (npm-installed), so fall back to process.cwd().\n if (workspaceRootDir === packageRootDir) {\n return process.cwd();\n }\n return workspaceRootDir;\n}\n\n// Pre-computed values for use throughout the server\nexport const packageRootDir = getPackageRootDir();\nexport const workspaceRootDir = getWorkspaceRootDir(packageRootDir);\n", "/**\n * Secure project-local temporary directory utilities.\n *\n * All temporary files are created under `/.tmp/` which is\n * `.gitignore`d. This avoids writing to the OS temp directory\n * (`os.tmpdir()` / `/tmp`), which is world-readable and triggers\n * CWE-377 / CWE-378 (js/insecure-temporary-file).\n */\n\nimport { mkdirSync, mkdtempSync } from 'fs';\nimport { isAbsolute, join, resolve } from 'path';\nimport { getPackageRootDir } from './package-paths';\n\n/**\n * Base directory for all project-local temporary data.\n *\n * Resolution order:\n * 1. `CODEQL_MCP_TMP_DIR` environment variable \u2014 for read-only package root\n * scenarios (e.g., npm global installs where the package directory is not\n * writable). Relative paths are resolved against process.cwd().\n * 2. `/.tmp` \u2014 default; excluded from version control.\n */\nconst PROJECT_TMP_BASE = process.env.CODEQL_MCP_TMP_DIR\n ? (isAbsolute(process.env.CODEQL_MCP_TMP_DIR) \n ? process.env.CODEQL_MCP_TMP_DIR \n : resolve(process.cwd(), process.env.CODEQL_MCP_TMP_DIR))\n : join(getPackageRootDir(), '.tmp');\n\n/**\n * Return the project-local `.tmp` base directory, creating it if needed.\n */\nexport function getProjectTmpBase(): string {\n mkdirSync(PROJECT_TMP_BASE, { recursive: true });\n return PROJECT_TMP_BASE;\n}\n\n/**\n * Create a unique temporary directory under the project `.tmp` root.\n *\n * Works identically to `fs.mkdtempSync(os.tmpdir(), prefix)` but is\n * scoped to the repository.\n *\n * @param prefix - Directory name prefix (e.g. `'codeql-external-'`).\n * @returns Absolute path to the newly created directory.\n */\nexport function createProjectTempDir(prefix: string): string {\n const base = getProjectTmpBase();\n return mkdtempSync(join(base, prefix));\n}\n\n/**\n * Return a deterministic subdirectory under `.tmp/`, creating it\n * if it does not already exist.\n *\n * Useful for well-known scratch areas such as `query-logs` or `quickeval`.\n *\n * @param name - Subdirectory name (e.g. `'query-logs'`).\n * @returns Absolute path to the subdirectory.\n */\nexport function getProjectTmpDir(name: string): string {\n const dir = join(getProjectTmpBase(), name);\n mkdirSync(dir, { recursive: true });\n return dir;\n}\n", "/**\n * Utility for waiting until a spawned child process is ready.\n *\n * CodeQL background servers (cli-server, query-server2, language-server) run\n * on the JVM and emit stderr log output once the JVM has initialised. Rather\n * than sleeping for a hard-coded duration \u2014 which is fragile on both fast and\n * slow machines \u2014 this helper resolves as soon as the first stderr output\n * arrives (indicating the JVM is alive), or when the maximum timeout expires.\n * It also rejects immediately if the process exits or errors before becoming\n * ready, giving callers a clear error instead of a silent hang.\n */\n\nimport { ChildProcess } from 'child_process';\nimport { clearTimeout, setTimeout } from 'timers';\nimport { logger } from './logger';\n\n/** Default maximum wait for a CodeQL server to become ready (30 s). */\nconst DEFAULT_READY_TIMEOUT_MS = 30_000;\n\n/**\n * Options for {@link waitForProcessReady}.\n */\nexport interface ProcessReadyOptions {\n /**\n * Maximum time in milliseconds to wait for the process to emit its first\n * stderr output. If the timeout is reached without a signal the promise\n * still resolves (best-effort) so the caller can attempt communication.\n *\n * Default: 30 000 ms.\n */\n timeoutMs?: number;\n}\n\n/**\n * Wait until a child process signals readiness.\n *\n * \"Ready\" is defined as any of:\n * 1. The process emits data on **stderr** (JVM startup log line).\n * 2. The process emits data on **stdout** (initial protocol message).\n * 3. The maximum timeout elapses (best-effort resolve).\n *\n * The promise **rejects** if the process emits an `error` event or exits\n * before any of the above conditions are met.\n *\n * @param child - The spawned child process.\n * @param name - A human-readable label for log messages.\n * @param opts - Optional configuration.\n */\nexport function waitForProcessReady(\n child: ChildProcess,\n name: string,\n opts?: ProcessReadyOptions,\n): Promise {\n const timeoutMs = opts?.timeoutMs ?? DEFAULT_READY_TIMEOUT_MS;\n\n return new Promise((resolve, reject) => {\n let settled = false;\n\n const cleanup = () => {\n settled = true;\n child.stderr?.removeListener('data', onStderr);\n child.stdout?.removeListener('data', onStdout);\n child.removeListener('error', onError);\n child.removeListener('exit', onExit);\n clearTimeout(timer);\n };\n\n const onStderr = () => {\n if (settled) return;\n logger.debug(`${name}: ready (stderr output detected)`);\n cleanup();\n resolve();\n };\n\n const onStdout = () => {\n if (settled) return;\n logger.debug(`${name}: ready (stdout output detected)`);\n cleanup();\n resolve();\n };\n\n const onError = (error: Error) => {\n if (settled) return;\n cleanup();\n reject(new Error(`${name} failed to start: ${error.message}`));\n };\n\n const onExit = (code: number | null) => {\n if (settled) return;\n cleanup();\n reject(new Error(`${name} exited before becoming ready (code: ${code})`));\n };\n\n const timer = setTimeout(() => {\n if (settled) return;\n logger.warn(`${name}: readiness timeout (${timeoutMs} ms) \u2014 proceeding anyway`);\n cleanup();\n resolve(); // best-effort: let the caller attempt communication\n }, timeoutMs);\n\n child.stderr?.on('data', onStderr);\n child.stdout?.on('data', onStdout);\n child.on('error', onError);\n child.on('exit', onExit);\n\n // If the process was dead before we even attached listeners, reject now\n if (child.killed || child.exitCode !== null) {\n cleanup();\n reject(new Error(`${name} is not running (exitCode: ${child.exitCode})`));\n }\n });\n}\n", "/**\n * CodeQL Language Server manager for LSP communication\n * Manages the lifecycle and communication with the CodeQL language server process\n */\n\nimport { spawn, ChildProcess } from 'child_process';\nimport { EventEmitter } from 'events';\nimport { setTimeout, clearTimeout } from 'timers';\nimport { pathToFileURL } from 'url';\nimport { delimiter, join } from 'path';\nimport { logger } from '../utils/logger';\nimport { getPackageVersion } from '../utils/package-paths';\nimport { getProjectTmpDir } from '../utils/temp-dir';\nimport { getResolvedCodeQLDir } from './cli-executor';\nimport { waitForProcessReady } from '../utils/process-ready';\n\nexport interface LSPMessage {\n jsonrpc: '2.0';\n id?: number | string;\n method: string;\n params?: unknown;\n result?: unknown;\n error?: {\n code: number;\n message: string;\n data?: unknown;\n };\n}\n\nexport interface Diagnostic {\n range: {\n start: { line: number; character: number };\n end: { line: number; character: number };\n };\n severity: number; // 1=Error, 2=Warning, 3=Information, 4=Hint\n source?: string;\n message: string;\n code?: string | number;\n}\n\nexport interface PublishDiagnosticsParams {\n uri: string;\n diagnostics: Diagnostic[];\n}\n\nexport interface LanguageServerOptions {\n searchPath?: string;\n logdir?: string;\n loglevel?: 'ALL' | 'DEBUG' | 'ERROR' | 'INFO' | 'OFF' | 'TRACE' | 'WARN';\n synchronous?: boolean;\n verbosity?: 'errors' | 'progress' | 'progress+' | 'progress++' | 'progress+++' | 'warnings';\n}\n\n/**\n * Position in a text document (0-based line and character).\n */\nexport interface LSPPosition {\n character: number;\n line: number;\n}\n\n/**\n * A range in a text document.\n */\nexport interface LSPRange {\n end: LSPPosition;\n start: LSPPosition;\n}\n\n/**\n * A location in a resource (file URI + range).\n */\nexport interface LSPLocation {\n range: LSPRange;\n uri: string;\n}\n\n/**\n * Identifies a text document by its URI.\n */\nexport interface TextDocumentIdentifier {\n uri: string;\n}\n\n/**\n * A text document position (document + position within it).\n */\nexport interface TextDocumentPositionParams {\n position: LSPPosition;\n textDocument: TextDocumentIdentifier;\n}\n\n/**\n * A completion item returned by the language server.\n */\nexport interface CompletionItem {\n detail?: string;\n documentation?: string | { kind: string; value: string };\n insertText?: string;\n kind?: number;\n label: string;\n sortText?: string;\n}\n\nexport class CodeQLLanguageServer extends EventEmitter {\n private server: ChildProcess | null = null;\n private messageId = 1;\n private pendingResponses = new Map void; reject: (_error: Error) => void }>();\n private isInitialized = false;\n private currentWorkspaceUri: string | undefined;\n private messageBuffer = '';\n\n constructor(private _options: LanguageServerOptions = {}) {\n super();\n }\n\n async start(): Promise {\n if (this.server) {\n throw new Error('Language server is already running');\n }\n\n logger.info('Starting CodeQL Language Server...');\n\n const args = [\n 'execute', 'language-server',\n '--check-errors=ON_CHANGE'\n ];\n\n // Add optional arguments\n if (this._options.searchPath) {\n args.push(`--search-path=${this._options.searchPath}`);\n }\n if (this._options.logdir) {\n args.push(`--logdir=${this._options.logdir}`);\n }\n if (this._options.loglevel) {\n args.push(`--loglevel=${this._options.loglevel}`);\n }\n if (this._options.synchronous) {\n args.push('--synchronous');\n }\n if (this._options.verbosity) {\n args.push(`--verbosity=${this._options.verbosity}`);\n }\n\n // Build environment with CODEQL_PATH directory prepended to PATH\n // (mirrors the approach in cli-executor.ts getSafeEnvironment).\n const spawnEnv = { ...process.env };\n const codeqlDir = getResolvedCodeQLDir();\n if (codeqlDir && spawnEnv.PATH) {\n spawnEnv.PATH = `${codeqlDir}${delimiter}${spawnEnv.PATH}`;\n } else if (codeqlDir) {\n spawnEnv.PATH = codeqlDir;\n }\n\n this.server = spawn('codeql', args, {\n stdio: ['pipe', 'pipe', 'pipe'],\n env: spawnEnv\n });\n\n this.server.stderr?.on('data', (data) => {\n logger.debug('CodeQL LS stderr:', data.toString());\n });\n\n this.server.stdout?.on('data', (data) => {\n this.handleStdout(data);\n });\n\n this.server.on('error', (error) => {\n logger.error('CodeQL Language Server error:', error);\n this.emit('error', error);\n });\n\n this.server.on('exit', (code) => {\n logger.info('CodeQL Language Server exited with code:', code);\n this.server = null;\n this.isInitialized = false;\n this.emit('exit', code);\n });\n\n // Wait for the JVM to initialise (resolves on first stderr/stdout output)\n await waitForProcessReady(this.server, 'CodeQL Language Server');\n }\n\n private handleStdout(data: Buffer): void {\n this.messageBuffer += data.toString();\n \n let headerEnd = this.messageBuffer.indexOf('\\r\\n\\r\\n');\n while (headerEnd !== -1) {\n const header = this.messageBuffer.substring(0, headerEnd);\n const contentLengthMatch = header.match(/Content-Length: (\\d+)/);\n \n if (contentLengthMatch) {\n const contentLength = parseInt(contentLengthMatch[1]);\n const messageStart = headerEnd + 4;\n const messageEnd = messageStart + contentLength;\n \n if (this.messageBuffer.length >= messageEnd) {\n const messageContent = this.messageBuffer.substring(messageStart, messageEnd);\n this.messageBuffer = this.messageBuffer.substring(messageEnd);\n \n try {\n const message: LSPMessage = JSON.parse(messageContent);\n this.handleMessage(message);\n } catch (error) {\n logger.error('Failed to parse LSP message:', error, messageContent);\n }\n \n headerEnd = this.messageBuffer.indexOf('\\r\\n\\r\\n');\n } else {\n break;\n }\n } else {\n logger.error('Invalid LSP header:', header);\n this.messageBuffer = '';\n break;\n }\n }\n }\n\n private handleMessage(message: LSPMessage): void {\n logger.debug('Received LSP message:', message);\n\n // Handle responses to our requests\n if (message.id !== undefined && this.pendingResponses.has(Number(message.id))) {\n const pending = this.pendingResponses.get(Number(message.id))!;\n this.pendingResponses.delete(Number(message.id));\n \n if (message.error) {\n pending.reject(new Error(`LSP Error: ${message.error.message}`));\n } else {\n pending.resolve(message.result);\n }\n return;\n }\n\n // Handle notifications from server\n if (message.method === 'textDocument/publishDiagnostics') {\n this.emit('diagnostics', message.params as PublishDiagnosticsParams);\n }\n }\n\n private sendMessage(message: LSPMessage): void {\n if (!this.server?.stdin) {\n throw new Error('Language server is not running');\n }\n\n const messageStr = JSON.stringify(message);\n const contentLength = Buffer.byteLength(messageStr, 'utf8');\n const header = `Content-Length: ${contentLength}\\r\\n\\r\\n`;\n const fullMessage = header + messageStr;\n\n logger.debug('Sending LSP message:', fullMessage);\n this.server.stdin.write(fullMessage);\n }\n\n private sendRequest(method: string, params?: unknown): Promise {\n const id = this.messageId++;\n const message: LSPMessage = {\n jsonrpc: '2.0',\n id,\n method,\n params\n };\n\n return new Promise((resolve, reject) => {\n // Wrap resolve/reject to clear the timer when the promise settles.\n const timer = setTimeout(() => {\n if (this.pendingResponses.has(id)) {\n this.pendingResponses.delete(id);\n reject(new Error(`LSP request timeout for method: ${method}`));\n }\n }, 60_000); // 60 second timeout (Windows CI cold JVM can exceed 30s)\n\n this.pendingResponses.set(id, {\n reject: (err: Error) => { clearTimeout(timer); reject(err); },\n resolve: (val: unknown) => { clearTimeout(timer); resolve(val); },\n });\n this.sendMessage(message);\n });\n }\n\n private sendNotification(method: string, params?: unknown): void {\n const message: LSPMessage = {\n jsonrpc: '2.0',\n method,\n params\n };\n this.sendMessage(message);\n }\n\n /**\n * Initialize the language server with an optional workspace URI.\n *\n * If the server is already initialized with a different workspace, a\n * `workspace/didChangeWorkspaceFolders` notification is sent to update\n * the workspace context instead of requiring a full restart.\n */\n async initialize(workspaceUri?: string): Promise {\n if (this.isInitialized) {\n // If workspace changed, notify the server\n if (workspaceUri && workspaceUri !== this.currentWorkspaceUri) {\n await this.updateWorkspace(workspaceUri);\n }\n return;\n }\n\n logger.info('Initializing CodeQL Language Server...');\n\n const initParams = {\n processId: process.pid,\n clientInfo: {\n name: 'codeql-development-mcp-server',\n version: getPackageVersion()\n },\n capabilities: {\n textDocument: {\n completion: { completionItem: { snippetSupport: false } },\n definition: {},\n publishDiagnostics: {},\n references: {},\n synchronization: {\n didClose: true,\n didChange: true,\n didOpen: true,\n },\n },\n workspace: {\n workspaceFolders: true,\n },\n }\n };\n\n if (workspaceUri) {\n (initParams as unknown as { workspaceFolders: unknown[] }).workspaceFolders = [{\n uri: workspaceUri,\n name: 'codeql-workspace'\n }];\n }\n\n await this.sendRequest('initialize', initParams);\n this.sendNotification('initialized', {});\n\n this.currentWorkspaceUri = workspaceUri;\n this.isInitialized = true;\n logger.info('CodeQL Language Server initialized successfully');\n }\n\n /**\n * Update the workspace folders on a running, initialized server.\n */\n private async updateWorkspace(newUri: string): Promise {\n logger.info(`Updating workspace from ${this.currentWorkspaceUri} to ${newUri}`);\n\n const removed = this.currentWorkspaceUri\n ? [{ uri: this.currentWorkspaceUri, name: 'codeql-workspace' }]\n : [];\n\n this.sendNotification('workspace/didChangeWorkspaceFolders', {\n event: {\n added: [{ uri: newUri, name: 'codeql-workspace' }],\n removed,\n },\n });\n\n this.currentWorkspaceUri = newUri;\n }\n\n /**\n * Get the current workspace URI.\n */\n getWorkspaceUri(): string | undefined {\n return this.currentWorkspaceUri;\n }\n\n async evaluateQL(qlCode: string, uri?: string): Promise {\n if (!this.isInitialized) {\n throw new Error('Language server is not initialized');\n }\n\n // Default to a project-local virtual URI rather than /tmp\n const documentUri = uri || pathToFileURL(join(getProjectTmpDir('lsp-eval'), 'eval.ql')).href;\n\n return new Promise((resolve, reject) => {\n let diagnosticsReceived = false;\n const timeout = setTimeout(() => {\n if (!diagnosticsReceived) {\n this.removeListener('diagnostics', diagnosticsHandler);\n reject(new Error('Timeout waiting for diagnostics'));\n }\n }, 90_000); // 90s \u2014 first call triggers JVM start + compilation; Windows CI is slow\n\n // Listen for diagnostics\n const diagnosticsHandler = (params: PublishDiagnosticsParams) => {\n if (params.uri === documentUri) {\n diagnosticsReceived = true;\n clearTimeout(timeout);\n this.removeListener('diagnostics', diagnosticsHandler);\n\n // Close the document\n this.sendNotification('textDocument/didClose', {\n textDocument: { uri: documentUri }\n });\n\n resolve(params.diagnostics);\n }\n };\n\n this.on('diagnostics', diagnosticsHandler);\n\n // Open the document with the QL code\n this.sendNotification('textDocument/didOpen', {\n textDocument: {\n uri: documentUri,\n languageId: 'ql',\n version: 1,\n text: qlCode\n }\n });\n });\n }\n\n // ---- LSP feature methods (issue #1) ----\n\n /**\n * Get code completions at a position in a document.\n */\n async getCompletions(params: TextDocumentPositionParams): Promise {\n if (!this.isInitialized) {\n throw new Error('Language server is not initialized');\n }\n if (!this.isRunning()) {\n throw new Error('Language server process is not running');\n }\n const result = await this.sendRequest('textDocument/completion', params);\n // The result may be a CompletionList or CompletionItem[]\n if (result && typeof result === 'object' && 'items' in (result as object)) {\n return (result as { items: CompletionItem[] }).items;\n }\n return (result as CompletionItem[]) || [];\n }\n\n /**\n * Find the definition(s) of a symbol at a position.\n */\n async getDefinition(params: TextDocumentPositionParams): Promise {\n if (!this.isInitialized) {\n throw new Error('Language server is not initialized');\n }\n if (!this.isRunning()) {\n throw new Error('Language server process is not running');\n }\n const result = await this.sendRequest('textDocument/definition', params);\n return this.normalizeLocations(result);\n }\n\n /**\n * Find all references to a symbol at a position.\n */\n async getReferences(params: TextDocumentPositionParams & { context?: { includeDeclaration: boolean } }): Promise {\n if (!this.isInitialized) {\n throw new Error('Language server is not initialized');\n }\n if (!this.isRunning()) {\n throw new Error('Language server process is not running');\n }\n const result = await this.sendRequest('textDocument/references', {\n ...params,\n context: params.context ?? { includeDeclaration: true },\n });\n return this.normalizeLocations(result);\n }\n\n /**\n * Open a text document in the language server.\n * The document must be opened before requesting completions, definitions, etc.\n */\n openDocument(uri: string, text: string, languageId = 'ql', version = 1): void {\n if (!this.isInitialized) {\n throw new Error('Language server is not initialized');\n }\n this.sendNotification('textDocument/didOpen', {\n textDocument: { uri, languageId, version, text },\n });\n }\n\n /**\n * Close a text document in the language server.\n */\n closeDocument(uri: string): void {\n if (!this.isInitialized) {\n throw new Error('Language server is not initialized');\n }\n this.sendNotification('textDocument/didClose', {\n textDocument: { uri },\n });\n }\n\n /**\n * Normalize a definition/references/implementation result to Location[].\n * The LSP spec allows Location | Location[] | LocationLink[].\n */\n private normalizeLocations(result: unknown): LSPLocation[] {\n if (!result) return [];\n if (Array.isArray(result)) {\n return result.map((item) => {\n // LocationLink has targetUri/targetRange\n if ('targetUri' in item) {\n return { uri: item.targetUri, range: item.targetRange } as LSPLocation;\n }\n return item as LSPLocation;\n });\n }\n // Single Location\n if (typeof result === 'object' && 'uri' in (result as object)) {\n return [result as LSPLocation];\n }\n return [];\n }\n\n async shutdown(): Promise {\n if (!this.server) {\n return;\n }\n\n logger.info('Shutting down CodeQL Language Server...');\n\n try {\n await this.sendRequest('shutdown', {});\n if (this.server) {\n this.sendNotification('exit', {});\n }\n } catch (error) {\n logger.warn('Error during graceful shutdown:', error);\n }\n\n // Force kill if needed\n await new Promise((resolve) => {\n const timer = setTimeout(() => {\n if (this.server) {\n this.server.kill('SIGTERM');\n }\n resolve();\n }, 1000);\n\n if (this.server) {\n this.server.once('exit', () => {\n clearTimeout(timer);\n this.server = null;\n resolve();\n });\n } else {\n clearTimeout(timer);\n resolve();\n }\n });\n\n this.isInitialized = false;\n }\n\n isRunning(): boolean {\n return this.server !== null && !this.server.killed;\n }\n}", "/**\n * CodeQL Query Server (query-server2) client.\n *\n * Manages a long-lived `codeql execute query-server2` process that evaluates\n * queries using a custom JSON-RPC protocol over stdio. Reusing the server\n * avoids repeated JVM startup for each query evaluation.\n *\n * Protocol: The query-server2 uses JSON-RPC with Content-Length headers\n * (same framing as LSP) over stdin/stdout.\n */\n\nimport { ChildProcess, spawn } from 'child_process';\nimport { delimiter } from 'path';\nimport { EventEmitter } from 'events';\nimport { clearTimeout, setTimeout } from 'timers';\nimport { buildQueryServerArgs, QueryServerConfig } from './server-config';\nimport { getResolvedCodeQLDir } from './cli-executor';\nimport { logger } from '../utils/logger';\nimport { waitForProcessReady } from '../utils/process-ready';\n\n/**\n * A pending request awaiting a response from the query server.\n */\ninterface PendingRequest {\n reject: (_error: Error) => void;\n resolve: (_value: unknown) => void;\n}\n\n/**\n * Client for the CodeQL query-server2 process.\n *\n * Spawns `codeql execute query-server2` and communicates over stdin/stdout\n * using JSON-RPC with Content-Length framing.\n */\nexport class CodeQLQueryServer extends EventEmitter {\n private messageBuffer = '';\n private messageId = 1;\n private pendingRequests = new Map();\n private process: ChildProcess | null = null;\n private readonly config: QueryServerConfig;\n\n constructor(config: QueryServerConfig) {\n super();\n this.config = config;\n }\n\n /**\n * Start the query-server2 process.\n */\n async start(): Promise {\n if (this.process) {\n throw new Error('Query server is already running');\n }\n\n logger.info('Starting CodeQL Query Server (query-server2)...');\n\n const args = buildQueryServerArgs(this.config);\n\n // Build environment with CODEQL_PATH directory prepended to PATH\n const spawnEnv = { ...process.env };\n const codeqlDir = getResolvedCodeQLDir();\n if (codeqlDir && spawnEnv.PATH) {\n spawnEnv.PATH = `${codeqlDir}${delimiter}${spawnEnv.PATH}`;\n } else if (codeqlDir) {\n spawnEnv.PATH = codeqlDir;\n }\n\n this.process = spawn('codeql', args, {\n stdio: ['pipe', 'pipe', 'pipe'],\n env: spawnEnv,\n });\n\n this.process.stderr?.on('data', (data: Buffer) => {\n logger.debug('QueryServer2 stderr:', data.toString());\n });\n\n this.process.stdout?.on('data', (data: Buffer) => {\n this.handleStdout(data);\n });\n\n this.process.on('error', (error: Error) => {\n logger.error('Query server process error:', error);\n this.emit('error', error);\n });\n\n this.process.on('exit', (code: number | null) => {\n logger.info(`Query server exited with code: ${code}`);\n this.rejectAllPending(new Error(`Query server exited with code: ${code}`));\n this.process = null;\n this.emit('exit', code);\n });\n\n // Wait for the JVM to initialise (resolves on first stderr/stdout output)\n await waitForProcessReady(this.process, 'CodeQL Query Server');\n logger.info('CodeQL Query Server started');\n }\n\n /**\n * Send a request to the query server and await the response.\n *\n * @param method - The JSON-RPC method name.\n * @param params - The method parameters.\n * @param timeoutMs - Request timeout in milliseconds (default: 300000 = 5 min).\n * @returns The result from the server.\n */\n sendRequest(method: string, params?: unknown, timeoutMs = 300_000): Promise {\n const id = this.messageId++;\n const message = {\n id,\n jsonrpc: '2.0' as const,\n method,\n params,\n };\n\n return new Promise((resolve, reject) => {\n this.pendingRequests.set(id, { reject, resolve });\n\n try {\n this.sendRaw(message);\n } catch (error) {\n // Clean up immediately \u2014 sendRaw() failed so no response will arrive.\n this.pendingRequests.delete(id);\n reject(error instanceof Error ? error : new Error(String(error)));\n return;\n }\n\n const timer = setTimeout(() => {\n if (this.pendingRequests.has(id)) {\n this.pendingRequests.delete(id);\n reject(new Error(`Query server request timeout for method: ${method}`));\n }\n }, timeoutMs);\n\n // Clear the timeout when the promise settles\n const originalResolve = resolve;\n const originalReject = reject;\n const wrapped = {\n reject: (err: Error) => { clearTimeout(timer); originalReject(err); },\n resolve: (val: unknown) => { clearTimeout(timer); originalResolve(val); },\n };\n this.pendingRequests.set(id, wrapped);\n });\n }\n\n /**\n * Gracefully shut down the query server.\n */\n async shutdown(): Promise {\n if (!this.process) {\n return;\n }\n\n logger.info('Shutting down CodeQL Query Server...');\n\n try {\n await this.sendRequest('shutdown', {}, 5000);\n } catch (error) {\n logger.warn('Error during query server graceful shutdown:', error);\n }\n\n // Force kill if process lingers\n await new Promise((resolve) => {\n const timer = setTimeout(() => {\n if (this.process) {\n this.process.kill('SIGTERM');\n this.process = null;\n }\n resolve();\n }, 2000);\n\n if (this.process) {\n this.process.once('exit', () => {\n clearTimeout(timer);\n this.process = null;\n resolve();\n });\n } else {\n clearTimeout(timer);\n resolve();\n }\n });\n }\n\n /**\n * Whether the query server process is running.\n */\n isRunning(): boolean {\n return this.process !== null && !this.process.killed;\n }\n\n // ---- private helpers ----\n\n private handleStdout(data: Buffer): void {\n this.messageBuffer += data.toString();\n\n let headerEnd = this.messageBuffer.indexOf('\\r\\n\\r\\n');\n while (headerEnd !== -1) {\n const header = this.messageBuffer.substring(0, headerEnd);\n const contentLengthMatch = header.match(/Content-Length: (\\d+)/);\n\n if (contentLengthMatch) {\n const contentLength = parseInt(contentLengthMatch[1]);\n const messageStart = headerEnd + 4;\n const messageEnd = messageStart + contentLength;\n\n if (this.messageBuffer.length >= messageEnd) {\n const messageContent = this.messageBuffer.substring(messageStart, messageEnd);\n this.messageBuffer = this.messageBuffer.substring(messageEnd);\n\n try {\n const message = JSON.parse(messageContent);\n this.handleMessage(message);\n } catch (error) {\n logger.error('Failed to parse query server message:', error);\n }\n\n headerEnd = this.messageBuffer.indexOf('\\r\\n\\r\\n');\n } else {\n break;\n }\n } else {\n logger.error('Invalid query server header:', header);\n this.messageBuffer = '';\n break;\n }\n }\n }\n\n private handleMessage(message: { error?: { message: string }; id?: number; method?: string; params?: unknown; result?: unknown }): void {\n logger.debug('QueryServer2 message:', message);\n\n // Handle responses\n if (message.id !== undefined && this.pendingRequests.has(Number(message.id))) {\n const pending = this.pendingRequests.get(Number(message.id))!;\n this.pendingRequests.delete(Number(message.id));\n\n if (message.error) {\n pending.reject(new Error(`Query server error: ${message.error.message}`));\n } else {\n pending.resolve(message.result);\n }\n return;\n }\n\n // Handle notifications (progress, etc.)\n if (message.method) {\n this.emit('notification', { method: message.method, params: message.params });\n }\n }\n\n private rejectAllPending(error: Error): void {\n for (const [id, pending] of this.pendingRequests) {\n pending.reject(error);\n this.pendingRequests.delete(id);\n }\n }\n\n private sendRaw(message: object): void {\n if (!this.process?.stdin) {\n throw new Error('Query server is not running');\n }\n\n const body = JSON.stringify(message);\n const contentLength = Buffer.byteLength(body, 'utf8');\n const frame = `Content-Length: ${contentLength}\\r\\n\\r\\n${body}`;\n this.process.stdin.write(frame);\n }\n}\n", "/**\n * CodeQL CLI Server client.\n *\n * Manages a long-lived `codeql execute cli-server` process that executes CLI\n * commands without repeated JVM startup overhead. Commands are serialized as\n * JSON arrays followed by a NUL byte, and responses are NUL-terminated JSON.\n *\n * Inspired by the `CodeQLCliServer` class in github/vscode-codeql.\n */\n\nimport { ChildProcess, spawn } from 'child_process';\nimport { delimiter } from 'path';\nimport { EventEmitter } from 'events';\nimport { clearTimeout, setTimeout } from 'timers';\nimport { buildCLIServerArgs, CLIServerConfig } from './server-config';\nimport { getResolvedCodeQLDir } from './cli-executor';\nimport { logger } from '../utils/logger';\nimport { waitForProcessReady } from '../utils/process-ready';\n\n/**\n * A queued command waiting to be sent to the CLI server.\n */\ninterface QueuedCommand {\n args: string[];\n reject: (_error: Error) => void;\n resolve: (_value: string) => void;\n}\n\n/**\n * Client for the CodeQL CLI Server process.\n *\n * The cli-server uses a simple NUL-delimited protocol:\n * - **Request**: JSON array of command arguments, followed by a NUL byte.\n * - **Response**: command stdout, terminated by a NUL byte on stdout.\n * stderr is forwarded as-is.\n */\nexport class CodeQLCLIServer extends EventEmitter {\n private commandInProgress = false;\n private commandQueue: Array<() => void> = [];\n private readonly config: CLIServerConfig;\n private currentReject: ((_error: Error) => void) | null = null;\n private currentResolve: ((_value: string) => void) | null = null;\n private nullBuffer = Buffer.alloc(1);\n private process: ChildProcess | null = null;\n private stdoutBuffer = '';\n\n constructor(config: CLIServerConfig) {\n super();\n this.config = config;\n }\n\n /**\n * Start the cli-server process.\n */\n async start(): Promise {\n if (this.process) {\n throw new Error('CLI server is already running');\n }\n\n logger.info('Starting CodeQL CLI Server...');\n\n const args = buildCLIServerArgs(this.config);\n\n const spawnEnv = { ...process.env };\n const codeqlDir = getResolvedCodeQLDir();\n if (codeqlDir && spawnEnv.PATH) {\n spawnEnv.PATH = `${codeqlDir}${delimiter}${spawnEnv.PATH}`;\n } else if (codeqlDir) {\n spawnEnv.PATH = codeqlDir;\n }\n\n this.process = spawn('codeql', args, {\n stdio: ['pipe', 'pipe', 'pipe'],\n env: spawnEnv,\n });\n\n this.process.stdout?.on('data', (data: Buffer) => {\n this.handleStdout(data);\n });\n\n this.process.stderr?.on('data', (data: Buffer) => {\n logger.debug('CLIServer stderr:', data.toString());\n });\n\n this.process.on('error', (error: Error) => {\n logger.error('CLI server process error:', error);\n if (this.currentReject) {\n this.currentReject(error);\n this.currentReject = null;\n this.currentResolve = null;\n }\n this.emit('error', error);\n });\n\n this.process.on('exit', (code: number | null) => {\n logger.info(`CLI server exited with code: ${code}`);\n if (this.currentReject) {\n this.currentReject(new Error(`CLI server exited unexpectedly with code: ${code}`));\n this.currentReject = null;\n this.currentResolve = null;\n }\n this.process = null;\n this.emit('exit', code);\n });\n\n // Wait for the JVM to initialise (resolves on first stderr/stdout output)\n await waitForProcessReady(this.process, 'CodeQL CLI Server');\n logger.info('CodeQL CLI Server started');\n }\n\n /**\n * Run a CodeQL CLI command through the persistent server.\n *\n * Commands are serialized and queued; only one command runs at a time.\n *\n * @param args - The full command arguments (e.g. `['resolve', 'qlpacks']`).\n * @returns The stdout output from the command.\n */\n runCommand(args: string[]): Promise {\n return new Promise((resolve, reject) => {\n const execute = () => {\n this.executeCommand({ args, reject, resolve });\n };\n\n if (this.commandInProgress) {\n this.commandQueue.push(execute);\n } else {\n execute();\n }\n });\n }\n\n /**\n * Gracefully shut down the CLI server.\n */\n async shutdown(): Promise {\n if (!this.process) {\n return;\n }\n\n logger.info('Shutting down CodeQL CLI Server...');\n\n try {\n // Send shutdown command\n this.process.stdin?.write(JSON.stringify(['shutdown']), 'utf8');\n this.process.stdin?.write(this.nullBuffer);\n } catch (error) {\n logger.warn('Error during CLI server shutdown request:', error);\n }\n\n // Give it a moment, then force kill\n await new Promise((resolve) => {\n const timer = setTimeout(() => {\n if (this.process) {\n this.process.kill('SIGTERM');\n this.process = null;\n }\n resolve();\n }, 2000);\n\n if (this.process) {\n this.process.once('exit', () => {\n clearTimeout(timer);\n this.process = null;\n resolve();\n });\n } else {\n clearTimeout(timer);\n resolve();\n }\n });\n\n this.commandInProgress = false;\n this.commandQueue = [];\n logger.info('CodeQL CLI Server stopped');\n }\n\n /**\n * Whether the CLI server process is running.\n */\n isRunning(): boolean {\n return this.process !== null && !this.process.killed;\n }\n\n // ---- private helpers ----\n\n private executeCommand(cmd: QueuedCommand): void {\n if (!this.process?.stdin) {\n cmd.reject(new Error('CLI server is not running'));\n return;\n }\n\n this.commandInProgress = true;\n this.currentResolve = cmd.resolve;\n this.currentReject = cmd.reject;\n\n try {\n this.process.stdin.write(JSON.stringify(cmd.args), 'utf8');\n this.process.stdin.write(this.nullBuffer);\n } catch (error) {\n this.commandInProgress = false;\n this.currentResolve = null;\n this.currentReject = null;\n cmd.reject(error instanceof Error ? error : new Error(String(error)));\n this.runNext();\n }\n }\n\n private handleStdout(data: Buffer): void {\n this.stdoutBuffer += data.toString();\n\n // Process all NUL-delimited responses in the buffer\n let nulIndex = this.stdoutBuffer.indexOf('\\0');\n while (nulIndex !== -1) {\n const result = this.stdoutBuffer.substring(0, nulIndex);\n this.stdoutBuffer = this.stdoutBuffer.substring(nulIndex + 1);\n\n if (this.currentResolve) {\n this.currentResolve(result);\n this.currentResolve = null;\n this.currentReject = null;\n }\n\n this.commandInProgress = false;\n this.runNext();\n\n nulIndex = this.stdoutBuffer.indexOf('\\0');\n }\n }\n\n private runNext(): void {\n const next = this.commandQueue.shift();\n if (next) {\n next();\n }\n }\n}\n", "/**\n * CodeQL Server Manager\n *\n * Manages the lifecycle of CodeQL background server processes:\n * - language-server (LSP-based QL validation)\n * - query-server2 (query evaluation)\n * - cli-server (JVM reuse for CLI commands)\n *\n * Servers are keyed by a hash of their configuration. When a caller requests\n * a server with a different configuration, the old server is shut down and a\n * new one is started. Session-specific cache directories provide isolation.\n */\n\nimport { mkdirSync } from 'fs';\nimport { join } from 'path';\nimport { randomUUID } from 'crypto';\nimport {\n CLIServerConfig,\n CodeQLServerType,\n computeConfigHash,\n LanguageServerConfig,\n QueryServerConfig,\n ServerConfig,\n} from './server-config';\nimport { CodeQLLanguageServer } from './language-server';\nimport { CodeQLQueryServer } from './query-server';\nimport { CodeQLCLIServer } from './cli-server';\nimport { getProjectTmpDir } from '../utils/temp-dir';\nimport { logger } from '../utils/logger';\n\n/**\n * Entry in the managed servers map.\n */\ninterface ManagedServer {\n configHash: string;\n server: CodeQLCLIServer | CodeQLLanguageServer | CodeQLQueryServer;\n sessionId: string;\n type: CodeQLServerType;\n}\n\n/**\n * Options for creating a session-specific cache layout.\n */\nexport interface SessionCacheOptions {\n /** Override the session ID (defaults to a random UUID). */\n sessionId?: string;\n}\n\n/**\n * Manages CodeQL background server processes with config-aware caching.\n *\n * Callers should use `getLanguageServer()`, `getQueryServer()`, or\n * `getCLIServer()` to obtain a running server. If the requested\n * configuration differs from the currently running server of that type, the\n * old server is stopped and replaced.\n */\nexport class CodeQLServerManager {\n /** Keyed by `CodeQLServerType` \u2014 at most one per type at a time. */\n private servers = new Map();\n\n /** In-flight `getOrRestart` promises, keyed by server type, to serialize concurrent calls. */\n private pendingStarts = new Map>();\n\n /** The session ID used for cache isolation. */\n private sessionId: string;\n\n /** Root directory for session-specific caches. */\n private sessionCacheDir: string;\n\n constructor(options?: SessionCacheOptions) {\n this.sessionId = options?.sessionId ?? randomUUID();\n this.sessionCacheDir = join(\n getProjectTmpDir('codeql-cache'),\n this.sessionId,\n );\n // Pre-create the cache directory tree\n for (const subdir of ['compilation-cache', 'logs', 'query-cache']) {\n mkdirSync(join(this.sessionCacheDir, subdir), { recursive: true });\n }\n logger.info(`CodeQLServerManager initialized (session: ${this.sessionId})`);\n }\n\n // ---- Public API ----\n\n /**\n * Get the current session ID.\n */\n getSessionId(): string {\n return this.sessionId;\n }\n\n /**\n * Get the session-specific cache directory.\n */\n getCacheDir(): string {\n return this.sessionCacheDir;\n }\n\n /**\n * Return the session-specific log directory.\n */\n getLogDir(): string {\n return join(this.sessionCacheDir, 'logs');\n }\n\n /**\n * Get or create a Language Server with the given configuration.\n *\n * If a language server is already running with the same config it is reused.\n * If the config has changed the old server is shut down first.\n */\n async getLanguageServer(config: LanguageServerConfig): Promise {\n const enriched = this.enrichConfig(config) as LanguageServerConfig;\n return this.getOrRestart('language', enriched, () => {\n // Convert LanguageServerConfig to the LanguageServerOptions the existing class expects\n return new CodeQLLanguageServer({\n loglevel: enriched.loglevel,\n logdir: enriched.logdir,\n searchPath: enriched.searchPath,\n synchronous: enriched.synchronous,\n verbosity: enriched.verbosity,\n });\n }) as Promise;\n }\n\n /**\n * Get or create a Query Server with the given configuration.\n */\n async getQueryServer(config: QueryServerConfig): Promise {\n const enriched = this.enrichConfig(config) as QueryServerConfig;\n return this.getOrRestart('query', enriched, () => {\n return new CodeQLQueryServer(enriched);\n }) as Promise;\n }\n\n /**\n * Get or create a CLI Server with the given configuration.\n */\n async getCLIServer(config: CLIServerConfig): Promise {\n const enriched = this.enrichConfig(config) as CLIServerConfig;\n return this.getOrRestart('cli', enriched, () => {\n return new CodeQLCLIServer(enriched);\n }) as Promise;\n }\n\n /**\n * Shut down a specific server type.\n */\n async shutdownServer(type: CodeQLServerType): Promise {\n const managed = this.servers.get(type);\n if (!managed) return;\n\n logger.info(`Shutting down ${type} server (session: ${managed.sessionId})`);\n await this.stopServer(managed);\n this.servers.delete(type);\n }\n\n /**\n * Shut down all managed servers.\n */\n async shutdownAll(): Promise {\n logger.info(`Shutting down all servers for session: ${this.sessionId}`);\n const shutdownPromises = Array.from(this.servers.entries()).map(\n async ([type, managed]) => {\n try {\n await this.stopServer(managed);\n } catch (error) {\n logger.error(`Error shutting down ${type} server:`, error);\n }\n },\n );\n await Promise.all(shutdownPromises);\n this.servers.clear();\n logger.info('All servers shut down');\n }\n\n /**\n * Check whether a server of the given type is currently running.\n */\n isRunning(type: CodeQLServerType): boolean {\n const managed = this.servers.get(type);\n if (!managed) return false;\n return managed.server.isRunning();\n }\n\n /**\n * Get status information for all managed servers.\n */\n getStatus(): Record {\n const status: Record = {\n cli: null,\n language: null,\n query: null,\n };\n for (const [type, managed] of this.servers) {\n status[type] = {\n configHash: managed.configHash,\n running: managed.server.isRunning(),\n sessionId: managed.sessionId,\n };\n }\n return status as Record;\n }\n\n // ---- Private helpers ----\n\n /**\n * Eagerly start the language server so the JVM is warm when the first\n * LSP tool call arrives. Uses the default configuration that\n * `lsp-handlers.ts` / `lsp-diagnostics.ts` would create on the first\n * `getLanguageServer()` call. The server is stored in the managed-servers\n * map and reused by subsequent tool invocations.\n *\n * This is fire-and-forget: errors are logged but do not prevent the MCP\n * server from starting.\n */\n async warmUpLanguageServer(): Promise {\n try {\n // Lazy-import to avoid circular dependency at module level\n const { packageRootDir } = await import('../utils/package-paths');\n const { resolve } = await import('path');\n\n const config: LanguageServerConfig = {\n checkErrors: 'ON_CHANGE',\n loglevel: 'WARN',\n searchPath: resolve(packageRootDir, 'ql'),\n };\n logger.info('Warming up language server (background JVM start)...');\n await this.getLanguageServer(config);\n logger.info('Language server warm-up complete');\n } catch (error) {\n logger.warn('Language server warm-up failed (will retry on first tool call):', error);\n }\n }\n\n /**\n * Eagerly start the CLI server so the JVM is warm when the first\n * `executeCodeQLCommand()` call routes through it.\n *\n * The CLI server uses only session-scoped `commonCaches` and `logdir`,\n * both injected by `enrichConfig()`. Passing an empty config is\n * intentional \u2014 it matches what `executeCodeQLCommand()` will request.\n *\n * Fire-and-forget: errors are logged but do not block startup.\n */\n async warmUpCLIServer(): Promise {\n try {\n logger.info('Warming up CLI server (background JVM start)...');\n await this.getCLIServer({});\n logger.info('CLI server warm-up complete');\n } catch (error) {\n logger.warn('CLI server warm-up failed (will retry on first tool call):', error);\n }\n }\n\n /**\n * Enrich a config with session-specific defaults for commonCaches and logdir.\n */\n private enrichConfig(config: T): T {\n return {\n ...config,\n commonCaches: config.commonCaches ?? this.sessionCacheDir,\n logdir: config.logdir ?? this.getLogDir(),\n };\n }\n\n /**\n * Get an existing server if its config matches, otherwise stop the old\n * one and start a new server.\n *\n * Concurrent calls for the same server type are serialized via\n * `pendingStarts` to avoid spawning duplicate server processes.\n */\n private async getOrRestart(\n type: CodeQLServerType,\n config: ServerConfig,\n factory: () => CodeQLCLIServer | CodeQLLanguageServer | CodeQLQueryServer,\n ): Promise {\n // If another call is already starting a server of this type, wait for it\n // to settle (success or failure) and then re-check whether the result is\n // still usable.\n const inflight = this.pendingStarts.get(type);\n if (inflight) {\n try { await inflight; } catch { /* swallow \u2014 original caller handles the rejection */ }\n }\n\n const work = this.doGetOrRestart(type, config, factory);\n this.pendingStarts.set(type, work);\n try {\n return await work;\n } finally {\n // Ensure `work` has settled before removing the pendingStarts entry so\n // that concurrent callers waiting on the same promise see a consistent\n // map state. The try/catch avoids re-throwing a rejection that the\n // outer `try` block already handles.\n try { await work; } catch { /* already handled */ }\n if (this.pendingStarts.get(type) === work) {\n this.pendingStarts.delete(type);\n }\n }\n }\n\n /**\n * Core logic for getOrRestart, separated to allow serialization.\n */\n private async doGetOrRestart(\n type: CodeQLServerType,\n config: ServerConfig,\n factory: () => CodeQLCLIServer | CodeQLLanguageServer | CodeQLQueryServer,\n ): Promise {\n const hash = computeConfigHash(type, config);\n const existing = this.servers.get(type);\n\n // Reuse if config matches and server is still running\n if (existing && existing.configHash === hash && existing.server.isRunning()) {\n logger.debug(`Reusing existing ${type} server (hash: ${hash.substring(0, 8)})`);\n return existing.server;\n }\n\n // Config changed or server died \u2014 stop the old one\n if (existing) {\n logger.info(`${type} server config changed or dead, restarting...`);\n await this.stopServer(existing);\n this.servers.delete(type);\n }\n\n // Start a new server\n const server = factory();\n await server.start();\n\n this.servers.set(type, {\n configHash: hash,\n server,\n sessionId: this.sessionId,\n type,\n });\n\n logger.info(`${type} server started (hash: ${hash.substring(0, 8)})`);\n return server;\n }\n\n /**\n * Stop a managed server, ignoring errors.\n */\n private async stopServer(managed: ManagedServer): Promise {\n try {\n await managed.server.shutdown();\n } catch (error) {\n logger.warn(`Error stopping ${managed.type} server:`, error);\n // Best-effort \u2014 don't propagate\n }\n }\n}\n\n/**\n * Global server manager singleton.\n *\n * Initialized lazily by `getServerManager()`. The MCP server entry point\n * should call `initServerManager()` at startup and `shutdownServerManager()`\n * at graceful shutdown.\n */\nlet globalServerManager: CodeQLServerManager | null = null;\n\n/**\n * Initialize the global server manager (idempotent).\n */\nexport function initServerManager(options?: SessionCacheOptions): CodeQLServerManager {\n if (!globalServerManager) {\n globalServerManager = new CodeQLServerManager(options);\n }\n return globalServerManager;\n}\n\n/**\n * Get the global server manager, creating it if needed.\n */\nexport function getServerManager(): CodeQLServerManager {\n if (!globalServerManager) {\n globalServerManager = new CodeQLServerManager();\n }\n return globalServerManager;\n}\n\n/**\n * Shut down the global server manager and all its servers.\n */\nexport async function shutdownServerManager(): Promise {\n if (globalServerManager) {\n await globalServerManager.shutdownAll();\n globalServerManager = null;\n }\n}\n\n/**\n * Reset the global server manager (for testing only).\n */\nexport function resetServerManager(): void {\n globalServerManager = null;\n}\n", "/**\n * Generic CLI command execution utilities for CodeQL and QLT commands\n */\n\nimport { execFile } from 'child_process';\nimport { existsSync } from 'fs';\nimport { basename, delimiter, dirname, isAbsolute } from 'path';\nimport { promisify } from 'util';\nimport { logger } from '../utils/logger';\n\nconst execFileAsync = promisify(execFile);\n\nexport interface CLIExecutionResult {\n stdout: string;\n stderr: string;\n success: boolean;\n error?: string;\n exitCode?: number;\n}\n\nexport interface CLIExecutionOptions {\n command: string;\n args: string[];\n cwd?: string;\n timeout?: number;\n env?: Record;\n}\n\n// Whitelist of allowed commands to prevent arbitrary command execution\nconst ALLOWED_COMMANDS = new Set([\n 'codeql',\n 'git',\n 'node',\n 'npm',\n 'qlt',\n 'which'\n]);\n\n// Additional commands allowed in test environments\nlet testCommands: Set | null = null;\n\n// Whitelist of safe environment variables to pass to child processes\n// This prevents potentially malicious environment variables from being passed through\nconst SAFE_ENV_VARS = [\n 'HOME', // User home directory\n 'LANG', // Locale setting\n 'LC_ALL', // Locale setting\n 'LC_CTYPE', // Locale setting\n 'PATH', // Required to find executables\n 'SHELL', // User's shell (Unix)\n 'TEMP', // Temporary directory (Windows)\n 'TERM', // Terminal type (Unix)\n 'TMP', // Temporary directory (Windows)\n 'TMPDIR', // Temporary directory (Unix)\n 'USER', // Current user (Unix)\n 'USERNAME', // Current user (Windows)\n] as const;\n\n// Whitelist of safe environment variable prefixes\n// These are needed for CodeQL and Node.js functionality\nconst SAFE_ENV_PREFIXES = [\n 'CODEQL_', // CodeQL-specific variables\n 'NODE_', // Node.js-specific variables (for npm, etc.)\n] as const;\n\n// Pattern for dangerous control characters in CLI arguments\n// Rejected characters:\n// \\x01-\\x08: SOH through BS (start of heading, text, null control chars, backspace)\n// \\x0B: Vertical tab - rarely used legitimately, can cause display issues\n// \\x0C: Form feed - can cause unexpected page breaks in output\n// \\x0E-\\x1F: SO through US (shift out/in, device controls, separators)\n// Allowed characters:\n// \\x00: Null byte - handled separately for clearer error messaging\n// \\x09: Horizontal tab - commonly used in file paths and arguments\n// \\x0A: Newline (LF) - may appear in multi-line arguments\n// \\x0D: Carriage return (CR) - may appear with newlines on Windows\n// eslint-disable-next-line no-control-regex\nconst DANGEROUS_CONTROL_CHARS = /[\\x01-\\x08\\x0B\\x0C\\x0E-\\x1F]/;\n\n/**\n * Enable test-specific commands for testing purposes\n * This should only be called in test environments\n */\nexport function enableTestCommands(): void {\n testCommands = new Set([\n 'cat',\n 'echo',\n 'ls',\n 'sh',\n 'sleep'\n ]);\n}\n\n/**\n * Disable test-specific commands\n */\nexport function disableTestCommands(): void {\n testCommands = null;\n}\n\n/**\n * Check if a command is allowed\n */\nfunction isCommandAllowed(command: string): boolean {\n return ALLOWED_COMMANDS.has(command) || (testCommands !== null && testCommands.has(command));\n}\n\n// Resolved CodeQL binary directory from CODEQL_PATH.\n// When set, this directory is prepended to PATH for all child processes\n// so that `execFile('codeql', ...)` finds the correct binary via execvp().\n// Using PATH lookup (rather than an absolute path) is essential because\n// execvp() handles shell-script shebangs correctly, whereas passing an\n// absolute path to execFile() can fail with ENOENT for shell scripts.\nlet resolvedCodeQLDir: string | null = null;\n\n// Cached result from resolveCodeQLBinary(). `undefined` means not yet resolved.\nlet resolvedBinaryResult: string | undefined;\n\n/**\n * Resolve the CodeQL CLI binary path.\n *\n * Resolution order:\n * 1. `CODEQL_PATH` environment variable \u2014 must point to an existing file whose\n * basename is `codeql` (or `codeql.exe` / `codeql.cmd` on Windows).\n * The parent directory is prepended to PATH for child processes.\n * 2. Falls back to the bare `codeql` command (resolved via PATH at exec time).\n *\n * The resolved value is cached for the lifetime of the process. Call this once\n * at startup; subsequent calls are a no-op and return the cached value.\n */\nexport function resolveCodeQLBinary(): string {\n // Short-circuit if already resolved\n if (resolvedBinaryResult !== undefined) {\n return resolvedBinaryResult;\n }\n\n const envPath = process.env.CODEQL_PATH;\n\n if (!envPath) {\n resolvedCodeQLDir = null;\n resolvedBinaryResult = 'codeql';\n return resolvedBinaryResult;\n }\n\n // Validate the path points to a plausible CodeQL binary\n const base = basename(envPath).toLowerCase();\n const validBaseNames = ['codeql', 'codeql.exe', 'codeql.cmd'];\n if (!validBaseNames.includes(base)) {\n throw new Error(\n `CODEQL_PATH must point to a CodeQL CLI binary (expected basename: codeql), got: ${base}`\n );\n }\n\n // Require an absolute path to avoid ambiguity\n if (!isAbsolute(envPath)) {\n throw new Error(\n `CODEQL_PATH must be an absolute path, got: ${envPath}`\n );\n }\n\n // Verify the file exists\n if (!existsSync(envPath)) {\n throw new Error(\n `CODEQL_PATH points to a file that does not exist: ${envPath}`\n );\n }\n\n resolvedCodeQLDir = dirname(envPath);\n resolvedBinaryResult = 'codeql';\n logger.info(`CodeQL CLI resolved via CODEQL_PATH: ${envPath} (dir: ${resolvedCodeQLDir})`);\n return resolvedBinaryResult;\n}\n\n/**\n * Get the currently resolved CodeQL binary directory, or null if using PATH.\n */\nexport function getResolvedCodeQLDir(): string | null {\n return resolvedCodeQLDir;\n}\n\n/**\n * Reset the resolved CodeQL binary to the default (for testing only).\n */\nexport function resetResolvedCodeQLBinary(): void {\n resolvedCodeQLDir = null;\n resolvedBinaryResult = undefined;\n}\n\n/**\n * Validate that the resolved CodeQL binary is actually callable.\n *\n * Runs `codeql version --format=terse` and verifies the process exits\n * successfully. This catches the case where `CODEQL_PATH` is unset and\n * `codeql` is not on PATH \u2014 the server would otherwise start normally\n * but every tool invocation would fail.\n *\n * @returns The version string reported by the CodeQL CLI.\n * @throws Error if the binary is not reachable or returns a non-zero exit code.\n */\nexport async function validateCodeQLBinaryReachable(): Promise {\n const binary = resolvedBinaryResult ?? 'codeql';\n const env = { ...process.env };\n if (resolvedCodeQLDir) {\n env.PATH = resolvedCodeQLDir + delimiter + (env.PATH || '');\n }\n\n try {\n const { stdout } = await execFileAsync(binary, ['version', '--format=terse'], {\n env,\n timeout: 15_000,\n });\n return stdout.trim();\n } catch (err: unknown) {\n const message = err instanceof Error ? err.message : String(err);\n throw new Error(\n `CodeQL CLI is not reachable (binary: ${binary}). ` +\n `Ensure codeql is on PATH or set the CODEQL_PATH environment variable ` +\n `to the absolute path of the CodeQL CLI binary. Details: ${message}`\n );\n }\n}\n\n/**\n * Sanitize a CLI argument to prevent potential issues with special characters.\n * \n * While execFile() does not spawn a shell (preventing shell injection), we still\n * validate arguments to:\n * 1. Reject null bytes that could truncate strings in C-based commands\n * 2. Reject control characters that could cause unexpected behavior\n * 3. Provide defense-in-depth against potential issues\n * \n * @param arg - The argument to sanitize\n * @returns The sanitized argument\n * @throws Error if the argument contains dangerous characters\n */\nexport function sanitizeCLIArgument(arg: string): string {\n // Reject null bytes - these can truncate strings in C-based commands\n // Error message intentionally excludes argument content to avoid logging potentially sensitive data\n if (arg.includes('\\0')) {\n throw new Error(`CLI argument contains null byte: argument rejected for security`);\n }\n \n // Reject control characters using the module-level constant pattern\n if (DANGEROUS_CONTROL_CHARS.test(arg)) {\n // Error message intentionally excludes argument content to avoid logging potentially sensitive data\n throw new Error(`CLI argument contains control characters: argument rejected for security`);\n }\n \n return arg;\n}\n\n/**\n * Sanitize an array of CLI arguments\n * @param args - The arguments to sanitize\n * @returns The sanitized arguments\n * @throws Error if any argument contains dangerous characters\n */\nexport function sanitizeCLIArguments(args: string[]): string[] {\n return args.map(sanitizeCLIArgument);\n}\n\n/**\n * Filter environment variables to only include safe ones\n * This prevents potentially malicious environment variables from being passed to child processes\n */\nfunction getSafeEnvironment(additionalEnv?: Record): Record {\n const safeEnv: Record = {};\n \n // Copy whitelisted environment variables from process.env\n for (const key of SAFE_ENV_VARS) {\n if (process.env[key] !== undefined) {\n safeEnv[key] = process.env[key]!;\n }\n }\n \n // Copy environment variables with whitelisted prefixes\n for (const [key, value] of Object.entries(process.env)) {\n if (value !== undefined && SAFE_ENV_PREFIXES.some(prefix => key.startsWith(prefix))) {\n safeEnv[key] = value;\n }\n }\n \n // When CODEQL_PATH was set, prepend the resolved directory to PATH so that\n // `execFile('codeql', ...)` finds the user-specified binary via execvp().\n // This approach (PATH manipulation + bare command name) is essential because\n // execvp() handles shell-script shebangs correctly, whereas passing an\n // absolute path to execFile() fails with ENOENT for shell-script launchers.\n if (resolvedCodeQLDir && safeEnv.PATH) {\n safeEnv.PATH = `${resolvedCodeQLDir}${delimiter}${safeEnv.PATH}`;\n } else if (resolvedCodeQLDir) {\n safeEnv.PATH = resolvedCodeQLDir;\n }\n \n // Merge with explicitly provided environment variables\n // These are trusted as they come from the application code, not external sources\n if (additionalEnv) {\n Object.assign(safeEnv, additionalEnv);\n }\n \n return safeEnv;\n}\n\n/**\n * Execute a CLI command and return the result.\n * \n * Security: This function uses execFile() instead of exec() to avoid shell interpretation.\n * Arguments are passed directly to the executable as an array, preventing shell injection.\n * Additional security measures include:\n * - Command whitelist validation\n * - Shell metacharacter detection in command names\n * - CLI argument sanitization (null bytes, control characters)\n * - Environment variable filtering\n */\nexport async function executeCLICommand(options: CLIExecutionOptions): Promise {\n try {\n const { command, args, cwd, timeout = 300000, env } = options; // 5 minute default timeout\n \n // Validate command is in the whitelist to prevent arbitrary command execution\n if (!isCommandAllowed(command)) {\n throw new Error(`Command not allowed: ${command}. Only whitelisted commands can be executed.`);\n }\n \n // Validate command to ensure it doesn't contain shell metacharacters\n if (command.includes(';') || command.includes('|') || command.includes('&') || \n command.includes('$') || command.includes('`') || command.includes('\\n') ||\n command.includes('\\r')) {\n throw new Error(`Invalid command: contains shell metacharacters: ${command}`);\n }\n \n // Sanitize CLI arguments to prevent issues with special characters\n // This provides defense-in-depth even though execFile() doesn't use a shell\n const sanitizedArgs = sanitizeCLIArguments(args);\n \n logger.info(`Executing CLI command: ${command}`, { args: sanitizedArgs, cwd, timeout });\n \n const execOptions = {\n cwd,\n timeout,\n env: getSafeEnvironment(env),\n };\n \n // execFile() is used instead of exec() to avoid shell interpretation\n // Arguments are passed as an array, not interpolated into a command string\n const { stdout, stderr } = await execFileAsync(command, sanitizedArgs, execOptions);\n\n return {\n stdout,\n stderr,\n success: true,\n exitCode: 0\n };\n\n } catch (error: unknown) {\n logger.error('CLI command execution failed:', error);\n \n const err = error as Error & { code?: number; stdout?: string; stderr?: string };\n const errorMessage = err instanceof Error ? err.message : String(error);\n const exitCode = err.code || 1;\n \n return {\n stdout: err.stdout || '',\n stderr: err.stderr || errorMessage,\n success: false,\n error: errorMessage,\n exitCode\n };\n }\n}\n\n/**\n * Build CodeQL command arguments with proper escaping\n */\nexport function buildCodeQLArgs(subcommand: string, options: Record): string[] {\n const args = [subcommand];\n \n // Single-letter parameters that should use -key=value format (with equals sign)\n // Note: CodeQL CLI uses -t=key=value format for metadata, not -t key=value\n const singleLetterParams = new Set(['t', 'o', 'v', 'q', 'h', 'J']);\n \n for (const [key, value] of Object.entries(options)) {\n if (value === undefined || value === null) {\n continue;\n }\n \n const isSingleLetter = key.length === 1 && singleLetterParams.has(key);\n \n if (typeof value === 'boolean') {\n if (value) {\n args.push(isSingleLetter ? `-${key}` : `--${key}`);\n }\n } else if (Array.isArray(value)) {\n // Handle array values (e.g., multiple -t flags for metadata)\n for (const item of value) {\n if (isSingleLetter) {\n // For single-letter params like -t, use -key=value format\n args.push(`-${key}=${String(item)}`);\n } else {\n // For long params, use --key=value format\n args.push(`--${key}=${String(item)}`);\n }\n }\n } else {\n // Handle string, number, and other values\n if (isSingleLetter) {\n // For single-letter params, use -key=value format\n args.push(`-${key}=${String(value)}`);\n } else {\n args.push(`--${key}=${String(value)}`);\n }\n }\n }\n \n return args;\n}\n\n/**\n * Build QLT command arguments with proper escaping\n */\nexport function buildQLTArgs(subcommand: string, options: Record): string[] {\n const args = [subcommand];\n \n for (const [key, value] of Object.entries(options)) {\n if (value === undefined || value === null) {\n continue;\n }\n \n if (typeof value === 'boolean') {\n if (value) {\n args.push(`--${key}`);\n }\n } else if (Array.isArray(value)) {\n // Handle array values\n for (const item of value) {\n args.push(`--${key}`, String(item));\n }\n } else {\n // Handle string, number, and other values\n args.push(`--${key}`, String(value));\n }\n }\n \n return args;\n}\n\n/**\n * CodeQL subcommands that MUST run as fresh processes.\n *\n * These cannot use the cli-server because they:\n * - Spawn extractors or other long-running child processes (database create, test extract)\n * - Produce multi-event NUL-delimited streams (test run)\n * - Are compound orchestration commands (database analyze)\n *\n * Everything else is routed through the persistent cli-server JVM for\n * sub-second execution instead of 2-5 s JVM cold-start per invocation.\n */\nconst FRESH_PROCESS_SUBCOMMANDS = new Set([\n 'database analyze',\n 'database create',\n 'test extract',\n 'test run',\n]);\n\n/**\n * Execute a CodeQL command.\n *\n * By default, commands are routed through the persistent `codeql execute\n * cli-server` process managed by {@link CodeQLServerManager}, eliminating\n * repeated JVM startup overhead (~2-5 s savings per call).\n *\n * Commands listed in {@link FRESH_PROCESS_SUBCOMMANDS} (e.g. `database create`,\n * `test run`) are always executed as fresh processes because they either spawn\n * child extractors, produce streaming output, or require a dedicated working\n * directory.\n *\n * If the cli-server is not available (e.g. during early startup before\n * `initServerManager()` is called), the function falls back transparently to\n * a fresh process.\n */\nexport async function executeCodeQLCommand(\n subcommand: string,\n options: Record,\n additionalArgs: string[] = [],\n cwd?: string\n): Promise {\n const args = buildCodeQLArgs(subcommand, options);\n args.push(...additionalArgs);\n\n // Determine whether this subcommand can use the persistent cli-server.\n // Commands that need a specific CWD also must use a fresh process because\n // the cli-server's CWD is fixed at startup.\n const canUseCLIServer = !FRESH_PROCESS_SUBCOMMANDS.has(subcommand) && !cwd;\n\n if (canUseCLIServer) {\n try {\n // Lazy-import to avoid circular dependency at module level.\n // Use getServerManager() (not initServerManager()) \u2014 if the manager\n // hasn't been initialized yet (e.g. during tests or early startup),\n // this creates one, but we only attempt to *use* the cli-server if\n // it is already running (warmed up at MCP server startup). We never\n // block on starting a new cli-server here \u2014 that would add JVM\n // startup latency to the first fresh-process-fallback call.\n const { getServerManager } = await import('./server-manager');\n const manager = getServerManager();\n\n if (manager.isRunning('cli')) {\n const cliServer = await manager.getCLIServer({});\n const sanitizedArgs = sanitizeCLIArguments(args);\n\n logger.info(`Executing CodeQL command via cli-server: ${subcommand}`, { args: sanitizedArgs });\n\n const stdout = await cliServer.runCommand(sanitizedArgs);\n\n return {\n stdout,\n stderr: '',\n success: true,\n exitCode: 0,\n };\n } else {\n logger.debug(`cli-server not yet running for \"${subcommand}\", using fresh process`);\n }\n } catch (error) {\n // If the cli-server call fails, check whether it's a command-level\n // error (the CLI returned non-zero) or a transport/startup error.\n // For transport errors we fall back to a fresh process; for command\n // errors we return the failure directly.\n const message = error instanceof Error ? error.message : String(error);\n\n // Transport-level errors that warrant a fallback:\n if (message.includes('CLI server is not running') ||\n message.includes('CLI server exited') ||\n message.includes('failed to start')) {\n logger.warn(`cli-server unavailable for \"${subcommand}\", falling back to fresh process: ${message}`);\n // Fall through to fresh-process execution below\n } else {\n // Command-level error \u2014 return it as a failed result\n logger.error(`cli-server command failed for \"${subcommand}\": ${message}`);\n return {\n stdout: '',\n stderr: message,\n success: false,\n error: message,\n exitCode: 1,\n };\n }\n }\n }\n\n // Fresh-process execution (for FRESH_PROCESS_SUBCOMMANDS, CWD-specific\n // calls, or as a fallback when the cli-server is unavailable).\n return executeCLICommand({\n command: 'codeql',\n args,\n cwd\n });\n}\n\n/**\n * Execute a QLT command\n */\nexport async function executeQLTCommand(\n subcommand: string, \n options: Record, \n additionalArgs: string[] = []\n): Promise {\n const args = buildQLTArgs(subcommand, options);\n args.push(...additionalArgs);\n \n return executeCLICommand({\n command: 'qlt',\n args\n });\n}\n\n/**\n * Get help text for a CLI command\n */\nexport async function getCommandHelp(command: string, subcommand?: string): Promise {\n const args = subcommand ? [subcommand, '--help'] : ['--help'];\n \n const result = await executeCLICommand({\n command,\n args\n });\n \n return result.stdout || result.stderr || 'No help available';\n}\n\n/**\n * Validate that a command exists on the system.\n */\nexport async function validateCommandExists(command: string): Promise {\n try {\n const result = await executeCLICommand({\n command: 'which',\n args: [command]\n });\n return result.success;\n } catch {\n return false;\n }\n}", "/**\n * CodeQL Development MCP McpServer\n * Main entry point for the server\n */\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';\nimport express from 'express';\nimport cors from 'cors';\nimport dotenv from 'dotenv';\nimport { resolve } from 'path';\nimport { pathToFileURL } from 'url';\nimport { registerCodeQLTools, registerCodeQLResources } from './tools';\nimport { registerLSPTools } from './tools/lsp';\nimport { registerLanguageResources } from './resources/language-resources';\nimport { registerWorkflowPrompts } from './prompts/workflow-prompts';\nimport { registerMonitoringTools } from './tools/monitoring-tools';\nimport { sessionDataManager } from './lib/session-data-manager';\nimport { resolveCodeQLBinary, validateCodeQLBinaryReachable } from './lib/cli-executor';\nimport { initServerManager, shutdownServerManager } from './lib/server-manager';\nimport { packageRootDir } from './utils/package-paths';\nimport { logger } from './utils/logger';\n\n// Load environment variables from a .env file co-located with the package root.\n// Uses the package directory (not CWD) so that npm-installed users don't\n// accidentally inherit a .env from their project.\ndotenv.config({ path: resolve(packageRootDir, '.env') });\n\nconst PACKAGE_NAME = 'codeql-development-mcp-server';\nconst VERSION = '2.24.1';\n\n/**\n * Start the MCP server\n */\nexport async function startServer(mode: 'stdio' | 'http' = 'stdio'): Promise {\n logger.info(`Starting CodeQL Development MCP McpServer v${VERSION} in ${mode} mode`);\n\n // Resolve the CodeQL CLI binary path (honors CODEQL_PATH env var).\n // This must happen before any tool registration so that all CodeQL commands\n // use the user-specified binary.\n const codeqlBinary = resolveCodeQLBinary();\n logger.info(`CodeQL CLI binary: ${codeqlBinary}`);\n\n // Validate that the resolved binary is actually callable. This catches\n // misconfigurations early (e.g. codeql not on PATH and CODEQL_PATH unset)\n // instead of failing silently and producing confusing tool-level errors.\n const codeqlVersion = await validateCodeQLBinaryReachable();\n logger.info(`CodeQL CLI version: ${codeqlVersion}`);\n\n const server = new McpServer({\n name: PACKAGE_NAME,\n version: VERSION,\n });\n\n // Register CodeQL tools (legacy high-level helpers)\n registerCodeQLTools(server);\n\n // Register LSP-based tools (diagnostics, completion, definition, references)\n registerLSPTools(server);\n\n // Register CodeQL resources (static guides)\n registerCodeQLResources(server);\n\n // Register language-specific resources (AST references, security patterns)\n registerLanguageResources(server);\n\n // Register high-level workflow prompts (complete development workflows)\n registerWorkflowPrompts(server);\n\n // Register monitoring and reporting tools\n registerMonitoringTools(server);\n\n // Initialize session data manager\n await sessionDataManager.initialize();\n\n // Initialize the CodeQL background server manager and eagerly start the\n // language server and CLI server JVMs so they are warm when the first tool\n // calls arrive. This avoids 2-60 s cold-start penalties per JVM.\n const manager = initServerManager();\n Promise.all([\n manager.warmUpLanguageServer(),\n manager.warmUpCLIServer(),\n ]).catch(() => { /* individual errors logged inside each warm-up method */ });\n\n if (mode === 'stdio') {\n const transport = new StdioServerTransport();\n await server.connect(transport);\n logger.info('McpServer started successfully on STDIO transport');\n } else {\n // HTTP mode\n const app = express();\n app.use(cors());\n app.use(express.json());\n\n const transport = new StreamableHTTPServerTransport({\n sessionIdGenerator: () => Math.random().toString(36).substring(7),\n });\n await server.connect(transport);\n\n app.all('/mcp', (req, res) => {\n transport.handleRequest(req, res, req.body).catch((err) => {\n logger.error('Error handling MCP request:', err);\n if (!res.headersSent) {\n res.status(500).json({ error: 'Internal McpServer Error' });\n }\n });\n });\n\n app.get('/', (_req, res) => {\n res.json({\n name: PACKAGE_NAME,\n version: VERSION,\n description: 'CodeQL Development MCP McpServer',\n status: 'running',\n });\n });\n\n const host = process.env.HTTP_HOST || 'localhost';\n const port = Number(process.env.HTTP_PORT || process.env.PORT) || 3000;\n \n // Return a promise that keeps the process alive\n return new Promise((resolve, reject) => {\n const httpServer = app.listen(port, host, () => {\n logger.info(`HTTP server listening on http://${host}:${port}/mcp`);\n resolve();\n });\n \n httpServer.on('error', (error) => {\n logger.error('HTTP server error:', error);\n reject(error);\n });\n });\n }\n\n setupGracefulShutdown(server);\n return server;\n}\n\n/**\n * Set up graceful shutdown handling\n */\nfunction setupGracefulShutdown(server: McpServer): void {\n const shutdown = async () => {\n logger.info('Shutting down server...');\n try {\n // Shut down all CodeQL background servers first\n await shutdownServerManager();\n await server.close();\n logger.info('McpServer closed gracefully');\n process.exit(0);\n } catch (error) {\n logger.error('Error during shutdown:', error);\n process.exit(1);\n }\n };\n\n process.on('SIGINT', shutdown);\n process.on('SIGTERM', shutdown);\n}\n\n/**\n * Main function to start the server\n */\nasync function main(): Promise {\n try {\n const transportMode = (process.env.TRANSPORT_MODE || 'stdio').toLowerCase();\n const mode: 'stdio' | 'http' = transportMode === 'http' ? 'http' : 'stdio';\n await startServer(mode);\n } catch (error) {\n logger.error('Failed to start server:', error);\n process.exit(1);\n }\n}\n\n// Start the server if this file is run directly\nconst scriptPath = process.argv[1] ? resolve(process.argv[1]) : undefined;\nif (scriptPath && import.meta.url === pathToFileURL(scriptPath).href) {\n main();\n}\n", "/**\n * CodeQL BQRS decode tool\n */\n\nimport { z } from 'zod';\nimport { CLIToolDefinition, createCodeQLSchemas, createBQRSResultProcessor } from '../../lib/cli-tool-registry';\n\nexport const codeqlBqrsDecodeTool: CLIToolDefinition = {\n name: 'codeql_bqrs_decode',\n description: 'Decode BQRS result files to human-readable formats',\n command: 'codeql',\n subcommand: 'bqrs decode',\n inputSchema: {\n files: z.array(z.string()).describe('BQRS file(s) to decode'),\n output: createCodeQLSchemas.output(),\n format: z.enum(['csv', 'json']).optional().describe('Output format'),\n 'max-paths': z.number().optional().describe('Maximum number of paths to output'),\n 'start-at': z.number().optional().describe('Start output at result number'),\n 'max-results': z.number().optional().describe('Maximum number of results'),\n verbose: createCodeQLSchemas.verbose(),\n additionalArgs: createCodeQLSchemas.additionalArgs()\n },\n examples: [\n 'codeql bqrs decode --format=csv --output=results.csv results.bqrs',\n 'codeql bqrs decode --format=json --max-results=100 results.bqrs'\n ],\n resultProcessor: createBQRSResultProcessor()\n};", "/**\n * Generic tool registry for creating MCP tools from CLI command definitions\n */\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport { executeCodeQLCommand, executeQLTCommand, CLIExecutionResult } from './cli-executor';\nimport { logger } from '../utils/logger';\nimport { evaluateQueryResults, QueryEvaluationResult, extractQueryMetadata } from './query-results-evaluator';\nimport { getOrCreateLogDirectory } from './log-directory-manager';\nimport { getUserWorkspaceDir, packageRootDir, resolveToolQueryPackPath } from '../utils/package-paths';\nimport { writeFileSync, rmSync, existsSync, mkdirSync } from 'fs';\nimport { basename, dirname, isAbsolute, join, resolve } from 'path';\nimport { createProjectTempDir } from '../utils/temp-dir';\n\nexport type { CLIExecutionResult } from './cli-executor';\n\nexport interface CLIToolDefinition {\n name: string;\n description: string;\n command: 'codeql' | 'qlt';\n subcommand: string;\n inputSchema: Record;\n examples?: string[];\n resultProcessor?: (_result: CLIExecutionResult, _params: Record) => string;\n}\n\n/**\n * Default result processor that formats CLI output appropriately\n */\nexport const defaultCLIResultProcessor = (\n result: CLIExecutionResult, \n _params: Record\n): string => {\n if (!result.success) {\n return `Command failed (exit code ${result.exitCode || 'unknown'}):\\n${result.error || result.stderr}`;\n }\n \n let output = '';\n \n if (result.stdout) {\n output += result.stdout;\n }\n \n if (result.stderr) {\n if (output) {\n output += '\\n\\nWarnings/Info:\\n';\n }\n output += result.stderr;\n }\n \n if (!output) {\n output = 'Command executed successfully (no output)';\n }\n \n return output;\n};\n\n/**\n * Register a CLI tool with the MCP server\n */\nexport function registerCLITool(server: McpServer, definition: CLIToolDefinition): void {\n const {\n name,\n description,\n command,\n subcommand,\n inputSchema,\n resultProcessor = defaultCLIResultProcessor\n } = definition;\n\n server.tool(\n name,\n description,\n inputSchema,\n async (params: Record) => {\n // Track temporary directories for cleanup\n const tempDirsToCleanup: string[] = [];\n \n try {\n logger.info(`Executing CLI tool: ${name}`, { command, subcommand, params });\n\n // Separate positional arguments from named options\n // Extract tool-specific parameters that should not be passed to CLI\n // Note: format is extracted for tools that use it internally but not on CLI\n // For codeql_bqrs_interpret, codeql_bqrs_decode, codeql_generate_query-help, and codeql_database_analyze, format should be passed to CLI\n const formatShouldBePassedToCLI = name === 'codeql_bqrs_interpret' || name === 'codeql_bqrs_decode' || name === 'codeql_generate_query-help' || name === 'codeql_database_analyze';\n \n const extractedParams = formatShouldBePassedToCLI\n ? {\n _positional: params._positional || [],\n files: params.files,\n file: params.file,\n dir: params.dir,\n packDir: params.packDir,\n tests: params.tests,\n query: params.query,\n queryName: params.queryName,\n queryLanguage: params.queryLanguage,\n queryPack: params.queryPack,\n sourceFiles: params.sourceFiles,\n sourceFunction: params.sourceFunction,\n targetFunction: params.targetFunction,\n interpretedOutput: params.interpretedOutput,\n evaluationFunction: params.evaluationFunction,\n evaluationOutput: params.evaluationOutput,\n directory: params.directory,\n logDir: params.logDir,\n qlref: params.qlref\n }\n : {\n _positional: params._positional || [],\n files: params.files,\n file: params.file,\n dir: params.dir,\n packDir: params.packDir,\n tests: params.tests,\n query: params.query,\n queryName: params.queryName,\n queryLanguage: params.queryLanguage,\n queryPack: params.queryPack, \n sourceFiles: params.sourceFiles,\n sourceFunction: params.sourceFunction,\n targetFunction: params.targetFunction,\n format: params.format,\n interpretedOutput: params.interpretedOutput,\n evaluationFunction: params.evaluationFunction,\n evaluationOutput: params.evaluationOutput,\n directory: params.directory,\n logDir: params.logDir,\n qlref: params.qlref\n };\n\n const {\n _positional = [],\n files,\n file,\n dir,\n packDir,\n tests,\n query,\n queryName,\n queryLanguage: _queryLanguage,\n queryPack: _queryPack,\n sourceFiles,\n sourceFunction,\n targetFunction,\n format: _format,\n interpretedOutput: _interpretedOutput,\n evaluationFunction: _evaluationFunction,\n evaluationOutput: _evaluationOutput,\n directory,\n logDir: customLogDir,\n qlref,\n } = extractedParams;\n\n // Get remaining options (everything not extracted above)\n const options = {...params};\n Object.keys(extractedParams).forEach(key => delete options[key]);\n let positionalArgs = Array.isArray(_positional) ? _positional as string[] : [_positional as string];\n\n // Handle files parameter as positional arguments for certain tools\n if (files && Array.isArray(files)) {\n positionalArgs = [...positionalArgs, ...files as string[]];\n }\n\n // Handle file parameter as positional argument for BQRS tools\n if (file && name.startsWith('codeql_bqrs_')) {\n positionalArgs = [...positionalArgs, file as string];\n }\n\n // Handle qlref parameter as positional argument for resolve qlref tool\n if (qlref && name === 'codeql_resolve_qlref') {\n positionalArgs = [...positionalArgs, qlref as string];\n }\n\n // Handle database parameter as positional argument for resolve database tool\n if (options.database && name === 'codeql_resolve_database') {\n positionalArgs = [...positionalArgs, options.database as string];\n delete options.database;\n }\n\n // Handle database parameter as positional argument for database create tool\n if (options.database && name === 'codeql_database_create') {\n positionalArgs = [...positionalArgs, options.database as string];\n delete options.database;\n }\n\n // Handle database and queries parameters as positional arguments for database analyze tool\n if (name === 'codeql_database_analyze') {\n if (options.database) {\n positionalArgs = [...positionalArgs, options.database as string];\n delete options.database;\n }\n if (options.queries) {\n positionalArgs = [...positionalArgs, options.queries as string];\n delete options.queries;\n }\n }\n\n // Handle query parameter as positional argument for generate query-help tool\n if (query && name === 'codeql_generate_query-help') {\n positionalArgs = [...positionalArgs, query as string];\n }\n\n // Handle dir parameter as positional argument for pack tools\n if (dir && (name === 'codeql_pack_ls')) {\n positionalArgs = [...positionalArgs, dir as string];\n }\n \n // Handle tool-specific parameters using switch pattern for better maintainability\n switch (name) {\n case 'codeql_test_accept':\n case 'codeql_test_extract':\n case 'codeql_test_run':\n case 'codeql_resolve_tests':\n // Handle tests parameter as positional arguments for test tools.\n // Resolve relative paths against the user's effective workspace\n // directory. In monorepo layouts this is the repo root; in npm-\n // installed layouts it falls back to process.cwd().\n if (tests && Array.isArray(tests)) {\n const userDir = getUserWorkspaceDir();\n positionalArgs = [...positionalArgs, ...(tests as string[]).map(\n t => isAbsolute(t) ? t : resolve(userDir, t)\n )];\n }\n break;\n \n case 'codeql_query_run': {\n // Resolve database path to absolute path if it's relative\n if (options.database && typeof options.database === 'string' && !isAbsolute(options.database)) {\n options.database = resolve(getUserWorkspaceDir(), options.database);\n logger.info(`Resolved database path to: ${options.database}`);\n }\n \n // Implement query resolution logic with enhanced results processing\n const resolvedQuery = await resolveQueryPath(params, logger);\n if (resolvedQuery) {\n positionalArgs = [...positionalArgs, resolvedQuery];\n } else if (query) {\n positionalArgs = [...positionalArgs, query as string];\n }\n \n // Handle external predicates for tool queries\n if (queryName === 'PrintAST' && sourceFiles) {\n // Create a CSV file with the source file paths for the external predicate\n // The external predicate expects a CSV file with one column containing file paths\n const filePaths = (sourceFiles as string).split(',').map((f: string) => f.trim());\n let tempDir: string;\n let csvPath: string;\n try {\n tempDir = createProjectTempDir('codeql-external-');\n tempDirsToCleanup.push(tempDir); // Track for cleanup\n csvPath = join(tempDir, 'selectedSourceFiles.csv');\n\n // Create CSV content\n const csvContent = filePaths.join('\\n') + '\\n';\n\n writeFileSync(csvPath, csvContent, 'utf8');\n } catch (err) {\n logger.error(`Failed to create external predicate CSV for PrintAST query at path ${csvPath || '[unknown]'}: ${err instanceof Error ? err.message : String(err)}`);\n throw err;\n }\n \n // Add the external predicate with the CSV file path\n const currentExternal = options.external || [];\n const externalArray = Array.isArray(currentExternal) ? currentExternal : [currentExternal];\n externalArray.push(`selectedSourceFiles=${csvPath}`);\n options.external = externalArray;\n \n logger.info(`Created external predicate CSV at ${csvPath} for files: ${filePaths.join(', ')}`);\n }\n \n // Handle external predicates for CallGraphFrom queries\n if (queryName === 'CallGraphFrom' && sourceFunction) {\n const functionNames = (sourceFunction as string).split(',').map((f: string) => f.trim());\n let tempDir: string;\n let csvPath: string;\n try {\n tempDir = createProjectTempDir('codeql-external-');\n tempDirsToCleanup.push(tempDir);\n csvPath = join(tempDir, 'sourceFunction.csv');\n\n // Create CSV content\n const csvContent = functionNames.join('\\n') + '\\n';\n\n writeFileSync(csvPath, csvContent, 'utf8');\n } catch (err) {\n logger.error(`Failed to create external predicate CSV for CallGraphFrom query at path ${csvPath || '[unknown]'}: ${err instanceof Error ? err.message : String(err)}`);\n throw err;\n }\n \n // Add the external predicate with the CSV file path\n const currentExternal = options.external || [];\n const externalArray = Array.isArray(currentExternal) ? currentExternal : [currentExternal];\n externalArray.push(`sourceFunction=${csvPath}`);\n options.external = externalArray;\n \n logger.info(`Created external predicate CSV at ${csvPath} for functions: ${functionNames.join(', ')}`);\n }\n \n // Handle external predicates for CallGraphTo queries\n if (queryName === 'CallGraphTo' && targetFunction) {\n const functionNames = (targetFunction as string).split(',').map((f: string) => f.trim());\n let tempDir: string;\n let csvPath: string;\n try {\n tempDir = createProjectTempDir('codeql-external-');\n tempDirsToCleanup.push(tempDir);\n csvPath = join(tempDir, 'targetFunction.csv');\n\n // Create CSV content\n const csvContent = functionNames.join('\\n') + '\\n';\n\n writeFileSync(csvPath, csvContent, 'utf8');\n } catch (err) {\n logger.error(`Failed to create external predicate CSV for CallGraphTo query at path ${csvPath || '[unknown]'}: ${err instanceof Error ? err.message : String(err)}`);\n throw err;\n }\n \n // Add the external predicate with the CSV file path\n const currentExternal = options.external || [];\n const externalArray = Array.isArray(currentExternal) ? currentExternal : [currentExternal];\n externalArray.push(`targetFunction=${csvPath}`);\n options.external = externalArray;\n \n logger.info(`Created external predicate CSV at ${csvPath} for functions: ${functionNames.join(', ')}`);\n }\n break;\n }\n \n case 'codeql_query_compile':\n case 'codeql_resolve_metadata':\n // Handle query parameter as positional argument for query compilation and metadata tools\n if (query) {\n positionalArgs = [...positionalArgs, query as string];\n }\n break;\n \n case 'codeql_resolve_queries':\n // Handle directory parameter as positional argument\n if (directory) {\n positionalArgs = [...positionalArgs, directory as string];\n }\n break;\n \n default:\n // No special parameter handling needed for other tools\n break;\n }\n\n // Set up logging directory for query/test runs\n let queryLogDir: string | undefined;\n if (name === 'codeql_query_run' || name === 'codeql_test_run') {\n queryLogDir = getOrCreateLogDirectory(customLogDir as string | undefined);\n logger.info(`Using log directory for ${name}: ${queryLogDir}`);\n \n // Create timestamp file to track when query/test run started\n const timestampPath = join(queryLogDir, 'timestamp');\n writeFileSync(timestampPath, Date.now().toString(), 'utf8');\n \n // Set the --logdir option for CodeQL CLI\n options.logdir = queryLogDir;\n \n // Set verbosity to progress+ to generate detailed query.log/test.log\n if (!options.verbosity) {\n options.verbosity = 'progress+';\n }\n \n // For query run, also handle the deprecated evaluator-log parameter and default output\n if (name === 'codeql_query_run') {\n // If evaluator-log was explicitly provided (deprecated), use it\n // Otherwise, set it to the log directory\n if (!options['evaluator-log']) {\n options['evaluator-log'] = join(queryLogDir, 'evaluator-log.jsonl');\n }\n \n // If output was not explicitly provided, set it to the log directory\n if (!options.output) {\n options.output = join(queryLogDir, 'results.bqrs');\n }\n }\n }\n\n let result: CLIExecutionResult;\n \n if (command === 'codeql') {\n // For pack commands, set the working directory to where qlpack.yml is located.\n // Resolve to absolute path since the MCP server's cwd may differ from\n // the workspace root (especially when launched by VS Code).\n let cwd: string | undefined;\n if ((name === 'codeql_pack_install' || name === 'codeql_pack_ls') && (dir || packDir)) {\n const rawCwd = (dir || packDir) as string;\n // Resolve relative paths against the user's effective workspace\n // directory rather than a potentially read-only package root.\n cwd = isAbsolute(rawCwd) ? rawCwd : resolve(getUserWorkspaceDir(), rawCwd);\n }\n \n // Add --additional-packs for commands that need to access local test packs.\n // Only set the default examples path when it actually exists on disk\n // (it may be absent in npm-installed layouts where ql/javascript/examples/\n // is not included in the published package).\n const defaultExamplesPath = resolve(packageRootDir, 'ql', 'javascript', 'examples');\n const additionalPacksPath = process.env.CODEQL_ADDITIONAL_PACKS\n || (existsSync(defaultExamplesPath) ? defaultExamplesPath : undefined);\n if (additionalPacksPath && (name === 'codeql_test_run' || name === 'codeql_query_run' || name === 'codeql_query_compile')) {\n options['additional-packs'] = additionalPacksPath;\n }\n \n // Keep test databases for codeql_test_run to allow subsequent query runs\n if (name === 'codeql_test_run') {\n options['keep-databases'] = true;\n }\n \n result = await executeCodeQLCommand(subcommand, options, positionalArgs, cwd);\n } else if (command === 'qlt') {\n result = await executeQLTCommand(subcommand, options, positionalArgs);\n } else {\n throw new Error(`Unsupported command: ${command}`);\n }\n\n // Post-execution processing for codeql_query_run\n if (name === 'codeql_query_run' && result.success && queryLogDir) {\n // Generate SARIF interpretation if results.bqrs exists\n const bqrsPath = options.output as string;\n const sarifPath = join(queryLogDir, 'results.sarif');\n \n if (existsSync(bqrsPath)) {\n try {\n const sarifResult = await executeCodeQLCommand(\n 'bqrs interpret',\n { format: 'sarif-latest', output: sarifPath },\n [bqrsPath]\n );\n \n if (sarifResult.success) {\n logger.info(`Generated SARIF interpretation at ${sarifPath}`);\n }\n } catch (error) {\n logger.warn(`Failed to generate SARIF interpretation: ${error}`);\n }\n }\n \n // Process evaluation results\n result = await processQueryRunResults(result, params, logger);\n }\n\n // Process the result\n const processedResult = resultProcessor(result, params);\n\n return {\n content: [{\n type: 'text' as const,\n text: processedResult\n }],\n isError: !result.success\n };\n\n } catch (error) {\n logger.error(`Error in CLI tool ${name}:`, error);\n \n return {\n content: [{\n type: 'text' as const,\n text: `Failed to execute CLI tool: ${error instanceof Error ? error.message : String(error)}`\n }],\n isError: true\n };\n } finally {\n // Clean up temporary directories\n for (const tempDir of tempDirsToCleanup) {\n try {\n rmSync(tempDir, { recursive: true, force: true });\n logger.info(`Cleaned up temporary directory: ${tempDir}`);\n } catch (cleanupError) {\n logger.error(`Failed to clean up temporary directory ${tempDir}:`, cleanupError);\n }\n }\n }\n }\n );\n}\n\n/**\n * Helper function to create common CodeQL input schemas\n */\nexport const createCodeQLSchemas = {\n database: () => z.string().describe('Path to the CodeQL database'),\n \n query: () => z.string().describe('Path to the CodeQL query file (.ql)'),\n \n output: () => z.string().optional().describe('Output file path'),\n \n outputFormat: () => z.enum(['csv', 'json', 'bqrs', 'sarif-latest', 'sarifv2.1.0']).optional()\n .describe('Output format for results'),\n \n language: () => z.string().optional().describe('Programming language'),\n \n threads: () => z.number().optional().describe('Number of threads to use'),\n \n ram: () => z.number().optional().describe('Amount of RAM to use (MB)'),\n \n timeout: () => z.number().optional().describe('Timeout in seconds'),\n \n verbose: () => z.boolean().optional().describe('Enable verbose output'),\n \n additionalArgs: () => z.array(z.string()).optional().describe('Additional command-line arguments'),\n \n positionalArgs: () => z.array(z.string()).optional().describe('Positional arguments')\n .transform((val) => ({ _positional: val }))\n};\n\n/**\n * Helper function to create common QLT input schemas\n */\nexport const createQLTSchemas = {\n language: () => z.string().describe('Programming language'),\n \n output: () => z.string().optional().describe('Output directory or file path'),\n \n template: () => z.string().optional().describe('Template to use'),\n \n name: () => z.string().optional().describe('Name for generated query'),\n \n description: () => z.string().optional().describe('Description for generated query'),\n \n verbose: () => z.boolean().optional().describe('Enable verbose output'),\n \n force: () => z.boolean().optional().describe('Force overwrite existing files'),\n \n additionalArgs: () => z.array(z.string()).optional().describe('Additional command-line arguments')\n};\n\n/**\n * Create a result processor that formats BQRS output specially\n */\nexport const createBQRSResultProcessor = () => (\n result: CLIExecutionResult, \n params: Record\n): string => {\n if (!result.success) {\n return defaultCLIResultProcessor(result, params);\n }\n \n // For BQRS commands, provide more context about the output\n let output = result.stdout;\n \n if (params.output) {\n output += `\\n\\nResults saved to: ${params.output}`;\n }\n \n if (result.stderr) {\n output += `\\n\\nAdditional information:\\n${result.stderr}`;\n }\n \n return output;\n};\n\n/**\n * Create a result processor that formats database creation output\n */\nexport const createDatabaseResultProcessor = () => (\n result: CLIExecutionResult, \n params: Record\n): string => {\n if (!result.success) {\n return defaultCLIResultProcessor(result, params);\n }\n \n let output = 'Database creation completed successfully';\n \n if (params.database || params._positional) {\n const dbPath = params.database || (Array.isArray(params._positional) ? params._positional[0] : params._positional);\n output += `\\n\\nDatabase location: ${dbPath}`;\n }\n \n if (result.stdout) {\n output += `\\n\\nOutput:\\n${result.stdout}`;\n }\n \n if (result.stderr) {\n output += `\\n\\nAdditional information:\\n${result.stderr}`;\n }\n \n return output;\n};\n\n/**\n * Resolve query path for codeql_query_run tool\n * If queryName and queryLanguage are provided, resolve using codeql resolve queries\n */\nasync function resolveQueryPath(\n params: Record, \n logger: { info: (_message: string, ..._args: unknown[]) => void; error: (_message: string, ..._args: unknown[]) => void }\n): Promise {\n const { queryName, queryLanguage, queryPack, query } = params;\n \n // Validate parameter usage - queryName and query are mutually exclusive\n if (queryName && query) {\n logger.error('Cannot use both \"query\" and \"queryName\" parameters simultaneously. Use either \"query\" for direct file path OR \"queryName\" + \"queryLanguage\" for tool queries.');\n throw new Error('Cannot use both \"query\" and \"queryName\" parameters simultaneously. Use either \"query\" for direct file path OR \"queryName\" + \"queryLanguage\" for tool queries.');\n }\n \n // If no queryName provided, fall back to direct query parameter\n if (!queryName) {\n return query as string || null;\n }\n \n // If queryName provided but no language, we can't resolve\n if (!queryLanguage) {\n logger.error('queryLanguage is required when using queryName parameter. Supported languages: actions, cpp, csharp, go, java, javascript, python, ruby, swift');\n throw new Error('queryLanguage is required when using queryName parameter. Supported languages: actions, cpp, csharp, go, java, javascript, python, ruby, swift');\n }\n \n try {\n // Determine the query pack path - use absolute path to ensure it works regardless of cwd\n const defaultPackPath = resolveToolQueryPackPath(queryLanguage as string);\n const packPath = queryPack as string || defaultPackPath;\n \n logger.info(`Resolving query: ${queryName} for language: ${queryLanguage} in pack: ${packPath}`);\n \n // Execute codeql resolve queries to get available queries\n const { executeCodeQLCommand } = await import('./cli-executor');\n const resolveResult = await executeCodeQLCommand(\n 'resolve queries',\n { format: 'json' },\n [packPath]\n );\n \n if (!resolveResult.success) {\n logger.error('Failed to resolve queries:', resolveResult.stderr || resolveResult.error);\n throw new Error(`Failed to resolve queries: ${resolveResult.stderr || resolveResult.error}`);\n }\n \n // Parse the JSON output to find matching queries\n let resolvedQueries: string[];\n try {\n resolvedQueries = JSON.parse(resolveResult.stdout);\n } catch (parseError) {\n logger.error('Failed to parse resolve queries output:', parseError);\n throw new Error('Failed to parse resolve queries output');\n }\n \n // Find the query that matches the requested name exactly\n const matchingQuery = resolvedQueries.find(queryPath => {\n const fileName = basename(queryPath);\n // Match exact query name: \"PrintAST\" should match \"PrintAST.ql\" only\n return fileName === `${queryName}.ql`;\n });\n\n if (!matchingQuery) {\n logger.error(`Query \"${queryName}.ql\" not found in pack \"${packPath}\". Available queries:`, resolvedQueries.map(q => basename(q)));\n throw new Error(`Query \"${queryName}.ql\" not found in pack \"${packPath}\"`);\n }\n \n logger.info(`Resolved query \"${queryName}\" to: ${matchingQuery}`);\n return matchingQuery;\n \n } catch (error) {\n logger.error('Error resolving query path:', error);\n throw error;\n }\n}\n\n/**\n * Interpret BQRS file using codeql bqrs interpret\n */\nasync function interpretBQRSFile(\n bqrsPath: string,\n queryPath: string,\n format: string,\n outputPath: string,\n logger: { info: (_message: string, ..._args: unknown[]) => void; error: (_message: string, ..._args: unknown[]) => void }\n): Promise {\n try {\n // Extract query metadata to get id and kind\n const metadata = await extractQueryMetadata(queryPath);\n \n // Validate required metadata fields\n const missingFields = [];\n if (!metadata.id) missingFields.push('id');\n if (!metadata.kind) missingFields.push('kind');\n \n if (missingFields.length > 0) {\n return {\n success: false,\n exitCode: 1,\n stdout: '',\n stderr: '',\n error: `Query metadata is incomplete. Missing required field(s): ${missingFields.join(', ')}. Ensure the query file contains @id and @kind metadata.`\n };\n }\n \n // Sanitize metadata values to prevent command injection\n const sanitizedKind = (metadata.kind || '').replace(/[^a-zA-Z0-9_-]/g, '');\n const sanitizedId = (metadata.id || '').replace(/[^a-zA-Z0-9_/:-]/g, '');\n \n // Validate format for query kind\n const graphFormats = ['graphtext', 'dgml', 'dot'];\n if (graphFormats.includes(format) && metadata.kind !== 'graph') {\n return {\n success: false,\n exitCode: 1,\n stdout: '',\n stderr: '',\n error: `Format '${format}' is only compatible with @kind graph queries, but this query has @kind ${metadata.kind}`\n };\n }\n \n // Ensure output directory exists\n mkdirSync(dirname(outputPath), { recursive: true });\n \n // Build the codeql bqrs interpret command\n const params: Record = {\n format: format,\n output: outputPath,\n t: [`kind=${sanitizedKind}`, `id=${sanitizedId}`]\n };\n \n logger.info(`Interpreting BQRS file ${bqrsPath} with format ${format} to ${outputPath}`);\n \n // Execute codeql bqrs interpret\n const result = await executeCodeQLCommand(\n 'bqrs interpret',\n params,\n [bqrsPath]\n );\n \n return result;\n } catch (error) {\n return {\n success: false,\n exitCode: 1,\n stdout: '',\n stderr: '',\n error: `Failed to interpret BQRS file: ${error}`\n };\n }\n}\n\n/**\n * Get default output extension based on format\n */\nfunction getDefaultExtension(format: string): string {\n switch (format) {\n case 'sarif-latest':\n case 'sarifv2.1.0':\n return '.sarif';\n case 'csv':\n return '.csv';\n case 'graphtext':\n return '.txt';\n case 'dgml':\n return '.dgml';\n case 'dot':\n return '.dot';\n default:\n return '.txt';\n }\n}\n\n/**\n * Process query run results with optional interpretation or evaluation\n */\nasync function processQueryRunResults(\n result: CLIExecutionResult,\n params: Record,\n logger: { info: (_message: string, ..._args: unknown[]) => void; error: (_message: string, ..._args: unknown[]) => void }\n): Promise {\n try {\n const { format, interpretedOutput, evaluationFunction, evaluationOutput, output, query, queryName, queryLanguage } = params;\n \n // If no format or evaluationFunction specified, return as-is\n if (!format && !evaluationFunction) {\n return result;\n }\n \n // Ensure output (bqrs file) was generated\n if (!output) {\n return result;\n }\n \n const bqrsPath = output as string;\n \n // Determine the query path for metadata extraction\n let queryPath: string | null = null;\n \n if (query) {\n queryPath = query as string;\n } else if (queryName && queryLanguage) {\n // Try to resolve the query path again for evaluation\n queryPath = await resolveQueryPath(params, logger);\n }\n \n if (!queryPath) {\n logger.error('Cannot determine query path for interpretation/evaluation');\n return {\n ...result,\n stdout: result.stdout + '\\n\\nWarning: Query interpretation skipped - could not determine query path'\n };\n }\n \n // Handle new format parameter (preferred approach)\n if (format) {\n const outputFormat = format as string;\n \n // Determine output path\n let outputFilePath = interpretedOutput as string | undefined;\n if (!outputFilePath) {\n const ext = getDefaultExtension(outputFormat);\n outputFilePath = bqrsPath.replace('.bqrs', ext);\n }\n \n logger.info(`Interpreting query results from ${bqrsPath} with format: ${outputFormat}`);\n \n // Interpret the BQRS file\n const interpretResult = await interpretBQRSFile(\n bqrsPath,\n queryPath,\n outputFormat,\n outputFilePath,\n logger\n );\n \n if (interpretResult.success) {\n let enhancedOutput = result.stdout;\n enhancedOutput += `\\n\\nQuery results interpreted successfully with format: ${outputFormat}`;\n enhancedOutput += `\\nInterpreted output saved to: ${outputFilePath}`;\n \n return {\n ...result,\n stdout: enhancedOutput\n };\n } else {\n logger.error('Query interpretation failed:', interpretResult.error);\n return {\n ...result,\n stdout: result.stdout + `\\n\\nWarning: Query interpretation failed - ${interpretResult.error || interpretResult.stderr}`\n };\n }\n }\n \n // Handle legacy evaluationFunction parameter (deprecated)\n if (evaluationFunction) {\n logger.info(`Using deprecated evaluationFunction parameter. Consider using format parameter instead.`);\n logger.info(`Evaluating query results from ${bqrsPath} using function: ${evaluationFunction}`);\n \n // Perform the evaluation\n const evaluationResult: QueryEvaluationResult = await evaluateQueryResults(\n bqrsPath,\n queryPath,\n evaluationFunction as string,\n evaluationOutput as string | undefined\n );\n \n if (evaluationResult.success) {\n // Append evaluation results to the command output\n let enhancedOutput = result.stdout;\n \n if (evaluationResult.outputPath) {\n enhancedOutput += `\\n\\nQuery evaluation completed successfully.`;\n enhancedOutput += `\\nEvaluation output saved to: ${evaluationResult.outputPath}`;\n }\n \n if (evaluationResult.content) {\n enhancedOutput += `\\n\\n=== Query Results Evaluation ===\\n`;\n enhancedOutput += evaluationResult.content;\n }\n \n return {\n ...result,\n stdout: enhancedOutput\n };\n } else {\n // Evaluation failed, but don't fail the whole operation\n logger.error('Query evaluation failed:', evaluationResult.error);\n return {\n ...result,\n stdout: result.stdout + `\\n\\nWarning: Query evaluation failed - ${evaluationResult.error}`\n };\n }\n }\n \n return result;\n } catch (error) {\n logger.error('Error in query results processing:', error);\n return {\n ...result,\n stdout: result.stdout + `\\n\\nWarning: Query processing error - ${error}`\n };\n }\n}", "/**\n * Query results evaluation functions for processing .bqrs files\n */\n\nimport { executeCodeQLCommand } from './cli-executor';\nimport { logger } from '../utils/logger';\nimport { writeFileSync, readFileSync } from 'fs';\nimport { dirname, isAbsolute } from 'path';\nimport { mkdirSync } from 'fs';\n\nexport interface QueryEvaluationResult {\n success: boolean;\n outputPath?: string;\n content?: string;\n error?: string;\n}\n\nexport interface QueryMetadata {\n kind?: string;\n name?: string;\n description?: string;\n id?: string;\n tags?: string[];\n}\n\n/**\n * Built-in evaluation functions\n */\nexport const BUILT_IN_EVALUATORS = {\n 'json-decode': 'JSON format decoder for query results',\n 'csv-decode': 'CSV format decoder for query results', \n 'mermaid-graph': 'Mermaid diagram generator for @kind graph queries (like PrintAST)',\n} as const;\n\nexport type BuiltInEvaluator = keyof typeof BUILT_IN_EVALUATORS;\n\n/**\n * Extract metadata from a CodeQL query file\n */\nexport async function extractQueryMetadata(queryPath: string): Promise {\n try {\n const queryContent = readFileSync(queryPath, 'utf-8');\n const metadata: QueryMetadata = {};\n \n // Extract metadata from comments using regex patterns\n const kindMatch = queryContent.match(/@kind\\s+([^\\s]+)/);\n if (kindMatch) metadata.kind = kindMatch[1];\n \n const nameMatch = queryContent.match(/@name\\s+(.+)/);\n if (nameMatch) metadata.name = nameMatch[1].trim();\n \n const descMatch = queryContent.match(/@description\\s+(.+)/);\n if (descMatch) metadata.description = descMatch[1].trim();\n \n const idMatch = queryContent.match(/@id\\s+(.+)/);\n if (idMatch) metadata.id = idMatch[1].trim();\n \n const tagsMatch = queryContent.match(/@tags\\s+(.+)/);\n if (tagsMatch) {\n metadata.tags = tagsMatch[1].split(/\\s+/).filter(t => t.length > 0);\n }\n \n return metadata;\n } catch (error) {\n logger.error('Failed to extract query metadata:', error);\n return {};\n }\n}\n\n/**\n * JSON decoder - converts .bqrs to JSON format\n */\nexport async function evaluateWithJsonDecoder(\n bqrsPath: string, \n outputPath?: string\n): Promise {\n try {\n const result = await executeCodeQLCommand(\n 'bqrs decode',\n { format: 'json' },\n [bqrsPath]\n );\n \n if (!result.success) {\n return {\n success: false,\n error: `Failed to decode BQRS file: ${result.stderr || result.error}`\n };\n }\n \n const defaultOutputPath = outputPath || bqrsPath.replace('.bqrs', '.json');\n \n // Ensure output directory exists\n mkdirSync(dirname(defaultOutputPath), { recursive: true });\n \n // Write JSON results\n writeFileSync(defaultOutputPath, result.stdout);\n \n return {\n success: true,\n outputPath: defaultOutputPath,\n content: result.stdout\n };\n } catch (error) {\n return {\n success: false,\n error: `JSON evaluation failed: ${error}`\n };\n }\n}\n\n/**\n * CSV decoder - converts .bqrs to CSV format\n */\nexport async function evaluateWithCsvDecoder(\n bqrsPath: string,\n outputPath?: string\n): Promise {\n try {\n const result = await executeCodeQLCommand(\n 'bqrs decode',\n { format: 'csv' },\n [bqrsPath]\n );\n \n if (!result.success) {\n return {\n success: false,\n error: `Failed to decode BQRS file: ${result.stderr || result.error}`\n };\n }\n \n const defaultOutputPath = outputPath || bqrsPath.replace('.bqrs', '.csv');\n \n // Ensure output directory exists\n mkdirSync(dirname(defaultOutputPath), { recursive: true });\n \n // Write CSV results\n writeFileSync(defaultOutputPath, result.stdout);\n \n return {\n success: true,\n outputPath: defaultOutputPath,\n content: result.stdout\n };\n } catch (error) {\n return {\n success: false,\n error: `CSV evaluation failed: ${error}`\n };\n }\n}\n\n/**\n * Mermaid graph generator - converts @kind graph query results to mermaid diagrams\n */\nexport async function evaluateWithMermaidGraph(\n bqrsPath: string,\n queryPath: string,\n outputPath?: string\n): Promise {\n try {\n // First extract query metadata to confirm this is a graph query\n const metadata = await extractQueryMetadata(queryPath);\n \n if (metadata.kind !== 'graph') {\n logger.error(`Query is not a graph query (kind: ${metadata.kind}), mermaid-graph evaluation is only for @kind graph queries`);\n return {\n success: false,\n error: `Query is not a graph query (kind: ${metadata.kind}), mermaid-graph evaluation is only for @kind graph queries`\n };\n }\n \n // Decode the BQRS file to JSON first\n const jsonResult = await executeCodeQLCommand(\n 'bqrs decode',\n { format: 'json' },\n [bqrsPath]\n );\n \n if (!jsonResult.success) {\n return {\n success: false,\n error: `Failed to decode BQRS file: ${jsonResult.stderr || jsonResult.error}`\n };\n }\n \n // Parse the JSON results\n let queryResults;\n try {\n queryResults = JSON.parse(jsonResult.stdout);\n } catch (parseError) {\n return {\n success: false,\n error: `Failed to parse query results JSON: ${parseError}`\n };\n }\n \n // Generate mermaid diagram from graph results\n const mermaidContent = generateMermaidFromGraphResults(queryResults, metadata);\n \n const defaultOutputPath = outputPath || bqrsPath.replace('.bqrs', '.md');\n \n // Ensure output directory exists\n mkdirSync(dirname(defaultOutputPath), { recursive: true });\n \n // Write markdown file with mermaid diagram\n writeFileSync(defaultOutputPath, mermaidContent);\n \n return {\n success: true,\n outputPath: defaultOutputPath,\n content: mermaidContent\n };\n } catch (error) {\n return {\n success: false,\n error: `Mermaid graph evaluation failed: ${error}`\n };\n }\n}\n\n/**\n * Generate mermaid diagram from CodeQL graph query results\n */\nfunction generateMermaidFromGraphResults(queryResults: unknown, metadata: QueryMetadata): string {\n const queryName = sanitizeMarkdown(metadata.name || 'CodeQL Query Results');\n const queryDesc = sanitizeMarkdown(metadata.description || 'Graph visualization of CodeQL query results');\n \n let mermaidContent = `# ${queryName}\\n\\n${queryDesc}\\n\\n`;\n \n // Handle different result structures that CodeQL graph queries can produce\n if (!queryResults || typeof queryResults !== 'object') {\n mermaidContent += '```mermaid\\ngraph TD\\n A[No Results]\\n```\\n';\n return mermaidContent;\n }\n \n // Check if results have the expected structure for graph queries\n const tuples = queryResults.tuples || queryResults;\n \n if (!Array.isArray(tuples) || tuples.length === 0) {\n mermaidContent += '```mermaid\\ngraph TD\\n A[No Graph Data]\\n```\\n';\n return mermaidContent;\n }\n \n mermaidContent += '```mermaid\\ngraph TD\\n';\n \n // Track nodes and edges to avoid duplicates\n const nodes = new Set();\n const edges = new Set();\n \n // Process each tuple in the results\n tuples.forEach((tuple: unknown, index: number) => {\n if (Array.isArray(tuple) && tuple.length >= 2) {\n // Extract source and target from tuple\n const source = sanitizeNodeId(tuple[0]?.toString() || `node_${index}_0`);\n const target = sanitizeNodeId(tuple[1]?.toString() || `node_${index}_1`);\n const label = tuple[2]?.toString() || '';\n \n // Add nodes\n nodes.add(source);\n nodes.add(target);\n \n // Add edge\n const edgeId = `${source}_${target}`;\n if (!edges.has(edgeId)) {\n if (label) {\n mermaidContent += ` ${source} -->|${sanitizeLabel(label)}| ${target}\\n`;\n } else {\n mermaidContent += ` ${source} --> ${target}\\n`;\n }\n edges.add(edgeId);\n }\n } else if (typeof tuple === 'object' && tuple !== null) {\n // Handle object-based results\n const source = sanitizeNodeId(tuple.source?.toString() || tuple.from?.toString() || `node_${index}_src`);\n const target = sanitizeNodeId(tuple.target?.toString() || tuple.to?.toString() || `node_${index}_tgt`);\n const label = tuple.label?.toString() || tuple.relation?.toString() || '';\n \n nodes.add(source);\n nodes.add(target);\n \n const edgeId = `${source}_${target}`;\n if (!edges.has(edgeId)) {\n if (label) {\n mermaidContent += ` ${source} -->|${sanitizeLabel(label)}| ${target}\\n`;\n } else {\n mermaidContent += ` ${source} --> ${target}\\n`;\n }\n edges.add(edgeId);\n }\n }\n });\n \n // If no edges were created, create a simple diagram showing the first few nodes\n if (edges.size === 0 && nodes.size > 0) {\n const nodeArray = Array.from(nodes).slice(0, 10); // Limit to avoid clutter\n nodeArray.forEach((node, index) => {\n if (index === 0) {\n mermaidContent += ` ${node}[${sanitizeLabel(node)}]\\n`;\n } else {\n mermaidContent += ` ${nodeArray[0]} --> ${node}\\n`;\n }\n });\n }\n \n mermaidContent += '```\\n\\n';\n \n // Add statistics\n mermaidContent += `## Query Statistics\\n\\n`;\n mermaidContent += `- Total nodes: ${nodes.size}\\n`;\n mermaidContent += `- Total edges: ${edges.size}\\n`;\n mermaidContent += `- Total tuples processed: ${tuples.length}\\n`;\n \n return mermaidContent;\n}\n\n/**\n * Sanitize node IDs for mermaid compatibility\n */\nfunction sanitizeNodeId(id: string): string {\n return id\n .replace(/[^a-zA-Z0-9_]/g, '_')\n .replace(/^(\\d)/, 'n$1') // Prefix with 'n' if starts with number\n .substring(0, 50); // Limit length\n}\n\n/**\n * Sanitize labels for mermaid compatibility\n */\nfunction sanitizeLabel(label: string): string {\n return label\n .replace(/[|\"`<>\\n\\r\\t]/g, '') // Remove problematic characters including backticks, newlines, angle brackets\n .replace(/\\s+/g, ' ') // Normalize whitespace\n .trim()\n .substring(0, 30); // Limit length for readability\n}\n\n/**\n * Sanitize markdown content to prevent injection\n */\nfunction sanitizeMarkdown(content: string): string {\n return content\n .replace(/[<>\"`]/g, '') // Remove potentially dangerous characters\n .replace(/\\n/g, ' ') // Convert newlines to spaces\n .replace(/\\s+/g, ' ') // Normalize whitespace\n .trim()\n .substring(0, 100); // Limit length\n}\n\n/**\n * Main evaluation function that determines which evaluator to use\n */\nexport async function evaluateQueryResults(\n bqrsPath: string,\n queryPath: string,\n evaluationFunction?: string,\n outputPath?: string\n): Promise {\n try {\n // If no evaluation function specified, default to json-decode\n const evalFunc = evaluationFunction || 'json-decode';\n \n logger.info(`Evaluating query results with function: ${evalFunc}`);\n \n // Handle built-in evaluation functions\n switch (evalFunc) {\n case 'json-decode':\n return await evaluateWithJsonDecoder(bqrsPath, outputPath);\n \n case 'csv-decode':\n return await evaluateWithCsvDecoder(bqrsPath, outputPath);\n \n case 'mermaid-graph':\n return await evaluateWithMermaidGraph(bqrsPath, queryPath, outputPath);\n \n default:\n // Check if it's a path to a custom evaluation script\n if (isAbsolute(evalFunc)) {\n return await evaluateWithCustomScript(bqrsPath, queryPath, evalFunc, outputPath);\n } else {\n return {\n success: false,\n error: `Unknown evaluation function: ${evalFunc}. Available built-in functions: ${Object.keys(BUILT_IN_EVALUATORS).join(', ')}`\n };\n }\n }\n } catch (error) {\n return {\n success: false,\n error: `Query evaluation failed: ${error}`\n };\n }\n}\n\n/**\n * Execute custom evaluation script\n */\nasync function evaluateWithCustomScript(\n _bqrsPath: string,\n _queryPath: string,\n _scriptPath: string,\n _outputPath?: string\n): Promise {\n // TODO: Implement custom script execution\n // This would need to execute the script with bqrsPath and queryPath as arguments\n // and capture the output\n return {\n success: false,\n error: 'Custom evaluation scripts are not yet implemented'\n };\n}", "/**\n * Log directory management utilities for CodeQL query and test runs\n */\n\nimport { mkdirSync, existsSync } from 'fs';\nimport { join, resolve } from 'path';\nimport { randomBytes } from 'crypto';\nimport { getProjectTmpDir } from '../utils/temp-dir';\n\n/**\n * Ensure that a given path is within a base directory.\n * Throws an error if the path is outside the base directory.\n */\nfunction ensurePathWithinBase(baseDir: string, targetPath: string): string {\n const absBase = resolve(baseDir);\n const absTarget = resolve(targetPath);\n if (!absTarget.startsWith(absBase + '/') && absTarget !== absBase) {\n throw new Error(`Provided log directory is outside the allowed base directory: ${absBase}`);\n }\n return absTarget;\n}\n\n/**\n * Get or create a unique log directory for query/test runs\n * \n * @param logDir - Optional custom log directory from parameters\n * @returns Absolute path to the log directory\n */\nexport function getOrCreateLogDirectory(logDir?: string): string {\n // Use CODEQL_QUERY_LOG_DIR env var or default base\n const baseLogDir = process.env.CODEQL_QUERY_LOG_DIR || getProjectTmpDir('query-logs');\n\n // If logDir is explicitly provided, validate it is within baseLogDir\n if (logDir) {\n const absLogDir = ensurePathWithinBase(baseLogDir, logDir);\n if (!existsSync(absLogDir)) {\n mkdirSync(absLogDir, { recursive: true });\n }\n return absLogDir;\n }\n \n // Otherwise, use baseLogDir and create a unique subdirectory\n \n // Create base directory if it doesn't exist\n if (!existsSync(baseLogDir)) {\n mkdirSync(baseLogDir, { recursive: true });\n }\n \n // Generate unique subdirectory with timestamp and random ID\n const timestamp = new Date().toISOString().replace(/[:.]/g, '-');\n const uniqueId = randomBytes(4).toString('hex');\n const uniqueLogDir = join(baseLogDir, `query-run-${timestamp}-${uniqueId}`);\n \n mkdirSync(uniqueLogDir, { recursive: true });\n \n return uniqueLogDir;\n}\n", "/**\n * CodeQL BQRS info tool\n */\n\nimport { z } from 'zod';\nimport { CLIToolDefinition, createCodeQLSchemas, createBQRSResultProcessor } from '../../lib/cli-tool-registry';\n\nexport const codeqlBqrsInfoTool: CLIToolDefinition = {\n name: 'codeql_bqrs_info',\n description: 'Get metadata and information about BQRS result files',\n command: 'codeql',\n subcommand: 'bqrs info',\n inputSchema: {\n files: z.array(z.string()).describe('BQRS file(s) to examine'),\n verbose: createCodeQLSchemas.verbose(),\n additionalArgs: createCodeQLSchemas.additionalArgs()\n },\n examples: [\n 'codeql bqrs info results.bqrs',\n 'codeql bqrs info --verbose results.bqrs'\n ],\n resultProcessor: createBQRSResultProcessor()\n};", "/**\n * CodeQL BQRS interpret tool\n */\n\nimport { z } from 'zod';\nimport { CLIToolDefinition, createCodeQLSchemas, createBQRSResultProcessor } from '../../lib/cli-tool-registry';\n\nexport const codeqlBqrsInterpretTool: CLIToolDefinition = {\n name: 'codeql_bqrs_interpret',\n description: 'Interpret BQRS result files according to query metadata and generate output in specified formats (CSV, SARIF, graph formats)',\n command: 'codeql',\n subcommand: 'bqrs interpret',\n inputSchema: {\n file: z.string().describe('The BQRS file to interpret'),\n format: z.enum(['csv', 'sarif-latest', 'sarifv2.1.0', 'graphtext', 'dgml', 'dot'])\n .describe('Output format: csv (comma-separated), sarif-latest/sarifv2.1.0 (SARIF), graphtext/dgml/dot (graph formats, only for @kind graph queries)'),\n output: createCodeQLSchemas.output(),\n t: z.array(z.string())\n .describe('Query metadata key=value pairs. At least \"kind\" and \"id\" must be specified (e.g., [\"kind=graph\", \"id=js/print-ast\"])'),\n 'max-paths': z.number().optional()\n .describe('Maximum number of paths to produce for each alert with paths (default: 4)'),\n 'sarif-add-file-contents': z.boolean().optional()\n .describe('[SARIF only] Include full file contents for all files referenced in results'),\n 'sarif-add-snippets': z.boolean().optional()\n .describe('[SARIF only] Include code snippets for each location with context'),\n 'sarif-group-rules-by-pack': z.boolean().optional()\n .describe('[SARIF only] Place rule objects under their QL pack in tool.extensions property'),\n 'sarif-multicause-markdown': z.boolean().optional()\n .describe('[SARIF only] Include multi-cause alerts as Markdown-formatted lists'),\n 'sarif-category': z.string().optional()\n .describe('[SARIF only] Category for this analysis (distinguishes multiple analyses on same code)'),\n 'csv-location-format': z.enum(['uri', 'line-column', 'offset-length']).optional()\n .describe('[CSV only] Format for locations in CSV output (default: line-column)'),\n 'dot-location-url-format': z.string().optional()\n .describe('[DOT only] Format string for file location URLs (placeholders: {path}, {start:line}, {start:column}, {end:line}, {end:column}, {offset}, {length})'),\n threads: z.number().optional()\n .describe('Number of threads for computing paths (0 = one per core, -N = leave N cores unused)'),\n 'column-kind': z.enum(['utf8', 'utf16', 'utf32', 'bytes']).optional()\n .describe('[SARIF only] Column kind for interpreting location columns'),\n 'unicode-new-lines': z.boolean().optional()\n .describe('[SARIF only] Whether unicode newlines (U+2028, U+2029) are considered as newlines'),\n verbose: createCodeQLSchemas.verbose(),\n additionalArgs: createCodeQLSchemas.additionalArgs()\n },\n examples: [\n 'codeql bqrs interpret --format=sarif-latest --output=results.sarif -t kind=problem -t id=js/sql-injection results.bqrs',\n 'codeql bqrs interpret --format=graphtext --output=ast.txt -t kind=graph -t id=js/print-ast results.bqrs',\n 'codeql bqrs interpret --format=csv --csv-location-format=line-column --output=results.csv -t kind=problem -t id=js/xss results.bqrs',\n 'codeql bqrs interpret --format=dot --output=graph.dot -t kind=graph -t id=java/call-graph results.bqrs',\n 'codeql bqrs interpret --format=sarif-latest --sarif-add-snippets --sarif-category=security --output=results.sarif -t kind=path-problem -t id=go/path-injection results.bqrs'\n ],\n resultProcessor: createBQRSResultProcessor()\n};\n", "/**\n * CodeQL database analyze tool\n */\n\nimport { z } from 'zod';\nimport { CLIToolDefinition } from '../../lib/cli-tool-registry';\n\nexport const codeqlDatabaseAnalyzeTool: CLIToolDefinition = {\n name: 'codeql_database_analyze',\n description: 'Run queries or query suites against CodeQL databases',\n command: 'codeql',\n subcommand: 'database analyze',\n inputSchema: {\n database: z.string().describe('Path to the CodeQL database'),\n queries: z.string().describe('Queries or query suite to run'),\n output: z.string().optional().describe('Output file path'),\n format: z.enum(['csv', 'json', 'sarif-latest', 'sarifv2.1.0']).optional()\n .describe('Output format for results'),\n 'download-location': z.string().optional()\n .describe('Location to download missing dependencies'),\n threads: z.number().optional().describe('Number of threads to use'),\n ram: z.number().optional().describe('Amount of RAM to use (MB)'),\n timeout: z.number().optional().describe('Timeout in seconds'),\n verbose: z.boolean().optional().describe('Enable verbose output'),\n additionalArgs: z.array(z.string()).optional().describe('Additional command-line arguments')\n },\n examples: [\n 'codeql database analyze mydb queries.qls --format=sarif-latest --output=results.sarif',\n 'codeql database analyze mydb codeql/java-queries --format=csv'\n ]\n};", "/**\n * CodeQL database create tool\n */\n\nimport { z } from 'zod';\nimport { CLIToolDefinition, createDatabaseResultProcessor } from '../../lib/cli-tool-registry';\n\nexport const codeqlDatabaseCreateTool: CLIToolDefinition = {\n name: 'codeql_database_create',\n description: 'Create a CodeQL database from source code',\n command: 'codeql',\n subcommand: 'database create',\n inputSchema: {\n database: z.string().describe('Database path/name to create'),\n language: z.string().optional().describe('Programming language(s) to extract'),\n 'source-root': z.string().optional().describe('Root directory of source code'),\n command: z.string().optional().describe('Build command for compiled languages'),\n 'build-mode': z.enum(['none', 'autobuild', 'manual']).optional()\n .describe('Build mode: none (interpreted langs), autobuild, or manual'),\n threads: z.number().optional().describe('Number of threads to use'),\n ram: z.number().optional().describe('Amount of RAM to use (MB)'),\n verbose: z.boolean().optional().describe('Enable verbose output'),\n overwrite: z.boolean().optional().describe('Overwrite existing database if it exists'),\n 'no-cleanup': z.boolean().optional().describe('Skip database cleanup after finalization'),\n additionalArgs: z.array(z.string()).optional().describe('Additional command-line arguments')\n },\n examples: [\n 'codeql database create --language=java --source-root=/path/to/project mydb',\n 'codeql database create --language=cpp --command=\"make all\" mydb',\n 'codeql database create --language=python,javascript mydb'\n ],\n resultProcessor: createDatabaseResultProcessor()\n};", "/**\n * CodeQL find class position tool\n * \n * Inspired by JordyZomer/codeql-mcp repository:\n * - https://github.com/JordyZomer/codeql-mcp/blob/main/server.py\n * - https://github.com/JordyZomer/codeql-mcp/blob/main/codeqlclient.py\n */\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport { readFile } from 'fs/promises';\nimport { logger } from '../../utils/logger';\n\nexport interface ClassPosition {\n start_line: number;\n start_col: number;\n end_line: number;\n end_col: number;\n}\n\n/**\n * Find the 1-based position of a class name identifier in a QL file.\n * Returns: { start_line, start_col, end_line, end_col }\n */\nexport async function findClassPosition(filepath: string, className: string): Promise {\n try {\n const content = await readFile(filepath, 'utf-8');\n const lines = content.split('\\n');\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n // Match class definition with the specific class name\n const classNameRegex = new RegExp(`\\\\bclass\\\\s+(${className.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')})\\\\b`);\n const match = classNameRegex.exec(line);\n \n if (match) {\n const start_line = i + 1; // 1-based line numbering\n // The class name is in capture group 1\n const classNameStart = match.index + match[0].indexOf(match[1]);\n const start_col = classNameStart + 1; // 1-based column numbering\n const end_col = start_col + className.length - 1; // end_col is inclusive\n \n return {\n start_line,\n start_col,\n end_line: start_line,\n end_col\n };\n }\n }\n\n throw new Error(`Class name '${className}' not found in file: ${filepath}`);\n } catch (error) {\n if (error instanceof Error && error.message.includes('not found in file')) {\n throw error;\n }\n throw new Error(`Failed to read or parse file ${filepath}: ${error instanceof Error ? error.message : 'Unknown error'}`);\n }\n}\n\n/**\n * Register the find class position tool with the MCP server\n */\nexport function registerFindClassPositionTool(server: McpServer): void {\n server.tool(\n 'find_class_position',\n 'Finds startline, startcol, endline endcol of a class for quickeval',\n {\n file: z.string().describe('Path to the .ql file to search'),\n name: z.string().describe('Name of the class to find'),\n },\n async ({ file, name }) => {\n try {\n const position = await findClassPosition(file, name);\n return {\n content: [{ type: 'text', text: JSON.stringify(position, null, 2) }],\n };\n } catch (error) {\n logger.error('Error finding class position:', error);\n return {\n content: [\n {\n type: 'text',\n text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`,\n },\n ],\n isError: true,\n };\n }\n }\n );\n}", "/**\n * CodeQL find predicate position tool\n * \n * Inspired by JordyZomer/codeql-mcp repository:\n * - https://github.com/JordyZomer/codeql-mcp/blob/main/server.py\n * - https://github.com/JordyZomer/codeql-mcp/blob/main/codeqlclient.py\n */\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport { readFile } from 'fs/promises';\nimport { logger } from '../../utils/logger';\n\nexport interface PredicatePosition {\n start_line: number;\n start_col: number;\n end_line: number;\n end_col: number;\n}\n\n/**\n * Find the 1-based position of a predicate name in a QL file.\n * Supports: \n * - predicate name(...) - predicates with no return type\n * - Type name(...) - predicates with return type (e.g., string foo())\n * - name(...) (inside class) - member predicates\n * Returns: { start_line, start_col, end_line, end_col }\n */\nexport async function findPredicatePosition(filepath: string, predicateName: string): Promise {\n try {\n const content = await readFile(filepath, 'utf-8');\n const lines = content.split('\\n');\n const escapedName = predicateName.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n \n // Match predicate definition with the specific predicate name\n // Pattern 1: predicate name(...)\n const predicateKeywordRegex = new RegExp(`\\\\bpredicate\\\\s+(${escapedName})\\\\s*\\\\(`);\n let match = predicateKeywordRegex.exec(line);\n \n // Pattern 2: Type name(...) - predicates with return type\n // Matches: string foo(), int bar(), MyClass baz(), etc.\n // Must start at beginning of line (with optional whitespace) or after certain keywords\n if (!match) {\n const returnTypeRegex = new RegExp(`(?:^|\\\\s)(?:abstract\\\\s+)?(?:cached\\\\s+)?(?:private\\\\s+)?(?:deprecated\\\\s+)?(?:\\\\w+)\\\\s+(${escapedName})\\\\s*\\\\(`);\n match = returnTypeRegex.exec(line);\n }\n \n if (match) {\n const start_line = i + 1; // 1-based line numbering\n // The predicate name is in capture group 1\n const predicateNameStart = match.index + match[0].indexOf(match[1]);\n const start_col = predicateNameStart + 1; // 1-based column numbering\n const end_col = start_col + predicateName.length - 1; // end_col is inclusive\n \n return {\n start_line,\n start_col,\n end_line: start_line,\n end_col\n };\n }\n }\n\n throw new Error(`Predicate name '${predicateName}' not found in file: ${filepath}`);\n } catch (error) {\n if (error instanceof Error && error.message.includes('not found in file')) {\n throw error;\n }\n throw new Error(`Failed to read or parse file ${filepath}: ${error instanceof Error ? error.message : 'Unknown error'}`);\n }\n}\n\n/**\n * Register the find predicate position tool with the MCP server\n */\nexport function registerFindPredicatePositionTool(server: McpServer): void {\n server.tool(\n 'find_predicate_position',\n 'Finds startline, startcol, endline endcol of a predicate for quickeval',\n {\n file: z.string().describe('Path to the .ql file to search'),\n name: z.string().describe('Name of the predicate to find'),\n },\n async ({ file, name }) => {\n try {\n const position = await findPredicatePosition(file, name);\n return {\n content: [{ type: 'text', text: JSON.stringify(position, null, 2) }],\n };\n } catch (error) {\n logger.error('Error finding predicate position:', error);\n return {\n content: [\n {\n type: 'text',\n text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`,\n },\n ],\n isError: true,\n };\n }\n }\n );\n}", "/**\n * CodeQL find query files tool\n * Discovers and tracks all files related to a CodeQL query\n */\n\nimport { z } from 'zod';\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { findCodeQLQueryFiles } from '../../lib/query-file-finder';\nimport { logger } from '../../utils/logger';\n\n/**\n * Register the find_codeql_query_files tool\n */\nexport function registerFindCodeQLQueryFilesTool(server: McpServer): void {\n server.tool(\n 'find_codeql_query_files',\n 'Find and track all files and directories related to a CodeQL query, including resolved metadata',\n {\n queryPath: z.string().describe('Path to the CodeQL query file (.ql)'),\n language: z.string().optional().describe('Programming language (optional, will be inferred if not provided)'),\n resolveMetadata: z.boolean().optional().describe('Whether to resolve query metadata (default: true)')\n },\n async ({ queryPath, language, resolveMetadata }) => {\n try {\n const result = await findCodeQLQueryFiles(\n queryPath,\n language,\n resolveMetadata !== false // Default to true if not specified\n );\n\n return {\n content: [{ type: 'text', text: JSON.stringify(result, null, 2) }]\n };\n } catch (error) {\n logger.error('Error finding CodeQL query files:', error);\n return {\n content: [\n {\n type: 'text',\n text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`\n }\n ],\n isError: true\n };\n }\n }\n );\n}\n", "/**\n * CodeQL query file finder utilities\n * Handles discovery and tracking of query-related files and directories\n */\n\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport * as yaml from 'js-yaml';\nimport { QueryFileInfo, QueryFilesResult } from '../types/codeql';\nimport { resolveQueryMetadata } from './metadata-resolver';\n\n// Supported CodeQL languages and their file extensions (alphabetically ordered)\nconst LANGUAGE_EXTENSIONS: Record = {\n actions: 'yml',\n cpp: 'cpp',\n csharp: 'cs',\n go: 'go',\n java: 'java',\n javascript: 'js',\n python: 'py',\n ruby: 'rb',\n swift: 'swift',\n typescript: 'ts'\n};\n\nconst KNOWN_LANGUAGES = Object.keys(LANGUAGE_EXTENSIONS);\n\n/**\n * Get the file extension for test code based on language\n */\nfunction getLanguageExtension(language: string): string {\n return LANGUAGE_EXTENSIONS[language.toLowerCase()] || 'txt';\n}\n\n/**\n * Infer the language from the query file's directory structure\n */\nfunction inferLanguageFromPath(queryPath: string): string {\n const parts = queryPath.split(path.sep);\n\n for (const part of parts) {\n if (KNOWN_LANGUAGES.includes(part.toLowerCase())) {\n return part.toLowerCase();\n }\n }\n\n // Default fallback\n return 'unknown';\n}\n\n/**\n * Find the nearest qlpack.yml or codeql-pack.yml file by walking up the directory tree\n */\nfunction findNearestQlpack(startPath: string): string | null {\n let currentPath = startPath;\n const root = path.parse(currentPath).root;\n\n while (currentPath !== root) {\n // Check for codeql-pack.yml first (newer format), then qlpack.yml\n for (const packFile of ['codeql-pack.yml', 'qlpack.yml']) {\n const packPath = path.join(currentPath, packFile);\n if (fs.existsSync(packPath) && fs.statSync(packPath).isFile()) {\n return packPath;\n }\n }\n currentPath = path.dirname(currentPath);\n }\n\n return null;\n}\n\n/**\n * Read and parse qlpack.yml file\n */\nfunction readQlpackMetadata(qlpackPath: string): Record | null {\n try {\n const content = fs.readFileSync(qlpackPath, 'utf-8');\n const parsed = yaml.load(content) as Record;\n return parsed;\n } catch (_error) {\n // If we can't parse it, return null rather than failing\n return null;\n }\n}\n\n/**\n * Check if a file exists and return QueryFileInfo\n */\nfunction checkFile(filePath: string): QueryFileInfo {\n try {\n const exists = fs.existsSync(filePath) && fs.statSync(filePath).isFile();\n return {\n exists,\n path: filePath // Always return path, whether it exists or not\n };\n } catch {\n return {\n exists: false,\n path: filePath // Return the path even on error\n };\n }\n}\n\n/**\n * Check if a directory exists and return QueryFileInfo\n */\nfunction checkDirectory(dirPath: string): QueryFileInfo {\n try {\n const exists = fs.existsSync(dirPath) && fs.statSync(dirPath).isDirectory();\n return {\n exists,\n path: dirPath // Always return path, whether it exists or not\n };\n } catch {\n return {\n exists: false,\n path: dirPath // Return the path even on error\n };\n }\n}\n\n/**\n * Find all test code files in the test directory\n */\nfunction findTestCodeFiles(testDir: string, queryName: string, language: string): string[] {\n if (!fs.existsSync(testDir)) {\n return [];\n }\n\n try {\n const files = fs.readdirSync(testDir);\n const languageExt = getLanguageExtension(language);\n const testFiles: string[] = [];\n\n // Look for files matching the query name or other test code files\n const allValidExtensions = [...new Set([...Object.values(LANGUAGE_EXTENSIONS), 'yaml'])]; // Include yaml as alias for yml\n\n for (const file of files) {\n const filePath = path.join(testDir, file);\n const stat = fs.statSync(filePath);\n\n if (stat.isFile()) {\n // Include the primary test file matching query name\n if (file === `${queryName}.${languageExt}`) {\n testFiles.push(filePath);\n }\n // Include other code files (but not .qlref, .expected, .actual files)\n else if (!file.endsWith('.qlref') && !file.endsWith('.expected') && !file.endsWith('.actual')) {\n const ext = path.extname(file).slice(1);\n if (ext === languageExt || allValidExtensions.includes(ext)) {\n testFiles.push(filePath);\n }\n }\n }\n }\n\n return testFiles;\n } catch {\n return [];\n }\n}\n\n/**\n * Find CodeQL query files and directories based on a query file path\n * Optionally resolves metadata if the query file exists\n */\nexport async function findCodeQLQueryFiles(\n queryFilePath: string,\n language?: string,\n resolveMetadata: boolean = true\n): Promise {\n // Resolve absolute path\n const absoluteQueryPath = path.resolve(queryFilePath);\n\n // Extract query name and directory\n const queryName = path.basename(absoluteQueryPath, '.ql');\n const queryDir = path.dirname(absoluteQueryPath);\n\n // Infer language if not provided\n const detectedLanguage = language || inferLanguageFromPath(absoluteQueryPath);\n\n // Check query file itself\n const queryPath = checkFile(absoluteQueryPath);\n const queryDirectory = checkDirectory(queryDir);\n\n // Check for documentation files\n const mdPath = path.join(queryDir, `${queryName}.md`);\n const qhelpPath = path.join(queryDir, `${queryName}.qhelp`);\n const mdInfo = checkFile(mdPath);\n const qhelpInfo = checkFile(qhelpPath);\n\n const documentationPath: QueryFileInfo = mdInfo.exists ? mdInfo : (qhelpInfo.exists ? qhelpInfo : {\n exists: false,\n path: mdPath // Suggest .md as the default\n });\n\n // Check for specification file\n const qspecPath = path.join(queryDir, `${queryName}.qspec`);\n const specificationPath = checkFile(qspecPath);\n\n // Determine test directory\n // Pattern: if query is in .../src/QueryName/, test should be in .../test/QueryName/\n let testDir: string;\n if (queryDir.includes(`${path.sep}src${path.sep}`)) {\n // Find the last occurrence of /src/ and replace it with /test/\n const parts = queryDir.split(path.sep);\n const srcIndex = parts.lastIndexOf('src');\n if (srcIndex !== -1) {\n parts[srcIndex] = 'test';\n testDir = parts.join(path.sep);\n } else {\n // Fallback in case split didn't find it\n testDir = path.join(path.dirname(queryDir), 'test', queryName);\n }\n } else {\n // Fallback: if not in src directory, construct test dir based on parent\n testDir = path.join(path.dirname(queryDir), 'test', queryName);\n }\n const testDirectory = checkDirectory(testDir);\n\n // Check for .qlref file\n const qlrefPath = path.join(testDir, `${queryName}.qlref`);\n const qlrefInfo = checkFile(qlrefPath);\n\n // Find test code files\n const testCodePaths = findTestCodeFiles(testDir, queryName, detectedLanguage);\n\n // Check for expected results\n const expectedPath = path.join(testDir, `${queryName}.expected`);\n const expectedResultsPath = checkFile(expectedPath);\n\n // Check for actual results\n const actualPath = path.join(testDir, `${queryName}.actual`);\n const actualResultsPath = checkFile(actualPath);\n\n // Check for test database\n const testprojPath = path.join(testDir, `${queryName}.testproj`);\n const testDatabasePath = checkDirectory(testprojPath);\n\n // Determine missing files\n const missingFiles: string[] = [];\n if (!queryPath.exists) missingFiles.push(queryPath.path);\n if (!documentationPath.exists) missingFiles.push(documentationPath.path);\n if (!specificationPath.exists) missingFiles.push(specificationPath.path);\n if (!testDirectory.exists) missingFiles.push(testDirectory.path);\n if (!qlrefInfo.exists) missingFiles.push(qlrefInfo.path);\n if (testCodePaths.length === 0) missingFiles.push(path.join(testDir, `${queryName}.${getLanguageExtension(detectedLanguage)}`));\n if (!expectedResultsPath.exists) missingFiles.push(expectedResultsPath.path);\n\n const allFilesExist = missingFiles.length === 0;\n\n // Resolve metadata if requested and query file exists\n let metadata: { [key: string]: string | string[] } | undefined;\n if (resolveMetadata && queryPath.exists) {\n const resolvedMetadata = await resolveQueryMetadata(absoluteQueryPath);\n if (resolvedMetadata) {\n metadata = resolvedMetadata;\n }\n }\n\n // Resolve pack metadata and directories by finding the nearest qlpack.yml files\n let packMetadata: Record | undefined;\n const queryPackPath = findNearestQlpack(queryDir);\n const queryPackDir = queryPackPath ? path.dirname(queryPackPath) : queryDir;\n if (queryPackPath) {\n const parsed = readQlpackMetadata(queryPackPath);\n if (parsed) {\n packMetadata = parsed;\n }\n }\n\n // Find pack directory for test files\n const testPackPath = findNearestQlpack(testDir);\n const testPackDir = testPackPath ? path.dirname(testPackPath) : testDir;\n\n return {\n queryName,\n language: detectedLanguage,\n\n allFilesExist,\n\n files: {\n query: {\n dir: queryDirectory.path,\n doc: path.basename(documentationPath.path),\n packDir: queryPackDir,\n query: path.basename(queryPath.path),\n spec: path.basename(specificationPath.path)\n },\n test: {\n actual: path.basename(actualResultsPath.path),\n dir: testDirectory.path,\n expected: path.basename(expectedResultsPath.path),\n packDir: testPackDir,\n qlref: path.basename(qlrefInfo.path),\n testCode: testCodePaths,\n testDatabaseDir: testDatabasePath.path\n }\n },\n\n metadata,\n\n missingFiles,\n\n packMetadata,\n\n status: {\n actualResultsExist: actualResultsPath.exists,\n documentationExists: documentationPath.exists,\n expectedResultsExist: expectedResultsPath.exists,\n hasTestCode: testCodePaths.length > 0,\n qlrefExists: qlrefInfo.exists,\n queryExists: queryPath.exists,\n specificationExists: specificationPath.exists,\n testDatabaseDirExists: testDatabasePath.exists,\n testDirectoryExists: testDirectory.exists\n }\n };\n}\n", "\n/*! js-yaml 4.1.1 https://github.com/nodeca/js-yaml @license MIT */\nfunction isNothing(subject) {\n return (typeof subject === 'undefined') || (subject === null);\n}\n\n\nfunction isObject(subject) {\n return (typeof subject === 'object') && (subject !== null);\n}\n\n\nfunction toArray(sequence) {\n if (Array.isArray(sequence)) return sequence;\n else if (isNothing(sequence)) return [];\n\n return [ sequence ];\n}\n\n\nfunction extend(target, source) {\n var index, length, key, sourceKeys;\n\n if (source) {\n sourceKeys = Object.keys(source);\n\n for (index = 0, length = sourceKeys.length; index < length; index += 1) {\n key = sourceKeys[index];\n target[key] = source[key];\n }\n }\n\n return target;\n}\n\n\nfunction repeat(string, count) {\n var result = '', cycle;\n\n for (cycle = 0; cycle < count; cycle += 1) {\n result += string;\n }\n\n return result;\n}\n\n\nfunction isNegativeZero(number) {\n return (number === 0) && (Number.NEGATIVE_INFINITY === 1 / number);\n}\n\n\nvar isNothing_1 = isNothing;\nvar isObject_1 = isObject;\nvar toArray_1 = toArray;\nvar repeat_1 = repeat;\nvar isNegativeZero_1 = isNegativeZero;\nvar extend_1 = extend;\n\nvar common = {\n\tisNothing: isNothing_1,\n\tisObject: isObject_1,\n\ttoArray: toArray_1,\n\trepeat: repeat_1,\n\tisNegativeZero: isNegativeZero_1,\n\textend: extend_1\n};\n\n// YAML error class. http://stackoverflow.com/questions/8458984\n\n\nfunction formatError(exception, compact) {\n var where = '', message = exception.reason || '(unknown reason)';\n\n if (!exception.mark) return message;\n\n if (exception.mark.name) {\n where += 'in \"' + exception.mark.name + '\" ';\n }\n\n where += '(' + (exception.mark.line + 1) + ':' + (exception.mark.column + 1) + ')';\n\n if (!compact && exception.mark.snippet) {\n where += '\\n\\n' + exception.mark.snippet;\n }\n\n return message + ' ' + where;\n}\n\n\nfunction YAMLException$1(reason, mark) {\n // Super constructor\n Error.call(this);\n\n this.name = 'YAMLException';\n this.reason = reason;\n this.mark = mark;\n this.message = formatError(this, false);\n\n // Include stack trace in error object\n if (Error.captureStackTrace) {\n // Chrome and NodeJS\n Error.captureStackTrace(this, this.constructor);\n } else {\n // FF, IE 10+ and Safari 6+. Fallback for others\n this.stack = (new Error()).stack || '';\n }\n}\n\n\n// Inherit from Error\nYAMLException$1.prototype = Object.create(Error.prototype);\nYAMLException$1.prototype.constructor = YAMLException$1;\n\n\nYAMLException$1.prototype.toString = function toString(compact) {\n return this.name + ': ' + formatError(this, compact);\n};\n\n\nvar exception = YAMLException$1;\n\n// get snippet for a single line, respecting maxLength\nfunction getLine(buffer, lineStart, lineEnd, position, maxLineLength) {\n var head = '';\n var tail = '';\n var maxHalfLength = Math.floor(maxLineLength / 2) - 1;\n\n if (position - lineStart > maxHalfLength) {\n head = ' ... ';\n lineStart = position - maxHalfLength + head.length;\n }\n\n if (lineEnd - position > maxHalfLength) {\n tail = ' ...';\n lineEnd = position + maxHalfLength - tail.length;\n }\n\n return {\n str: head + buffer.slice(lineStart, lineEnd).replace(/\\t/g, '\u2192') + tail,\n pos: position - lineStart + head.length // relative position\n };\n}\n\n\nfunction padStart(string, max) {\n return common.repeat(' ', max - string.length) + string;\n}\n\n\nfunction makeSnippet(mark, options) {\n options = Object.create(options || null);\n\n if (!mark.buffer) return null;\n\n if (!options.maxLength) options.maxLength = 79;\n if (typeof options.indent !== 'number') options.indent = 1;\n if (typeof options.linesBefore !== 'number') options.linesBefore = 3;\n if (typeof options.linesAfter !== 'number') options.linesAfter = 2;\n\n var re = /\\r?\\n|\\r|\\0/g;\n var lineStarts = [ 0 ];\n var lineEnds = [];\n var match;\n var foundLineNo = -1;\n\n while ((match = re.exec(mark.buffer))) {\n lineEnds.push(match.index);\n lineStarts.push(match.index + match[0].length);\n\n if (mark.position <= match.index && foundLineNo < 0) {\n foundLineNo = lineStarts.length - 2;\n }\n }\n\n if (foundLineNo < 0) foundLineNo = lineStarts.length - 1;\n\n var result = '', i, line;\n var lineNoLength = Math.min(mark.line + options.linesAfter, lineEnds.length).toString().length;\n var maxLineLength = options.maxLength - (options.indent + lineNoLength + 3);\n\n for (i = 1; i <= options.linesBefore; i++) {\n if (foundLineNo - i < 0) break;\n line = getLine(\n mark.buffer,\n lineStarts[foundLineNo - i],\n lineEnds[foundLineNo - i],\n mark.position - (lineStarts[foundLineNo] - lineStarts[foundLineNo - i]),\n maxLineLength\n );\n result = common.repeat(' ', options.indent) + padStart((mark.line - i + 1).toString(), lineNoLength) +\n ' | ' + line.str + '\\n' + result;\n }\n\n line = getLine(mark.buffer, lineStarts[foundLineNo], lineEnds[foundLineNo], mark.position, maxLineLength);\n result += common.repeat(' ', options.indent) + padStart((mark.line + 1).toString(), lineNoLength) +\n ' | ' + line.str + '\\n';\n result += common.repeat('-', options.indent + lineNoLength + 3 + line.pos) + '^' + '\\n';\n\n for (i = 1; i <= options.linesAfter; i++) {\n if (foundLineNo + i >= lineEnds.length) break;\n line = getLine(\n mark.buffer,\n lineStarts[foundLineNo + i],\n lineEnds[foundLineNo + i],\n mark.position - (lineStarts[foundLineNo] - lineStarts[foundLineNo + i]),\n maxLineLength\n );\n result += common.repeat(' ', options.indent) + padStart((mark.line + i + 1).toString(), lineNoLength) +\n ' | ' + line.str + '\\n';\n }\n\n return result.replace(/\\n$/, '');\n}\n\n\nvar snippet = makeSnippet;\n\nvar TYPE_CONSTRUCTOR_OPTIONS = [\n 'kind',\n 'multi',\n 'resolve',\n 'construct',\n 'instanceOf',\n 'predicate',\n 'represent',\n 'representName',\n 'defaultStyle',\n 'styleAliases'\n];\n\nvar YAML_NODE_KINDS = [\n 'scalar',\n 'sequence',\n 'mapping'\n];\n\nfunction compileStyleAliases(map) {\n var result = {};\n\n if (map !== null) {\n Object.keys(map).forEach(function (style) {\n map[style].forEach(function (alias) {\n result[String(alias)] = style;\n });\n });\n }\n\n return result;\n}\n\nfunction Type$1(tag, options) {\n options = options || {};\n\n Object.keys(options).forEach(function (name) {\n if (TYPE_CONSTRUCTOR_OPTIONS.indexOf(name) === -1) {\n throw new exception('Unknown option \"' + name + '\" is met in definition of \"' + tag + '\" YAML type.');\n }\n });\n\n // TODO: Add tag format check.\n this.options = options; // keep original options in case user wants to extend this type later\n this.tag = tag;\n this.kind = options['kind'] || null;\n this.resolve = options['resolve'] || function () { return true; };\n this.construct = options['construct'] || function (data) { return data; };\n this.instanceOf = options['instanceOf'] || null;\n this.predicate = options['predicate'] || null;\n this.represent = options['represent'] || null;\n this.representName = options['representName'] || null;\n this.defaultStyle = options['defaultStyle'] || null;\n this.multi = options['multi'] || false;\n this.styleAliases = compileStyleAliases(options['styleAliases'] || null);\n\n if (YAML_NODE_KINDS.indexOf(this.kind) === -1) {\n throw new exception('Unknown kind \"' + this.kind + '\" is specified for \"' + tag + '\" YAML type.');\n }\n}\n\nvar type = Type$1;\n\n/*eslint-disable max-len*/\n\n\n\n\n\nfunction compileList(schema, name) {\n var result = [];\n\n schema[name].forEach(function (currentType) {\n var newIndex = result.length;\n\n result.forEach(function (previousType, previousIndex) {\n if (previousType.tag === currentType.tag &&\n previousType.kind === currentType.kind &&\n previousType.multi === currentType.multi) {\n\n newIndex = previousIndex;\n }\n });\n\n result[newIndex] = currentType;\n });\n\n return result;\n}\n\n\nfunction compileMap(/* lists... */) {\n var result = {\n scalar: {},\n sequence: {},\n mapping: {},\n fallback: {},\n multi: {\n scalar: [],\n sequence: [],\n mapping: [],\n fallback: []\n }\n }, index, length;\n\n function collectType(type) {\n if (type.multi) {\n result.multi[type.kind].push(type);\n result.multi['fallback'].push(type);\n } else {\n result[type.kind][type.tag] = result['fallback'][type.tag] = type;\n }\n }\n\n for (index = 0, length = arguments.length; index < length; index += 1) {\n arguments[index].forEach(collectType);\n }\n return result;\n}\n\n\nfunction Schema$1(definition) {\n return this.extend(definition);\n}\n\n\nSchema$1.prototype.extend = function extend(definition) {\n var implicit = [];\n var explicit = [];\n\n if (definition instanceof type) {\n // Schema.extend(type)\n explicit.push(definition);\n\n } else if (Array.isArray(definition)) {\n // Schema.extend([ type1, type2, ... ])\n explicit = explicit.concat(definition);\n\n } else if (definition && (Array.isArray(definition.implicit) || Array.isArray(definition.explicit))) {\n // Schema.extend({ explicit: [ type1, type2, ... ], implicit: [ type1, type2, ... ] })\n if (definition.implicit) implicit = implicit.concat(definition.implicit);\n if (definition.explicit) explicit = explicit.concat(definition.explicit);\n\n } else {\n throw new exception('Schema.extend argument should be a Type, [ Type ], ' +\n 'or a schema definition ({ implicit: [...], explicit: [...] })');\n }\n\n implicit.forEach(function (type$1) {\n if (!(type$1 instanceof type)) {\n throw new exception('Specified list of YAML types (or a single Type object) contains a non-Type object.');\n }\n\n if (type$1.loadKind && type$1.loadKind !== 'scalar') {\n throw new exception('There is a non-scalar type in the implicit list of a schema. Implicit resolving of such types is not supported.');\n }\n\n if (type$1.multi) {\n throw new exception('There is a multi type in the implicit list of a schema. Multi tags can only be listed as explicit.');\n }\n });\n\n explicit.forEach(function (type$1) {\n if (!(type$1 instanceof type)) {\n throw new exception('Specified list of YAML types (or a single Type object) contains a non-Type object.');\n }\n });\n\n var result = Object.create(Schema$1.prototype);\n\n result.implicit = (this.implicit || []).concat(implicit);\n result.explicit = (this.explicit || []).concat(explicit);\n\n result.compiledImplicit = compileList(result, 'implicit');\n result.compiledExplicit = compileList(result, 'explicit');\n result.compiledTypeMap = compileMap(result.compiledImplicit, result.compiledExplicit);\n\n return result;\n};\n\n\nvar schema = Schema$1;\n\nvar str = new type('tag:yaml.org,2002:str', {\n kind: 'scalar',\n construct: function (data) { return data !== null ? data : ''; }\n});\n\nvar seq = new type('tag:yaml.org,2002:seq', {\n kind: 'sequence',\n construct: function (data) { return data !== null ? data : []; }\n});\n\nvar map = new type('tag:yaml.org,2002:map', {\n kind: 'mapping',\n construct: function (data) { return data !== null ? data : {}; }\n});\n\nvar failsafe = new schema({\n explicit: [\n str,\n seq,\n map\n ]\n});\n\nfunction resolveYamlNull(data) {\n if (data === null) return true;\n\n var max = data.length;\n\n return (max === 1 && data === '~') ||\n (max === 4 && (data === 'null' || data === 'Null' || data === 'NULL'));\n}\n\nfunction constructYamlNull() {\n return null;\n}\n\nfunction isNull(object) {\n return object === null;\n}\n\nvar _null = new type('tag:yaml.org,2002:null', {\n kind: 'scalar',\n resolve: resolveYamlNull,\n construct: constructYamlNull,\n predicate: isNull,\n represent: {\n canonical: function () { return '~'; },\n lowercase: function () { return 'null'; },\n uppercase: function () { return 'NULL'; },\n camelcase: function () { return 'Null'; },\n empty: function () { return ''; }\n },\n defaultStyle: 'lowercase'\n});\n\nfunction resolveYamlBoolean(data) {\n if (data === null) return false;\n\n var max = data.length;\n\n return (max === 4 && (data === 'true' || data === 'True' || data === 'TRUE')) ||\n (max === 5 && (data === 'false' || data === 'False' || data === 'FALSE'));\n}\n\nfunction constructYamlBoolean(data) {\n return data === 'true' ||\n data === 'True' ||\n data === 'TRUE';\n}\n\nfunction isBoolean(object) {\n return Object.prototype.toString.call(object) === '[object Boolean]';\n}\n\nvar bool = new type('tag:yaml.org,2002:bool', {\n kind: 'scalar',\n resolve: resolveYamlBoolean,\n construct: constructYamlBoolean,\n predicate: isBoolean,\n represent: {\n lowercase: function (object) { return object ? 'true' : 'false'; },\n uppercase: function (object) { return object ? 'TRUE' : 'FALSE'; },\n camelcase: function (object) { return object ? 'True' : 'False'; }\n },\n defaultStyle: 'lowercase'\n});\n\nfunction isHexCode(c) {\n return ((0x30/* 0 */ <= c) && (c <= 0x39/* 9 */)) ||\n ((0x41/* A */ <= c) && (c <= 0x46/* F */)) ||\n ((0x61/* a */ <= c) && (c <= 0x66/* f */));\n}\n\nfunction isOctCode(c) {\n return ((0x30/* 0 */ <= c) && (c <= 0x37/* 7 */));\n}\n\nfunction isDecCode(c) {\n return ((0x30/* 0 */ <= c) && (c <= 0x39/* 9 */));\n}\n\nfunction resolveYamlInteger(data) {\n if (data === null) return false;\n\n var max = data.length,\n index = 0,\n hasDigits = false,\n ch;\n\n if (!max) return false;\n\n ch = data[index];\n\n // sign\n if (ch === '-' || ch === '+') {\n ch = data[++index];\n }\n\n if (ch === '0') {\n // 0\n if (index + 1 === max) return true;\n ch = data[++index];\n\n // base 2, base 8, base 16\n\n if (ch === 'b') {\n // base 2\n index++;\n\n for (; index < max; index++) {\n ch = data[index];\n if (ch === '_') continue;\n if (ch !== '0' && ch !== '1') return false;\n hasDigits = true;\n }\n return hasDigits && ch !== '_';\n }\n\n\n if (ch === 'x') {\n // base 16\n index++;\n\n for (; index < max; index++) {\n ch = data[index];\n if (ch === '_') continue;\n if (!isHexCode(data.charCodeAt(index))) return false;\n hasDigits = true;\n }\n return hasDigits && ch !== '_';\n }\n\n\n if (ch === 'o') {\n // base 8\n index++;\n\n for (; index < max; index++) {\n ch = data[index];\n if (ch === '_') continue;\n if (!isOctCode(data.charCodeAt(index))) return false;\n hasDigits = true;\n }\n return hasDigits && ch !== '_';\n }\n }\n\n // base 10 (except 0)\n\n // value should not start with `_`;\n if (ch === '_') return false;\n\n for (; index < max; index++) {\n ch = data[index];\n if (ch === '_') continue;\n if (!isDecCode(data.charCodeAt(index))) {\n return false;\n }\n hasDigits = true;\n }\n\n // Should have digits and should not end with `_`\n if (!hasDigits || ch === '_') return false;\n\n return true;\n}\n\nfunction constructYamlInteger(data) {\n var value = data, sign = 1, ch;\n\n if (value.indexOf('_') !== -1) {\n value = value.replace(/_/g, '');\n }\n\n ch = value[0];\n\n if (ch === '-' || ch === '+') {\n if (ch === '-') sign = -1;\n value = value.slice(1);\n ch = value[0];\n }\n\n if (value === '0') return 0;\n\n if (ch === '0') {\n if (value[1] === 'b') return sign * parseInt(value.slice(2), 2);\n if (value[1] === 'x') return sign * parseInt(value.slice(2), 16);\n if (value[1] === 'o') return sign * parseInt(value.slice(2), 8);\n }\n\n return sign * parseInt(value, 10);\n}\n\nfunction isInteger(object) {\n return (Object.prototype.toString.call(object)) === '[object Number]' &&\n (object % 1 === 0 && !common.isNegativeZero(object));\n}\n\nvar int = new type('tag:yaml.org,2002:int', {\n kind: 'scalar',\n resolve: resolveYamlInteger,\n construct: constructYamlInteger,\n predicate: isInteger,\n represent: {\n binary: function (obj) { return obj >= 0 ? '0b' + obj.toString(2) : '-0b' + obj.toString(2).slice(1); },\n octal: function (obj) { return obj >= 0 ? '0o' + obj.toString(8) : '-0o' + obj.toString(8).slice(1); },\n decimal: function (obj) { return obj.toString(10); },\n /* eslint-disable max-len */\n hexadecimal: function (obj) { return obj >= 0 ? '0x' + obj.toString(16).toUpperCase() : '-0x' + obj.toString(16).toUpperCase().slice(1); }\n },\n defaultStyle: 'decimal',\n styleAliases: {\n binary: [ 2, 'bin' ],\n octal: [ 8, 'oct' ],\n decimal: [ 10, 'dec' ],\n hexadecimal: [ 16, 'hex' ]\n }\n});\n\nvar YAML_FLOAT_PATTERN = new RegExp(\n // 2.5e4, 2.5 and integers\n '^(?:[-+]?(?:[0-9][0-9_]*)(?:\\\\.[0-9_]*)?(?:[eE][-+]?[0-9]+)?' +\n // .2e4, .2\n // special case, seems not from spec\n '|\\\\.[0-9_]+(?:[eE][-+]?[0-9]+)?' +\n // .inf\n '|[-+]?\\\\.(?:inf|Inf|INF)' +\n // .nan\n '|\\\\.(?:nan|NaN|NAN))$');\n\nfunction resolveYamlFloat(data) {\n if (data === null) return false;\n\n if (!YAML_FLOAT_PATTERN.test(data) ||\n // Quick hack to not allow integers end with `_`\n // Probably should update regexp & check speed\n data[data.length - 1] === '_') {\n return false;\n }\n\n return true;\n}\n\nfunction constructYamlFloat(data) {\n var value, sign;\n\n value = data.replace(/_/g, '').toLowerCase();\n sign = value[0] === '-' ? -1 : 1;\n\n if ('+-'.indexOf(value[0]) >= 0) {\n value = value.slice(1);\n }\n\n if (value === '.inf') {\n return (sign === 1) ? Number.POSITIVE_INFINITY : Number.NEGATIVE_INFINITY;\n\n } else if (value === '.nan') {\n return NaN;\n }\n return sign * parseFloat(value, 10);\n}\n\n\nvar SCIENTIFIC_WITHOUT_DOT = /^[-+]?[0-9]+e/;\n\nfunction representYamlFloat(object, style) {\n var res;\n\n if (isNaN(object)) {\n switch (style) {\n case 'lowercase': return '.nan';\n case 'uppercase': return '.NAN';\n case 'camelcase': return '.NaN';\n }\n } else if (Number.POSITIVE_INFINITY === object) {\n switch (style) {\n case 'lowercase': return '.inf';\n case 'uppercase': return '.INF';\n case 'camelcase': return '.Inf';\n }\n } else if (Number.NEGATIVE_INFINITY === object) {\n switch (style) {\n case 'lowercase': return '-.inf';\n case 'uppercase': return '-.INF';\n case 'camelcase': return '-.Inf';\n }\n } else if (common.isNegativeZero(object)) {\n return '-0.0';\n }\n\n res = object.toString(10);\n\n // JS stringifier can build scientific format without dots: 5e-100,\n // while YAML requres dot: 5.e-100. Fix it with simple hack\n\n return SCIENTIFIC_WITHOUT_DOT.test(res) ? res.replace('e', '.e') : res;\n}\n\nfunction isFloat(object) {\n return (Object.prototype.toString.call(object) === '[object Number]') &&\n (object % 1 !== 0 || common.isNegativeZero(object));\n}\n\nvar float = new type('tag:yaml.org,2002:float', {\n kind: 'scalar',\n resolve: resolveYamlFloat,\n construct: constructYamlFloat,\n predicate: isFloat,\n represent: representYamlFloat,\n defaultStyle: 'lowercase'\n});\n\nvar json = failsafe.extend({\n implicit: [\n _null,\n bool,\n int,\n float\n ]\n});\n\nvar core = json;\n\nvar YAML_DATE_REGEXP = new RegExp(\n '^([0-9][0-9][0-9][0-9])' + // [1] year\n '-([0-9][0-9])' + // [2] month\n '-([0-9][0-9])$'); // [3] day\n\nvar YAML_TIMESTAMP_REGEXP = new RegExp(\n '^([0-9][0-9][0-9][0-9])' + // [1] year\n '-([0-9][0-9]?)' + // [2] month\n '-([0-9][0-9]?)' + // [3] day\n '(?:[Tt]|[ \\\\t]+)' + // ...\n '([0-9][0-9]?)' + // [4] hour\n ':([0-9][0-9])' + // [5] minute\n ':([0-9][0-9])' + // [6] second\n '(?:\\\\.([0-9]*))?' + // [7] fraction\n '(?:[ \\\\t]*(Z|([-+])([0-9][0-9]?)' + // [8] tz [9] tz_sign [10] tz_hour\n '(?::([0-9][0-9]))?))?$'); // [11] tz_minute\n\nfunction resolveYamlTimestamp(data) {\n if (data === null) return false;\n if (YAML_DATE_REGEXP.exec(data) !== null) return true;\n if (YAML_TIMESTAMP_REGEXP.exec(data) !== null) return true;\n return false;\n}\n\nfunction constructYamlTimestamp(data) {\n var match, year, month, day, hour, minute, second, fraction = 0,\n delta = null, tz_hour, tz_minute, date;\n\n match = YAML_DATE_REGEXP.exec(data);\n if (match === null) match = YAML_TIMESTAMP_REGEXP.exec(data);\n\n if (match === null) throw new Error('Date resolve error');\n\n // match: [1] year [2] month [3] day\n\n year = +(match[1]);\n month = +(match[2]) - 1; // JS month starts with 0\n day = +(match[3]);\n\n if (!match[4]) { // no hour\n return new Date(Date.UTC(year, month, day));\n }\n\n // match: [4] hour [5] minute [6] second [7] fraction\n\n hour = +(match[4]);\n minute = +(match[5]);\n second = +(match[6]);\n\n if (match[7]) {\n fraction = match[7].slice(0, 3);\n while (fraction.length < 3) { // milli-seconds\n fraction += '0';\n }\n fraction = +fraction;\n }\n\n // match: [8] tz [9] tz_sign [10] tz_hour [11] tz_minute\n\n if (match[9]) {\n tz_hour = +(match[10]);\n tz_minute = +(match[11] || 0);\n delta = (tz_hour * 60 + tz_minute) * 60000; // delta in mili-seconds\n if (match[9] === '-') delta = -delta;\n }\n\n date = new Date(Date.UTC(year, month, day, hour, minute, second, fraction));\n\n if (delta) date.setTime(date.getTime() - delta);\n\n return date;\n}\n\nfunction representYamlTimestamp(object /*, style*/) {\n return object.toISOString();\n}\n\nvar timestamp = new type('tag:yaml.org,2002:timestamp', {\n kind: 'scalar',\n resolve: resolveYamlTimestamp,\n construct: constructYamlTimestamp,\n instanceOf: Date,\n represent: representYamlTimestamp\n});\n\nfunction resolveYamlMerge(data) {\n return data === '<<' || data === null;\n}\n\nvar merge = new type('tag:yaml.org,2002:merge', {\n kind: 'scalar',\n resolve: resolveYamlMerge\n});\n\n/*eslint-disable no-bitwise*/\n\n\n\n\n\n// [ 64, 65, 66 ] -> [ padding, CR, LF ]\nvar BASE64_MAP = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\\n\\r';\n\n\nfunction resolveYamlBinary(data) {\n if (data === null) return false;\n\n var code, idx, bitlen = 0, max = data.length, map = BASE64_MAP;\n\n // Convert one by one.\n for (idx = 0; idx < max; idx++) {\n code = map.indexOf(data.charAt(idx));\n\n // Skip CR/LF\n if (code > 64) continue;\n\n // Fail on illegal characters\n if (code < 0) return false;\n\n bitlen += 6;\n }\n\n // If there are any bits left, source was corrupted\n return (bitlen % 8) === 0;\n}\n\nfunction constructYamlBinary(data) {\n var idx, tailbits,\n input = data.replace(/[\\r\\n=]/g, ''), // remove CR/LF & padding to simplify scan\n max = input.length,\n map = BASE64_MAP,\n bits = 0,\n result = [];\n\n // Collect by 6*4 bits (3 bytes)\n\n for (idx = 0; idx < max; idx++) {\n if ((idx % 4 === 0) && idx) {\n result.push((bits >> 16) & 0xFF);\n result.push((bits >> 8) & 0xFF);\n result.push(bits & 0xFF);\n }\n\n bits = (bits << 6) | map.indexOf(input.charAt(idx));\n }\n\n // Dump tail\n\n tailbits = (max % 4) * 6;\n\n if (tailbits === 0) {\n result.push((bits >> 16) & 0xFF);\n result.push((bits >> 8) & 0xFF);\n result.push(bits & 0xFF);\n } else if (tailbits === 18) {\n result.push((bits >> 10) & 0xFF);\n result.push((bits >> 2) & 0xFF);\n } else if (tailbits === 12) {\n result.push((bits >> 4) & 0xFF);\n }\n\n return new Uint8Array(result);\n}\n\nfunction representYamlBinary(object /*, style*/) {\n var result = '', bits = 0, idx, tail,\n max = object.length,\n map = BASE64_MAP;\n\n // Convert every three bytes to 4 ASCII characters.\n\n for (idx = 0; idx < max; idx++) {\n if ((idx % 3 === 0) && idx) {\n result += map[(bits >> 18) & 0x3F];\n result += map[(bits >> 12) & 0x3F];\n result += map[(bits >> 6) & 0x3F];\n result += map[bits & 0x3F];\n }\n\n bits = (bits << 8) + object[idx];\n }\n\n // Dump tail\n\n tail = max % 3;\n\n if (tail === 0) {\n result += map[(bits >> 18) & 0x3F];\n result += map[(bits >> 12) & 0x3F];\n result += map[(bits >> 6) & 0x3F];\n result += map[bits & 0x3F];\n } else if (tail === 2) {\n result += map[(bits >> 10) & 0x3F];\n result += map[(bits >> 4) & 0x3F];\n result += map[(bits << 2) & 0x3F];\n result += map[64];\n } else if (tail === 1) {\n result += map[(bits >> 2) & 0x3F];\n result += map[(bits << 4) & 0x3F];\n result += map[64];\n result += map[64];\n }\n\n return result;\n}\n\nfunction isBinary(obj) {\n return Object.prototype.toString.call(obj) === '[object Uint8Array]';\n}\n\nvar binary = new type('tag:yaml.org,2002:binary', {\n kind: 'scalar',\n resolve: resolveYamlBinary,\n construct: constructYamlBinary,\n predicate: isBinary,\n represent: representYamlBinary\n});\n\nvar _hasOwnProperty$3 = Object.prototype.hasOwnProperty;\nvar _toString$2 = Object.prototype.toString;\n\nfunction resolveYamlOmap(data) {\n if (data === null) return true;\n\n var objectKeys = [], index, length, pair, pairKey, pairHasKey,\n object = data;\n\n for (index = 0, length = object.length; index < length; index += 1) {\n pair = object[index];\n pairHasKey = false;\n\n if (_toString$2.call(pair) !== '[object Object]') return false;\n\n for (pairKey in pair) {\n if (_hasOwnProperty$3.call(pair, pairKey)) {\n if (!pairHasKey) pairHasKey = true;\n else return false;\n }\n }\n\n if (!pairHasKey) return false;\n\n if (objectKeys.indexOf(pairKey) === -1) objectKeys.push(pairKey);\n else return false;\n }\n\n return true;\n}\n\nfunction constructYamlOmap(data) {\n return data !== null ? data : [];\n}\n\nvar omap = new type('tag:yaml.org,2002:omap', {\n kind: 'sequence',\n resolve: resolveYamlOmap,\n construct: constructYamlOmap\n});\n\nvar _toString$1 = Object.prototype.toString;\n\nfunction resolveYamlPairs(data) {\n if (data === null) return true;\n\n var index, length, pair, keys, result,\n object = data;\n\n result = new Array(object.length);\n\n for (index = 0, length = object.length; index < length; index += 1) {\n pair = object[index];\n\n if (_toString$1.call(pair) !== '[object Object]') return false;\n\n keys = Object.keys(pair);\n\n if (keys.length !== 1) return false;\n\n result[index] = [ keys[0], pair[keys[0]] ];\n }\n\n return true;\n}\n\nfunction constructYamlPairs(data) {\n if (data === null) return [];\n\n var index, length, pair, keys, result,\n object = data;\n\n result = new Array(object.length);\n\n for (index = 0, length = object.length; index < length; index += 1) {\n pair = object[index];\n\n keys = Object.keys(pair);\n\n result[index] = [ keys[0], pair[keys[0]] ];\n }\n\n return result;\n}\n\nvar pairs = new type('tag:yaml.org,2002:pairs', {\n kind: 'sequence',\n resolve: resolveYamlPairs,\n construct: constructYamlPairs\n});\n\nvar _hasOwnProperty$2 = Object.prototype.hasOwnProperty;\n\nfunction resolveYamlSet(data) {\n if (data === null) return true;\n\n var key, object = data;\n\n for (key in object) {\n if (_hasOwnProperty$2.call(object, key)) {\n if (object[key] !== null) return false;\n }\n }\n\n return true;\n}\n\nfunction constructYamlSet(data) {\n return data !== null ? data : {};\n}\n\nvar set = new type('tag:yaml.org,2002:set', {\n kind: 'mapping',\n resolve: resolveYamlSet,\n construct: constructYamlSet\n});\n\nvar _default = core.extend({\n implicit: [\n timestamp,\n merge\n ],\n explicit: [\n binary,\n omap,\n pairs,\n set\n ]\n});\n\n/*eslint-disable max-len,no-use-before-define*/\n\n\n\n\n\n\n\nvar _hasOwnProperty$1 = Object.prototype.hasOwnProperty;\n\n\nvar CONTEXT_FLOW_IN = 1;\nvar CONTEXT_FLOW_OUT = 2;\nvar CONTEXT_BLOCK_IN = 3;\nvar CONTEXT_BLOCK_OUT = 4;\n\n\nvar CHOMPING_CLIP = 1;\nvar CHOMPING_STRIP = 2;\nvar CHOMPING_KEEP = 3;\n\n\nvar PATTERN_NON_PRINTABLE = /[\\x00-\\x08\\x0B\\x0C\\x0E-\\x1F\\x7F-\\x84\\x86-\\x9F\\uFFFE\\uFFFF]|[\\uD800-\\uDBFF](?![\\uDC00-\\uDFFF])|(?:[^\\uD800-\\uDBFF]|^)[\\uDC00-\\uDFFF]/;\nvar PATTERN_NON_ASCII_LINE_BREAKS = /[\\x85\\u2028\\u2029]/;\nvar PATTERN_FLOW_INDICATORS = /[,\\[\\]\\{\\}]/;\nvar PATTERN_TAG_HANDLE = /^(?:!|!!|![a-z\\-]+!)$/i;\nvar PATTERN_TAG_URI = /^(?:!|[^,\\[\\]\\{\\}])(?:%[0-9a-f]{2}|[0-9a-z\\-#;\\/\\?:@&=\\+\\$,_\\.!~\\*'\\(\\)\\[\\]])*$/i;\n\n\nfunction _class(obj) { return Object.prototype.toString.call(obj); }\n\nfunction is_EOL(c) {\n return (c === 0x0A/* LF */) || (c === 0x0D/* CR */);\n}\n\nfunction is_WHITE_SPACE(c) {\n return (c === 0x09/* Tab */) || (c === 0x20/* Space */);\n}\n\nfunction is_WS_OR_EOL(c) {\n return (c === 0x09/* Tab */) ||\n (c === 0x20/* Space */) ||\n (c === 0x0A/* LF */) ||\n (c === 0x0D/* CR */);\n}\n\nfunction is_FLOW_INDICATOR(c) {\n return c === 0x2C/* , */ ||\n c === 0x5B/* [ */ ||\n c === 0x5D/* ] */ ||\n c === 0x7B/* { */ ||\n c === 0x7D/* } */;\n}\n\nfunction fromHexCode(c) {\n var lc;\n\n if ((0x30/* 0 */ <= c) && (c <= 0x39/* 9 */)) {\n return c - 0x30;\n }\n\n /*eslint-disable no-bitwise*/\n lc = c | 0x20;\n\n if ((0x61/* a */ <= lc) && (lc <= 0x66/* f */)) {\n return lc - 0x61 + 10;\n }\n\n return -1;\n}\n\nfunction escapedHexLen(c) {\n if (c === 0x78/* x */) { return 2; }\n if (c === 0x75/* u */) { return 4; }\n if (c === 0x55/* U */) { return 8; }\n return 0;\n}\n\nfunction fromDecimalCode(c) {\n if ((0x30/* 0 */ <= c) && (c <= 0x39/* 9 */)) {\n return c - 0x30;\n }\n\n return -1;\n}\n\nfunction simpleEscapeSequence(c) {\n /* eslint-disable indent */\n return (c === 0x30/* 0 */) ? '\\x00' :\n (c === 0x61/* a */) ? '\\x07' :\n (c === 0x62/* b */) ? '\\x08' :\n (c === 0x74/* t */) ? '\\x09' :\n (c === 0x09/* Tab */) ? '\\x09' :\n (c === 0x6E/* n */) ? '\\x0A' :\n (c === 0x76/* v */) ? '\\x0B' :\n (c === 0x66/* f */) ? '\\x0C' :\n (c === 0x72/* r */) ? '\\x0D' :\n (c === 0x65/* e */) ? '\\x1B' :\n (c === 0x20/* Space */) ? ' ' :\n (c === 0x22/* \" */) ? '\\x22' :\n (c === 0x2F/* / */) ? '/' :\n (c === 0x5C/* \\ */) ? '\\x5C' :\n (c === 0x4E/* N */) ? '\\x85' :\n (c === 0x5F/* _ */) ? '\\xA0' :\n (c === 0x4C/* L */) ? '\\u2028' :\n (c === 0x50/* P */) ? '\\u2029' : '';\n}\n\nfunction charFromCodepoint(c) {\n if (c <= 0xFFFF) {\n return String.fromCharCode(c);\n }\n // Encode UTF-16 surrogate pair\n // https://en.wikipedia.org/wiki/UTF-16#Code_points_U.2B010000_to_U.2B10FFFF\n return String.fromCharCode(\n ((c - 0x010000) >> 10) + 0xD800,\n ((c - 0x010000) & 0x03FF) + 0xDC00\n );\n}\n\n// set a property of a literal object, while protecting against prototype pollution,\n// see https://github.com/nodeca/js-yaml/issues/164 for more details\nfunction setProperty(object, key, value) {\n // used for this specific key only because Object.defineProperty is slow\n if (key === '__proto__') {\n Object.defineProperty(object, key, {\n configurable: true,\n enumerable: true,\n writable: true,\n value: value\n });\n } else {\n object[key] = value;\n }\n}\n\nvar simpleEscapeCheck = new Array(256); // integer, for fast access\nvar simpleEscapeMap = new Array(256);\nfor (var i = 0; i < 256; i++) {\n simpleEscapeCheck[i] = simpleEscapeSequence(i) ? 1 : 0;\n simpleEscapeMap[i] = simpleEscapeSequence(i);\n}\n\n\nfunction State$1(input, options) {\n this.input = input;\n\n this.filename = options['filename'] || null;\n this.schema = options['schema'] || _default;\n this.onWarning = options['onWarning'] || null;\n // (Hidden) Remove? makes the loader to expect YAML 1.1 documents\n // if such documents have no explicit %YAML directive\n this.legacy = options['legacy'] || false;\n\n this.json = options['json'] || false;\n this.listener = options['listener'] || null;\n\n this.implicitTypes = this.schema.compiledImplicit;\n this.typeMap = this.schema.compiledTypeMap;\n\n this.length = input.length;\n this.position = 0;\n this.line = 0;\n this.lineStart = 0;\n this.lineIndent = 0;\n\n // position of first leading tab in the current line,\n // used to make sure there are no tabs in the indentation\n this.firstTabInLine = -1;\n\n this.documents = [];\n\n /*\n this.version;\n this.checkLineBreaks;\n this.tagMap;\n this.anchorMap;\n this.tag;\n this.anchor;\n this.kind;\n this.result;*/\n\n}\n\n\nfunction generateError(state, message) {\n var mark = {\n name: state.filename,\n buffer: state.input.slice(0, -1), // omit trailing \\0\n position: state.position,\n line: state.line,\n column: state.position - state.lineStart\n };\n\n mark.snippet = snippet(mark);\n\n return new exception(message, mark);\n}\n\nfunction throwError(state, message) {\n throw generateError(state, message);\n}\n\nfunction throwWarning(state, message) {\n if (state.onWarning) {\n state.onWarning.call(null, generateError(state, message));\n }\n}\n\n\nvar directiveHandlers = {\n\n YAML: function handleYamlDirective(state, name, args) {\n\n var match, major, minor;\n\n if (state.version !== null) {\n throwError(state, 'duplication of %YAML directive');\n }\n\n if (args.length !== 1) {\n throwError(state, 'YAML directive accepts exactly one argument');\n }\n\n match = /^([0-9]+)\\.([0-9]+)$/.exec(args[0]);\n\n if (match === null) {\n throwError(state, 'ill-formed argument of the YAML directive');\n }\n\n major = parseInt(match[1], 10);\n minor = parseInt(match[2], 10);\n\n if (major !== 1) {\n throwError(state, 'unacceptable YAML version of the document');\n }\n\n state.version = args[0];\n state.checkLineBreaks = (minor < 2);\n\n if (minor !== 1 && minor !== 2) {\n throwWarning(state, 'unsupported YAML version of the document');\n }\n },\n\n TAG: function handleTagDirective(state, name, args) {\n\n var handle, prefix;\n\n if (args.length !== 2) {\n throwError(state, 'TAG directive accepts exactly two arguments');\n }\n\n handle = args[0];\n prefix = args[1];\n\n if (!PATTERN_TAG_HANDLE.test(handle)) {\n throwError(state, 'ill-formed tag handle (first argument) of the TAG directive');\n }\n\n if (_hasOwnProperty$1.call(state.tagMap, handle)) {\n throwError(state, 'there is a previously declared suffix for \"' + handle + '\" tag handle');\n }\n\n if (!PATTERN_TAG_URI.test(prefix)) {\n throwError(state, 'ill-formed tag prefix (second argument) of the TAG directive');\n }\n\n try {\n prefix = decodeURIComponent(prefix);\n } catch (err) {\n throwError(state, 'tag prefix is malformed: ' + prefix);\n }\n\n state.tagMap[handle] = prefix;\n }\n};\n\n\nfunction captureSegment(state, start, end, checkJson) {\n var _position, _length, _character, _result;\n\n if (start < end) {\n _result = state.input.slice(start, end);\n\n if (checkJson) {\n for (_position = 0, _length = _result.length; _position < _length; _position += 1) {\n _character = _result.charCodeAt(_position);\n if (!(_character === 0x09 ||\n (0x20 <= _character && _character <= 0x10FFFF))) {\n throwError(state, 'expected valid JSON character');\n }\n }\n } else if (PATTERN_NON_PRINTABLE.test(_result)) {\n throwError(state, 'the stream contains non-printable characters');\n }\n\n state.result += _result;\n }\n}\n\nfunction mergeMappings(state, destination, source, overridableKeys) {\n var sourceKeys, key, index, quantity;\n\n if (!common.isObject(source)) {\n throwError(state, 'cannot merge mappings; the provided source object is unacceptable');\n }\n\n sourceKeys = Object.keys(source);\n\n for (index = 0, quantity = sourceKeys.length; index < quantity; index += 1) {\n key = sourceKeys[index];\n\n if (!_hasOwnProperty$1.call(destination, key)) {\n setProperty(destination, key, source[key]);\n overridableKeys[key] = true;\n }\n }\n}\n\nfunction storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, valueNode,\n startLine, startLineStart, startPos) {\n\n var index, quantity;\n\n // The output is a plain object here, so keys can only be strings.\n // We need to convert keyNode to a string, but doing so can hang the process\n // (deeply nested arrays that explode exponentially using aliases).\n if (Array.isArray(keyNode)) {\n keyNode = Array.prototype.slice.call(keyNode);\n\n for (index = 0, quantity = keyNode.length; index < quantity; index += 1) {\n if (Array.isArray(keyNode[index])) {\n throwError(state, 'nested arrays are not supported inside keys');\n }\n\n if (typeof keyNode === 'object' && _class(keyNode[index]) === '[object Object]') {\n keyNode[index] = '[object Object]';\n }\n }\n }\n\n // Avoid code execution in load() via toString property\n // (still use its own toString for arrays, timestamps,\n // and whatever user schema extensions happen to have @@toStringTag)\n if (typeof keyNode === 'object' && _class(keyNode) === '[object Object]') {\n keyNode = '[object Object]';\n }\n\n\n keyNode = String(keyNode);\n\n if (_result === null) {\n _result = {};\n }\n\n if (keyTag === 'tag:yaml.org,2002:merge') {\n if (Array.isArray(valueNode)) {\n for (index = 0, quantity = valueNode.length; index < quantity; index += 1) {\n mergeMappings(state, _result, valueNode[index], overridableKeys);\n }\n } else {\n mergeMappings(state, _result, valueNode, overridableKeys);\n }\n } else {\n if (!state.json &&\n !_hasOwnProperty$1.call(overridableKeys, keyNode) &&\n _hasOwnProperty$1.call(_result, keyNode)) {\n state.line = startLine || state.line;\n state.lineStart = startLineStart || state.lineStart;\n state.position = startPos || state.position;\n throwError(state, 'duplicated mapping key');\n }\n\n setProperty(_result, keyNode, valueNode);\n delete overridableKeys[keyNode];\n }\n\n return _result;\n}\n\nfunction readLineBreak(state) {\n var ch;\n\n ch = state.input.charCodeAt(state.position);\n\n if (ch === 0x0A/* LF */) {\n state.position++;\n } else if (ch === 0x0D/* CR */) {\n state.position++;\n if (state.input.charCodeAt(state.position) === 0x0A/* LF */) {\n state.position++;\n }\n } else {\n throwError(state, 'a line break is expected');\n }\n\n state.line += 1;\n state.lineStart = state.position;\n state.firstTabInLine = -1;\n}\n\nfunction skipSeparationSpace(state, allowComments, checkIndent) {\n var lineBreaks = 0,\n ch = state.input.charCodeAt(state.position);\n\n while (ch !== 0) {\n while (is_WHITE_SPACE(ch)) {\n if (ch === 0x09/* Tab */ && state.firstTabInLine === -1) {\n state.firstTabInLine = state.position;\n }\n ch = state.input.charCodeAt(++state.position);\n }\n\n if (allowComments && ch === 0x23/* # */) {\n do {\n ch = state.input.charCodeAt(++state.position);\n } while (ch !== 0x0A/* LF */ && ch !== 0x0D/* CR */ && ch !== 0);\n }\n\n if (is_EOL(ch)) {\n readLineBreak(state);\n\n ch = state.input.charCodeAt(state.position);\n lineBreaks++;\n state.lineIndent = 0;\n\n while (ch === 0x20/* Space */) {\n state.lineIndent++;\n ch = state.input.charCodeAt(++state.position);\n }\n } else {\n break;\n }\n }\n\n if (checkIndent !== -1 && lineBreaks !== 0 && state.lineIndent < checkIndent) {\n throwWarning(state, 'deficient indentation');\n }\n\n return lineBreaks;\n}\n\nfunction testDocumentSeparator(state) {\n var _position = state.position,\n ch;\n\n ch = state.input.charCodeAt(_position);\n\n // Condition state.position === state.lineStart is tested\n // in parent on each call, for efficiency. No needs to test here again.\n if ((ch === 0x2D/* - */ || ch === 0x2E/* . */) &&\n ch === state.input.charCodeAt(_position + 1) &&\n ch === state.input.charCodeAt(_position + 2)) {\n\n _position += 3;\n\n ch = state.input.charCodeAt(_position);\n\n if (ch === 0 || is_WS_OR_EOL(ch)) {\n return true;\n }\n }\n\n return false;\n}\n\nfunction writeFoldedLines(state, count) {\n if (count === 1) {\n state.result += ' ';\n } else if (count > 1) {\n state.result += common.repeat('\\n', count - 1);\n }\n}\n\n\nfunction readPlainScalar(state, nodeIndent, withinFlowCollection) {\n var preceding,\n following,\n captureStart,\n captureEnd,\n hasPendingContent,\n _line,\n _lineStart,\n _lineIndent,\n _kind = state.kind,\n _result = state.result,\n ch;\n\n ch = state.input.charCodeAt(state.position);\n\n if (is_WS_OR_EOL(ch) ||\n is_FLOW_INDICATOR(ch) ||\n ch === 0x23/* # */ ||\n ch === 0x26/* & */ ||\n ch === 0x2A/* * */ ||\n ch === 0x21/* ! */ ||\n ch === 0x7C/* | */ ||\n ch === 0x3E/* > */ ||\n ch === 0x27/* ' */ ||\n ch === 0x22/* \" */ ||\n ch === 0x25/* % */ ||\n ch === 0x40/* @ */ ||\n ch === 0x60/* ` */) {\n return false;\n }\n\n if (ch === 0x3F/* ? */ || ch === 0x2D/* - */) {\n following = state.input.charCodeAt(state.position + 1);\n\n if (is_WS_OR_EOL(following) ||\n withinFlowCollection && is_FLOW_INDICATOR(following)) {\n return false;\n }\n }\n\n state.kind = 'scalar';\n state.result = '';\n captureStart = captureEnd = state.position;\n hasPendingContent = false;\n\n while (ch !== 0) {\n if (ch === 0x3A/* : */) {\n following = state.input.charCodeAt(state.position + 1);\n\n if (is_WS_OR_EOL(following) ||\n withinFlowCollection && is_FLOW_INDICATOR(following)) {\n break;\n }\n\n } else if (ch === 0x23/* # */) {\n preceding = state.input.charCodeAt(state.position - 1);\n\n if (is_WS_OR_EOL(preceding)) {\n break;\n }\n\n } else if ((state.position === state.lineStart && testDocumentSeparator(state)) ||\n withinFlowCollection && is_FLOW_INDICATOR(ch)) {\n break;\n\n } else if (is_EOL(ch)) {\n _line = state.line;\n _lineStart = state.lineStart;\n _lineIndent = state.lineIndent;\n skipSeparationSpace(state, false, -1);\n\n if (state.lineIndent >= nodeIndent) {\n hasPendingContent = true;\n ch = state.input.charCodeAt(state.position);\n continue;\n } else {\n state.position = captureEnd;\n state.line = _line;\n state.lineStart = _lineStart;\n state.lineIndent = _lineIndent;\n break;\n }\n }\n\n if (hasPendingContent) {\n captureSegment(state, captureStart, captureEnd, false);\n writeFoldedLines(state, state.line - _line);\n captureStart = captureEnd = state.position;\n hasPendingContent = false;\n }\n\n if (!is_WHITE_SPACE(ch)) {\n captureEnd = state.position + 1;\n }\n\n ch = state.input.charCodeAt(++state.position);\n }\n\n captureSegment(state, captureStart, captureEnd, false);\n\n if (state.result) {\n return true;\n }\n\n state.kind = _kind;\n state.result = _result;\n return false;\n}\n\nfunction readSingleQuotedScalar(state, nodeIndent) {\n var ch,\n captureStart, captureEnd;\n\n ch = state.input.charCodeAt(state.position);\n\n if (ch !== 0x27/* ' */) {\n return false;\n }\n\n state.kind = 'scalar';\n state.result = '';\n state.position++;\n captureStart = captureEnd = state.position;\n\n while ((ch = state.input.charCodeAt(state.position)) !== 0) {\n if (ch === 0x27/* ' */) {\n captureSegment(state, captureStart, state.position, true);\n ch = state.input.charCodeAt(++state.position);\n\n if (ch === 0x27/* ' */) {\n captureStart = state.position;\n state.position++;\n captureEnd = state.position;\n } else {\n return true;\n }\n\n } else if (is_EOL(ch)) {\n captureSegment(state, captureStart, captureEnd, true);\n writeFoldedLines(state, skipSeparationSpace(state, false, nodeIndent));\n captureStart = captureEnd = state.position;\n\n } else if (state.position === state.lineStart && testDocumentSeparator(state)) {\n throwError(state, 'unexpected end of the document within a single quoted scalar');\n\n } else {\n state.position++;\n captureEnd = state.position;\n }\n }\n\n throwError(state, 'unexpected end of the stream within a single quoted scalar');\n}\n\nfunction readDoubleQuotedScalar(state, nodeIndent) {\n var captureStart,\n captureEnd,\n hexLength,\n hexResult,\n tmp,\n ch;\n\n ch = state.input.charCodeAt(state.position);\n\n if (ch !== 0x22/* \" */) {\n return false;\n }\n\n state.kind = 'scalar';\n state.result = '';\n state.position++;\n captureStart = captureEnd = state.position;\n\n while ((ch = state.input.charCodeAt(state.position)) !== 0) {\n if (ch === 0x22/* \" */) {\n captureSegment(state, captureStart, state.position, true);\n state.position++;\n return true;\n\n } else if (ch === 0x5C/* \\ */) {\n captureSegment(state, captureStart, state.position, true);\n ch = state.input.charCodeAt(++state.position);\n\n if (is_EOL(ch)) {\n skipSeparationSpace(state, false, nodeIndent);\n\n // TODO: rework to inline fn with no type cast?\n } else if (ch < 256 && simpleEscapeCheck[ch]) {\n state.result += simpleEscapeMap[ch];\n state.position++;\n\n } else if ((tmp = escapedHexLen(ch)) > 0) {\n hexLength = tmp;\n hexResult = 0;\n\n for (; hexLength > 0; hexLength--) {\n ch = state.input.charCodeAt(++state.position);\n\n if ((tmp = fromHexCode(ch)) >= 0) {\n hexResult = (hexResult << 4) + tmp;\n\n } else {\n throwError(state, 'expected hexadecimal character');\n }\n }\n\n state.result += charFromCodepoint(hexResult);\n\n state.position++;\n\n } else {\n throwError(state, 'unknown escape sequence');\n }\n\n captureStart = captureEnd = state.position;\n\n } else if (is_EOL(ch)) {\n captureSegment(state, captureStart, captureEnd, true);\n writeFoldedLines(state, skipSeparationSpace(state, false, nodeIndent));\n captureStart = captureEnd = state.position;\n\n } else if (state.position === state.lineStart && testDocumentSeparator(state)) {\n throwError(state, 'unexpected end of the document within a double quoted scalar');\n\n } else {\n state.position++;\n captureEnd = state.position;\n }\n }\n\n throwError(state, 'unexpected end of the stream within a double quoted scalar');\n}\n\nfunction readFlowCollection(state, nodeIndent) {\n var readNext = true,\n _line,\n _lineStart,\n _pos,\n _tag = state.tag,\n _result,\n _anchor = state.anchor,\n following,\n terminator,\n isPair,\n isExplicitPair,\n isMapping,\n overridableKeys = Object.create(null),\n keyNode,\n keyTag,\n valueNode,\n ch;\n\n ch = state.input.charCodeAt(state.position);\n\n if (ch === 0x5B/* [ */) {\n terminator = 0x5D;/* ] */\n isMapping = false;\n _result = [];\n } else if (ch === 0x7B/* { */) {\n terminator = 0x7D;/* } */\n isMapping = true;\n _result = {};\n } else {\n return false;\n }\n\n if (state.anchor !== null) {\n state.anchorMap[state.anchor] = _result;\n }\n\n ch = state.input.charCodeAt(++state.position);\n\n while (ch !== 0) {\n skipSeparationSpace(state, true, nodeIndent);\n\n ch = state.input.charCodeAt(state.position);\n\n if (ch === terminator) {\n state.position++;\n state.tag = _tag;\n state.anchor = _anchor;\n state.kind = isMapping ? 'mapping' : 'sequence';\n state.result = _result;\n return true;\n } else if (!readNext) {\n throwError(state, 'missed comma between flow collection entries');\n } else if (ch === 0x2C/* , */) {\n // \"flow collection entries can never be completely empty\", as per YAML 1.2, section 7.4\n throwError(state, \"expected the node content, but found ','\");\n }\n\n keyTag = keyNode = valueNode = null;\n isPair = isExplicitPair = false;\n\n if (ch === 0x3F/* ? */) {\n following = state.input.charCodeAt(state.position + 1);\n\n if (is_WS_OR_EOL(following)) {\n isPair = isExplicitPair = true;\n state.position++;\n skipSeparationSpace(state, true, nodeIndent);\n }\n }\n\n _line = state.line; // Save the current line.\n _lineStart = state.lineStart;\n _pos = state.position;\n composeNode(state, nodeIndent, CONTEXT_FLOW_IN, false, true);\n keyTag = state.tag;\n keyNode = state.result;\n skipSeparationSpace(state, true, nodeIndent);\n\n ch = state.input.charCodeAt(state.position);\n\n if ((isExplicitPair || state.line === _line) && ch === 0x3A/* : */) {\n isPair = true;\n ch = state.input.charCodeAt(++state.position);\n skipSeparationSpace(state, true, nodeIndent);\n composeNode(state, nodeIndent, CONTEXT_FLOW_IN, false, true);\n valueNode = state.result;\n }\n\n if (isMapping) {\n storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, valueNode, _line, _lineStart, _pos);\n } else if (isPair) {\n _result.push(storeMappingPair(state, null, overridableKeys, keyTag, keyNode, valueNode, _line, _lineStart, _pos));\n } else {\n _result.push(keyNode);\n }\n\n skipSeparationSpace(state, true, nodeIndent);\n\n ch = state.input.charCodeAt(state.position);\n\n if (ch === 0x2C/* , */) {\n readNext = true;\n ch = state.input.charCodeAt(++state.position);\n } else {\n readNext = false;\n }\n }\n\n throwError(state, 'unexpected end of the stream within a flow collection');\n}\n\nfunction readBlockScalar(state, nodeIndent) {\n var captureStart,\n folding,\n chomping = CHOMPING_CLIP,\n didReadContent = false,\n detectedIndent = false,\n textIndent = nodeIndent,\n emptyLines = 0,\n atMoreIndented = false,\n tmp,\n ch;\n\n ch = state.input.charCodeAt(state.position);\n\n if (ch === 0x7C/* | */) {\n folding = false;\n } else if (ch === 0x3E/* > */) {\n folding = true;\n } else {\n return false;\n }\n\n state.kind = 'scalar';\n state.result = '';\n\n while (ch !== 0) {\n ch = state.input.charCodeAt(++state.position);\n\n if (ch === 0x2B/* + */ || ch === 0x2D/* - */) {\n if (CHOMPING_CLIP === chomping) {\n chomping = (ch === 0x2B/* + */) ? CHOMPING_KEEP : CHOMPING_STRIP;\n } else {\n throwError(state, 'repeat of a chomping mode identifier');\n }\n\n } else if ((tmp = fromDecimalCode(ch)) >= 0) {\n if (tmp === 0) {\n throwError(state, 'bad explicit indentation width of a block scalar; it cannot be less than one');\n } else if (!detectedIndent) {\n textIndent = nodeIndent + tmp - 1;\n detectedIndent = true;\n } else {\n throwError(state, 'repeat of an indentation width identifier');\n }\n\n } else {\n break;\n }\n }\n\n if (is_WHITE_SPACE(ch)) {\n do { ch = state.input.charCodeAt(++state.position); }\n while (is_WHITE_SPACE(ch));\n\n if (ch === 0x23/* # */) {\n do { ch = state.input.charCodeAt(++state.position); }\n while (!is_EOL(ch) && (ch !== 0));\n }\n }\n\n while (ch !== 0) {\n readLineBreak(state);\n state.lineIndent = 0;\n\n ch = state.input.charCodeAt(state.position);\n\n while ((!detectedIndent || state.lineIndent < textIndent) &&\n (ch === 0x20/* Space */)) {\n state.lineIndent++;\n ch = state.input.charCodeAt(++state.position);\n }\n\n if (!detectedIndent && state.lineIndent > textIndent) {\n textIndent = state.lineIndent;\n }\n\n if (is_EOL(ch)) {\n emptyLines++;\n continue;\n }\n\n // End of the scalar.\n if (state.lineIndent < textIndent) {\n\n // Perform the chomping.\n if (chomping === CHOMPING_KEEP) {\n state.result += common.repeat('\\n', didReadContent ? 1 + emptyLines : emptyLines);\n } else if (chomping === CHOMPING_CLIP) {\n if (didReadContent) { // i.e. only if the scalar is not empty.\n state.result += '\\n';\n }\n }\n\n // Break this `while` cycle and go to the funciton's epilogue.\n break;\n }\n\n // Folded style: use fancy rules to handle line breaks.\n if (folding) {\n\n // Lines starting with white space characters (more-indented lines) are not folded.\n if (is_WHITE_SPACE(ch)) {\n atMoreIndented = true;\n // except for the first content line (cf. Example 8.1)\n state.result += common.repeat('\\n', didReadContent ? 1 + emptyLines : emptyLines);\n\n // End of more-indented block.\n } else if (atMoreIndented) {\n atMoreIndented = false;\n state.result += common.repeat('\\n', emptyLines + 1);\n\n // Just one line break - perceive as the same line.\n } else if (emptyLines === 0) {\n if (didReadContent) { // i.e. only if we have already read some scalar content.\n state.result += ' ';\n }\n\n // Several line breaks - perceive as different lines.\n } else {\n state.result += common.repeat('\\n', emptyLines);\n }\n\n // Literal style: just add exact number of line breaks between content lines.\n } else {\n // Keep all line breaks except the header line break.\n state.result += common.repeat('\\n', didReadContent ? 1 + emptyLines : emptyLines);\n }\n\n didReadContent = true;\n detectedIndent = true;\n emptyLines = 0;\n captureStart = state.position;\n\n while (!is_EOL(ch) && (ch !== 0)) {\n ch = state.input.charCodeAt(++state.position);\n }\n\n captureSegment(state, captureStart, state.position, false);\n }\n\n return true;\n}\n\nfunction readBlockSequence(state, nodeIndent) {\n var _line,\n _tag = state.tag,\n _anchor = state.anchor,\n _result = [],\n following,\n detected = false,\n ch;\n\n // there is a leading tab before this token, so it can't be a block sequence/mapping;\n // it can still be flow sequence/mapping or a scalar\n if (state.firstTabInLine !== -1) return false;\n\n if (state.anchor !== null) {\n state.anchorMap[state.anchor] = _result;\n }\n\n ch = state.input.charCodeAt(state.position);\n\n while (ch !== 0) {\n if (state.firstTabInLine !== -1) {\n state.position = state.firstTabInLine;\n throwError(state, 'tab characters must not be used in indentation');\n }\n\n if (ch !== 0x2D/* - */) {\n break;\n }\n\n following = state.input.charCodeAt(state.position + 1);\n\n if (!is_WS_OR_EOL(following)) {\n break;\n }\n\n detected = true;\n state.position++;\n\n if (skipSeparationSpace(state, true, -1)) {\n if (state.lineIndent <= nodeIndent) {\n _result.push(null);\n ch = state.input.charCodeAt(state.position);\n continue;\n }\n }\n\n _line = state.line;\n composeNode(state, nodeIndent, CONTEXT_BLOCK_IN, false, true);\n _result.push(state.result);\n skipSeparationSpace(state, true, -1);\n\n ch = state.input.charCodeAt(state.position);\n\n if ((state.line === _line || state.lineIndent > nodeIndent) && (ch !== 0)) {\n throwError(state, 'bad indentation of a sequence entry');\n } else if (state.lineIndent < nodeIndent) {\n break;\n }\n }\n\n if (detected) {\n state.tag = _tag;\n state.anchor = _anchor;\n state.kind = 'sequence';\n state.result = _result;\n return true;\n }\n return false;\n}\n\nfunction readBlockMapping(state, nodeIndent, flowIndent) {\n var following,\n allowCompact,\n _line,\n _keyLine,\n _keyLineStart,\n _keyPos,\n _tag = state.tag,\n _anchor = state.anchor,\n _result = {},\n overridableKeys = Object.create(null),\n keyTag = null,\n keyNode = null,\n valueNode = null,\n atExplicitKey = false,\n detected = false,\n ch;\n\n // there is a leading tab before this token, so it can't be a block sequence/mapping;\n // it can still be flow sequence/mapping or a scalar\n if (state.firstTabInLine !== -1) return false;\n\n if (state.anchor !== null) {\n state.anchorMap[state.anchor] = _result;\n }\n\n ch = state.input.charCodeAt(state.position);\n\n while (ch !== 0) {\n if (!atExplicitKey && state.firstTabInLine !== -1) {\n state.position = state.firstTabInLine;\n throwError(state, 'tab characters must not be used in indentation');\n }\n\n following = state.input.charCodeAt(state.position + 1);\n _line = state.line; // Save the current line.\n\n //\n // Explicit notation case. There are two separate blocks:\n // first for the key (denoted by \"?\") and second for the value (denoted by \":\")\n //\n if ((ch === 0x3F/* ? */ || ch === 0x3A/* : */) && is_WS_OR_EOL(following)) {\n\n if (ch === 0x3F/* ? */) {\n if (atExplicitKey) {\n storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, null, _keyLine, _keyLineStart, _keyPos);\n keyTag = keyNode = valueNode = null;\n }\n\n detected = true;\n atExplicitKey = true;\n allowCompact = true;\n\n } else if (atExplicitKey) {\n // i.e. 0x3A/* : */ === character after the explicit key.\n atExplicitKey = false;\n allowCompact = true;\n\n } else {\n throwError(state, 'incomplete explicit mapping pair; a key node is missed; or followed by a non-tabulated empty line');\n }\n\n state.position += 1;\n ch = following;\n\n //\n // Implicit notation case. Flow-style node as the key first, then \":\", and the value.\n //\n } else {\n _keyLine = state.line;\n _keyLineStart = state.lineStart;\n _keyPos = state.position;\n\n if (!composeNode(state, flowIndent, CONTEXT_FLOW_OUT, false, true)) {\n // Neither implicit nor explicit notation.\n // Reading is done. Go to the epilogue.\n break;\n }\n\n if (state.line === _line) {\n ch = state.input.charCodeAt(state.position);\n\n while (is_WHITE_SPACE(ch)) {\n ch = state.input.charCodeAt(++state.position);\n }\n\n if (ch === 0x3A/* : */) {\n ch = state.input.charCodeAt(++state.position);\n\n if (!is_WS_OR_EOL(ch)) {\n throwError(state, 'a whitespace character is expected after the key-value separator within a block mapping');\n }\n\n if (atExplicitKey) {\n storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, null, _keyLine, _keyLineStart, _keyPos);\n keyTag = keyNode = valueNode = null;\n }\n\n detected = true;\n atExplicitKey = false;\n allowCompact = false;\n keyTag = state.tag;\n keyNode = state.result;\n\n } else if (detected) {\n throwError(state, 'can not read an implicit mapping pair; a colon is missed');\n\n } else {\n state.tag = _tag;\n state.anchor = _anchor;\n return true; // Keep the result of `composeNode`.\n }\n\n } else if (detected) {\n throwError(state, 'can not read a block mapping entry; a multiline key may not be an implicit key');\n\n } else {\n state.tag = _tag;\n state.anchor = _anchor;\n return true; // Keep the result of `composeNode`.\n }\n }\n\n //\n // Common reading code for both explicit and implicit notations.\n //\n if (state.line === _line || state.lineIndent > nodeIndent) {\n if (atExplicitKey) {\n _keyLine = state.line;\n _keyLineStart = state.lineStart;\n _keyPos = state.position;\n }\n\n if (composeNode(state, nodeIndent, CONTEXT_BLOCK_OUT, true, allowCompact)) {\n if (atExplicitKey) {\n keyNode = state.result;\n } else {\n valueNode = state.result;\n }\n }\n\n if (!atExplicitKey) {\n storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, valueNode, _keyLine, _keyLineStart, _keyPos);\n keyTag = keyNode = valueNode = null;\n }\n\n skipSeparationSpace(state, true, -1);\n ch = state.input.charCodeAt(state.position);\n }\n\n if ((state.line === _line || state.lineIndent > nodeIndent) && (ch !== 0)) {\n throwError(state, 'bad indentation of a mapping entry');\n } else if (state.lineIndent < nodeIndent) {\n break;\n }\n }\n\n //\n // Epilogue.\n //\n\n // Special case: last mapping's node contains only the key in explicit notation.\n if (atExplicitKey) {\n storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, null, _keyLine, _keyLineStart, _keyPos);\n }\n\n // Expose the resulting mapping.\n if (detected) {\n state.tag = _tag;\n state.anchor = _anchor;\n state.kind = 'mapping';\n state.result = _result;\n }\n\n return detected;\n}\n\nfunction readTagProperty(state) {\n var _position,\n isVerbatim = false,\n isNamed = false,\n tagHandle,\n tagName,\n ch;\n\n ch = state.input.charCodeAt(state.position);\n\n if (ch !== 0x21/* ! */) return false;\n\n if (state.tag !== null) {\n throwError(state, 'duplication of a tag property');\n }\n\n ch = state.input.charCodeAt(++state.position);\n\n if (ch === 0x3C/* < */) {\n isVerbatim = true;\n ch = state.input.charCodeAt(++state.position);\n\n } else if (ch === 0x21/* ! */) {\n isNamed = true;\n tagHandle = '!!';\n ch = state.input.charCodeAt(++state.position);\n\n } else {\n tagHandle = '!';\n }\n\n _position = state.position;\n\n if (isVerbatim) {\n do { ch = state.input.charCodeAt(++state.position); }\n while (ch !== 0 && ch !== 0x3E/* > */);\n\n if (state.position < state.length) {\n tagName = state.input.slice(_position, state.position);\n ch = state.input.charCodeAt(++state.position);\n } else {\n throwError(state, 'unexpected end of the stream within a verbatim tag');\n }\n } else {\n while (ch !== 0 && !is_WS_OR_EOL(ch)) {\n\n if (ch === 0x21/* ! */) {\n if (!isNamed) {\n tagHandle = state.input.slice(_position - 1, state.position + 1);\n\n if (!PATTERN_TAG_HANDLE.test(tagHandle)) {\n throwError(state, 'named tag handle cannot contain such characters');\n }\n\n isNamed = true;\n _position = state.position + 1;\n } else {\n throwError(state, 'tag suffix cannot contain exclamation marks');\n }\n }\n\n ch = state.input.charCodeAt(++state.position);\n }\n\n tagName = state.input.slice(_position, state.position);\n\n if (PATTERN_FLOW_INDICATORS.test(tagName)) {\n throwError(state, 'tag suffix cannot contain flow indicator characters');\n }\n }\n\n if (tagName && !PATTERN_TAG_URI.test(tagName)) {\n throwError(state, 'tag name cannot contain such characters: ' + tagName);\n }\n\n try {\n tagName = decodeURIComponent(tagName);\n } catch (err) {\n throwError(state, 'tag name is malformed: ' + tagName);\n }\n\n if (isVerbatim) {\n state.tag = tagName;\n\n } else if (_hasOwnProperty$1.call(state.tagMap, tagHandle)) {\n state.tag = state.tagMap[tagHandle] + tagName;\n\n } else if (tagHandle === '!') {\n state.tag = '!' + tagName;\n\n } else if (tagHandle === '!!') {\n state.tag = 'tag:yaml.org,2002:' + tagName;\n\n } else {\n throwError(state, 'undeclared tag handle \"' + tagHandle + '\"');\n }\n\n return true;\n}\n\nfunction readAnchorProperty(state) {\n var _position,\n ch;\n\n ch = state.input.charCodeAt(state.position);\n\n if (ch !== 0x26/* & */) return false;\n\n if (state.anchor !== null) {\n throwError(state, 'duplication of an anchor property');\n }\n\n ch = state.input.charCodeAt(++state.position);\n _position = state.position;\n\n while (ch !== 0 && !is_WS_OR_EOL(ch) && !is_FLOW_INDICATOR(ch)) {\n ch = state.input.charCodeAt(++state.position);\n }\n\n if (state.position === _position) {\n throwError(state, 'name of an anchor node must contain at least one character');\n }\n\n state.anchor = state.input.slice(_position, state.position);\n return true;\n}\n\nfunction readAlias(state) {\n var _position, alias,\n ch;\n\n ch = state.input.charCodeAt(state.position);\n\n if (ch !== 0x2A/* * */) return false;\n\n ch = state.input.charCodeAt(++state.position);\n _position = state.position;\n\n while (ch !== 0 && !is_WS_OR_EOL(ch) && !is_FLOW_INDICATOR(ch)) {\n ch = state.input.charCodeAt(++state.position);\n }\n\n if (state.position === _position) {\n throwError(state, 'name of an alias node must contain at least one character');\n }\n\n alias = state.input.slice(_position, state.position);\n\n if (!_hasOwnProperty$1.call(state.anchorMap, alias)) {\n throwError(state, 'unidentified alias \"' + alias + '\"');\n }\n\n state.result = state.anchorMap[alias];\n skipSeparationSpace(state, true, -1);\n return true;\n}\n\nfunction composeNode(state, parentIndent, nodeContext, allowToSeek, allowCompact) {\n var allowBlockStyles,\n allowBlockScalars,\n allowBlockCollections,\n indentStatus = 1, // 1: this>parent, 0: this=parent, -1: this parentIndent) {\n indentStatus = 1;\n } else if (state.lineIndent === parentIndent) {\n indentStatus = 0;\n } else if (state.lineIndent < parentIndent) {\n indentStatus = -1;\n }\n }\n }\n\n if (indentStatus === 1) {\n while (readTagProperty(state) || readAnchorProperty(state)) {\n if (skipSeparationSpace(state, true, -1)) {\n atNewLine = true;\n allowBlockCollections = allowBlockStyles;\n\n if (state.lineIndent > parentIndent) {\n indentStatus = 1;\n } else if (state.lineIndent === parentIndent) {\n indentStatus = 0;\n } else if (state.lineIndent < parentIndent) {\n indentStatus = -1;\n }\n } else {\n allowBlockCollections = false;\n }\n }\n }\n\n if (allowBlockCollections) {\n allowBlockCollections = atNewLine || allowCompact;\n }\n\n if (indentStatus === 1 || CONTEXT_BLOCK_OUT === nodeContext) {\n if (CONTEXT_FLOW_IN === nodeContext || CONTEXT_FLOW_OUT === nodeContext) {\n flowIndent = parentIndent;\n } else {\n flowIndent = parentIndent + 1;\n }\n\n blockIndent = state.position - state.lineStart;\n\n if (indentStatus === 1) {\n if (allowBlockCollections &&\n (readBlockSequence(state, blockIndent) ||\n readBlockMapping(state, blockIndent, flowIndent)) ||\n readFlowCollection(state, flowIndent)) {\n hasContent = true;\n } else {\n if ((allowBlockScalars && readBlockScalar(state, flowIndent)) ||\n readSingleQuotedScalar(state, flowIndent) ||\n readDoubleQuotedScalar(state, flowIndent)) {\n hasContent = true;\n\n } else if (readAlias(state)) {\n hasContent = true;\n\n if (state.tag !== null || state.anchor !== null) {\n throwError(state, 'alias node should not have any properties');\n }\n\n } else if (readPlainScalar(state, flowIndent, CONTEXT_FLOW_IN === nodeContext)) {\n hasContent = true;\n\n if (state.tag === null) {\n state.tag = '?';\n }\n }\n\n if (state.anchor !== null) {\n state.anchorMap[state.anchor] = state.result;\n }\n }\n } else if (indentStatus === 0) {\n // Special case: block sequences are allowed to have same indentation level as the parent.\n // http://www.yaml.org/spec/1.2/spec.html#id2799784\n hasContent = allowBlockCollections && readBlockSequence(state, blockIndent);\n }\n }\n\n if (state.tag === null) {\n if (state.anchor !== null) {\n state.anchorMap[state.anchor] = state.result;\n }\n\n } else if (state.tag === '?') {\n // Implicit resolving is not allowed for non-scalar types, and '?'\n // non-specific tag is only automatically assigned to plain scalars.\n //\n // We only need to check kind conformity in case user explicitly assigns '?'\n // tag, for example like this: \"! [0]\"\n //\n if (state.result !== null && state.kind !== 'scalar') {\n throwError(state, 'unacceptable node kind for ! tag; it should be \"scalar\", not \"' + state.kind + '\"');\n }\n\n for (typeIndex = 0, typeQuantity = state.implicitTypes.length; typeIndex < typeQuantity; typeIndex += 1) {\n type = state.implicitTypes[typeIndex];\n\n if (type.resolve(state.result)) { // `state.result` updated in resolver if matched\n state.result = type.construct(state.result);\n state.tag = type.tag;\n if (state.anchor !== null) {\n state.anchorMap[state.anchor] = state.result;\n }\n break;\n }\n }\n } else if (state.tag !== '!') {\n if (_hasOwnProperty$1.call(state.typeMap[state.kind || 'fallback'], state.tag)) {\n type = state.typeMap[state.kind || 'fallback'][state.tag];\n } else {\n // looking for multi type\n type = null;\n typeList = state.typeMap.multi[state.kind || 'fallback'];\n\n for (typeIndex = 0, typeQuantity = typeList.length; typeIndex < typeQuantity; typeIndex += 1) {\n if (state.tag.slice(0, typeList[typeIndex].tag.length) === typeList[typeIndex].tag) {\n type = typeList[typeIndex];\n break;\n }\n }\n }\n\n if (!type) {\n throwError(state, 'unknown tag !<' + state.tag + '>');\n }\n\n if (state.result !== null && type.kind !== state.kind) {\n throwError(state, 'unacceptable node kind for !<' + state.tag + '> tag; it should be \"' + type.kind + '\", not \"' + state.kind + '\"');\n }\n\n if (!type.resolve(state.result, state.tag)) { // `state.result` updated in resolver if matched\n throwError(state, 'cannot resolve a node with !<' + state.tag + '> explicit tag');\n } else {\n state.result = type.construct(state.result, state.tag);\n if (state.anchor !== null) {\n state.anchorMap[state.anchor] = state.result;\n }\n }\n }\n\n if (state.listener !== null) {\n state.listener('close', state);\n }\n return state.tag !== null || state.anchor !== null || hasContent;\n}\n\nfunction readDocument(state) {\n var documentStart = state.position,\n _position,\n directiveName,\n directiveArgs,\n hasDirectives = false,\n ch;\n\n state.version = null;\n state.checkLineBreaks = state.legacy;\n state.tagMap = Object.create(null);\n state.anchorMap = Object.create(null);\n\n while ((ch = state.input.charCodeAt(state.position)) !== 0) {\n skipSeparationSpace(state, true, -1);\n\n ch = state.input.charCodeAt(state.position);\n\n if (state.lineIndent > 0 || ch !== 0x25/* % */) {\n break;\n }\n\n hasDirectives = true;\n ch = state.input.charCodeAt(++state.position);\n _position = state.position;\n\n while (ch !== 0 && !is_WS_OR_EOL(ch)) {\n ch = state.input.charCodeAt(++state.position);\n }\n\n directiveName = state.input.slice(_position, state.position);\n directiveArgs = [];\n\n if (directiveName.length < 1) {\n throwError(state, 'directive name must not be less than one character in length');\n }\n\n while (ch !== 0) {\n while (is_WHITE_SPACE(ch)) {\n ch = state.input.charCodeAt(++state.position);\n }\n\n if (ch === 0x23/* # */) {\n do { ch = state.input.charCodeAt(++state.position); }\n while (ch !== 0 && !is_EOL(ch));\n break;\n }\n\n if (is_EOL(ch)) break;\n\n _position = state.position;\n\n while (ch !== 0 && !is_WS_OR_EOL(ch)) {\n ch = state.input.charCodeAt(++state.position);\n }\n\n directiveArgs.push(state.input.slice(_position, state.position));\n }\n\n if (ch !== 0) readLineBreak(state);\n\n if (_hasOwnProperty$1.call(directiveHandlers, directiveName)) {\n directiveHandlers[directiveName](state, directiveName, directiveArgs);\n } else {\n throwWarning(state, 'unknown document directive \"' + directiveName + '\"');\n }\n }\n\n skipSeparationSpace(state, true, -1);\n\n if (state.lineIndent === 0 &&\n state.input.charCodeAt(state.position) === 0x2D/* - */ &&\n state.input.charCodeAt(state.position + 1) === 0x2D/* - */ &&\n state.input.charCodeAt(state.position + 2) === 0x2D/* - */) {\n state.position += 3;\n skipSeparationSpace(state, true, -1);\n\n } else if (hasDirectives) {\n throwError(state, 'directives end mark is expected');\n }\n\n composeNode(state, state.lineIndent - 1, CONTEXT_BLOCK_OUT, false, true);\n skipSeparationSpace(state, true, -1);\n\n if (state.checkLineBreaks &&\n PATTERN_NON_ASCII_LINE_BREAKS.test(state.input.slice(documentStart, state.position))) {\n throwWarning(state, 'non-ASCII line breaks are interpreted as content');\n }\n\n state.documents.push(state.result);\n\n if (state.position === state.lineStart && testDocumentSeparator(state)) {\n\n if (state.input.charCodeAt(state.position) === 0x2E/* . */) {\n state.position += 3;\n skipSeparationSpace(state, true, -1);\n }\n return;\n }\n\n if (state.position < (state.length - 1)) {\n throwError(state, 'end of the stream or a document separator is expected');\n } else {\n return;\n }\n}\n\n\nfunction loadDocuments(input, options) {\n input = String(input);\n options = options || {};\n\n if (input.length !== 0) {\n\n // Add tailing `\\n` if not exists\n if (input.charCodeAt(input.length - 1) !== 0x0A/* LF */ &&\n input.charCodeAt(input.length - 1) !== 0x0D/* CR */) {\n input += '\\n';\n }\n\n // Strip BOM\n if (input.charCodeAt(0) === 0xFEFF) {\n input = input.slice(1);\n }\n }\n\n var state = new State$1(input, options);\n\n var nullpos = input.indexOf('\\0');\n\n if (nullpos !== -1) {\n state.position = nullpos;\n throwError(state, 'null byte is not allowed in input');\n }\n\n // Use 0 as string terminator. That significantly simplifies bounds check.\n state.input += '\\0';\n\n while (state.input.charCodeAt(state.position) === 0x20/* Space */) {\n state.lineIndent += 1;\n state.position += 1;\n }\n\n while (state.position < (state.length - 1)) {\n readDocument(state);\n }\n\n return state.documents;\n}\n\n\nfunction loadAll$1(input, iterator, options) {\n if (iterator !== null && typeof iterator === 'object' && typeof options === 'undefined') {\n options = iterator;\n iterator = null;\n }\n\n var documents = loadDocuments(input, options);\n\n if (typeof iterator !== 'function') {\n return documents;\n }\n\n for (var index = 0, length = documents.length; index < length; index += 1) {\n iterator(documents[index]);\n }\n}\n\n\nfunction load$1(input, options) {\n var documents = loadDocuments(input, options);\n\n if (documents.length === 0) {\n /*eslint-disable no-undefined*/\n return undefined;\n } else if (documents.length === 1) {\n return documents[0];\n }\n throw new exception('expected a single document in the stream, but found more');\n}\n\n\nvar loadAll_1 = loadAll$1;\nvar load_1 = load$1;\n\nvar loader = {\n\tloadAll: loadAll_1,\n\tload: load_1\n};\n\n/*eslint-disable no-use-before-define*/\n\n\n\n\n\nvar _toString = Object.prototype.toString;\nvar _hasOwnProperty = Object.prototype.hasOwnProperty;\n\nvar CHAR_BOM = 0xFEFF;\nvar CHAR_TAB = 0x09; /* Tab */\nvar CHAR_LINE_FEED = 0x0A; /* LF */\nvar CHAR_CARRIAGE_RETURN = 0x0D; /* CR */\nvar CHAR_SPACE = 0x20; /* Space */\nvar CHAR_EXCLAMATION = 0x21; /* ! */\nvar CHAR_DOUBLE_QUOTE = 0x22; /* \" */\nvar CHAR_SHARP = 0x23; /* # */\nvar CHAR_PERCENT = 0x25; /* % */\nvar CHAR_AMPERSAND = 0x26; /* & */\nvar CHAR_SINGLE_QUOTE = 0x27; /* ' */\nvar CHAR_ASTERISK = 0x2A; /* * */\nvar CHAR_COMMA = 0x2C; /* , */\nvar CHAR_MINUS = 0x2D; /* - */\nvar CHAR_COLON = 0x3A; /* : */\nvar CHAR_EQUALS = 0x3D; /* = */\nvar CHAR_GREATER_THAN = 0x3E; /* > */\nvar CHAR_QUESTION = 0x3F; /* ? */\nvar CHAR_COMMERCIAL_AT = 0x40; /* @ */\nvar CHAR_LEFT_SQUARE_BRACKET = 0x5B; /* [ */\nvar CHAR_RIGHT_SQUARE_BRACKET = 0x5D; /* ] */\nvar CHAR_GRAVE_ACCENT = 0x60; /* ` */\nvar CHAR_LEFT_CURLY_BRACKET = 0x7B; /* { */\nvar CHAR_VERTICAL_LINE = 0x7C; /* | */\nvar CHAR_RIGHT_CURLY_BRACKET = 0x7D; /* } */\n\nvar ESCAPE_SEQUENCES = {};\n\nESCAPE_SEQUENCES[0x00] = '\\\\0';\nESCAPE_SEQUENCES[0x07] = '\\\\a';\nESCAPE_SEQUENCES[0x08] = '\\\\b';\nESCAPE_SEQUENCES[0x09] = '\\\\t';\nESCAPE_SEQUENCES[0x0A] = '\\\\n';\nESCAPE_SEQUENCES[0x0B] = '\\\\v';\nESCAPE_SEQUENCES[0x0C] = '\\\\f';\nESCAPE_SEQUENCES[0x0D] = '\\\\r';\nESCAPE_SEQUENCES[0x1B] = '\\\\e';\nESCAPE_SEQUENCES[0x22] = '\\\\\"';\nESCAPE_SEQUENCES[0x5C] = '\\\\\\\\';\nESCAPE_SEQUENCES[0x85] = '\\\\N';\nESCAPE_SEQUENCES[0xA0] = '\\\\_';\nESCAPE_SEQUENCES[0x2028] = '\\\\L';\nESCAPE_SEQUENCES[0x2029] = '\\\\P';\n\nvar DEPRECATED_BOOLEANS_SYNTAX = [\n 'y', 'Y', 'yes', 'Yes', 'YES', 'on', 'On', 'ON',\n 'n', 'N', 'no', 'No', 'NO', 'off', 'Off', 'OFF'\n];\n\nvar DEPRECATED_BASE60_SYNTAX = /^[-+]?[0-9_]+(?::[0-9_]+)+(?:\\.[0-9_]*)?$/;\n\nfunction compileStyleMap(schema, map) {\n var result, keys, index, length, tag, style, type;\n\n if (map === null) return {};\n\n result = {};\n keys = Object.keys(map);\n\n for (index = 0, length = keys.length; index < length; index += 1) {\n tag = keys[index];\n style = String(map[tag]);\n\n if (tag.slice(0, 2) === '!!') {\n tag = 'tag:yaml.org,2002:' + tag.slice(2);\n }\n type = schema.compiledTypeMap['fallback'][tag];\n\n if (type && _hasOwnProperty.call(type.styleAliases, style)) {\n style = type.styleAliases[style];\n }\n\n result[tag] = style;\n }\n\n return result;\n}\n\nfunction encodeHex(character) {\n var string, handle, length;\n\n string = character.toString(16).toUpperCase();\n\n if (character <= 0xFF) {\n handle = 'x';\n length = 2;\n } else if (character <= 0xFFFF) {\n handle = 'u';\n length = 4;\n } else if (character <= 0xFFFFFFFF) {\n handle = 'U';\n length = 8;\n } else {\n throw new exception('code point within a string may not be greater than 0xFFFFFFFF');\n }\n\n return '\\\\' + handle + common.repeat('0', length - string.length) + string;\n}\n\n\nvar QUOTING_TYPE_SINGLE = 1,\n QUOTING_TYPE_DOUBLE = 2;\n\nfunction State(options) {\n this.schema = options['schema'] || _default;\n this.indent = Math.max(1, (options['indent'] || 2));\n this.noArrayIndent = options['noArrayIndent'] || false;\n this.skipInvalid = options['skipInvalid'] || false;\n this.flowLevel = (common.isNothing(options['flowLevel']) ? -1 : options['flowLevel']);\n this.styleMap = compileStyleMap(this.schema, options['styles'] || null);\n this.sortKeys = options['sortKeys'] || false;\n this.lineWidth = options['lineWidth'] || 80;\n this.noRefs = options['noRefs'] || false;\n this.noCompatMode = options['noCompatMode'] || false;\n this.condenseFlow = options['condenseFlow'] || false;\n this.quotingType = options['quotingType'] === '\"' ? QUOTING_TYPE_DOUBLE : QUOTING_TYPE_SINGLE;\n this.forceQuotes = options['forceQuotes'] || false;\n this.replacer = typeof options['replacer'] === 'function' ? options['replacer'] : null;\n\n this.implicitTypes = this.schema.compiledImplicit;\n this.explicitTypes = this.schema.compiledExplicit;\n\n this.tag = null;\n this.result = '';\n\n this.duplicates = [];\n this.usedDuplicates = null;\n}\n\n// Indents every line in a string. Empty lines (\\n only) are not indented.\nfunction indentString(string, spaces) {\n var ind = common.repeat(' ', spaces),\n position = 0,\n next = -1,\n result = '',\n line,\n length = string.length;\n\n while (position < length) {\n next = string.indexOf('\\n', position);\n if (next === -1) {\n line = string.slice(position);\n position = length;\n } else {\n line = string.slice(position, next + 1);\n position = next + 1;\n }\n\n if (line.length && line !== '\\n') result += ind;\n\n result += line;\n }\n\n return result;\n}\n\nfunction generateNextLine(state, level) {\n return '\\n' + common.repeat(' ', state.indent * level);\n}\n\nfunction testImplicitResolving(state, str) {\n var index, length, type;\n\n for (index = 0, length = state.implicitTypes.length; index < length; index += 1) {\n type = state.implicitTypes[index];\n\n if (type.resolve(str)) {\n return true;\n }\n }\n\n return false;\n}\n\n// [33] s-white ::= s-space | s-tab\nfunction isWhitespace(c) {\n return c === CHAR_SPACE || c === CHAR_TAB;\n}\n\n// Returns true if the character can be printed without escaping.\n// From YAML 1.2: \"any allowed characters known to be non-printable\n// should also be escaped. [However,] This isn\u2019t mandatory\"\n// Derived from nb-char - \\t - #x85 - #xA0 - #x2028 - #x2029.\nfunction isPrintable(c) {\n return (0x00020 <= c && c <= 0x00007E)\n || ((0x000A1 <= c && c <= 0x00D7FF) && c !== 0x2028 && c !== 0x2029)\n || ((0x0E000 <= c && c <= 0x00FFFD) && c !== CHAR_BOM)\n || (0x10000 <= c && c <= 0x10FFFF);\n}\n\n// [34] ns-char ::= nb-char - s-white\n// [27] nb-char ::= c-printable - b-char - c-byte-order-mark\n// [26] b-char ::= b-line-feed | b-carriage-return\n// Including s-white (for some reason, examples doesn't match specs in this aspect)\n// ns-char ::= c-printable - b-line-feed - b-carriage-return - c-byte-order-mark\nfunction isNsCharOrWhitespace(c) {\n return isPrintable(c)\n && c !== CHAR_BOM\n // - b-char\n && c !== CHAR_CARRIAGE_RETURN\n && c !== CHAR_LINE_FEED;\n}\n\n// [127] ns-plain-safe(c) ::= c = flow-out \u21D2 ns-plain-safe-out\n// c = flow-in \u21D2 ns-plain-safe-in\n// c = block-key \u21D2 ns-plain-safe-out\n// c = flow-key \u21D2 ns-plain-safe-in\n// [128] ns-plain-safe-out ::= ns-char\n// [129] ns-plain-safe-in ::= ns-char - c-flow-indicator\n// [130] ns-plain-char(c) ::= ( ns-plain-safe(c) - \u201C:\u201D - \u201C#\u201D )\n// | ( /* An ns-char preceding */ \u201C#\u201D )\n// | ( \u201C:\u201D /* Followed by an ns-plain-safe(c) */ )\nfunction isPlainSafe(c, prev, inblock) {\n var cIsNsCharOrWhitespace = isNsCharOrWhitespace(c);\n var cIsNsChar = cIsNsCharOrWhitespace && !isWhitespace(c);\n return (\n // ns-plain-safe\n inblock ? // c = flow-in\n cIsNsCharOrWhitespace\n : cIsNsCharOrWhitespace\n // - c-flow-indicator\n && c !== CHAR_COMMA\n && c !== CHAR_LEFT_SQUARE_BRACKET\n && c !== CHAR_RIGHT_SQUARE_BRACKET\n && c !== CHAR_LEFT_CURLY_BRACKET\n && c !== CHAR_RIGHT_CURLY_BRACKET\n )\n // ns-plain-char\n && c !== CHAR_SHARP // false on '#'\n && !(prev === CHAR_COLON && !cIsNsChar) // false on ': '\n || (isNsCharOrWhitespace(prev) && !isWhitespace(prev) && c === CHAR_SHARP) // change to true on '[^ ]#'\n || (prev === CHAR_COLON && cIsNsChar); // change to true on ':[^ ]'\n}\n\n// Simplified test for values allowed as the first character in plain style.\nfunction isPlainSafeFirst(c) {\n // Uses a subset of ns-char - c-indicator\n // where ns-char = nb-char - s-white.\n // No support of ( ( \u201C?\u201D | \u201C:\u201D | \u201C-\u201D ) /* Followed by an ns-plain-safe(c)) */ ) part\n return isPrintable(c) && c !== CHAR_BOM\n && !isWhitespace(c) // - s-white\n // - (c-indicator ::=\n // \u201C-\u201D | \u201C?\u201D | \u201C:\u201D | \u201C,\u201D | \u201C[\u201D | \u201C]\u201D | \u201C{\u201D | \u201C}\u201D\n && c !== CHAR_MINUS\n && c !== CHAR_QUESTION\n && c !== CHAR_COLON\n && c !== CHAR_COMMA\n && c !== CHAR_LEFT_SQUARE_BRACKET\n && c !== CHAR_RIGHT_SQUARE_BRACKET\n && c !== CHAR_LEFT_CURLY_BRACKET\n && c !== CHAR_RIGHT_CURLY_BRACKET\n // | \u201C#\u201D | \u201C&\u201D | \u201C*\u201D | \u201C!\u201D | \u201C|\u201D | \u201C=\u201D | \u201C>\u201D | \u201C'\u201D | \u201C\"\u201D\n && c !== CHAR_SHARP\n && c !== CHAR_AMPERSAND\n && c !== CHAR_ASTERISK\n && c !== CHAR_EXCLAMATION\n && c !== CHAR_VERTICAL_LINE\n && c !== CHAR_EQUALS\n && c !== CHAR_GREATER_THAN\n && c !== CHAR_SINGLE_QUOTE\n && c !== CHAR_DOUBLE_QUOTE\n // | \u201C%\u201D | \u201C@\u201D | \u201C`\u201D)\n && c !== CHAR_PERCENT\n && c !== CHAR_COMMERCIAL_AT\n && c !== CHAR_GRAVE_ACCENT;\n}\n\n// Simplified test for values allowed as the last character in plain style.\nfunction isPlainSafeLast(c) {\n // just not whitespace or colon, it will be checked to be plain character later\n return !isWhitespace(c) && c !== CHAR_COLON;\n}\n\n// Same as 'string'.codePointAt(pos), but works in older browsers.\nfunction codePointAt(string, pos) {\n var first = string.charCodeAt(pos), second;\n if (first >= 0xD800 && first <= 0xDBFF && pos + 1 < string.length) {\n second = string.charCodeAt(pos + 1);\n if (second >= 0xDC00 && second <= 0xDFFF) {\n // https://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae\n return (first - 0xD800) * 0x400 + second - 0xDC00 + 0x10000;\n }\n }\n return first;\n}\n\n// Determines whether block indentation indicator is required.\nfunction needIndentIndicator(string) {\n var leadingSpaceRe = /^\\n* /;\n return leadingSpaceRe.test(string);\n}\n\nvar STYLE_PLAIN = 1,\n STYLE_SINGLE = 2,\n STYLE_LITERAL = 3,\n STYLE_FOLDED = 4,\n STYLE_DOUBLE = 5;\n\n// Determines which scalar styles are possible and returns the preferred style.\n// lineWidth = -1 => no limit.\n// Pre-conditions: str.length > 0.\n// Post-conditions:\n// STYLE_PLAIN or STYLE_SINGLE => no \\n are in the string.\n// STYLE_LITERAL => no lines are suitable for folding (or lineWidth is -1).\n// STYLE_FOLDED => a line > lineWidth and can be folded (and lineWidth != -1).\nfunction chooseScalarStyle(string, singleLineOnly, indentPerLevel, lineWidth,\n testAmbiguousType, quotingType, forceQuotes, inblock) {\n\n var i;\n var char = 0;\n var prevChar = null;\n var hasLineBreak = false;\n var hasFoldableLine = false; // only checked if shouldTrackWidth\n var shouldTrackWidth = lineWidth !== -1;\n var previousLineBreak = -1; // count the first line correctly\n var plain = isPlainSafeFirst(codePointAt(string, 0))\n && isPlainSafeLast(codePointAt(string, string.length - 1));\n\n if (singleLineOnly || forceQuotes) {\n // Case: no block styles.\n // Check for disallowed characters to rule out plain and single.\n for (i = 0; i < string.length; char >= 0x10000 ? i += 2 : i++) {\n char = codePointAt(string, i);\n if (!isPrintable(char)) {\n return STYLE_DOUBLE;\n }\n plain = plain && isPlainSafe(char, prevChar, inblock);\n prevChar = char;\n }\n } else {\n // Case: block styles permitted.\n for (i = 0; i < string.length; char >= 0x10000 ? i += 2 : i++) {\n char = codePointAt(string, i);\n if (char === CHAR_LINE_FEED) {\n hasLineBreak = true;\n // Check if any line can be folded.\n if (shouldTrackWidth) {\n hasFoldableLine = hasFoldableLine ||\n // Foldable line = too long, and not more-indented.\n (i - previousLineBreak - 1 > lineWidth &&\n string[previousLineBreak + 1] !== ' ');\n previousLineBreak = i;\n }\n } else if (!isPrintable(char)) {\n return STYLE_DOUBLE;\n }\n plain = plain && isPlainSafe(char, prevChar, inblock);\n prevChar = char;\n }\n // in case the end is missing a \\n\n hasFoldableLine = hasFoldableLine || (shouldTrackWidth &&\n (i - previousLineBreak - 1 > lineWidth &&\n string[previousLineBreak + 1] !== ' '));\n }\n // Although every style can represent \\n without escaping, prefer block styles\n // for multiline, since they're more readable and they don't add empty lines.\n // Also prefer folding a super-long line.\n if (!hasLineBreak && !hasFoldableLine) {\n // Strings interpretable as another type have to be quoted;\n // e.g. the string 'true' vs. the boolean true.\n if (plain && !forceQuotes && !testAmbiguousType(string)) {\n return STYLE_PLAIN;\n }\n return quotingType === QUOTING_TYPE_DOUBLE ? STYLE_DOUBLE : STYLE_SINGLE;\n }\n // Edge case: block indentation indicator can only have one digit.\n if (indentPerLevel > 9 && needIndentIndicator(string)) {\n return STYLE_DOUBLE;\n }\n // At this point we know block styles are valid.\n // Prefer literal style unless we want to fold.\n if (!forceQuotes) {\n return hasFoldableLine ? STYLE_FOLDED : STYLE_LITERAL;\n }\n return quotingType === QUOTING_TYPE_DOUBLE ? STYLE_DOUBLE : STYLE_SINGLE;\n}\n\n// Note: line breaking/folding is implemented for only the folded style.\n// NB. We drop the last trailing newline (if any) of a returned block scalar\n// since the dumper adds its own newline. This always works:\n// \u2022 No ending newline => unaffected; already using strip \"-\" chomping.\n// \u2022 Ending newline => removed then restored.\n// Importantly, this keeps the \"+\" chomp indicator from gaining an extra line.\nfunction writeScalar(state, string, level, iskey, inblock) {\n state.dump = (function () {\n if (string.length === 0) {\n return state.quotingType === QUOTING_TYPE_DOUBLE ? '\"\"' : \"''\";\n }\n if (!state.noCompatMode) {\n if (DEPRECATED_BOOLEANS_SYNTAX.indexOf(string) !== -1 || DEPRECATED_BASE60_SYNTAX.test(string)) {\n return state.quotingType === QUOTING_TYPE_DOUBLE ? ('\"' + string + '\"') : (\"'\" + string + \"'\");\n }\n }\n\n var indent = state.indent * Math.max(1, level); // no 0-indent scalars\n // As indentation gets deeper, let the width decrease monotonically\n // to the lower bound min(state.lineWidth, 40).\n // Note that this implies\n // state.lineWidth \u2264 40 + state.indent: width is fixed at the lower bound.\n // state.lineWidth > 40 + state.indent: width decreases until the lower bound.\n // This behaves better than a constant minimum width which disallows narrower options,\n // or an indent threshold which causes the width to suddenly increase.\n var lineWidth = state.lineWidth === -1\n ? -1 : Math.max(Math.min(state.lineWidth, 40), state.lineWidth - indent);\n\n // Without knowing if keys are implicit/explicit, assume implicit for safety.\n var singleLineOnly = iskey\n // No block styles in flow mode.\n || (state.flowLevel > -1 && level >= state.flowLevel);\n function testAmbiguity(string) {\n return testImplicitResolving(state, string);\n }\n\n switch (chooseScalarStyle(string, singleLineOnly, state.indent, lineWidth,\n testAmbiguity, state.quotingType, state.forceQuotes && !iskey, inblock)) {\n\n case STYLE_PLAIN:\n return string;\n case STYLE_SINGLE:\n return \"'\" + string.replace(/'/g, \"''\") + \"'\";\n case STYLE_LITERAL:\n return '|' + blockHeader(string, state.indent)\n + dropEndingNewline(indentString(string, indent));\n case STYLE_FOLDED:\n return '>' + blockHeader(string, state.indent)\n + dropEndingNewline(indentString(foldString(string, lineWidth), indent));\n case STYLE_DOUBLE:\n return '\"' + escapeString(string) + '\"';\n default:\n throw new exception('impossible error: invalid scalar style');\n }\n }());\n}\n\n// Pre-conditions: string is valid for a block scalar, 1 <= indentPerLevel <= 9.\nfunction blockHeader(string, indentPerLevel) {\n var indentIndicator = needIndentIndicator(string) ? String(indentPerLevel) : '';\n\n // note the special case: the string '\\n' counts as a \"trailing\" empty line.\n var clip = string[string.length - 1] === '\\n';\n var keep = clip && (string[string.length - 2] === '\\n' || string === '\\n');\n var chomp = keep ? '+' : (clip ? '' : '-');\n\n return indentIndicator + chomp + '\\n';\n}\n\n// (See the note for writeScalar.)\nfunction dropEndingNewline(string) {\n return string[string.length - 1] === '\\n' ? string.slice(0, -1) : string;\n}\n\n// Note: a long line without a suitable break point will exceed the width limit.\n// Pre-conditions: every char in str isPrintable, str.length > 0, width > 0.\nfunction foldString(string, width) {\n // In folded style, $k$ consecutive newlines output as $k+1$ newlines\u2014\n // unless they're before or after a more-indented line, or at the very\n // beginning or end, in which case $k$ maps to $k$.\n // Therefore, parse each chunk as newline(s) followed by a content line.\n var lineRe = /(\\n+)([^\\n]*)/g;\n\n // first line (possibly an empty line)\n var result = (function () {\n var nextLF = string.indexOf('\\n');\n nextLF = nextLF !== -1 ? nextLF : string.length;\n lineRe.lastIndex = nextLF;\n return foldLine(string.slice(0, nextLF), width);\n }());\n // If we haven't reached the first content line yet, don't add an extra \\n.\n var prevMoreIndented = string[0] === '\\n' || string[0] === ' ';\n var moreIndented;\n\n // rest of the lines\n var match;\n while ((match = lineRe.exec(string))) {\n var prefix = match[1], line = match[2];\n moreIndented = (line[0] === ' ');\n result += prefix\n + (!prevMoreIndented && !moreIndented && line !== ''\n ? '\\n' : '')\n + foldLine(line, width);\n prevMoreIndented = moreIndented;\n }\n\n return result;\n}\n\n// Greedy line breaking.\n// Picks the longest line under the limit each time,\n// otherwise settles for the shortest line over the limit.\n// NB. More-indented lines *cannot* be folded, as that would add an extra \\n.\nfunction foldLine(line, width) {\n if (line === '' || line[0] === ' ') return line;\n\n // Since a more-indented line adds a \\n, breaks can't be followed by a space.\n var breakRe = / [^ ]/g; // note: the match index will always be <= length-2.\n var match;\n // start is an inclusive index. end, curr, and next are exclusive.\n var start = 0, end, curr = 0, next = 0;\n var result = '';\n\n // Invariants: 0 <= start <= length-1.\n // 0 <= curr <= next <= max(0, length-2). curr - start <= width.\n // Inside the loop:\n // A match implies length >= 2, so curr and next are <= length-2.\n while ((match = breakRe.exec(line))) {\n next = match.index;\n // maintain invariant: curr - start <= width\n if (next - start > width) {\n end = (curr > start) ? curr : next; // derive end <= length-2\n result += '\\n' + line.slice(start, end);\n // skip the space that was output as \\n\n start = end + 1; // derive start <= length-1\n }\n curr = next;\n }\n\n // By the invariants, start <= length-1, so there is something left over.\n // It is either the whole string or a part starting from non-whitespace.\n result += '\\n';\n // Insert a break if the remainder is too long and there is a break available.\n if (line.length - start > width && curr > start) {\n result += line.slice(start, curr) + '\\n' + line.slice(curr + 1);\n } else {\n result += line.slice(start);\n }\n\n return result.slice(1); // drop extra \\n joiner\n}\n\n// Escapes a double-quoted string.\nfunction escapeString(string) {\n var result = '';\n var char = 0;\n var escapeSeq;\n\n for (var i = 0; i < string.length; char >= 0x10000 ? i += 2 : i++) {\n char = codePointAt(string, i);\n escapeSeq = ESCAPE_SEQUENCES[char];\n\n if (!escapeSeq && isPrintable(char)) {\n result += string[i];\n if (char >= 0x10000) result += string[i + 1];\n } else {\n result += escapeSeq || encodeHex(char);\n }\n }\n\n return result;\n}\n\nfunction writeFlowSequence(state, level, object) {\n var _result = '',\n _tag = state.tag,\n index,\n length,\n value;\n\n for (index = 0, length = object.length; index < length; index += 1) {\n value = object[index];\n\n if (state.replacer) {\n value = state.replacer.call(object, String(index), value);\n }\n\n // Write only valid elements, put null instead of invalid elements.\n if (writeNode(state, level, value, false, false) ||\n (typeof value === 'undefined' &&\n writeNode(state, level, null, false, false))) {\n\n if (_result !== '') _result += ',' + (!state.condenseFlow ? ' ' : '');\n _result += state.dump;\n }\n }\n\n state.tag = _tag;\n state.dump = '[' + _result + ']';\n}\n\nfunction writeBlockSequence(state, level, object, compact) {\n var _result = '',\n _tag = state.tag,\n index,\n length,\n value;\n\n for (index = 0, length = object.length; index < length; index += 1) {\n value = object[index];\n\n if (state.replacer) {\n value = state.replacer.call(object, String(index), value);\n }\n\n // Write only valid elements, put null instead of invalid elements.\n if (writeNode(state, level + 1, value, true, true, false, true) ||\n (typeof value === 'undefined' &&\n writeNode(state, level + 1, null, true, true, false, true))) {\n\n if (!compact || _result !== '') {\n _result += generateNextLine(state, level);\n }\n\n if (state.dump && CHAR_LINE_FEED === state.dump.charCodeAt(0)) {\n _result += '-';\n } else {\n _result += '- ';\n }\n\n _result += state.dump;\n }\n }\n\n state.tag = _tag;\n state.dump = _result || '[]'; // Empty sequence if no valid values.\n}\n\nfunction writeFlowMapping(state, level, object) {\n var _result = '',\n _tag = state.tag,\n objectKeyList = Object.keys(object),\n index,\n length,\n objectKey,\n objectValue,\n pairBuffer;\n\n for (index = 0, length = objectKeyList.length; index < length; index += 1) {\n\n pairBuffer = '';\n if (_result !== '') pairBuffer += ', ';\n\n if (state.condenseFlow) pairBuffer += '\"';\n\n objectKey = objectKeyList[index];\n objectValue = object[objectKey];\n\n if (state.replacer) {\n objectValue = state.replacer.call(object, objectKey, objectValue);\n }\n\n if (!writeNode(state, level, objectKey, false, false)) {\n continue; // Skip this pair because of invalid key;\n }\n\n if (state.dump.length > 1024) pairBuffer += '? ';\n\n pairBuffer += state.dump + (state.condenseFlow ? '\"' : '') + ':' + (state.condenseFlow ? '' : ' ');\n\n if (!writeNode(state, level, objectValue, false, false)) {\n continue; // Skip this pair because of invalid value.\n }\n\n pairBuffer += state.dump;\n\n // Both key and value are valid.\n _result += pairBuffer;\n }\n\n state.tag = _tag;\n state.dump = '{' + _result + '}';\n}\n\nfunction writeBlockMapping(state, level, object, compact) {\n var _result = '',\n _tag = state.tag,\n objectKeyList = Object.keys(object),\n index,\n length,\n objectKey,\n objectValue,\n explicitPair,\n pairBuffer;\n\n // Allow sorting keys so that the output file is deterministic\n if (state.sortKeys === true) {\n // Default sorting\n objectKeyList.sort();\n } else if (typeof state.sortKeys === 'function') {\n // Custom sort function\n objectKeyList.sort(state.sortKeys);\n } else if (state.sortKeys) {\n // Something is wrong\n throw new exception('sortKeys must be a boolean or a function');\n }\n\n for (index = 0, length = objectKeyList.length; index < length; index += 1) {\n pairBuffer = '';\n\n if (!compact || _result !== '') {\n pairBuffer += generateNextLine(state, level);\n }\n\n objectKey = objectKeyList[index];\n objectValue = object[objectKey];\n\n if (state.replacer) {\n objectValue = state.replacer.call(object, objectKey, objectValue);\n }\n\n if (!writeNode(state, level + 1, objectKey, true, true, true)) {\n continue; // Skip this pair because of invalid key.\n }\n\n explicitPair = (state.tag !== null && state.tag !== '?') ||\n (state.dump && state.dump.length > 1024);\n\n if (explicitPair) {\n if (state.dump && CHAR_LINE_FEED === state.dump.charCodeAt(0)) {\n pairBuffer += '?';\n } else {\n pairBuffer += '? ';\n }\n }\n\n pairBuffer += state.dump;\n\n if (explicitPair) {\n pairBuffer += generateNextLine(state, level);\n }\n\n if (!writeNode(state, level + 1, objectValue, true, explicitPair)) {\n continue; // Skip this pair because of invalid value.\n }\n\n if (state.dump && CHAR_LINE_FEED === state.dump.charCodeAt(0)) {\n pairBuffer += ':';\n } else {\n pairBuffer += ': ';\n }\n\n pairBuffer += state.dump;\n\n // Both key and value are valid.\n _result += pairBuffer;\n }\n\n state.tag = _tag;\n state.dump = _result || '{}'; // Empty mapping if no valid pairs.\n}\n\nfunction detectType(state, object, explicit) {\n var _result, typeList, index, length, type, style;\n\n typeList = explicit ? state.explicitTypes : state.implicitTypes;\n\n for (index = 0, length = typeList.length; index < length; index += 1) {\n type = typeList[index];\n\n if ((type.instanceOf || type.predicate) &&\n (!type.instanceOf || ((typeof object === 'object') && (object instanceof type.instanceOf))) &&\n (!type.predicate || type.predicate(object))) {\n\n if (explicit) {\n if (type.multi && type.representName) {\n state.tag = type.representName(object);\n } else {\n state.tag = type.tag;\n }\n } else {\n state.tag = '?';\n }\n\n if (type.represent) {\n style = state.styleMap[type.tag] || type.defaultStyle;\n\n if (_toString.call(type.represent) === '[object Function]') {\n _result = type.represent(object, style);\n } else if (_hasOwnProperty.call(type.represent, style)) {\n _result = type.represent[style](object, style);\n } else {\n throw new exception('!<' + type.tag + '> tag resolver accepts not \"' + style + '\" style');\n }\n\n state.dump = _result;\n }\n\n return true;\n }\n }\n\n return false;\n}\n\n// Serializes `object` and writes it to global `result`.\n// Returns true on success, or false on invalid object.\n//\nfunction writeNode(state, level, object, block, compact, iskey, isblockseq) {\n state.tag = null;\n state.dump = object;\n\n if (!detectType(state, object, false)) {\n detectType(state, object, true);\n }\n\n var type = _toString.call(state.dump);\n var inblock = block;\n var tagStr;\n\n if (block) {\n block = (state.flowLevel < 0 || state.flowLevel > level);\n }\n\n var objectOrArray = type === '[object Object]' || type === '[object Array]',\n duplicateIndex,\n duplicate;\n\n if (objectOrArray) {\n duplicateIndex = state.duplicates.indexOf(object);\n duplicate = duplicateIndex !== -1;\n }\n\n if ((state.tag !== null && state.tag !== '?') || duplicate || (state.indent !== 2 && level > 0)) {\n compact = false;\n }\n\n if (duplicate && state.usedDuplicates[duplicateIndex]) {\n state.dump = '*ref_' + duplicateIndex;\n } else {\n if (objectOrArray && duplicate && !state.usedDuplicates[duplicateIndex]) {\n state.usedDuplicates[duplicateIndex] = true;\n }\n if (type === '[object Object]') {\n if (block && (Object.keys(state.dump).length !== 0)) {\n writeBlockMapping(state, level, state.dump, compact);\n if (duplicate) {\n state.dump = '&ref_' + duplicateIndex + state.dump;\n }\n } else {\n writeFlowMapping(state, level, state.dump);\n if (duplicate) {\n state.dump = '&ref_' + duplicateIndex + ' ' + state.dump;\n }\n }\n } else if (type === '[object Array]') {\n if (block && (state.dump.length !== 0)) {\n if (state.noArrayIndent && !isblockseq && level > 0) {\n writeBlockSequence(state, level - 1, state.dump, compact);\n } else {\n writeBlockSequence(state, level, state.dump, compact);\n }\n if (duplicate) {\n state.dump = '&ref_' + duplicateIndex + state.dump;\n }\n } else {\n writeFlowSequence(state, level, state.dump);\n if (duplicate) {\n state.dump = '&ref_' + duplicateIndex + ' ' + state.dump;\n }\n }\n } else if (type === '[object String]') {\n if (state.tag !== '?') {\n writeScalar(state, state.dump, level, iskey, inblock);\n }\n } else if (type === '[object Undefined]') {\n return false;\n } else {\n if (state.skipInvalid) return false;\n throw new exception('unacceptable kind of an object to dump ' + type);\n }\n\n if (state.tag !== null && state.tag !== '?') {\n // Need to encode all characters except those allowed by the spec:\n //\n // [35] ns-dec-digit ::= [#x30-#x39] /* 0-9 */\n // [36] ns-hex-digit ::= ns-dec-digit\n // | [#x41-#x46] /* A-F */ | [#x61-#x66] /* a-f */\n // [37] ns-ascii-letter ::= [#x41-#x5A] /* A-Z */ | [#x61-#x7A] /* a-z */\n // [38] ns-word-char ::= ns-dec-digit | ns-ascii-letter | \u201C-\u201D\n // [39] ns-uri-char ::= \u201C%\u201D ns-hex-digit ns-hex-digit | ns-word-char | \u201C#\u201D\n // | \u201C;\u201D | \u201C/\u201D | \u201C?\u201D | \u201C:\u201D | \u201C@\u201D | \u201C&\u201D | \u201C=\u201D | \u201C+\u201D | \u201C$\u201D | \u201C,\u201D\n // | \u201C_\u201D | \u201C.\u201D | \u201C!\u201D | \u201C~\u201D | \u201C*\u201D | \u201C'\u201D | \u201C(\u201D | \u201C)\u201D | \u201C[\u201D | \u201C]\u201D\n //\n // Also need to encode '!' because it has special meaning (end of tag prefix).\n //\n tagStr = encodeURI(\n state.tag[0] === '!' ? state.tag.slice(1) : state.tag\n ).replace(/!/g, '%21');\n\n if (state.tag[0] === '!') {\n tagStr = '!' + tagStr;\n } else if (tagStr.slice(0, 18) === 'tag:yaml.org,2002:') {\n tagStr = '!!' + tagStr.slice(18);\n } else {\n tagStr = '!<' + tagStr + '>';\n }\n\n state.dump = tagStr + ' ' + state.dump;\n }\n }\n\n return true;\n}\n\nfunction getDuplicateReferences(object, state) {\n var objects = [],\n duplicatesIndexes = [],\n index,\n length;\n\n inspectNode(object, objects, duplicatesIndexes);\n\n for (index = 0, length = duplicatesIndexes.length; index < length; index += 1) {\n state.duplicates.push(objects[duplicatesIndexes[index]]);\n }\n state.usedDuplicates = new Array(length);\n}\n\nfunction inspectNode(object, objects, duplicatesIndexes) {\n var objectKeyList,\n index,\n length;\n\n if (object !== null && typeof object === 'object') {\n index = objects.indexOf(object);\n if (index !== -1) {\n if (duplicatesIndexes.indexOf(index) === -1) {\n duplicatesIndexes.push(index);\n }\n } else {\n objects.push(object);\n\n if (Array.isArray(object)) {\n for (index = 0, length = object.length; index < length; index += 1) {\n inspectNode(object[index], objects, duplicatesIndexes);\n }\n } else {\n objectKeyList = Object.keys(object);\n\n for (index = 0, length = objectKeyList.length; index < length; index += 1) {\n inspectNode(object[objectKeyList[index]], objects, duplicatesIndexes);\n }\n }\n }\n }\n}\n\nfunction dump$1(input, options) {\n options = options || {};\n\n var state = new State(options);\n\n if (!state.noRefs) getDuplicateReferences(input, state);\n\n var value = input;\n\n if (state.replacer) {\n value = state.replacer.call({ '': value }, '', value);\n }\n\n if (writeNode(state, 0, value, true, true)) return state.dump + '\\n';\n\n return '';\n}\n\nvar dump_1 = dump$1;\n\nvar dumper = {\n\tdump: dump_1\n};\n\nfunction renamed(from, to) {\n return function () {\n throw new Error('Function yaml.' + from + ' is removed in js-yaml 4. ' +\n 'Use yaml.' + to + ' instead, which is now safe by default.');\n };\n}\n\n\nvar Type = type;\nvar Schema = schema;\nvar FAILSAFE_SCHEMA = failsafe;\nvar JSON_SCHEMA = json;\nvar CORE_SCHEMA = core;\nvar DEFAULT_SCHEMA = _default;\nvar load = loader.load;\nvar loadAll = loader.loadAll;\nvar dump = dumper.dump;\nvar YAMLException = exception;\n\n// Re-export all types in case user wants to create custom schema\nvar types = {\n binary: binary,\n float: float,\n map: map,\n null: _null,\n pairs: pairs,\n set: set,\n timestamp: timestamp,\n bool: bool,\n int: int,\n merge: merge,\n omap: omap,\n seq: seq,\n str: str\n};\n\n// Removed functions from JS-YAML 3.0.x\nvar safeLoad = renamed('safeLoad', 'load');\nvar safeLoadAll = renamed('safeLoadAll', 'loadAll');\nvar safeDump = renamed('safeDump', 'dump');\n\nvar jsYaml = {\n\tType: Type,\n\tSchema: Schema,\n\tFAILSAFE_SCHEMA: FAILSAFE_SCHEMA,\n\tJSON_SCHEMA: JSON_SCHEMA,\n\tCORE_SCHEMA: CORE_SCHEMA,\n\tDEFAULT_SCHEMA: DEFAULT_SCHEMA,\n\tload: load,\n\tloadAll: loadAll,\n\tdump: dump,\n\tYAMLException: YAMLException,\n\ttypes: types,\n\tsafeLoad: safeLoad,\n\tsafeLoadAll: safeLoadAll,\n\tsafeDump: safeDump\n};\n\nexport { CORE_SCHEMA, DEFAULT_SCHEMA, FAILSAFE_SCHEMA, JSON_SCHEMA, Schema, Type, YAMLException, jsYaml as default, dump, load, loadAll, safeDump, safeLoad, safeLoadAll, types };\n", "/**\n * CodeQL metadata resolver utilities\n * Handles resolution of query metadata using the CodeQL CLI\n */\n\nimport { executeCodeQLCommand } from './cli-executor';\nimport { logger } from '../utils/logger';\n\n/**\n * Query metadata structure returned by codeql resolve metadata\n */\nexport interface QueryMetadata {\n [key: string]: string | string[];\n}\n\n/**\n * Resolve metadata for a CodeQL query file\n * @param queryPath - Absolute or relative path to the .ql query file\n * @returns Parsed metadata object or null if resolution fails\n */\nexport async function resolveQueryMetadata(queryPath: string): Promise {\n try {\n logger.info(`Resolving metadata for query: ${queryPath}`);\n\n const result = await executeCodeQLCommand(\n 'resolve metadata',\n { format: 'json' },\n [queryPath]\n );\n\n if (!result.success) {\n logger.error(`Failed to resolve metadata for ${queryPath}:`, result.stderr || result.error);\n return null;\n }\n\n // Parse the JSON output\n try {\n const metadata = JSON.parse(result.stdout);\n return metadata;\n } catch (parseError) {\n logger.error(`Failed to parse metadata JSON for ${queryPath}:`, parseError);\n return null;\n }\n } catch (error) {\n logger.error(`Error resolving metadata for ${queryPath}:`, error);\n return null;\n }\n}\n", "/**\n * CodeQL generate log-summary tool\n */\n\nimport { z } from 'zod';\nimport { CLIToolDefinition, createCodeQLSchemas, defaultCLIResultProcessor } from '../../lib/cli-tool-registry';\n\nexport const codeqlGenerateLogSummaryTool: CLIToolDefinition = {\n name: 'codeql_generate_log-summary',\n description: 'Create a summary of a structured JSON evaluator event log file',\n command: 'codeql',\n subcommand: 'generate log-summary',\n inputSchema: {\n inputLog: z.string().describe('Path to the evaluator log file to summarize'),\n outputFile: z.string().optional().describe('Path to write the summary (optional, defaults to stdout)'),\n format: z.enum(['text', 'predicates', 'overall']).optional()\n .describe('Output format: text (human-readable), predicates (JSON), or overall (stats)'),\n 'minify-output': z.boolean().optional().describe('Minify JSON output'),\n utc: z.boolean().optional().describe('Force UTC timestamps'),\n 'deduplicate-stage-summaries': z.boolean().optional().describe('Deduplicate stage summaries'),\n verbose: createCodeQLSchemas.verbose(),\n additionalArgs: createCodeQLSchemas.additionalArgs()\n },\n examples: [\n 'codeql generate log-summary --format=text -- evaluator-log.json.txt summary.txt',\n 'codeql generate log-summary --format=predicates --minify-output -- evaluator-log.json.txt',\n 'codeql generate log-summary --format=overall -- evaluator-log.json.txt overall-stats.json'\n ],\n resultProcessor: defaultCLIResultProcessor\n};", "/**\n * CodeQL generate query-help tool\n */\n\nimport { z } from 'zod';\nimport { CLIToolDefinition, createCodeQLSchemas, defaultCLIResultProcessor } from '../../lib/cli-tool-registry';\n\nexport const codeqlGenerateQueryHelpTool: CLIToolDefinition = {\n name: 'codeql_generate_query-help',\n description: 'Generate query help documentation from QLDoc comments',\n command: 'codeql',\n subcommand: 'generate query-help',\n inputSchema: {\n query: z.string().describe('Path to the query file to generate help for'),\n outputFile: z.string().optional().describe('Path to write the help documentation'),\n format: z.enum(['markdown', 'text', 'html']).optional()\n .describe('Output format for the help documentation'),\n verbose: createCodeQLSchemas.verbose(),\n additionalArgs: createCodeQLSchemas.additionalArgs()\n },\n examples: [\n 'codeql generate query-help -- MyQuery.ql',\n 'codeql generate query-help --format=markdown -- MyQuery.ql help.md',\n 'codeql generate query-help --format=html -- MyQuery.ql help.html'\n ],\n resultProcessor: defaultCLIResultProcessor\n};", "/**\n * CodeQL pack install tool\n */\n\nimport { z } from 'zod';\nimport { CLIToolDefinition, createCodeQLSchemas } from '../../lib/cli-tool-registry';\n\nexport const codeqlPackInstallTool: CLIToolDefinition = {\n name: 'codeql_pack_install',\n description: 'Install CodeQL pack dependencies',\n command: 'codeql',\n subcommand: 'pack install',\n inputSchema: {\n packDir: z.string().optional().describe('Directory containing qlpack.yml (default: current)'),\n force: z.boolean().optional().describe('Force reinstall of dependencies'),\n 'no-strict-mode': z.boolean().optional()\n .describe('Allow non-strict dependency resolution'),\n verbose: createCodeQLSchemas.verbose(),\n additionalArgs: createCodeQLSchemas.additionalArgs()\n },\n examples: [\n 'codeql pack install',\n 'codeql pack install --force /path/to/pack',\n 'codeql pack install --no-strict-mode'\n ]\n};", "/**\n * CodeQL pack ls tool\n */\n\nimport { z } from 'zod';\nimport { CLIToolDefinition, createCodeQLSchemas, defaultCLIResultProcessor } from '../../lib/cli-tool-registry';\n\nexport const codeqlPackLsTool: CLIToolDefinition = {\n name: 'codeql_pack_ls',\n description: 'List CodeQL packs under some local directory path',\n command: 'codeql',\n subcommand: 'pack ls',\n inputSchema: {\n dir: z.string().optional().describe('The root directory of the package or workspace, defaults to the current working directory'),\n format: z.enum(['text', 'json']).optional()\n .describe('Output format: text (default) or json'),\n groups: z.string().optional()\n .describe('List of CodeQL pack groups to include or exclude'),\n verbose: createCodeQLSchemas.verbose(),\n additionalArgs: createCodeQLSchemas.additionalArgs()\n },\n examples: [\n 'codeql pack ls -- .',\n 'codeql pack ls --format=json -- /path/to/pack-directory',\n 'codeql pack ls --format=json --groups=queries,tests -- .'\n ],\n resultProcessor: defaultCLIResultProcessor\n};", "/**\n * CodeQL query profiling tool\n */\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport { executeCodeQLCommand } from '../../lib/cli-executor';\nimport { logger } from '../../utils/logger';\nimport { writeFileSync, readFileSync, existsSync } from 'fs';\nimport { join, dirname, basename } from 'path';\nimport { mkdirSync } from 'fs';\n\ninterface EvaluatorLogEvent {\n time: string;\n type: string;\n eventId: number;\n nanoTime: number;\n [key: string]: unknown;\n}\n\ninterface PipelineNode {\n eventId: number;\n name: string;\n position?: string;\n type?: string;\n startTime: number;\n endTime: number;\n duration: number;\n resultSize?: number;\n tupleCount?: number;\n dependencies: string[];\n dependencyEventIds: number[];\n}\n\ninterface ProfileData {\n queryName: string;\n totalDuration: number;\n totalEvents: number;\n pipelines: PipelineNode[];\n}\n\n/**\n * Parse evaluator log and create profile data\n */\nfunction parseEvaluatorLog(logPath: string): ProfileData {\n const logContent = readFileSync(logPath, 'utf-8');\n \n // Split by empty lines to get each JSON object (handles both JSONL and pretty-printed JSON)\n const jsonObjects = logContent.split('\\n\\n').filter((s) => s.trim());\n const events: EvaluatorLogEvent[] = jsonObjects\n .map((obj) => {\n try {\n return JSON.parse(obj);\n } catch (_error) {\n logger.warn(`Failed to parse evaluator log object: ${obj.substring(0, 100)}...`);\n return null;\n }\n })\n .filter((event): event is EvaluatorLogEvent => event !== null);\n\n // Map to track pipeline nodes by their start event ID\n const pipelineMap = new Map>();\n // Map to track dependency event IDs by predicate name\n const predicateNameToEventId = new Map();\n \n let queryName = '';\n let queryStartTime = 0;\n let queryEndTime = 0;\n\n for (const event of events) {\n switch (event.type) {\n case 'QUERY_STARTED':\n queryName = (event.queryName as string) || '';\n queryStartTime = event.nanoTime;\n break;\n\n case 'QUERY_COMPLETED':\n queryEndTime = event.nanoTime;\n break;\n\n case 'PREDICATE_STARTED': {\n const predicateName = event.predicateName as string;\n const position = event.position as string | undefined;\n const predicateType = event.predicateType as string | undefined;\n const dependencies = event.dependencies as Record | undefined;\n \n // Track this predicate's event ID by name for dependency resolution\n predicateNameToEventId.set(predicateName, event.eventId);\n \n // Get dependency event IDs\n const dependencyEventIds: number[] = [];\n const dependencyNames: string[] = [];\n if (dependencies) {\n for (const depName of Object.keys(dependencies)) {\n dependencyNames.push(depName);\n const depEventId = predicateNameToEventId.get(depName);\n if (depEventId !== undefined) {\n dependencyEventIds.push(depEventId);\n }\n }\n }\n\n pipelineMap.set(event.eventId, {\n eventId: event.eventId,\n name: predicateName,\n position,\n type: predicateType,\n startTime: event.nanoTime,\n dependencies: dependencyNames,\n dependencyEventIds,\n });\n break;\n }\n\n case 'PREDICATE_COMPLETED': {\n const startEventId = event.startEvent as number;\n const pipelineInfo = pipelineMap.get(startEventId);\n if (pipelineInfo) {\n const startEvent = events.find((e) => e.eventId === startEventId);\n if (startEvent) {\n const duration = (event.nanoTime - startEvent.nanoTime) / 1_000_000; // Convert to ms\n pipelineInfo.endTime = event.nanoTime;\n pipelineInfo.duration = duration;\n pipelineInfo.resultSize = event.resultSize as number | undefined;\n pipelineInfo.tupleCount = event.tupleCount as number | undefined;\n }\n }\n break;\n }\n }\n }\n\n // Convert to array and maintain original evaluation order (by eventId)\n const pipelines: PipelineNode[] = Array.from(pipelineMap.values())\n .filter((p): p is PipelineNode => p.duration !== undefined)\n .sort((a, b) => a.eventId - b.eventId);\n\n const totalDuration = queryEndTime > 0 ? (queryEndTime - queryStartTime) / 1_000_000 : 0;\n\n return {\n queryName,\n totalDuration,\n totalEvents: events.length,\n pipelines,\n };\n}\n\n/**\n * Format profile data as JSON\n */\nfunction formatAsJson(profile: ProfileData): string {\n return JSON.stringify(profile, null, 2);\n}\n\n/**\n * Format profile data as Mermaid diagram\n * Creates a graph showing the query evaluation pipelines in order of execution\n * with dependencies as edges, annotated with duration and result sizes\n */\nfunction formatAsMermaid(profile: ProfileData): string {\n const lines: string[] = [];\n\n lines.push('```mermaid');\n lines.push('graph TD');\n lines.push('');\n \n // Add query root node\n lines.push(` QUERY[\"${basename(profile.queryName)}
Total: ${profile.totalDuration.toFixed(2)}ms\"]`);\n lines.push('');\n \n // Create nodes for each pipeline in evaluation order (already sorted by eventId)\n profile.pipelines.forEach((pipeline) => {\n const nodeId = `P${pipeline.eventId}`;\n const duration = pipeline.duration.toFixed(2);\n const resultSize = pipeline.resultSize !== undefined ? pipeline.resultSize : '?';\n // Sanitize predicate name for Mermaid\n const name = pipeline.name.replace(/[<>]/g, '').substring(0, 40);\n lines.push(` ${nodeId}[\"${name}
${duration}ms | ${resultSize} results\"]`);\n });\n \n lines.push('');\n \n // Add edges from QUERY to root pipelines (those with no dependencies)\n const rootPipelines = profile.pipelines.filter((p) => p.dependencies.length === 0);\n \n rootPipelines.forEach((pipeline) => {\n lines.push(` QUERY --> P${pipeline.eventId}`);\n });\n \n lines.push('');\n \n // Add edges between pipelines based on dependencies (using eventIds to preserve links)\n profile.pipelines.forEach((pipeline) => {\n pipeline.dependencyEventIds.forEach((depEventId) => {\n const duration = pipeline.duration.toFixed(2);\n lines.push(` P${depEventId} -->|\"${duration}ms\"| P${pipeline.eventId}`);\n });\n });\n \n lines.push('');\n lines.push(' classDef default fill:#e1f5ff,stroke:#333,stroke-width:2px');\n lines.push(' classDef query fill:#ffe1e1,stroke:#333,stroke-width:3px');\n lines.push(' class QUERY query');\n lines.push('```');\n\n return lines.join('\\n');\n}\n\n/**\n * Register the profile_codeql_query tool\n */\nexport function registerProfileCodeQLQueryTool(server: McpServer): void {\n server.tool(\n 'profile_codeql_query',\n 'Profile the performance of a CodeQL query run against a specific database by analyzing the evaluator log JSON file',\n {\n query: z.string().describe('Path to the .ql query file'),\n database: z.string().describe('Path to the CodeQL database directory'),\n evaluatorLog: z\n .string()\n .optional()\n .describe(\n 'Path to an existing structured JSON log (e.g., evaluator-log.jsonl) file. If not provided, the tool will run the query to generate one.'\n ),\n outputDir: z\n .string()\n .optional()\n .describe('Directory to write profiling data files (defaults to same directory as evaluator log)'),\n },\n async (params) => {\n try {\n const { query, database, evaluatorLog, outputDir } = params;\n let logPath = evaluatorLog;\n let bqrsPath: string | undefined;\n let sarifPath: string | undefined;\n\n // If evaluator log not provided, run the query to generate one\n if (!logPath) {\n logger.info('No evaluator log provided, running query to generate one');\n\n // Determine output directory\n const defaultOutputDir = outputDir || join(dirname(query as string), 'profile-output');\n mkdirSync(defaultOutputDir, { recursive: true });\n\n logPath = join(defaultOutputDir, 'evaluator-log.jsonl');\n bqrsPath = join(defaultOutputDir, 'query-results.bqrs');\n sarifPath = join(defaultOutputDir, 'query-results.sarif');\n\n // Run query with evaluator logging and tuple counting\n const queryResult = await executeCodeQLCommand(\n 'query run',\n {\n database: database as string,\n output: bqrsPath,\n 'evaluator-log': logPath,\n 'tuple-counting': true,\n 'evaluator-log-level': 5,\n },\n [query as string]\n );\n\n if (!queryResult.success) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Failed to run query: ${queryResult.stderr || queryResult.error}`,\n },\n ],\n isError: true,\n };\n }\n\n // Generate SARIF interpretation\n if (existsSync(bqrsPath)) {\n try {\n const sarifResult = await executeCodeQLCommand(\n 'bqrs interpret',\n { format: 'sarif-latest', output: sarifPath },\n [bqrsPath]\n );\n\n if (sarifResult.success) {\n logger.info(`Generated SARIF interpretation at ${sarifPath}`);\n }\n } catch (error) {\n logger.warn(`Failed to generate SARIF interpretation: ${error}`);\n }\n }\n }\n\n // Verify evaluator log exists\n if (!existsSync(logPath)) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Evaluator log not found at: ${logPath}`,\n },\n ],\n isError: true,\n };\n }\n\n // Parse the evaluator log\n logger.info(`Parsing evaluator log from: ${logPath}`);\n const profile = parseEvaluatorLog(logPath);\n\n // Determine output directory for profile\n const profileOutputDir = outputDir || dirname(logPath);\n mkdirSync(profileOutputDir, { recursive: true });\n\n // Write profile JSON file\n const jsonPath = join(profileOutputDir, 'query-evaluation-profile.json');\n const jsonContent = formatAsJson(profile);\n writeFileSync(jsonPath, jsonContent);\n logger.info(`Profile JSON written to: ${jsonPath}`);\n\n // Write profile Mermaid diagram file\n const mdPath = join(profileOutputDir, 'query-evaluation-profile.md');\n const mdContent = formatAsMermaid(profile);\n writeFileSync(mdPath, mdContent);\n logger.info(`Profile Mermaid diagram written to: ${mdPath}`);\n\n // Build response message\n const outputFiles: string[] = [\n `Profile JSON: ${jsonPath}`,\n `Profile Mermaid: ${mdPath}`,\n `Evaluator Log: ${logPath}`,\n ];\n\n if (bqrsPath) {\n outputFiles.push(`Query Results (BQRS): ${bqrsPath}`);\n }\n\n if (sarifPath && existsSync(sarifPath)) {\n outputFiles.push(`Query Results (SARIF): ${sarifPath}`);\n }\n\n const responseText = [\n 'Query profiling completed successfully!',\n '',\n 'Output Files:',\n ...outputFiles.map((f) => ` - ${f}`),\n '',\n 'Profile Summary:',\n ` - Query: ${basename(profile.queryName)}`,\n ` - Total Duration: ${profile.totalDuration.toFixed(2)} ms`,\n ` - Total Pipelines: ${profile.pipelines.length}`,\n ` - Total Events: ${profile.totalEvents}`,\n '',\n 'First 5 Pipeline Nodes (in evaluation order):',\n ...profile.pipelines.slice(0, 5).map((p, idx) => {\n return ` ${idx + 1}. ${p.name} (${p.duration.toFixed(2)} ms, ${p.resultSize || '?'} results)`;\n }),\n ].join('\\n');\n\n return {\n content: [\n {\n type: 'text' as const,\n text: responseText,\n },\n ],\n };\n } catch (error) {\n logger.error('Error profiling CodeQL query:', error);\n\n return {\n content: [\n {\n type: 'text' as const,\n text: `Failed to profile query: ${error instanceof Error ? error.message : String(error)}`,\n },\n ],\n isError: true,\n };\n }\n }\n );\n}\n", "/**\n * CodeQL query compile tool\n */\n\nimport { z } from 'zod';\nimport { CLIToolDefinition } from '../../lib/cli-tool-registry';\n\nexport const codeqlQueryCompileTool: CLIToolDefinition = {\n name: 'codeql_query_compile',\n description: 'Compile and validate CodeQL queries',\n command: 'codeql',\n subcommand: 'query compile',\n inputSchema: {\n query: z.string().describe('Path to the CodeQL query file (.ql)'),\n database: z.string().optional().describe('Path to the CodeQL database'),\n library: z.string().optional().describe('Path to query library'),\n output: z.string().optional().describe('Output file path'),\n warnings: z.enum(['hide', 'show', 'error']).optional()\n .describe('How to handle compilation warnings'),\n verbose: z.boolean().optional().describe('Enable verbose output'),\n additionalArgs: z.array(z.string()).optional().describe('Additional command-line arguments')\n },\n examples: [\n 'codeql query compile --database=/path/to/db MyQuery.ql',\n 'codeql query compile --library=/path/to/lib --output=compiled.qlo MyQuery.ql'\n ]\n};", "/**\n * CodeQL query format tool\n */\n\nimport { z } from 'zod';\nimport { CLIToolDefinition, createCodeQLSchemas, defaultCLIResultProcessor, CLIExecutionResult } from '../../lib/cli-tool-registry';\n\n/**\n * Custom result processor for codeql query format tool\n * Exit code 1 with --check-only means \"file would change\" - this is success for format checking\n */\nfunction formatResultProcessor(result: CLIExecutionResult, params: Record): string {\n const isCheckOnly = params['check-only'];\n const hasFormatChanges = result.exitCode === 1;\n \n if (isCheckOnly && hasFormatChanges) {\n // Mark as success for the CLI tool registry since detecting format changes is the intended behavior\n result.success = true;\n return result.stdout || result.stderr || 'File would change by autoformatting.';\n }\n \n return defaultCLIResultProcessor(result, params);\n}\n\nexport const codeqlQueryFormatTool: CLIToolDefinition = {\n name: 'codeql_query_format',\n description: 'Automatically format CodeQL source code files',\n command: 'codeql',\n subcommand: 'query format',\n inputSchema: {\n files: z.array(z.string()).describe('One or more .ql or .qll source files to format'),\n output: z.string().optional().describe('Write formatted code to this file instead of stdout'),\n 'in-place': z.boolean().optional().describe('Overwrite each input file with formatted version'),\n 'check-only': z.boolean().optional().describe('Check formatting without writing output'),\n backup: z.string().optional().describe('Backup extension when overwriting existing files'),\n 'no-syntax-errors': z.boolean().optional().describe('Ignore syntax errors and pretend file is formatted'),\n verbose: createCodeQLSchemas.verbose(),\n additionalArgs: createCodeQLSchemas.additionalArgs()\n },\n examples: [\n 'codeql query format -i -- ExampleQuery.ql',\n 'codeql query format --in-place -- queries/*.ql',\n 'codeql query format --check-only -- queries/*.ql'\n ],\n resultProcessor: formatResultProcessor\n};", "/**\n * CodeQL query run tool\n */\n\nimport { z } from 'zod';\nimport { CLIToolDefinition, createCodeQLSchemas } from '../../lib/cli-tool-registry';\n\nexport const codeqlQueryRunTool: CLIToolDefinition = {\n name: 'codeql_query_run',\n description: 'Execute a CodeQL query against a database. Use either \"query\" parameter for direct file path OR \"queryName\" + \"queryLanguage\" for pre-defined tool queries.',\n command: 'codeql',\n subcommand: 'query run',\n inputSchema: {\n query: z.string().optional().describe('Path to the CodeQL query file (.ql) - cannot be used with queryName'),\n queryName: z.string().optional().describe('Name of pre-defined query to run (e.g., \"PrintAST\", \"CallGraphFrom\", \"CallGraphTo\") - requires queryLanguage'),\n queryLanguage: z.string().optional().describe('Programming language for tools queries (e.g., \"javascript\", \"java\", \"python\") - required when using queryName'),\n queryPack: z.string().optional().describe('Query pack path (defaults to server/ql//tools/src/ for tool queries)'),\n sourceFiles: z.string().optional().describe('Comma-separated list of source file paths for PrintAST queries (e.g., \"src/main.js,src/utils.js\" or just \"main.js\")'),\n sourceFunction: z.string().optional().describe('Comma-separated list of source function names for CallGraphFrom queries (e.g., \"main,processData\")'),\n targetFunction: z.string().optional().describe('Comma-separated list of target function names for CallGraphTo queries (e.g., \"helper,validateInput\")'),\n database: createCodeQLSchemas.database(),\n output: createCodeQLSchemas.output(),\n external: z.array(z.string()).optional()\n .describe('External predicate data: predicate=file.csv'),\n timeout: createCodeQLSchemas.timeout(),\n logDir: z.string().optional()\n .describe('Custom directory for query execution logs (overrides CODEQL_QUERY_LOG_DIR environment variable). If not provided, uses CODEQL_QUERY_LOG_DIR or defaults to .tmp/query-logs/'),\n 'evaluator-log': z.string().optional().describe('Path to save evaluator log (deprecated: use logDir instead)'),\n 'evaluator-log-minify': z.boolean().optional()\n .describe('Minimize evaluator log for smaller size'),\n 'evaluator-log-level': z.number().min(1).max(5).optional()\n .describe('Evaluator log verbosity level (1-5, default 5)'),\n 'tuple-counting': z.boolean().optional()\n .describe('Display tuple counts for each evaluation step in evaluator logs'),\n format: z.enum(['sarif-latest', 'sarifv2.1.0', 'csv', 'graphtext', 'dgml', 'dot']).optional()\n .describe('Output format for query results via codeql bqrs interpret. Defaults to sarif-latest for @kind problem/path-problem queries, graphtext for @kind graph queries. Graph formats (graphtext, dgml, dot) only work with @kind graph queries.'),\n interpretedOutput: z.string().optional()\n .describe('Output file for interpreted results (e.g., results.sarif, results.txt). If not provided, defaults based on format: .sarif for SARIF, .txt for graphtext/csv, .dgml for dgml, .dot for dot'),\n evaluationFunction: z.string().optional()\n .describe('[DEPRECATED - use format parameter instead] Built-in function for query results evaluation (e.g., \"mermaid-graph\", \"json-decode\", \"csv-decode\") or path to custom evaluation script'),\n evaluationOutput: z.string().optional()\n .describe('[DEPRECATED - use interpretedOutput parameter instead] Output file for evaluation results'),\n verbose: createCodeQLSchemas.verbose(),\n additionalArgs: createCodeQLSchemas.additionalArgs()\n },\n examples: [\n 'codeql query run --database=mydb --output=results.bqrs MyQuery.ql',\n 'codeql query run --database=mydb --query-name=PrintAST --query-language=javascript --source-files=src/index.js --output=results.bqrs --format=graphtext --interpreted-output=results.txt',\n 'codeql query run --database=mydb --external=data=input.csv --output=results.bqrs MyQuery.ql --format=sarif-latest --interpreted-output=results.sarif',\n 'codeql query run --database=mydb --evaluator-log=eval.log --tuple-counting --evaluator-log-level=5 --output=results.bqrs MyQuery.ql',\n 'codeql query run --database=mydb --query-name=PrintAST --query-language=javascript --source-files=\"main.js,utils.js\" --format=graphtext',\n 'codeql query run --database=mydb --log-dir=/custom/log/path --tuple-counting --output=results.bqrs MyQuery.ql'\n ]\n};", "/**\n * CodeQL quick evaluate tool\n * \n * Inspired by JordyZomer/codeql-mcp repository:\n * - https://github.com/JordyZomer/codeql-mcp/blob/main/server.py\n * - https://github.com/JordyZomer/codeql-mcp/blob/main/codeqlclient.py\n */\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport { join, resolve } from 'path';\nimport { findClassPosition } from './find-class-position';\nimport { findPredicatePosition } from './find-predicate-position';\nimport { logger } from '../../utils/logger';\nimport { getProjectTmpDir } from '../../utils/temp-dir';\n\nexport interface QuickEvaluateParams {\n file: string;\n db: string;\n symbol: string;\n output_path?: string;\n}\n\n/**\n * Quick evaluate either a class or a predicate in a CodeQL query.\n * This allows debugging a select portion of QL code without running the whole query.\n */\nexport async function quickEvaluate({\n file,\n db: _db,\n symbol,\n output_path\n}: QuickEvaluateParams): Promise {\n try {\n // Try to find as a class first, then as a predicate\n try {\n await findClassPosition(file, symbol);\n } catch {\n try {\n await findPredicatePosition(file, symbol);\n } catch {\n throw new Error(`Symbol '${symbol}' not found as class or predicate in file: ${file}`);\n }\n }\n \n const resolvedOutput = resolve(output_path || join(getProjectTmpDir('quickeval'), 'quickeval.bqrs'));\n \n // For now, return the resolved output path\n // In a full implementation, this would use the CodeQL CLI or query server\n // to perform the actual quick evaluation with the position parameters\n return resolvedOutput;\n } catch (error) {\n throw new Error(`CodeQL evaluation failed: ${error instanceof Error ? error.message : 'Unknown error'}`);\n }\n}\n\n/**\n * Register the quick evaluate tool with the MCP server\n */\nexport function registerQuickEvaluateTool(server: McpServer): void {\n server.tool(\n 'quick_evaluate',\n 'Quick evaluate either a class or a predicate in a CodeQL query for debugging',\n {\n file: z.string().describe('Path to the .ql file containing the symbol'),\n db: z.string().describe('Path to the CodeQL database'),\n symbol: z.string().describe('Name of the class or predicate to evaluate'),\n output_path: z.string().optional().describe('Output path for results (defaults to project-local .tmp/quickeval/)'),\n },\n async ({ file, db, symbol, output_path }) => {\n try {\n const result = await quickEvaluate({ file, db, symbol, output_path });\n return {\n content: [{ type: 'text', text: result }],\n };\n } catch (error) {\n logger.error('Error in quick evaluate:', error);\n return {\n content: [\n {\n type: 'text',\n text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`,\n },\n ],\n isError: true,\n };\n }\n }\n );\n}", "/**\n * CodeQL register database tool\n * \n * Inspired by JordyZomer/codeql-mcp repository:\n * - https://github.com/JordyZomer/codeql-mcp/blob/main/server.py\n * - https://github.com/JordyZomer/codeql-mcp/blob/main/codeqlclient.py\n */\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport { access, constants } from 'fs/promises';\nimport { resolve } from 'path';\nimport { logger } from '../../utils/logger';\n\nexport interface DatabaseRegistration {\n uri: string;\n content: {\n sourceArchiveZip: string;\n dbDir: string;\n };\n}\n\n/**\n * Register a CodeQL database given a local path to the database directory.\n * Validates that the database exists and has required structure.\n * Supports both full databases (with src.zip) and test databases (with src/ folder).\n */\nexport async function registerDatabase(dbPath: string): Promise {\n try {\n const resolvedPath = resolve(dbPath);\n \n // Check if database directory exists\n await access(resolvedPath, constants.F_OK);\n \n // Check for codeql-database.yml (required for all CodeQL databases)\n const dbYmlPath = resolve(resolvedPath, 'codeql-database.yml');\n await access(dbYmlPath, constants.F_OK);\n \n // Check if src.zip exists (for full databases) OR src/ directory exists (for test databases)\n const srcZipPath = resolve(resolvedPath, 'src.zip');\n const srcDirPath = resolve(resolvedPath, 'src');\n \n let hasSrcZip = false;\n let hasSrcDir = false;\n \n try {\n await access(srcZipPath, constants.F_OK);\n hasSrcZip = true;\n } catch {\n // src.zip not found, check for src/ directory\n }\n \n if (!hasSrcZip) {\n try {\n await access(srcDirPath, constants.F_OK);\n hasSrcDir = true;\n } catch {\n // src directory not found either\n }\n }\n \n if (!hasSrcZip && !hasSrcDir) {\n throw new Error(`Missing required source archive (src.zip) or source directory (src/) in: ${dbPath}`);\n }\n \n // For now, we just validate and return success message\n // In a full implementation, this would register with a query server\n const sourceType = hasSrcZip ? 'src.zip' : 'src/';\n return `Database registered: ${dbPath} (source: ${sourceType})`;\n } catch (error) {\n if (error instanceof Error) {\n const errorCode = (error as { code?: string }).code;\n if (errorCode === 'ENOENT') {\n if (error.message.includes('codeql-database.yml')) {\n throw new Error(`Missing required codeql-database.yml in: ${dbPath}`);\n }\n throw new Error(`Database path does not exist: ${dbPath}`);\n }\n if (errorCode === 'EACCES') {\n throw new Error(`Database path does not exist: ${dbPath}`);\n }\n }\n throw new Error(`Failed to register database: ${error instanceof Error ? error.message : 'Unknown error'}`);\n }\n}\n\n/**\n * Register the register database tool with the MCP server\n */\nexport function registerRegisterDatabaseTool(server: McpServer): void {\n server.tool(\n 'register_database',\n 'Register a CodeQL database given a local path to the database directory',\n {\n db_path: z.string().describe('Path to the CodeQL database directory'),\n },\n async ({ db_path }) => {\n try {\n const result = await registerDatabase(db_path);\n return {\n content: [{ type: 'text', text: result }],\n };\n } catch (error) {\n logger.error('Error registering database:', error);\n return {\n content: [\n {\n type: 'text',\n text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`,\n },\n ],\n isError: true,\n };\n }\n }\n );\n}", "/**\n * CodeQL resolve database tool\n */\n\nimport { z } from 'zod';\nimport { CLIToolDefinition, createCodeQLSchemas, defaultCLIResultProcessor } from '../../lib/cli-tool-registry';\n\nexport const codeqlResolveDatabaseTool: CLIToolDefinition = {\n name: 'codeql_resolve_database',\n description: 'Resolve database path and validate database structure',\n command: 'codeql',\n subcommand: 'resolve database',\n inputSchema: {\n database: z.string().describe('Database path to resolve'),\n format: z.enum(['text', 'json', 'betterjson']).optional()\n .describe('Output format for database information'),\n verbose: createCodeQLSchemas.verbose(),\n additionalArgs: createCodeQLSchemas.additionalArgs()\n },\n examples: [\n 'codeql resolve database -- /path/to/database',\n 'codeql resolve database --format=json -- my-database',\n 'codeql resolve database --format=betterjson -- database-dir'\n ],\n resultProcessor: defaultCLIResultProcessor\n};", "/**\n * CodeQL resolve languages tool\n */\n\nimport { z } from 'zod';\nimport { CLIToolDefinition, defaultCLIResultProcessor } from '../../lib/cli-tool-registry';\n\nexport const codeqlResolveLanguagesTool: CLIToolDefinition = {\n name: 'codeql_resolve_languages',\n description: 'List installed CodeQL extractor packs',\n command: 'codeql',\n subcommand: 'resolve languages',\n inputSchema: {\n format: z.enum(['text', 'json', 'betterjson']).optional()\n .describe('Output format for language information'),\n verbose: z.boolean().optional().describe('Enable verbose output'),\n additionalArgs: z.array(z.string()).optional().describe('Additional command-line arguments')\n },\n examples: [\n 'codeql resolve languages --format=text',\n 'codeql resolve languages --format=json',\n 'codeql resolve languages --format=betterjson'\n ],\n resultProcessor: defaultCLIResultProcessor\n};", "/**\n * CodeQL resolve library-path tool\n */\n\nimport { z } from 'zod';\nimport { CLIToolDefinition, defaultCLIResultProcessor } from '../../lib/cli-tool-registry';\n\nexport const codeqlResolveLibraryPathTool: CLIToolDefinition = {\n name: 'codeql_resolve_library-path',\n description: 'Resolve library path for CodeQL queries and libraries',\n command: 'codeql',\n subcommand: 'resolve library-path',\n inputSchema: {\n language: z.string().optional().describe('Programming language to resolve library path for'),\n format: z.enum(['text', 'json', 'betterjson']).optional()\n .describe('Output format for library path information'),\n verbose: z.boolean().optional().describe('Enable verbose output'),\n additionalArgs: z.array(z.string()).optional().describe('Additional command-line arguments')\n },\n examples: [\n 'codeql resolve library-path --language=java',\n 'codeql resolve library-path --format=json --language=python',\n 'codeql resolve library-path --format=betterjson'\n ],\n resultProcessor: defaultCLIResultProcessor\n};", "/**\n * CodeQL resolve metadata tool\n */\n\nimport { z } from 'zod';\nimport { CLIToolDefinition, defaultCLIResultProcessor } from '../../lib/cli-tool-registry';\n\nexport const codeqlResolveMetadataTool: CLIToolDefinition = {\n name: 'codeql_resolve_metadata',\n description: 'Resolve and return the key-value metadata pairs from a CodeQL query source file.',\n command: 'codeql',\n subcommand: 'resolve metadata',\n inputSchema: {\n query: z.string().describe('Query file to resolve metadata for'),\n format: z.enum(['json']).optional()\n .describe('Output format for metadata information (always JSON, optional for future compatibility)'),\n verbose: z.boolean().optional().describe('Enable verbose output'),\n additionalArgs: z.array(z.string()).optional().describe('Additional command-line arguments')\n },\n examples: [\n 'codeql resolve metadata -- relative-path/2/MyQuery.ql',\n 'codeql resolve metadata --format=json -- /absolute-plus/relative-path/2/MyQuery.ql'\n ],\n resultProcessor: defaultCLIResultProcessor\n};", "/**\n * CodeQL resolve qlref tool\n */\n\nimport { z } from 'zod';\nimport { CLIToolDefinition, createCodeQLSchemas, defaultCLIResultProcessor } from '../../lib/cli-tool-registry';\n\nexport const codeqlResolveQlrefTool: CLIToolDefinition = {\n name: 'codeql_resolve_qlref',\n description: 'Resolve qlref files to their corresponding query files',\n command: 'codeql',\n subcommand: 'resolve qlref',\n inputSchema: {\n qlref: z.string().describe('Path to the .qlref file to resolve'),\n format: z.enum(['text', 'json', 'betterjson']).optional()\n .describe('Output format for qlref resolution'),\n verbose: createCodeQLSchemas.verbose(),\n additionalArgs: createCodeQLSchemas.additionalArgs()\n },\n examples: [\n 'codeql resolve qlref -- test/MyQuery.qlref',\n 'codeql resolve qlref --format=json -- test/MyQuery.qlref',\n 'codeql resolve qlref --format=betterjson -- test/MyQuery.qlref'\n ],\n resultProcessor: defaultCLIResultProcessor\n};", "/**\n * CodeQL resolve queries tool\n */\n\nimport { z } from 'zod';\nimport { CLIToolDefinition, createCodeQLSchemas, CLIExecutionResult } from '../../lib/cli-tool-registry';\n\n/**\n * Result processor that only returns stdout for JSON formats\n * This prevents warnings/info from stderr from corrupting JSON output\n */\nconst jsonOnlyResultProcessor = (\n result: CLIExecutionResult,\n params: Record\n): string => {\n if (!result.success) {\n return `Command failed (exit code ${result.exitCode || 'unknown'}):\\n${result.error || result.stderr}`;\n }\n\n // For JSON formats (including bylanguage), only return stdout to avoid mixing warnings with JSON\n if (params.format === 'json' || params.format === 'betterjson' || params.format === 'bylanguage') {\n return result.stdout || '[]';\n }\n\n // For text format, include warnings\n let output = '';\n\n if (result.stdout) {\n output += result.stdout;\n }\n\n if (result.stderr) {\n if (output) {\n output += '\\n\\nWarnings/Info:\\n';\n }\n output += result.stderr;\n }\n\n if (!output) {\n output = 'Command executed successfully (no output)';\n }\n\n return output;\n};\n\nexport const codeqlResolveQueriesTool: CLIToolDefinition = {\n name: 'codeql_resolve_queries',\n description: 'List available CodeQL queries found on the local filesystem',\n command: 'codeql',\n subcommand: 'resolve queries',\n inputSchema: {\n directory: z.string().optional().describe('Directory to search for queries'),\n language: z.string().optional().describe('Filter queries by programming language'),\n format: z.enum(['text', 'json', 'betterjson', 'bylanguage']).optional()\n .describe('Output format for query list'),\n 'additional-packs': z.union([z.string(), z.array(z.string())]).optional()\n .describe('Additional pack directories to search for CodeQL packs'),\n verbose: createCodeQLSchemas.verbose(),\n additionalArgs: createCodeQLSchemas.additionalArgs()\n },\n examples: [\n 'codeql resolve queries',\n 'codeql resolve queries --language=java --format=json',\n 'codeql resolve queries --format=betterjson -- /path/to/queries',\n 'codeql resolve queries --additional-packs=/path/to/packs codeql/java-queries'\n ],\n resultProcessor: jsonOnlyResultProcessor\n};", "/**\n * CodeQL resolve tests tool\n */\n\nimport { z } from 'zod';\nimport { CLIToolDefinition, createCodeQLSchemas, defaultCLIResultProcessor } from '../../lib/cli-tool-registry';\n\nexport const codeqlResolveTestsTool: CLIToolDefinition = {\n name: 'codeql_resolve_tests',\n description: 'Resolve the local filesystem paths of unit tests and/or queries under some base directory',\n command: 'codeql',\n subcommand: 'resolve tests',\n inputSchema: {\n tests: z.array(z.string()).optional().describe('One or more tests (.ql, .qlref files, or test directories)'),\n format: z.enum(['text', 'json']).optional()\n .describe('Output format for test list'),\n verbose: createCodeQLSchemas.verbose(),\n additionalArgs: createCodeQLSchemas.additionalArgs()\n },\n examples: [\n 'codeql resolve tests',\n 'codeql resolve tests --format=json -- test-directory',\n 'codeql resolve tests --format=json -- test1.ql test2.ql'\n ],\n resultProcessor: defaultCLIResultProcessor\n};", "/**\n * CodeQL test accept tool\n */\n\nimport { z } from 'zod';\nimport { CLIToolDefinition, createCodeQLSchemas, defaultCLIResultProcessor } from '../../lib/cli-tool-registry';\n\nexport const codeqlTestAcceptTool: CLIToolDefinition = {\n name: 'codeql_test_accept',\n description: 'Accept new test results as the expected baseline',\n command: 'codeql',\n subcommand: 'test accept',\n inputSchema: {\n tests: z.array(z.string()).describe('One or more tests (.ql, .qlref files, or test directories)'),\n verbose: createCodeQLSchemas.verbose(),\n additionalArgs: createCodeQLSchemas.additionalArgs()\n },\n examples: [\n 'codeql test accept -- languages/java/test/MyQuery/MyQuery.qlref',\n 'codeql test accept -- languages/java/test/MyQuery/',\n 'codeql test accept -- languages/java/src/MyQuery/MyQuery.ql'\n ],\n resultProcessor: defaultCLIResultProcessor\n};", "/**\n * CodeQL test extract tool\n */\n\nimport { z } from 'zod';\nimport { CLIToolDefinition, createCodeQLSchemas, defaultCLIResultProcessor } from '../../lib/cli-tool-registry';\n\nexport const codeqlTestExtractTool: CLIToolDefinition = {\n name: 'codeql_test_extract',\n description: 'Extract test databases for CodeQL query tests',\n command: 'codeql',\n subcommand: 'test extract',\n inputSchema: {\n tests: z.array(z.string()).describe('One or more test directories or files'),\n language: z.string().optional().describe('Programming language for extraction'),\n threads: createCodeQLSchemas.threads(),\n ram: createCodeQLSchemas.ram(),\n verbose: createCodeQLSchemas.verbose(),\n additionalArgs: createCodeQLSchemas.additionalArgs()\n },\n examples: [\n 'codeql test extract -- languages/java/test/MyQuery/',\n 'codeql test extract --language=java --threads=4 -- test-directory',\n 'codeql test extract --threads=2 --ram=2048 -- multiple/test/directories'\n ],\n resultProcessor: defaultCLIResultProcessor\n};", "/**\n * CodeQL test run tool\n */\n\nimport { CLIToolDefinition, createCodeQLSchemas } from '../../lib/cli-tool-registry';\nimport { z } from 'zod';\n\nexport const codeqlTestRunTool: CLIToolDefinition = {\n name: 'codeql_test_run',\n description: 'Run CodeQL query tests',\n command: 'codeql',\n subcommand: 'test run',\n inputSchema: {\n tests: z.array(z.string()).describe('One or more tests (.ql, .qlref files, or test directories)'),\n 'show-extractor-output': z.boolean().optional()\n .describe('Show output from extractors during test execution'),\n 'keep-databases': z.boolean().optional()\n .describe('Keep test databases after running tests'),\n 'learn': z.boolean().optional()\n .describe('Accept current output as expected for failing tests'),\n logDir: z.string().optional()\n .describe('Custom directory for test execution logs (overrides CODEQL_QUERY_LOG_DIR environment variable). If not provided, uses CODEQL_QUERY_LOG_DIR or defaults to .tmp/query-logs/'),\n threads: createCodeQLSchemas.threads(),\n ram: createCodeQLSchemas.ram(),\n verbose: createCodeQLSchemas.verbose(),\n additionalArgs: createCodeQLSchemas.additionalArgs()\n },\n examples: [\n 'codeql test run /path/to/tests',\n 'codeql test run --learn /path/to/failing/tests',\n 'codeql test run --threads=4 --keep-databases /path/to/tests',\n 'codeql test run --log-dir=/custom/log/path /path/to/tests'\n ]\n};", "/**\n * CodeQL tools registration for MCP server\n * Includes both high-level helpers and CLI command wrappers\n */\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport { validateCodeQLSyntax } from '../lib/validation';\nimport { createCodeQLQuery } from '../lib/query-scaffolding';\nimport { registerCLITool } from '../lib/cli-tool-registry';\nimport {\n codeqlBqrsDecodeTool,\n codeqlBqrsInfoTool,\n codeqlBqrsInterpretTool,\n codeqlDatabaseAnalyzeTool,\n codeqlDatabaseCreateTool,\n codeqlGenerateLogSummaryTool,\n codeqlGenerateQueryHelpTool,\n codeqlPackInstallTool,\n codeqlPackLsTool,\n codeqlQueryCompileTool,\n codeqlQueryFormatTool,\n codeqlQueryRunTool,\n codeqlResolveDatabaseTool,\n codeqlResolveLanguagesTool,\n codeqlResolveLibraryPathTool,\n codeqlResolveMetadataTool,\n codeqlResolveQlrefTool,\n codeqlResolveQueriesTool,\n codeqlResolveTestsTool,\n codeqlTestAcceptTool,\n codeqlTestExtractTool,\n codeqlTestRunTool,\n registerFindClassPositionTool,\n registerFindCodeQLQueryFilesTool,\n registerFindPredicatePositionTool,\n registerProfileCodeQLQueryTool,\n registerQuickEvaluateTool,\n registerRegisterDatabaseTool\n} from './codeql';\nimport { logger } from '../utils/logger';\n\n/**\n * Register all CodeQL tools with the MCP server\n */\nexport function registerCodeQLTools(server: McpServer): void {\n // Register high-level helper tools\n \n // Tool: Validate CodeQL Query (heuristic-based)\n server.tool(\n 'validate_codeql_query',\n 'Quick heuristic validation for CodeQL query structure - checks for common patterns like from/where/select clauses and metadata presence. Does NOT compile the query. For authoritative validation with actual compilation, use codeql_lsp_diagnostics instead.',\n {\n query: z.string().describe('The CodeQL query to validate'),\n language: z.string().optional().describe('Target programming language'),\n },\n async ({ query, language }) => {\n try {\n const validation = validateCodeQLSyntax(query, language);\n return {\n content: [{ type: 'text', text: JSON.stringify(validation, null, 2) }],\n };\n } catch (error) {\n logger.error('Error validating CodeQL query:', error);\n return {\n content: [\n {\n type: 'text',\n text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`,\n },\n ],\n isError: true,\n };\n }\n }\n );\n\n // Tool: Create CodeQL Query\n server.tool(\n 'create_codeql_query',\n 'Create directory structure and files for a new CodeQL query with tests',\n {\n basePath: z.string().describe('Base path where src/ and test/ directories will be created'),\n queryName: z.string().describe('Name of the query (e.g., MySecurityQuery)'),\n language: z.string().describe('Target programming language (e.g., javascript, python, java)'),\n description: z.string().optional().describe('Description of what the query does'),\n queryId: z.string().optional().describe('Custom query ID (defaults to language/example/queryname)'),\n },\n async ({ basePath, queryName, language, description, queryId }) => {\n try {\n const result = createCodeQLQuery({\n basePath,\n queryName,\n language,\n description,\n queryId\n });\n \n const summary = {\n success: true,\n queryPath: result.queryPath,\n testPath: result.testPath,\n qlrefPath: result.qlrefPath,\n testCodePath: result.testCodePath,\n filesCreated: result.filesCreated,\n nextSteps: [\n 'Review and customize the generated query in: ' + result.queryPath,\n 'Add test cases to: ' + result.testCodePath,\n 'Run codeql_pack_install to install dependencies',\n 'Run codeql_test_extract to create test database',\n 'Run codeql_test_run to execute tests'\n ]\n };\n \n return {\n content: [{ type: 'text', text: JSON.stringify(summary, null, 2) }],\n };\n } catch (error) {\n logger.error('Error creating CodeQL query:', error);\n return {\n content: [\n {\n type: 'text',\n text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`,\n },\n ],\n isError: true,\n };\n }\n }\n );\n\n // Register CLI tools (alphabetically by tool name)\n registerCLITool(server, codeqlBqrsDecodeTool);\n registerCLITool(server, codeqlBqrsInfoTool);\n registerCLITool(server, codeqlBqrsInterpretTool);\n registerCLITool(server, codeqlDatabaseAnalyzeTool);\n registerCLITool(server, codeqlDatabaseCreateTool);\n registerCLITool(server, codeqlGenerateLogSummaryTool);\n registerCLITool(server, codeqlGenerateQueryHelpTool);\n registerCLITool(server, codeqlPackInstallTool);\n registerCLITool(server, codeqlPackLsTool);\n registerCLITool(server, codeqlQueryCompileTool);\n registerCLITool(server, codeqlQueryFormatTool);\n registerCLITool(server, codeqlQueryRunTool);\n registerCLITool(server, codeqlResolveDatabaseTool);\n registerCLITool(server, codeqlResolveLanguagesTool);\n registerCLITool(server, codeqlResolveLibraryPathTool);\n registerCLITool(server, codeqlResolveMetadataTool);\n registerCLITool(server, codeqlResolveQlrefTool);\n registerCLITool(server, codeqlResolveQueriesTool);\n registerCLITool(server, codeqlResolveTestsTool);\n registerCLITool(server, codeqlTestAcceptTool);\n registerCLITool(server, codeqlTestExtractTool);\n registerCLITool(server, codeqlTestRunTool);\n\n // Register new MCP tools (inspired by JordyZomer/codeql-mcp repository)\n registerFindClassPositionTool(server);\n registerFindCodeQLQueryFilesTool(server);\n registerFindPredicatePositionTool(server);\n registerProfileCodeQLQueryTool(server);\n registerQuickEvaluateTool(server);\n registerRegisterDatabaseTool(server);\n}\n", "/**\n * CodeQL query validation utilities\n */\n\nimport { resolve, normalize, isAbsolute, relative } from 'path';\n\nexport interface CodeQLValidationResult {\n isValid: boolean;\n errors: string[];\n warnings: string[];\n suggestions: string[];\n}\n\n/**\n * Validates CodeQL query syntax and structure\n */\nexport function validateCodeQLSyntax(query: string, _language?: string): CodeQLValidationResult {\n const validation: CodeQLValidationResult = {\n isValid: true,\n errors: [],\n warnings: [],\n suggestions: [],\n };\n\n if (!query.trim()) {\n validation.isValid = false;\n validation.errors.push('Query cannot be empty');\n return validation;\n }\n\n if (!query.includes('from') && !query.includes('select')) {\n validation.warnings.push('Query should typically include \"from\" and \"select\" clauses');\n }\n\n if (!query.includes('@name') && !query.includes('@description')) {\n validation.suggestions.push('Consider adding @name and @description metadata');\n }\n\n return validation;\n}\n\n/**\n * Validates a file path to prevent path traversal attacks\n * @param filePath - The file path to validate\n * @param workspaceRoot - Optional workspace root directory. If not provided, allows any absolute path but still blocks traversal attempts\n * @returns The validated absolute path\n * @throws Error if the path contains path traversal sequences\n */\nexport function validateFilePath(filePath: string, workspaceRoot?: string): string {\n // Normalize the path to resolve any . or .. segments\n const normalizedPath = normalize(filePath);\n \n // Check for path traversal attempts in the normalized path\n // This blocks paths like \"../../../etc/passwd\" even after normalization\n if (normalizedPath.includes('..')) {\n throw new Error(`Invalid file path: path traversal detected in \"${filePath}\"`);\n }\n \n // Resolve to absolute path\n const absolutePath = isAbsolute(normalizedPath) \n ? normalizedPath \n : resolve(workspaceRoot || process.cwd(), normalizedPath);\n \n // If workspace root is specified, ensure the resolved path is within it\n if (workspaceRoot) {\n const relativePath = relative(workspaceRoot, absolutePath);\n \n // If relative path starts with .. or is absolute, it's outside workspace\n if (relativePath.startsWith('..') || isAbsolute(relativePath)) {\n throw new Error(`Invalid file path: \"${filePath}\" is outside the workspace root`);\n }\n }\n \n return absolutePath;\n}", "/**\n * CodeQL query scaffolding utilities\n * Handles creation of query directory structure and files\n */\n\nimport * as fs from 'fs';\nimport * as path from 'path';\n\nexport interface QueryScaffoldingOptions {\n basePath: string;\n queryName: string;\n language: string;\n description?: string;\n queryId?: string;\n}\n\nexport interface QueryScaffoldingResult {\n queryPath: string;\n testPath: string;\n qlrefPath: string;\n testCodePath: string;\n filesCreated: string[];\n}\n\n/**\n * Get the file extension for test code based on language\n */\nfunction getLanguageExtension(language: string): string {\n const extensions: Record = {\n javascript: 'js',\n typescript: 'ts',\n python: 'py',\n java: 'java',\n csharp: 'cs',\n cpp: 'cpp',\n go: 'go',\n ruby: 'rb',\n actions: 'yml'\n };\n return extensions[language.toLowerCase()] || 'txt';\n}\n\n/**\n * Generate query template content\n */\nfunction generateQueryTemplate(\n queryName: string,\n language: string,\n description?: string,\n queryId?: string\n): string {\n const desc = description || `${queryName} query`;\n const id = queryId || `${language}/example/${queryName.toLowerCase()}`;\n \n return `/**\n * @id ${id}\n * @name ${queryName}\n * @description ${desc}\n * @kind problem\n * @precision medium\n * @problem.severity warning\n */\n\nimport ${language}\n\n// TODO: Implement query logic\nfrom File f\nwhere f.getBaseName() = \"${queryName}.${getLanguageExtension(language)}\"\nselect f, \"TODO: Add query logic\"\n`;\n}\n\n/**\n * Create the directory structure and files for a new CodeQL query\n */\nexport function createCodeQLQuery(options: QueryScaffoldingOptions): QueryScaffoldingResult {\n const { basePath, queryName, language, description, queryId } = options;\n \n // Resolve absolute paths\n const absoluteBasePath = path.resolve(basePath);\n \n // Define paths with intermediate directory\n const srcDir = path.join(absoluteBasePath, 'src', queryName);\n const testDir = path.join(absoluteBasePath, 'test', queryName);\n \n const queryPath = path.join(srcDir, `${queryName}.ql`);\n const qlrefPath = path.join(testDir, `${queryName}.qlref`);\n const testCodePath = path.join(testDir, `${queryName}.${getLanguageExtension(language)}`);\n \n const filesCreated: string[] = [];\n \n try {\n // Create directories (recursive: true is a no-op if they already exist)\n fs.mkdirSync(srcDir, { recursive: true });\n fs.mkdirSync(testDir, { recursive: true });\n \n // Create files atomically using 'wx' flag (exclusive create) to avoid\n // TOCTOU race between existsSync check and writeFileSync (CWE-367).\n // The 'wx' flag fails with EEXIST if the file already exists.\n try {\n const queryContent = generateQueryTemplate(queryName, language, description, queryId);\n fs.writeFileSync(queryPath, queryContent, { encoding: 'utf8', flag: 'wx' });\n filesCreated.push(queryPath);\n } catch (e: unknown) {\n const err = e as { code?: string };\n if (err.code !== 'EEXIST') throw e;\n }\n \n try {\n const qlrefContent = `${queryName}/${queryName}.ql\\n`;\n fs.writeFileSync(qlrefPath, qlrefContent, { encoding: 'utf8', flag: 'wx' });\n filesCreated.push(qlrefPath);\n } catch (e: unknown) {\n const err = e as { code?: string };\n if (err.code !== 'EEXIST') throw e;\n }\n \n try {\n const testCodeContent = `// Test code for ${queryName}\\n// TODO: Add test cases\\n`;\n fs.writeFileSync(testCodePath, testCodeContent, { encoding: 'utf8', flag: 'wx' });\n filesCreated.push(testCodePath);\n } catch (e: unknown) {\n const err = e as { code?: string };\n if (err.code !== 'EEXIST') throw e;\n }\n \n return {\n queryPath,\n testPath: testDir,\n qlrefPath,\n testCodePath,\n filesCreated\n };\n } catch (error) {\n throw new Error(`Failed to create query scaffolding: ${error instanceof Error ? error.message : 'Unknown error'}`);\n }\n}\n", "/**\n * CodeQL learning resources utilities\n */\n\nimport { readFileSync } from 'fs';\nimport { join, dirname } from 'path';\nimport { fileURLToPath } from 'url';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\n\n/**\n * Get the getting started guide content\n */\nexport function getGettingStartedGuide(): string {\n try {\n return readFileSync(join(__dirname, '../resources/getting-started.md'), 'utf-8');\n } catch {\n return 'Getting started guide not available';\n }\n}\n\n/**\n * Get the query basics guide content\n */\nexport function getQueryBasicsGuide(): string {\n try {\n return readFileSync(join(__dirname, '../resources/query-basics.md'), 'utf-8');\n } catch {\n return 'Query basics guide not available';\n }\n}\n\n/**\n * Get the security templates content\n */\nexport function getSecurityTemplates(): string {\n try {\n return readFileSync(join(__dirname, '../resources/security-templates.md'), 'utf-8');\n } catch {\n return 'Security templates not available';\n }\n}\n\n/**\n * Get the performance patterns content\n */\nexport function getPerformancePatterns(): string {\n try {\n return readFileSync(join(__dirname, '../resources/performance-patterns.md'), 'utf-8');\n } catch {\n return 'Performance patterns not available';\n }\n}", "/**\n * CodeQL resources registration for MCP server\n */\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport {\n getGettingStartedGuide,\n getQueryBasicsGuide,\n getSecurityTemplates,\n getPerformancePatterns,\n} from '../lib/resources';\n\n/**\n * Register all CodeQL resources with the MCP server\n */\nexport function registerCodeQLResources(server: McpServer): void {\n // Getting Started Guide\n server.resource(\n 'CodeQL Getting Started',\n 'codeql://learning/getting-started',\n {\n description: 'Comprehensive introduction to CodeQL for beginners',\n mimeType: 'text/markdown',\n },\n async () => {\n return {\n contents: [\n {\n uri: 'codeql://learning/getting-started',\n mimeType: 'text/markdown',\n text: getGettingStartedGuide(),\n },\n ],\n };\n }\n );\n\n // Query Basics Guide\n server.resource(\n 'CodeQL Query Basics',\n 'codeql://learning/query-basics',\n {\n description: 'Learn the fundamentals of writing CodeQL queries',\n mimeType: 'text/markdown',\n },\n async () => {\n return {\n contents: [\n {\n uri: 'codeql://learning/query-basics',\n mimeType: 'text/markdown',\n text: getQueryBasicsGuide(),\n },\n ],\n };\n }\n );\n\n // Security Templates\n server.resource(\n 'CodeQL Security Templates',\n 'codeql://templates/security',\n {\n description: 'Ready-to-use security query templates',\n mimeType: 'text/markdown',\n },\n async () => {\n return {\n contents: [\n {\n uri: 'codeql://templates/security',\n mimeType: 'text/markdown',\n text: getSecurityTemplates(),\n },\n ],\n };\n }\n );\n\n // Performance Patterns\n server.resource(\n 'CodeQL Performance Patterns',\n 'codeql://patterns/performance',\n {\n description: 'Best practices for writing efficient CodeQL queries',\n mimeType: 'text/markdown',\n },\n async () => {\n return {\n contents: [\n {\n uri: 'codeql://patterns/performance',\n mimeType: 'text/markdown',\n text: getPerformancePatterns(),\n },\n ],\n };\n }\n );\n}\n", "/**\n * CodeQL LSP Diagnostics tool for MCP server.\n *\n * Provides real-time QL code validation through LSP communication.\n * Renamed from `codeql_language_server_eval` to `codeql_lsp_diagnostics`\n * for consistency with the `codeql_lsp_*` tool naming convention.\n */\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport { Diagnostic, LanguageServerOptions } from '../../lib/language-server';\nimport { logger } from '../../utils/logger';\nimport { getProjectTmpDir } from '../../utils/temp-dir';\nimport { join } from 'path';\nimport { pathToFileURL } from 'url';\nimport { getInitializedLanguageServer } from './lsp-server-helper';\n\nexport interface LspDiagnosticsParams {\n qlCode: string;\n serverOptions?: LanguageServerOptions;\n workspaceUri?: string;\n}\n\nexport interface LspDiagnosticsResult {\n diagnostics: Diagnostic[];\n formattedOutput: string;\n isValid: boolean;\n summary: {\n errorCount: number;\n hintCount: number;\n infoCount: number;\n warningCount: number;\n };\n}\n\n/**\n * Format diagnostics for human-readable output.\n */\nfunction formatDiagnostics(diagnostics: Diagnostic[]): string {\n if (diagnostics.length === 0) {\n return '\u2705 No issues found in QL code';\n }\n\n const lines: string[] = [];\n lines.push(`Found ${diagnostics.length} issue(s):\\n`);\n\n diagnostics.forEach((diagnostic, index) => {\n const severityIcon = getSeverityIcon(diagnostic.severity);\n const severityName = getSeverityName(diagnostic.severity);\n const location = `Line ${diagnostic.range.start.line + 1}, Column ${diagnostic.range.start.character + 1}`;\n\n lines.push(`${index + 1}. ${severityIcon} ${severityName} at ${location}`);\n lines.push(` ${diagnostic.message}`);\n if (diagnostic.source) {\n lines.push(` Source: ${diagnostic.source}`);\n }\n if (diagnostic.code) {\n lines.push(` Code: ${diagnostic.code}`);\n }\n lines.push('');\n });\n\n return lines.join('\\n');\n}\n\nfunction getSeverityIcon(severity: number): string {\n switch (severity) {\n case 1: return '\u274C'; // Error\n case 2: return '\u26A0\uFE0F'; // Warning\n case 3: return '\u2139\uFE0F'; // Information\n case 4: return '\uD83D\uDCA1'; // Hint\n default: return '\u2753';\n }\n}\n\nfunction getSeverityName(severity: number): string {\n switch (severity) {\n case 1: return 'Error';\n case 2: return 'Warning';\n case 3: return 'Information';\n case 4: return 'Hint';\n default: return 'Unknown';\n }\n}\n\n/**\n * Evaluate QL code using the CodeQL Language Server and return diagnostics.\n */\nexport async function lspDiagnostics({\n qlCode,\n workspaceUri,\n serverOptions = {}\n}: LspDiagnosticsParams): Promise {\n try {\n logger.info('Evaluating QL code via Language Server...');\n\n const languageServer = await getInitializedLanguageServer({\n serverOptions,\n workspaceUri,\n });\n\n // Generate unique URI for this evaluation\n const evalUri = pathToFileURL(join(getProjectTmpDir('lsp-eval'), `eval_${Date.now()}.ql`)).href;\n\n const diagnostics = await languageServer.evaluateQL(qlCode, evalUri);\n\n // Count diagnostics by severity\n const summary = {\n errorCount: diagnostics.filter(d => d.severity === 1).length,\n hintCount: diagnostics.filter(d => d.severity === 4).length,\n infoCount: diagnostics.filter(d => d.severity === 3).length,\n warningCount: diagnostics.filter(d => d.severity === 2).length,\n };\n\n const isValid = summary.errorCount === 0;\n const formattedOutput = formatDiagnostics(diagnostics);\n\n logger.info(`QL evaluation complete. Valid: ${isValid}, Issues: ${diagnostics.length}`);\n\n return {\n diagnostics,\n formattedOutput,\n isValid,\n summary,\n };\n\n } catch (error) {\n logger.error('Error evaluating QL code:', error);\n throw new Error(`QL evaluation failed: ${error instanceof Error ? error.message : 'Unknown error'}`);\n }\n}\n\n/**\n * Shutdown the language server via the server manager.\n */\nexport async function shutdownDiagnosticsServer(): Promise {\n const { getServerManager } = await import('../../lib/server-manager');\n const manager = getServerManager();\n await manager.shutdownServer('language');\n}\n\n/**\n * Register the codeql_lsp_diagnostics tool with the MCP server.\n */\nexport function registerLspDiagnosticsTool(server: McpServer): void {\n server.tool(\n 'codeql_lsp_diagnostics',\n 'Authoritative syntax and semantic validation of CodeQL (QL) code via the CodeQL Language Server. Compiles the query and provides real-time diagnostics with precise error locations. Use this for accurate validation; for quick heuristic checks without compilation, use validate_codeql_query instead. Note: inline ql_code is evaluated as a virtual document and cannot resolve pack imports (e.g. `import javascript`). For validating queries with imports, use codeql_query_compile on the actual file instead.',\n {\n log_level: z.enum(['OFF', 'ERROR', 'WARN', 'INFO', 'DEBUG', 'TRACE', 'ALL']).optional().describe('Language server log level'),\n ql_code: z.string().describe('The CodeQL (QL) code to evaluate for syntax and semantic errors'),\n search_path: z.string().optional().describe('Optional search path for CodeQL libraries'),\n workspace_uri: z.string().optional().describe('Optional workspace URI for context (defaults to ./ql directory)'),\n },\n async ({ ql_code, workspace_uri, search_path, log_level }) => {\n try {\n const serverOptions: LanguageServerOptions = {};\n\n if (search_path) {\n serverOptions.searchPath = search_path;\n }\n if (log_level) {\n serverOptions.loglevel = log_level;\n }\n\n const result = await lspDiagnostics({\n qlCode: ql_code,\n serverOptions,\n workspaceUri: workspace_uri,\n });\n\n // Return structured result\n const responseContent = {\n diagnostics: result.diagnostics.map(d => ({\n code: d.code,\n column: d.range.start.character + 1, // Convert to 1-based column numbers\n line: d.range.start.line + 1, // Convert to 1-based line numbers\n message: d.message,\n severity: getSeverityName(d.severity),\n source: d.source,\n })),\n formattedOutput: result.formattedOutput,\n isValid: result.isValid,\n summary: result.summary,\n };\n\n return {\n content: [\n {\n text: JSON.stringify(responseContent, null, 2),\n type: 'text',\n }\n ],\n };\n\n } catch (error) {\n logger.error('Error in codeql_lsp_diagnostics tool:', error);\n return {\n content: [\n {\n text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`,\n type: 'text',\n },\n ],\n isError: true,\n };\n }\n }\n );\n\n // NOTE: Cleanup is handled centrally by shutdownServerManager() in\n // codeql-development-mcp-server.ts (setupGracefulShutdown). Registering\n // additional process.on('SIGINT'/'SIGTERM') handlers here would\n // accumulate on repeated calls and is unnecessary.\n}\n", "/**\n * Shared helper for obtaining a running, initialized CodeQL Language Server.\n *\n * Both `lsp-diagnostics.ts` and `lsp-handlers.ts` need to:\n * 1. Build a `LanguageServerConfig` with sensible defaults\n * 2. Obtain a server instance from the `CodeQLServerManager`\n * 3. Resolve a workspace URI (relative \u2192 absolute \u2192 `file://`)\n * 4. Initialize the server with the resolved workspace\n *\n * Centralizing this logic avoids duplication and ensures consistent\n * default behaviour across all LSP tools.\n */\n\nimport { isAbsolute, resolve } from 'path';\nimport { pathToFileURL } from 'url';\n\nimport { CodeQLLanguageServer, LanguageServerOptions } from '../../lib/language-server';\nimport { LanguageServerConfig } from '../../lib/server-config';\nimport { getServerManager } from '../../lib/server-manager';\nimport { logger } from '../../utils/logger';\n\n/**\n * Options accepted by {@link getInitializedLanguageServer}.\n */\nexport interface InitializedServerOptions {\n /** Language-server-level options (loglevel, searchPath, etc.). */\n serverOptions?: LanguageServerOptions;\n /** Workspace URI \u2014 may be a `file://` URI, absolute path, or relative path. */\n workspaceUri?: string;\n}\n\n/**\n * Return a running, initialized `CodeQLLanguageServer`.\n *\n * - Resolves `searchPath` to the bundled `ql` directory when not provided.\n * - Converts relative / bare-directory `workspaceUri` paths to `file://` URIs\n * resolved against `getUserWorkspaceDir()` (respects `CODEQL_MCP_WORKSPACE`).\n * - Falls back to the bundled `ql` directory when no workspace is given.\n * - Delegates lifecycle management to the global `CodeQLServerManager`.\n */\nexport async function getInitializedLanguageServer(\n opts: InitializedServerOptions = {},\n): Promise {\n const { packageRootDir: pkgRoot, getUserWorkspaceDir } = await import('../../utils/package-paths');\n const options = opts.serverOptions ?? {};\n\n const config: LanguageServerConfig = {\n checkErrors: 'ON_CHANGE',\n loglevel: options.loglevel ?? 'WARN',\n searchPath: options.searchPath ?? resolve(pkgRoot, 'ql'),\n synchronous: options.synchronous,\n verbosity: options.verbosity,\n };\n\n const manager = getServerManager();\n const server = await manager.getLanguageServer(config);\n\n // Normalize workspace URI: convert relative / bare directory paths to\n // file:// URIs against getUserWorkspaceDir() (respects CODEQL_MCP_WORKSPACE).\n let effectiveUri = opts.workspaceUri;\n if (effectiveUri && !effectiveUri.startsWith('file://')) {\n const absWorkspace = isAbsolute(effectiveUri)\n ? effectiveUri\n : resolve(getUserWorkspaceDir(), effectiveUri);\n effectiveUri = pathToFileURL(absWorkspace).href;\n }\n effectiveUri = effectiveUri ?? pathToFileURL(resolve(pkgRoot, 'ql')).href;\n\n await server.initialize(effectiveUri);\n logger.debug(`Language server initialized with workspace: ${effectiveUri}`);\n\n return server;\n}\n", "/**\n * CodeQL LSP tool handlers.\n *\n * Bridges MCP tool invocations to LSP requests on the CodeQL Language Server.\n * Each handler acquires a language server via the CodeQLServerManager,\n * opens the requested document, sends the LSP request, and returns the result.\n */\n\nimport { readFile } from 'fs/promises';\nimport { isAbsolute, resolve } from 'path';\nimport { pathToFileURL } from 'url';\n\nimport {\n CompletionItem,\n LSPLocation,\n TextDocumentPositionParams,\n} from '../../lib/language-server';\nimport { logger } from '../../utils/logger';\nimport { getUserWorkspaceDir } from '../../utils/package-paths';\nimport { getInitializedLanguageServer } from './lsp-server-helper';\n\n/**\n * Common parameters for LSP tool invocations.\n */\nexport interface LSPToolParams {\n /** 0-based character offset within the line. */\n character: number;\n /** Optional override for the file content (if not reading from disk). */\n fileContent?: string;\n /**\n * Path to the QL file. May be absolute or relative.\n * Relative paths are resolved against `getUserWorkspaceDir()`\n * (respects the `CODEQL_MCP_WORKSPACE` environment variable).\n */\n filePath: string;\n /** 0-based line number in the document. */\n line: number;\n /** Optional search path for CodeQL libraries. */\n searchPath?: string;\n /** Optional workspace URI for context. */\n workspaceUri?: string;\n}\n\n/**\n * Get a running, initialized language server for the given parameters.\n */\nasync function getInitializedServer(params: LSPToolParams) {\n return getInitializedLanguageServer({\n serverOptions: { searchPath: params.searchPath },\n workspaceUri: params.workspaceUri,\n });\n}\n\n/**\n * Resolve the file path to an absolute path and file:// URI.\n */\nfunction prepareDocumentPosition(\n params: LSPToolParams,\n): { absPath: string; docUri: string } {\n // Resolve relative paths against getUserWorkspaceDir() so that\n // CODEQL_MCP_WORKSPACE is respected and behaviour is consistent across tools.\n const absPath = isAbsolute(params.filePath)\n ? params.filePath\n : resolve(getUserWorkspaceDir(), params.filePath);\n const docUri = pathToFileURL(absPath).href;\n\n return { absPath, docUri };\n}\n\n/**\n * Read file content and open the document in the language server.\n */\nasync function openDocumentForPosition(\n server: Awaited>,\n params: LSPToolParams,\n absPath: string,\n docUri: string,\n): Promise {\n // Read file content from disk or use provided content\n let text: string;\n if (params.fileContent) {\n text = params.fileContent;\n } else {\n try {\n text = await readFile(absPath, 'utf-8');\n } catch (error) {\n throw new Error(`Cannot read file: ${absPath}: ${error instanceof Error ? error.message : error}`);\n }\n }\n\n // Open the document so the language server knows about it\n server.openDocument(docUri, text);\n\n return {\n position: { character: params.character, line: params.line },\n textDocument: { uri: docUri },\n };\n}\n\n/**\n * Get code completions at a position.\n */\nexport async function lspCompletion(params: LSPToolParams): Promise {\n logger.info(`LSP completion at ${params.filePath}:${params.line}:${params.character}`);\n const server = await getInitializedServer(params);\n const { absPath, docUri } = prepareDocumentPosition(params);\n const positionParams = await openDocumentForPosition(server, params, absPath, docUri);\n\n try {\n return await server.getCompletions(positionParams);\n } finally {\n server.closeDocument(docUri);\n }\n}\n\n/**\n * Go to definition of a symbol at a position.\n */\nexport async function lspDefinition(params: LSPToolParams): Promise {\n logger.info(`LSP definition at ${params.filePath}:${params.line}:${params.character}`);\n const server = await getInitializedServer(params);\n const { absPath, docUri } = prepareDocumentPosition(params);\n const positionParams = await openDocumentForPosition(server, params, absPath, docUri);\n\n try {\n return await server.getDefinition(positionParams);\n } finally {\n server.closeDocument(docUri);\n }\n}\n\n/**\n * Find all references to a symbol at a position.\n */\nexport async function lspReferences(params: LSPToolParams): Promise {\n logger.info(`LSP references at ${params.filePath}:${params.line}:${params.character}`);\n const server = await getInitializedServer(params);\n const { absPath, docUri } = prepareDocumentPosition(params);\n const positionParams = await openDocumentForPosition(server, params, absPath, docUri);\n\n try {\n return await server.getReferences({\n ...positionParams,\n context: { includeDeclaration: true },\n });\n } finally {\n server.closeDocument(docUri);\n }\n}\n", "/**\n * CodeQL LSP MCP tool definitions.\n *\n * Registers four LSP-based tools:\n * - codeql_lsp_completion \u2013 code completions at cursor position\n * - codeql_lsp_definition \u2013 go to definition\n * - codeql_lsp_diagnostics \u2013 QL code validation via LSP diagnostics\n * - codeql_lsp_references \u2013 find all references\n */\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport { registerLspDiagnosticsTool } from './lsp-diagnostics';\nimport {\n lspCompletion,\n lspDefinition,\n lspReferences,\n} from './lsp-handlers';\nimport { logger } from '../../utils/logger';\n\n/**\n * Shared Zod schema for the common LSP tool parameters.\n */\nconst lspParamsSchema = {\n character: z.number().int().min(0).describe('0-based character offset within the line'),\n file_content: z.string().optional().describe('Optional file content override (reads from disk if omitted)'),\n file_path: z.string().describe('Path to the CodeQL (.ql/.qll) file. Relative paths are resolved against the user workspace directory (see CODEQL_MCP_WORKSPACE).'),\n line: z.number().int().min(0).describe('0-based line number in the document'),\n search_path: z.string().optional().describe('Optional search path for CodeQL libraries'),\n workspace_uri: z.string().optional().describe('Optional workspace URI for context (defaults to ./ql directory)'),\n};\n\n/**\n * Helper to build the handler params from the raw MCP tool input.\n */\nfunction toHandlerParams(input: {\n character: number;\n file_content?: string;\n file_path: string;\n line: number;\n search_path?: string;\n workspace_uri?: string;\n}) {\n return {\n character: input.character,\n fileContent: input.file_content,\n filePath: input.file_path,\n line: input.line,\n searchPath: input.search_path,\n workspaceUri: input.workspace_uri,\n };\n}\n\n/**\n * Register all LSP-based tools with the MCP server.\n */\nexport function registerLSPTools(server: McpServer): void {\n // --- codeql_lsp_diagnostics (relocated from codeql_language_server_eval) ---\n registerLspDiagnosticsTool(server);\n\n // --- codeql_lsp_completion ---\n server.tool(\n 'codeql_lsp_completion',\n 'Get code completions at a cursor position in a CodeQL file. Returns completion items with labels, documentation, and insert text. The file must be a .ql or .qll file. IMPORTANT: Set workspace_uri to the pack or workspace root directory for dependency resolution; without it, completions for imported libraries will be empty.',\n lspParamsSchema,\n async (input) => {\n try {\n const items = await lspCompletion(toHandlerParams(input));\n return {\n content: [{\n text: JSON.stringify({\n completionCount: items.length,\n items: items.map((item) => ({\n detail: item.detail,\n documentation: item.documentation,\n insertText: item.insertText,\n kind: item.kind,\n label: item.label,\n })),\n }, null, 2),\n type: 'text' as const,\n }],\n };\n } catch (error) {\n logger.error('codeql_lsp_completion error:', error);\n return {\n content: [{ text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`, type: 'text' as const }],\n isError: true,\n };\n }\n },\n );\n\n // --- codeql_lsp_definition ---\n server.tool(\n 'codeql_lsp_definition',\n 'Go to the definition of a CodeQL symbol at a given position. Returns one or more file locations where the symbol is defined. Set workspace_uri to the pack root for dependency resolution.',\n lspParamsSchema,\n async (input) => {\n try {\n const locations = await lspDefinition(toHandlerParams(input));\n return {\n content: [{\n text: JSON.stringify({\n definitionCount: locations.length,\n locations: locations.map((loc) => ({\n endCharacter: loc.range.end.character,\n endLine: loc.range.end.line + 1,\n startCharacter: loc.range.start.character,\n startLine: loc.range.start.line + 1,\n uri: loc.uri,\n })),\n }, null, 2),\n type: 'text' as const,\n }],\n };\n } catch (error) {\n logger.error('codeql_lsp_definition error:', error);\n return {\n content: [{ text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`, type: 'text' as const }],\n isError: true,\n };\n }\n },\n );\n\n // --- codeql_lsp_references ---\n server.tool(\n 'codeql_lsp_references',\n 'Find all references to a CodeQL symbol at a given position. Returns file locations of all usages, including the declaration. Set workspace_uri to the pack root for dependency resolution.',\n lspParamsSchema,\n async (input) => {\n try {\n const locations = await lspReferences(toHandlerParams(input));\n return {\n content: [{\n text: JSON.stringify({\n locations: locations.map((loc) => ({\n endCharacter: loc.range.end.character,\n endLine: loc.range.end.line + 1,\n startCharacter: loc.range.start.character,\n startLine: loc.range.start.line + 1,\n uri: loc.uri,\n })),\n referenceCount: locations.length,\n }, null, 2),\n type: 'text' as const,\n }],\n };\n } catch (error) {\n logger.error('codeql_lsp_references error:', error);\n return {\n content: [{ text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`, type: 'text' as const }],\n isError: true,\n };\n }\n },\n );\n}\n", "/**\n * Language-specific resources implementation\n * Dynamically loads and serves language-specific AST references and security patterns\n */\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { readFileSync, existsSync } from 'fs';\nimport { join } from 'path';\nimport { LANGUAGE_RESOURCES } from '../types/language-types';\nimport { workspaceRootDir } from '../utils/package-paths';\nimport { logger } from '../utils/logger';\n\n/**\n * Get the base path for ql resources.\n * Uses the workspace root (monorepo root or package root) so that\n * resource files are found regardless of the server's process.cwd().\n */\nfunction getQLBasePath(): string {\n return workspaceRootDir;\n}\n\n/**\n * Load content from a resource file\n */\nfunction loadResourceContent(relativePath: string): string | null {\n try {\n const fullPath = join(getQLBasePath(), relativePath);\n \n if (!existsSync(fullPath)) {\n logger.warn(`Resource file not found: ${fullPath}`);\n return null;\n }\n \n return readFileSync(fullPath, 'utf-8');\n } catch (error) {\n logger.error(`Error loading resource file ${relativePath}:`, error);\n return null;\n }\n}\n\n/**\n * Register language-specific AST resources\n */\nexport function registerLanguageASTResources(server: McpServer): void {\n for (const langResource of LANGUAGE_RESOURCES) {\n if (!langResource.astFile) continue;\n \n const resourceUri = `codeql://languages/${langResource.language}/ast`;\n \n server.resource(\n `${langResource.language.toUpperCase()} AST Reference`,\n resourceUri,\n {\n description: `CodeQL AST class reference for ${langResource.language} programs`,\n mimeType: 'text/markdown'\n },\n async () => {\n const content = loadResourceContent(langResource.astFile!);\n \n if (!content) {\n return {\n contents: [{\n uri: resourceUri,\n mimeType: 'text/markdown',\n text: `# ${langResource.language.toUpperCase()} AST Reference\\n\\nResource file not found or could not be loaded.`\n }]\n };\n }\n \n return {\n contents: [{\n uri: resourceUri,\n mimeType: 'text/markdown',\n text: content\n }]\n };\n }\n );\n }\n}\n\n/**\n * Register language-specific security pattern resources\n */\nexport function registerLanguageSecurityResources(server: McpServer): void {\n for (const langResource of LANGUAGE_RESOURCES) {\n if (!langResource.securityFile) continue;\n \n const resourceUri = `codeql://languages/${langResource.language}/security`;\n \n server.resource(\n `${langResource.language.toUpperCase()} Security Patterns`,\n resourceUri,\n {\n description: `CodeQL security query patterns and framework modeling for ${langResource.language}`,\n mimeType: 'text/markdown'\n },\n async () => {\n const content = loadResourceContent(langResource.securityFile!);\n \n if (!content) {\n return {\n contents: [{\n uri: resourceUri,\n mimeType: 'text/markdown',\n text: `# ${langResource.language.toUpperCase()} Security Patterns\\n\\nResource file not found or could not be loaded.`\n }]\n };\n }\n \n return {\n contents: [{\n uri: resourceUri,\n mimeType: 'text/markdown',\n text: content\n }]\n };\n }\n );\n }\n}\n\n/**\n * Register additional language-specific resources (like Go's dataflow patterns)\n */\nexport function registerLanguageAdditionalResources(server: McpServer): void {\n for (const langResource of LANGUAGE_RESOURCES) {\n if (!langResource.additionalFiles) continue;\n \n for (const [resourceType, filePath] of Object.entries(langResource.additionalFiles)) {\n const resourceUri = `codeql://languages/${langResource.language}/${resourceType}`;\n \n server.resource(\n `${langResource.language.toUpperCase()} ${resourceType.replace('-', ' ').replace(/\\b\\w/g, l => l.toUpperCase())}`,\n resourceUri,\n {\n description: `CodeQL ${resourceType.replace('-', ' ')} guide for ${langResource.language}`,\n mimeType: 'text/markdown'\n },\n async () => {\n const content = loadResourceContent(filePath);\n \n if (!content) {\n return {\n contents: [{\n uri: resourceUri,\n mimeType: 'text/markdown',\n text: `# ${langResource.language.toUpperCase()} ${resourceType.replace('-', ' ').replace(/\\b\\w/g, l => l.toUpperCase())}\\n\\nResource file not found or could not be loaded.`\n }]\n };\n }\n \n return {\n contents: [{\n uri: resourceUri,\n mimeType: 'text/markdown',\n text: content\n }]\n };\n }\n );\n }\n }\n}\n\n/**\n * Register all language-specific resources\n */\nexport function registerLanguageResources(server: McpServer): void {\n logger.info('Registering language-specific resources...');\n \n // Register AST references for all languages\n registerLanguageASTResources(server);\n \n // Register security patterns for languages that have them\n registerLanguageSecurityResources(server);\n \n // Register additional resources (like Go's dataflow patterns)\n registerLanguageAdditionalResources(server);\n \n logger.info(`Registered resources for ${LANGUAGE_RESOURCES.length} languages`);\n}", "/**\n * Type definitions and constants for language-specific resources\n */\n\n// Language mappings to resource files\nexport interface LanguageResource {\n language: string;\n astFile?: string;\n securityFile?: string;\n additionalFiles?: Record;\n}\n\nexport const LANGUAGE_RESOURCES: LanguageResource[] = [\n {\n language: 'actions',\n astFile: 'ql/languages/actions/tools/dev/actions_ast.prompt.md'\n },\n {\n language: 'cpp',\n astFile: 'ql/languages/cpp/tools/dev/cpp_ast.prompt.md',\n securityFile: 'ql/languages/cpp/tools/dev/cpp_security_query_guide.prompt.md'\n },\n {\n language: 'csharp',\n astFile: 'ql/languages/csharp/tools/dev/csharp_ast.prompt.md',\n securityFile: 'ql/languages/csharp/tools/dev/csharp_security_query_guide.prompt.md'\n },\n {\n language: 'go',\n astFile: 'ql/languages/go/tools/dev/go_ast.prompt.md',\n securityFile: 'ql/languages/go/tools/dev/go_security_query_guide.prompt.md',\n additionalFiles: {\n 'dataflow': 'ql/languages/go/tools/dev/go_dataflow.prompt.md',\n 'library-modeling': 'ql/languages/go/tools/dev/go_library_modeling.prompt.md',\n 'basic-queries': 'ql/languages/go/tools/dev/go_basic_queries.prompt.md'\n }\n },\n {\n language: 'java',\n astFile: 'ql/languages/java/tools/dev/java_ast.prompt.md'\n },\n {\n language: 'javascript',\n astFile: 'ql/languages/javascript/tools/dev/javascript_ast.prompt.md',\n securityFile: 'ql/languages/javascript/tools/dev/javascript_security_query_guide.prompt.md'\n },\n {\n language: 'python',\n astFile: 'ql/languages/python/tools/dev/python_ast.prompt.md',\n securityFile: 'ql/languages/python/tools/dev/python_security_query_guide.prompt.md'\n },\n {\n language: 'ql',\n astFile: 'ql/languages/ql/tools/dev/ql_ast.prompt.md'\n },\n {\n language: 'ruby',\n astFile: 'ql/languages/ruby/tools/dev/ruby_ast.prompt.md'\n }\n];", "/**\n * MCP Server workflow prompts for CodeQL development\n *\n * All prompt content is loaded from .prompt.md files in this directory.\n * This file only handles prompt registration and parameter processing.\n */\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport { basename } from 'path';\nimport { loadPromptTemplate, processPromptTemplate } from './prompt-loader';\nimport { logger } from '../utils/logger';\n\n/** Supported CodeQL languages for tools queries */\nexport const SUPPORTED_LANGUAGES = [\n 'actions',\n 'cpp',\n 'csharp',\n 'go',\n 'java',\n 'javascript',\n 'python',\n 'ruby',\n 'swift'\n] as const;\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Exported parameter schemas for each workflow prompt.\n//\n// Extracting the schemas makes it easy to unit-test required vs optional\n// validation independently of the MCP server registration.\n//\n// **Convention for VS Code UX consistency**:\n// Every prompt MUST expose at least one parameter \u2013 even if all parameters\n// are optional \u2013 so that VS Code always displays the parameter input dialog\n// and allows the user to customize the prompt before Copilot Chat processes\n// it. The `description` field on each Zod schema member doubles as the\n// placeholder text shown in the VS Code input box.\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Schema for test_driven_development prompt parameters.\n *\n * - `language` is **required** \u2013 the TDD workflow is language-specific.\n * - `queryName` is optional \u2013 defaults to '[QueryName]' if omitted.\n */\nexport const testDrivenDevelopmentSchema = z.object({\n language: z\n .enum(SUPPORTED_LANGUAGES)\n .describe('Programming language for the query'),\n queryName: z\n .string()\n .optional()\n .describe('Name of the query to develop'),\n});\n\n/**\n * Schema for tools_query_workflow prompt parameters.\n *\n * - `language` and `database` are **required**.\n * - `sourceFiles`, `sourceFunction`, `targetFunction` are optional context.\n */\nexport const toolsQueryWorkflowSchema = z.object({\n database: z\n .string()\n .describe('Path to the CodeQL database'),\n language: z\n .enum(SUPPORTED_LANGUAGES)\n .describe('Programming language for the tools queries'),\n sourceFiles: z\n .string()\n .optional()\n .describe('Comma-separated source file names for PrintAST (e.g., \"main.js,utils.js\")'),\n sourceFunction: z\n .string()\n .optional()\n .describe('Function name for PrintCFG or CallGraphFrom (e.g., \"processData\")'),\n targetFunction: z\n .string()\n .optional()\n .describe('Function name for CallGraphTo (e.g., \"validate\")'),\n});\n\n/**\n * Schema for workshop_creation_workflow prompt parameters.\n * Uses z.coerce.number() for numStages to handle string inputs from VSCode slash commands.\n *\n * - `queryPath` and `language` are **required**.\n * - `workshopName` and `numStages` are optional.\n */\nexport const workshopCreationWorkflowSchema = z.object({\n queryPath: z\n .string()\n .describe('Path to the production-grade CodeQL query (.ql or .qlref)'),\n language: z\n .enum(SUPPORTED_LANGUAGES)\n .describe('Programming language of the query'),\n workshopName: z\n .string()\n .optional()\n .describe('Name for the workshop directory'),\n numStages: z\n .coerce.number()\n .optional()\n .describe('Number of incremental stages (default: 4-8)'),\n});\n\n/**\n * Schema for ql_tdd_basic prompt parameters.\n *\n * All parameters are optional \u2013 but at least one should be present so the\n * VS Code quick-pick dialog appears.\n */\nexport const qlTddBasicSchema = z.object({\n language: z\n .enum(SUPPORTED_LANGUAGES)\n .optional()\n .describe('Programming language for the query (optional)'),\n queryName: z\n .string()\n .optional()\n .describe('Name of the query to develop'),\n});\n\n/**\n * Schema for ql_tdd_advanced prompt parameters.\n *\n * All parameters are optional.\n */\nexport const qlTddAdvancedSchema = z.object({\n database: z\n .string()\n .optional()\n .describe('Path to the CodeQL database for analysis'),\n language: z\n .enum(SUPPORTED_LANGUAGES)\n .optional()\n .describe('Programming language for the query (optional)'),\n queryName: z\n .string()\n .optional()\n .describe('Name of the query to develop'),\n});\n\n/**\n * Schema for sarif_rank_false_positives / sarif_rank_true_positives.\n *\n * Both parameters are optional.\n */\nexport const sarifRankSchema = z.object({\n queryId: z\n .string()\n .optional()\n .describe('CodeQL query/rule identifier'),\n sarifPath: z\n .string()\n .optional()\n .describe('Path to the SARIF file to analyze'),\n});\n\n/**\n * Schema for explain_codeql_query prompt parameters.\n *\n * - `queryPath` and `language` are **required**.\n * - `databasePath` is optional.\n */\nexport const explainCodeqlQuerySchema = z.object({\n databasePath: z\n .string()\n .optional()\n .describe('Optional path to a real CodeQL database for profiling'),\n language: z\n .enum(SUPPORTED_LANGUAGES)\n .describe('Programming language of the query'),\n queryPath: z\n .string()\n .describe('Path to the CodeQL query file (.ql or .qlref)'),\n});\n\n/**\n * Schema for document_codeql_query prompt parameters.\n *\n * - `queryPath` and `language` are **required**.\n */\nexport const documentCodeqlQuerySchema = z.object({\n language: z\n .enum(SUPPORTED_LANGUAGES)\n .describe('Programming language of the query'),\n queryPath: z\n .string()\n .describe('Path to the CodeQL query file (.ql or .qlref)'),\n});\n\n/**\n * Schema for ql_lsp_iterative_development prompt parameters.\n *\n * All parameters are optional.\n */\nexport const qlLspIterativeDevelopmentSchema = z.object({\n language: z\n .enum(SUPPORTED_LANGUAGES)\n .optional()\n .describe('Programming language for the query'),\n queryPath: z\n .string()\n .optional()\n .describe('Path to the query file being developed'),\n workspaceUri: z\n .string()\n .optional()\n .describe('Workspace URI for LSP dependency resolution'),\n});\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Prompt names (exported for testing)\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** Names of every workflow prompt registered with the MCP server. */\nexport const WORKFLOW_PROMPT_NAMES = [\n 'document_codeql_query',\n 'explain_codeql_query',\n 'ql_lsp_iterative_development',\n 'ql_tdd_advanced',\n 'ql_tdd_basic',\n 'sarif_rank_false_positives',\n 'sarif_rank_true_positives',\n 'test_driven_development',\n 'tools_query_workflow',\n 'workshop_creation_workflow',\n] as const;\n\n/**\n * Register MCP workflow prompts\n *\n * Each prompt loads its content from a corresponding .prompt.md file\n * and processes any parameter substitutions.\n *\n * **UX note**: Every prompt schema is passed to `server.prompt()` so that\n * VS Code always displays the parameter-input quick-pick before the prompt\n * is sent to Copilot Chat. This lets users review and customise the values.\n */\nexport function registerWorkflowPrompts(server: McpServer): void {\n // Test-Driven Development Prompt\n server.prompt(\n 'test_driven_development',\n 'Test-driven development workflow for CodeQL queries using MCP tools',\n testDrivenDevelopmentSchema.shape,\n async ({ language, queryName }) => {\n const template = loadPromptTemplate('ql-tdd-basic.prompt.md');\n const content = processPromptTemplate(template, {\n language,\n queryName: queryName || '[QueryName]'\n });\n\n return {\n messages: [\n {\n role: 'user',\n content: {\n type: 'text',\n text: `## Context\\n\\n- **Language**: ${language}\\n${queryName ? `- **Query Name**: ${queryName}\\n` : ''}\\n${content}`\n }\n }\n ]\n };\n }\n );\n\n // Tools Query Workflow Prompt\n server.prompt(\n 'tools_query_workflow',\n 'Guide for using built-in tools queries (PrintAST, PrintCFG, CallGraphFrom, CallGraphTo) to understand code structure',\n toolsQueryWorkflowSchema.shape,\n async ({\n language,\n database,\n sourceFiles,\n sourceFunction,\n targetFunction\n }) => {\n const template = loadPromptTemplate('tools-query-workflow.prompt.md');\n const content = processPromptTemplate(template, {\n language,\n database\n });\n\n const contextSection = buildToolsQueryContext(\n language,\n database,\n sourceFiles,\n sourceFunction,\n targetFunction\n );\n\n return {\n messages: [\n {\n role: 'user',\n content: {\n type: 'text',\n text: contextSection + content\n }\n }\n ]\n };\n }\n );\n\n // Workshop Creation Workflow Prompt\n server.prompt(\n 'workshop_creation_workflow',\n 'Guide for creating CodeQL query development workshops from production-grade queries',\n workshopCreationWorkflowSchema.shape,\n async ({ queryPath, language, workshopName, numStages }) => {\n const template = loadPromptTemplate('workshop-creation-workflow.prompt.md');\n\n // Derive workshop name from query path if not provided\n const derivedName =\n workshopName ||\n basename(queryPath)\n .replace(/\\.(ql|qlref)$/, '')\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-') ||\n 'codeql-workshop';\n\n const contextSection = buildWorkshopContext(\n queryPath,\n language,\n derivedName,\n numStages\n );\n\n return {\n messages: [\n {\n role: 'user',\n content: {\n type: 'text',\n text: contextSection + template\n }\n }\n ]\n };\n }\n );\n\n // TDD Basic Prompt - Test-Driven Development Checklist\n server.prompt(\n 'ql_tdd_basic',\n 'Test-driven CodeQL query development checklist - write tests first, implement query, iterate until tests pass',\n qlTddBasicSchema.shape,\n async ({ language, queryName }) => {\n const template = loadPromptTemplate('ql-tdd-basic.prompt.md');\n\n let contextSection = '## Your Development Context\\n\\n';\n if (language) {\n contextSection += `- **Language**: ${language}\\n`;\n }\n if (queryName) {\n contextSection += `- **Query Name**: ${queryName}\\n`;\n }\n if (language || queryName) {\n contextSection += '\\n';\n }\n\n return {\n messages: [\n {\n role: 'user',\n content: {\n type: 'text',\n text: contextSection + template\n }\n }\n ]\n };\n }\n );\n\n // TDD Advanced Prompt - Advanced Techniques with AST/CFG/CallGraph\n server.prompt(\n 'ql_tdd_advanced',\n 'Advanced test-driven CodeQL development with AST visualization, control flow, and call graph analysis',\n qlTddAdvancedSchema.shape,\n async ({ language, queryName, database }) => {\n const template = loadPromptTemplate('ql-tdd-advanced.prompt.md');\n\n let contextSection = '## Your Development Context\\n\\n';\n if (language) {\n contextSection += `- **Language**: ${language}\\n`;\n }\n if (queryName) {\n contextSection += `- **Query Name**: ${queryName}\\n`;\n }\n if (database) {\n contextSection += `- **Database**: ${database}\\n`;\n }\n if (language || queryName || database) {\n contextSection += '\\n';\n }\n\n return {\n messages: [\n {\n role: 'user',\n content: {\n type: 'text',\n text: contextSection + template\n }\n }\n ]\n };\n }\n );\n\n // SARIF Rank False Positives Prompt\n server.prompt(\n 'sarif_rank_false_positives',\n 'Analyze SARIF results to identify likely false positives in CodeQL query results',\n sarifRankSchema.shape,\n async ({ queryId, sarifPath }) => {\n const template = loadPromptTemplate('sarif-rank-false-positives.prompt.md');\n\n let contextSection = '## Analysis Context\\n\\n';\n if (queryId) {\n contextSection += `- **Query ID**: ${queryId}\\n`;\n }\n if (sarifPath) {\n contextSection += `- **SARIF File**: ${sarifPath}\\n`;\n }\n if (queryId || sarifPath) {\n contextSection += '\\n';\n }\n\n return {\n messages: [\n {\n role: 'user',\n content: {\n type: 'text',\n text: contextSection + template\n }\n }\n ]\n };\n }\n );\n\n // SARIF Rank True Positives Prompt\n server.prompt(\n 'sarif_rank_true_positives',\n 'Analyze SARIF results to identify likely true positives in CodeQL query results',\n sarifRankSchema.shape,\n async ({ queryId, sarifPath }) => {\n const template = loadPromptTemplate('sarif-rank-true-positives.prompt.md');\n\n let contextSection = '## Analysis Context\\n\\n';\n if (queryId) {\n contextSection += `- **Query ID**: ${queryId}\\n`;\n }\n if (sarifPath) {\n contextSection += `- **SARIF File**: ${sarifPath}\\n`;\n }\n if (queryId || sarifPath) {\n contextSection += '\\n';\n }\n\n return {\n messages: [\n {\n role: 'user',\n content: {\n type: 'text',\n text: contextSection + template\n }\n }\n ]\n };\n }\n );\n\n // Explain CodeQL Query Prompt (for workshop learning content)\n server.prompt(\n 'explain_codeql_query',\n 'Generate detailed explanation of a CodeQL query for workshop learning content - uses MCP tools to gather context and produces both verbal explanations and mermaid evaluation diagrams',\n explainCodeqlQuerySchema.shape,\n async ({ queryPath, language, databasePath }) => {\n const template = loadPromptTemplate('explain-codeql-query.prompt.md');\n\n let contextSection = '## Query to Explain\\n\\n';\n contextSection += `- **Query Path**: ${queryPath}\\n`;\n contextSection += `- **Language**: ${language}\\n`;\n if (databasePath) {\n contextSection += `- **Database Path**: ${databasePath}\\n`;\n }\n contextSection += '\\n';\n\n return {\n messages: [\n {\n role: 'user',\n content: {\n type: 'text',\n text: contextSection + template\n }\n }\n ]\n };\n }\n );\n\n // Document CodeQL Query Prompt\n server.prompt(\n 'document_codeql_query',\n 'Create or update documentation for a CodeQL query - generates standardized markdown documentation as a sibling file to the query',\n documentCodeqlQuerySchema.shape,\n async ({ queryPath, language }) => {\n const template = loadPromptTemplate('document-codeql-query.prompt.md');\n\n const contextSection = `## Query to Document\n\n- **Query Path**: ${queryPath}\n- **Language**: ${language}\n\n`;\n\n return {\n messages: [\n {\n role: 'user',\n content: {\n type: 'text',\n text: contextSection + template\n }\n }\n ]\n };\n }\n );\n\n // LSP-powered Iterative Development Prompt\n server.prompt(\n 'ql_lsp_iterative_development',\n 'Iterative CodeQL query development using LSP tools for completion, navigation, and validation',\n qlLspIterativeDevelopmentSchema.shape,\n async ({ language, queryPath, workspaceUri }) => {\n const template = loadPromptTemplate('ql-lsp-iterative-development.prompt.md');\n\n let contextSection = '## Your Development Context\\n\\n';\n if (language) {\n contextSection += `- **Language**: ${language}\\n`;\n }\n if (queryPath) {\n contextSection += `- **Query Path**: ${queryPath}\\n`;\n }\n if (workspaceUri) {\n contextSection += `- **Workspace URI**: ${workspaceUri}\\n`;\n }\n if (language || queryPath || workspaceUri) {\n contextSection += '\\n';\n }\n\n return {\n messages: [\n {\n role: 'user',\n content: {\n type: 'text',\n text: contextSection + template,\n },\n },\n ],\n };\n }\n );\n\n logger.info(`Registered ${WORKFLOW_PROMPT_NAMES.length} workflow prompts`);\n}\n\n/**\n * Build context section for tools query workflow\n */\nexport function buildToolsQueryContext(\n language: string,\n database: string,\n sourceFiles?: string,\n sourceFunction?: string,\n targetFunction?: string\n): string {\n const lines = [\n '## Your Context',\n '',\n `- **Language**: ${language}`,\n `- **Database**: ${database}`\n ];\n\n if (sourceFiles) {\n lines.push(`- **Source Files**: ${sourceFiles}`);\n }\n if (sourceFunction) {\n lines.push(`- **Source Function**: ${sourceFunction}`);\n }\n if (targetFunction) {\n lines.push(`- **Target Function**: ${targetFunction}`);\n }\n\n lines.push('', '## Recommended Next Steps', '');\n\n if (sourceFiles) {\n lines.push(\n `1. Run \\`codeql_query_run\\` with queryName=\"PrintAST\", sourceFiles=\"${sourceFiles}\"`\n );\n } else {\n lines.push('1. Identify source files to analyze with PrintAST');\n }\n\n if (sourceFunction) {\n lines.push(\n `2. Run \\`codeql_query_run\\` with queryName=\"PrintCFG\" or \"CallGraphFrom\", sourceFunction=\"${sourceFunction}\"`\n );\n } else {\n lines.push(\n '2. Identify key functions for CFG or call graph analysis'\n );\n }\n\n if (targetFunction) {\n lines.push(\n `3. Run \\`codeql_query_run\\` with queryName=\"CallGraphTo\", targetFunction=\"${targetFunction}\"`\n );\n } else {\n lines.push('3. Identify target functions to find callers');\n }\n\n lines.push('', '');\n return lines.join('\\n');\n}\n\n/**\n * Build context section for workshop creation workflow\n */\nexport function buildWorkshopContext(\n queryPath: string,\n language: string,\n workshopName: string,\n numStages?: number\n): string {\n return `## Your Workshop Context\n\n- **Target Query**: ${queryPath}\n- **Language**: ${language}\n- **Workshop Name**: ${workshopName}\n- **Suggested Stages**: ${numStages || '4-8 (auto-detect based on query complexity)'}\n\n## Immediate Actions\n\n1. **Locate query files**: Use \\`find_codeql_query_files\\` with queryPath=\"${queryPath}\"\n2. **Understand query for learning content**: Use the \\`explain_codeql_query\\` prompt with queryPath=\"${queryPath}\" and language=\"${language}\"\n3. **Document each workshop stage**: Use the \\`document_codeql_query\\` prompt to create/update documentation for each solution query\n4. **Verify tests pass**: Use \\`codeql_test_run\\` on existing tests\n5. **Run tools queries**: Generate AST/CFG understanding for workshop materials\n\n`;\n}\n", "/**\n * Utility functions for loading prompt template files\n */\n\nimport { readFileSync } from 'fs';\nimport { join, dirname } from 'path';\nimport { fileURLToPath } from 'url';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\n\n/**\n * Load a prompt template from a .prompt.md file\n */\nexport function loadPromptTemplate(promptFileName: string): string {\n try {\n const promptPath = join(__dirname, promptFileName);\n return readFileSync(promptPath, 'utf-8');\n } catch (error) {\n return `Prompt template '${promptFileName}' not available: ${error instanceof Error ? error.message : 'Unknown error'}`;\n }\n}\n\n/**\n * Process prompt template by replacing placeholders with actual values\n */\nexport function processPromptTemplate(template: string, variables: Record): string {\n let processed = template;\n \n // Replace variables in the format {{variable}} or {variable}\n for (const [key, value] of Object.entries(variables)) {\n const patterns = [\n new RegExp(`\\\\{\\\\{${key}\\\\}\\\\}`, 'g'),\n new RegExp(`\\\\{${key}\\\\}`, 'g')\n ];\n \n for (const pattern of patterns) {\n processed = processed.replace(pattern, value);\n }\n }\n \n return processed;\n}", "/**\n * Monitoring Tools - MCP tool implementations for session management and reporting\n * Provides the MCP Tool APIs specified in the monitoring specification\n */\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport { randomUUID } from 'crypto';\nimport { sessionDataManager } from '../lib/session-data-manager';\nimport {\n QueryDevelopmentSession,\n SessionFilter,\n QualityScoreRecord,\n ComparisonReport,\n AggregateReport,\n ExportResult,\n FunctionalTestResult,\n} from '../types/monitoring';\nimport { logger } from '../utils/logger';\n\n/**\n * Register all monitoring and reporting tools with the MCP server\n * Note: These tools are opt-in and disabled by default for end-users.\n * Set enableMonitoringTools: true in monitoring config to enable them.\n */\nexport function registerMonitoringTools(server: McpServer): void {\n const config = sessionDataManager.getConfig();\n \n // Check if monitoring tools are enabled (opt-in, disabled by default)\n if (!config.enableMonitoringTools) {\n logger.info('Monitoring tools are disabled (opt-in). Set enableMonitoringTools: true to enable session_* tools.');\n return;\n }\n\n // Session Management Tools - session_start removed per feedback (auto-creation instead)\n registerSessionEndTool(server);\n registerSessionGetTool(server);\n registerSessionListTool(server);\n registerSessionUpdateStateTool(server);\n\n // Session Analytics Tools\n registerSessionGetCallHistoryTool(server);\n registerSessionGetTestHistoryTool(server);\n registerSessionGetScoreHistoryTool(server);\n registerSessionCalculateCurrentScoreTool(server);\n\n // Batch Operations Tools\n registerSessionsCompareTool(server);\n registerSessionsAggregateTool(server);\n registerSessionsExportTool(server);\n\n // Note: Functional Testing Support Tools are internal only, not exposed as MCP tools\n\n logger.info('Registered monitoring and reporting tools');\n}\n\n/**\n * Session Management Tools\n */\n\n// session_start tool removed - sessions are now auto-created when needed\n// Sessions are automatically created when MCP tools are called with queryPath\n// If explicit session creation is needed, provide sessionId=null and it will auto-create\n\nfunction registerSessionEndTool(server: McpServer): void {\n server.tool(\n 'session_end',\n 'End a query development session with final status',\n {\n sessionId: z.string().describe('ID of the session to end'),\n status: z.enum(['completed', 'failed', 'abandoned']).describe('Final status of the session'),\n },\n async ({ sessionId, status }) => {\n try {\n const session = await sessionDataManager.endSession(sessionId, status);\n \n if (!session) {\n return {\n content: [\n {\n type: 'text',\n text: `Session not found: ${sessionId}`,\n },\n ],\n isError: true,\n };\n }\n\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify(session, null, 2),\n },\n ],\n };\n } catch (error) {\n logger.error('Error ending session:', error);\n return {\n content: [\n {\n type: 'text',\n text: `Error ending session: ${error instanceof Error ? error.message : 'Unknown error'}`,\n },\n ],\n isError: true,\n };\n }\n }\n );\n}\n\nfunction registerSessionGetTool(server: McpServer): void {\n server.tool(\n 'session_get',\n 'Get complete details of a specific query development session',\n {\n sessionId: z.string().describe('ID of the session to retrieve'),\n },\n async ({ sessionId }) => {\n try {\n const session = await sessionDataManager.getSession(sessionId);\n \n if (!session) {\n return {\n content: [\n {\n type: 'text',\n text: `Session not found: ${sessionId}`,\n },\n ],\n isError: true,\n };\n }\n\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify(session, null, 2),\n },\n ],\n };\n } catch (error) {\n logger.error('Error getting session:', error);\n return {\n content: [\n {\n type: 'text',\n text: `Error getting session: ${error instanceof Error ? error.message : 'Unknown error'}`,\n },\n ],\n isError: true,\n };\n }\n }\n );\n}\n\nfunction registerSessionListTool(server: McpServer): void {\n server.tool(\n 'session_list',\n 'List query development sessions with optional filtering',\n {\n queryPath: z.string().optional().describe('Filter by query path (partial match)'),\n status: z.string().optional().describe('Filter by session status'),\n dateRange: z.array(z.string()).length(2).optional().describe('Filter by date range [start, end] (ISO timestamps)'),\n language: z.string().optional().describe('Filter by programming language'),\n queryType: z.string().optional().describe('Filter by query type'),\n },\n async ({ queryPath, status, dateRange, language, queryType }) => {\n try {\n const filters: SessionFilter = {};\n if (queryPath) filters.queryPath = queryPath;\n if (status) filters.status = status;\n if (dateRange) filters.dateRange = [dateRange[0], dateRange[1]];\n if (language) filters.language = language;\n if (queryType) filters.queryType = queryType;\n\n const sessions = await sessionDataManager.listSessions(\n Object.keys(filters).length > 0 ? filters : undefined\n );\n\n const sessionList = {\n totalSessions: sessions.length,\n sessions: sessions.map(s => ({\n sessionId: s.sessionId,\n queryPath: s.queryPath,\n language: s.language,\n status: s.status,\n startTime: s.startTime,\n endTime: s.endTime,\n mcpCallsCount: s.mcpCalls.length,\n testExecutionsCount: s.testExecutions.length,\n currentScore: s.qualityScores.length > 0 \n ? s.qualityScores[s.qualityScores.length - 1].overallScore \n : null,\n })),\n };\n\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify(sessionList, null, 2),\n },\n ],\n recommendations: generateListRecommendations(sessions),\n };\n } catch (error) {\n logger.error('Error listing sessions:', error);\n return {\n content: [\n {\n type: 'text',\n text: `Error listing sessions: ${error instanceof Error ? error.message : 'Unknown error'}`,\n },\n ],\n isError: true,\n };\n }\n }\n );\n}\n\nfunction registerSessionUpdateStateTool(server: McpServer): void {\n server.tool(\n 'session_update_state',\n 'Update the current state of a query development session',\n {\n sessionId: z.string().describe('ID of the session to update'),\n filesPresent: z.array(z.string()).optional().describe('List of files present in the query development'),\n compilationStatus: z.enum(['unknown', 'success', 'failed']).optional().describe('Current compilation status'),\n testStatus: z.enum(['unknown', 'passing', 'failing', 'no_tests']).optional().describe('Current test status'),\n documentationStatus: z.enum(['unknown', 'present', 'missing', 'incomplete']).optional().describe('Documentation status'),\n },\n async ({ sessionId, filesPresent, compilationStatus, testStatus, documentationStatus }) => {\n try {\n const stateUpdate: Record = {};\n if (filesPresent !== undefined) stateUpdate.filesPresent = filesPresent;\n if (compilationStatus !== undefined) stateUpdate.compilationStatus = compilationStatus;\n if (testStatus !== undefined) stateUpdate.testStatus = testStatus;\n if (documentationStatus !== undefined) stateUpdate.documentationStatus = documentationStatus;\n\n const session = await sessionDataManager.updateSessionState(sessionId, stateUpdate);\n \n if (!session) {\n return {\n content: [\n {\n type: 'text',\n text: `Session not found: ${sessionId}`,\n },\n ],\n isError: true,\n };\n }\n\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify(session, null, 2),\n },\n ],\n recommendations: generateRecommendations(session, 'session_update_state'),\n };\n } catch (error) {\n logger.error('Error updating session state:', error);\n return {\n content: [\n {\n type: 'text',\n text: `Error updating session state: ${error instanceof Error ? error.message : 'Unknown error'}`,\n },\n ],\n isError: true,\n };\n }\n }\n );\n}\n\n/**\n * Session Analytics Tools\n */\n\nfunction registerSessionGetCallHistoryTool(server: McpServer): void {\n server.tool(\n 'session_get_call_history',\n 'Get MCP call history for a specific session',\n {\n sessionId: z.string().describe('ID of the session'),\n limit: z.number().optional().describe('Maximum number of calls to return (most recent first)'),\n },\n async ({ sessionId, limit }) => {\n try {\n const session = await sessionDataManager.getSession(sessionId);\n \n if (!session) {\n return {\n content: [\n {\n type: 'text',\n text: `Session not found: ${sessionId}`,\n },\n ],\n isError: true,\n };\n }\n\n let calls = [...session.mcpCalls].reverse(); // Most recent first\n if (limit && limit > 0) {\n calls = calls.slice(0, limit);\n }\n\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify({\n sessionId,\n totalCalls: session.mcpCalls.length,\n callHistory: calls,\n }, null, 2),\n },\n ],\n };\n } catch (error) {\n logger.error('Error getting call history:', error);\n return {\n content: [\n {\n type: 'text',\n text: `Error getting call history: ${error instanceof Error ? error.message : 'Unknown error'}`,\n },\n ],\n isError: true,\n };\n }\n }\n );\n}\n\nfunction registerSessionGetTestHistoryTool(server: McpServer): void {\n server.tool(\n 'session_get_test_history',\n 'Get test execution history for a specific session',\n {\n sessionId: z.string().describe('ID of the session'),\n limit: z.number().optional().describe('Maximum number of test executions to return (most recent first)'),\n },\n async ({ sessionId, limit }) => {\n try {\n const session = await sessionDataManager.getSession(sessionId);\n \n if (!session) {\n return {\n content: [\n {\n type: 'text',\n text: `Session not found: ${sessionId}`,\n },\n ],\n isError: true,\n };\n }\n\n let tests = [...session.testExecutions].reverse(); // Most recent first\n if (limit && limit > 0) {\n tests = tests.slice(0, limit);\n }\n\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify({\n sessionId,\n totalTests: session.testExecutions.length,\n testHistory: tests,\n }, null, 2),\n },\n ],\n };\n } catch (error) {\n logger.error('Error getting test history:', error);\n return {\n content: [\n {\n type: 'text',\n text: `Error getting test history: ${error instanceof Error ? error.message : 'Unknown error'}`,\n },\n ],\n isError: true,\n };\n }\n }\n );\n}\n\nfunction registerSessionGetScoreHistoryTool(server: McpServer): void {\n server.tool(\n 'session_get_score_history',\n 'Get quality score history for a specific session',\n {\n sessionId: z.string().describe('ID of the session'),\n limit: z.number().optional().describe('Maximum number of scores to return (most recent first)'),\n },\n async ({ sessionId, limit }) => {\n try {\n const session = await sessionDataManager.getSession(sessionId);\n \n if (!session) {\n return {\n content: [\n {\n type: 'text',\n text: `Session not found: ${sessionId}`,\n },\n ],\n isError: true,\n };\n }\n\n let scores = [...session.qualityScores].reverse(); // Most recent first\n if (limit && limit > 0) {\n scores = scores.slice(0, limit);\n }\n\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify({\n sessionId,\n totalScores: session.qualityScores.length,\n scoreHistory: scores,\n }, null, 2),\n },\n ],\n };\n } catch (error) {\n logger.error('Error getting score history:', error);\n return {\n content: [\n {\n type: 'text',\n text: `Error getting score history: ${error instanceof Error ? error.message : 'Unknown error'}`,\n },\n ],\n isError: true,\n };\n }\n }\n );\n}\n\nfunction registerSessionCalculateCurrentScoreTool(server: McpServer): void {\n server.tool(\n 'session_calculate_current_score',\n 'Calculate current quality score for a session based on its state',\n {\n sessionId: z.string().describe('ID of the session'),\n },\n async ({ sessionId }) => {\n try {\n const session = await sessionDataManager.getSession(sessionId);\n \n if (!session) {\n return {\n content: [\n {\n type: 'text',\n text: `Session not found: ${sessionId}`,\n },\n ],\n isError: true,\n };\n }\n\n // Calculate quality score based on current state\n const scoreRecord = calculateQualityScore(session);\n \n // Add the score to the session\n await sessionDataManager.addQualityScore(sessionId, scoreRecord);\n \n // Get updated session with new score\n const updatedSession = await sessionDataManager.getSession(sessionId);\n\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify(scoreRecord, null, 2),\n },\n ],\n recommendations: generateRecommendations(updatedSession, 'session_calculate_current_score'),\n };\n } catch (error) {\n logger.error('Error calculating current score:', error);\n return {\n content: [\n {\n type: 'text',\n text: `Error calculating current score: ${error instanceof Error ? error.message : 'Unknown error'}`,\n },\n ],\n isError: true,\n };\n }\n }\n );\n}\n\n/**\n * Batch Operations Tools\n */\n\nfunction registerSessionsCompareTool(server: McpServer): void {\n server.tool(\n 'sessions_compare',\n 'Compare multiple query development sessions across specified dimensions',\n {\n sessionIds: z.array(z.string()).describe('Array of session IDs to compare'),\n dimensions: z.array(z.string()).optional().describe('Specific dimensions to compare (default: all)'),\n },\n async ({ sessionIds, dimensions }) => {\n try {\n const sessions = await Promise.all(\n sessionIds.map(id => sessionDataManager.getSession(id))\n );\n\n const validSessions = sessions.filter(s => s !== null) as QueryDevelopmentSession[];\n \n if (validSessions.length === 0) {\n return {\n content: [\n {\n type: 'text',\n text: 'No valid sessions found for comparison',\n },\n ],\n isError: true,\n };\n }\n\n const comparison = await compareSessions(validSessions, dimensions);\n\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify(comparison, null, 2),\n },\n ],\n };\n } catch (error) {\n logger.error('Error comparing sessions:', error);\n return {\n content: [\n {\n type: 'text',\n text: `Error comparing sessions: ${error instanceof Error ? error.message : 'Unknown error'}`,\n },\n ],\n isError: true,\n };\n }\n }\n );\n}\n\nfunction registerSessionsAggregateTool(server: McpServer): void {\n server.tool(\n 'sessions_aggregate',\n 'Generate aggregate insights from multiple sessions based on filters',\n {\n queryPath: z.string().optional().describe('Filter by query path (partial match)'),\n status: z.string().optional().describe('Filter by session status'),\n dateRange: z.array(z.string()).length(2).optional().describe('Filter by date range [start, end] (ISO timestamps)'),\n language: z.string().optional().describe('Filter by programming language'),\n queryType: z.string().optional().describe('Filter by query type'),\n },\n async ({ queryPath, status, dateRange, language, queryType }) => {\n try {\n const filters: SessionFilter = {};\n if (queryPath) filters.queryPath = queryPath;\n if (status) filters.status = status;\n if (dateRange) filters.dateRange = [dateRange[0], dateRange[1]];\n if (language) filters.language = language;\n if (queryType) filters.queryType = queryType;\n\n const sessions = await sessionDataManager.listSessions(\n Object.keys(filters).length > 0 ? filters : undefined\n );\n\n const aggregate = await aggregateSessions(sessions, filters);\n\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify(aggregate, null, 2),\n },\n ],\n };\n } catch (error) {\n logger.error('Error aggregating sessions:', error);\n return {\n content: [\n {\n type: 'text',\n text: `Error aggregating sessions: ${error instanceof Error ? error.message : 'Unknown error'}`,\n },\n ],\n isError: true,\n };\n }\n }\n );\n}\n\nfunction registerSessionsExportTool(server: McpServer): void {\n server.tool(\n 'sessions_export',\n 'Export session data in specified format for external analysis',\n {\n sessionIds: z.array(z.string()).describe('Array of session IDs to export'),\n format: z.enum(['json', 'html', 'markdown']).optional().default('json').describe('Export format'),\n },\n async ({ sessionIds, format = 'json' }) => {\n try {\n const sessions = await Promise.all(\n sessionIds.map(id => sessionDataManager.getSession(id))\n );\n\n const validSessions = sessions.filter(s => s !== null) as QueryDevelopmentSession[];\n \n if (validSessions.length === 0) {\n return {\n content: [\n {\n type: 'text',\n text: 'No valid sessions found for export',\n },\n ],\n isError: true,\n };\n }\n\n const exportResult = await exportSessions(validSessions, format);\n\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify(exportResult, null, 2),\n },\n ],\n };\n } catch (error) {\n logger.error('Error exporting sessions:', error);\n return {\n content: [\n {\n type: 'text',\n text: `Error exporting sessions: ${error instanceof Error ? error.message : 'Unknown error'}`,\n },\n ],\n isError: true,\n };\n }\n }\n );\n}\n\n/**\n * Helper Functions\n */\n\n/**\n * Calculate quality score for a session based on current state and history\n */\nfunction calculateQualityScore(session: QueryDevelopmentSession): QualityScoreRecord {\n const timestamp = new Date().toISOString();\n \n // Calculate syntactic correctness (25%)\n const syntacticCorrectness = session.currentState.compilationStatus === 'success' ? 100 :\n session.currentState.compilationStatus === 'failed' ? 0 : 50;\n\n // Calculate test coverage and results (30%)\n const testCoverageResults = session.currentState.testStatus === 'passing' ? 100 :\n session.currentState.testStatus === 'failing' ? 25 :\n session.currentState.testStatus === 'no_tests' ? 0 : 50;\n\n // Calculate documentation quality (20%)\n const documentationQuality = session.currentState.documentationStatus === 'present' ? 100 :\n session.currentState.documentationStatus === 'incomplete' ? 60 :\n session.currentState.documentationStatus === 'missing' ? 0 : 50;\n\n // Calculate functional correctness (25%) - based on successful test runs\n const successfulTests = session.testExecutions.filter(t => t.success && t.type === 'test_run').length;\n const totalTests = session.testExecutions.filter(t => t.type === 'test_run').length;\n const functionalCorrectness = totalTests > 0 ? (successfulTests / totalTests) * 100 : 50;\n\n // Calculate overall score\n const overallScore = Math.round(\n (syntacticCorrectness * 0.25) +\n (testCoverageResults * 0.30) +\n (documentationQuality * 0.20) +\n (functionalCorrectness * 0.25)\n );\n\n // Determine grade\n const grade = overallScore >= 90 ? 'A' :\n overallScore >= 80 ? 'B' :\n overallScore >= 70 ? 'C' :\n overallScore >= 60 ? 'D' : 'F';\n\n // Generate recommendations\n const recommendations: string[] = [];\n if (syntacticCorrectness < 100) {\n recommendations.push('Fix compilation errors to improve syntactic correctness');\n }\n if (testCoverageResults < 70) {\n recommendations.push('Add comprehensive tests and ensure they pass');\n }\n if (documentationQuality < 80) {\n recommendations.push('Add or improve query documentation with examples');\n }\n if (functionalCorrectness < 80) {\n recommendations.push('Improve test pass rate and verify query logic');\n }\n\n return {\n scoreId: randomUUID(),\n timestamp,\n overallScore,\n dimensions: {\n syntacticCorrectness,\n testCoverageResults,\n documentationQuality,\n functionalCorrectness,\n },\n grade,\n recommendations,\n };\n}\n\n/**\n * Compare multiple sessions\n */\nasync function compareSessions(\n sessions: QueryDevelopmentSession[],\n dimensions?: string[]\n): Promise {\n const timestamp = new Date().toISOString();\n const sessionIds = sessions.map(s => s.sessionId);\n \n const results: Record = {\n sessionCount: sessions.length,\n sessionOverview: sessions.map(s => ({\n sessionId: s.sessionId,\n queryPath: s.queryPath,\n status: s.status,\n mcpCallsCount: s.mcpCalls.length,\n duration: s.endTime ? \n new Date(s.endTime).getTime() - new Date(s.startTime).getTime() : \n new Date().getTime() - new Date(s.startTime).getTime(),\n currentScore: s.qualityScores.length > 0 ? \n s.qualityScores[s.qualityScores.length - 1].overallScore : null,\n })),\n };\n\n if (!dimensions || dimensions.includes('quality')) {\n const qualityScores = sessions.map(s => \n s.qualityScores.length > 0 ? s.qualityScores[s.qualityScores.length - 1] : null\n );\n results.qualityComparison = {\n averageScore: qualityScores\n .filter(q => q !== null)\n .reduce((sum, q) => sum + q!.overallScore, 0) / qualityScores.filter(q => q !== null).length,\n scoreRange: {\n min: Math.min(...qualityScores.filter(q => q !== null).map(q => q!.overallScore)),\n max: Math.max(...qualityScores.filter(q => q !== null).map(q => q!.overallScore)),\n },\n };\n }\n\n if (!dimensions || dimensions.includes('activity')) {\n results.activityComparison = {\n totalMCPCalls: sessions.reduce((sum, s) => sum + s.mcpCalls.length, 0),\n averageCallsPerSession: sessions.reduce((sum, s) => sum + s.mcpCalls.length, 0) / sessions.length,\n mostActiveTool: getMostUsedTool(sessions),\n };\n }\n\n return {\n sessionIds,\n dimensions: dimensions || ['all'],\n timestamp,\n results,\n };\n}\n\n/**\n * Aggregate insights from multiple sessions\n */\nasync function aggregateSessions(\n sessions: QueryDevelopmentSession[],\n filters: SessionFilter\n): Promise {\n const timestamp = new Date().toISOString();\n \n const completedSessions = sessions.filter(s => s.status === 'completed');\n const successRate = sessions.length > 0 ? completedSessions.length / sessions.length : 0;\n \n const qualityScores = sessions\n .map(s => s.qualityScores.length > 0 ? s.qualityScores[s.qualityScores.length - 1].overallScore : null)\n .filter(score => score !== null) as number[];\n \n const averageQualityScore = qualityScores.length > 0 ? \n qualityScores.reduce((sum, score) => sum + score, 0) / qualityScores.length : 0;\n\n const commonPatterns = identifyCommonPatterns(sessions);\n const recommendations = generateAggregateRecommendations(sessions);\n\n return {\n filters,\n timestamp,\n totalSessions: sessions.length,\n successRate,\n averageQualityScore,\n commonPatterns,\n recommendations,\n };\n}\n\n/**\n * Export sessions in specified format\n */\nasync function exportSessions(\n sessions: QueryDevelopmentSession[],\n format: 'json' | 'html' | 'markdown'\n): Promise {\n const timestamp = new Date().toISOString();\n const filename = `session-export-${timestamp.replace(/[:.]/g, '-')}.${format}`;\n \n let content: string;\n \n switch (format) {\n case 'json':\n content = JSON.stringify(sessions, null, 2);\n break;\n case 'html':\n content = generateHTMLReport(sessions);\n break;\n case 'markdown':\n content = generateMarkdownReport(sessions);\n break;\n }\n\n return {\n format,\n filename,\n content,\n timestamp,\n };\n}\n\n/**\n * Utility functions\n */\n\nfunction getMostUsedTool(sessions: QueryDevelopmentSession[]): string {\n const toolCounts: Record = {};\n \n sessions.forEach(session => {\n session.mcpCalls.forEach(call => {\n toolCounts[call.toolName] = (toolCounts[call.toolName] || 0) + 1;\n });\n });\n\n return Object.entries(toolCounts)\n .sort(([, a], [, b]) => b - a)[0]?.[0] || 'none';\n}\n\nfunction identifyCommonPatterns(sessions: QueryDevelopmentSession[]): string[] {\n const patterns: string[] = [];\n \n const commonTools = getMostUsedTool(sessions);\n if (commonTools && commonTools !== 'none') {\n patterns.push(`Most commonly used tool: ${commonTools}`);\n }\n\n const completionRate = sessions.filter(s => s.status === 'completed').length / sessions.length;\n if (completionRate > 0.8) {\n patterns.push('High completion rate indicates effective workflow');\n } else if (completionRate < 0.5) {\n patterns.push('Low completion rate suggests workflow issues');\n }\n\n return patterns;\n}\n\nfunction generateAggregateRecommendations(sessions: QueryDevelopmentSession[]): string[] {\n const recommendations: string[] = [];\n \n const failedSessions = sessions.filter(s => s.status === 'failed');\n if (failedSessions.length > sessions.length * 0.3) {\n recommendations.push('High failure rate - consider improving error handling and guidance');\n }\n\n const averageCallsPerSession = sessions.reduce((sum, s) => sum + s.mcpCalls.length, 0) / sessions.length;\n if (averageCallsPerSession > 20) {\n recommendations.push('High number of MCP calls per session - consider workflow optimization');\n }\n\n return recommendations;\n}\n\nfunction generateHTMLReport(sessions: QueryDevelopmentSession[]): string {\n const html = `\n\n\n\n Session Export Report\n \n\n\n

Query Development Sessions Report

\n

Generated: ${new Date().toISOString()}

\n

Total Sessions: ${sessions.length}

\n \n ${sessions.map(session => `\n
\n

Session: ${session.sessionId}

\n

Query Path: ${session.queryPath}

\n

Status: ${session.status}

\n

Language: ${session.language}

\n

Start Time: ${session.startTime}

\n

MCP Calls: ${session.mcpCalls.length}

\n

Test Executions: ${session.testExecutions.length}

\n

Quality Scores: ${session.qualityScores.length}

\n
\n `).join('')}\n\n`;\n \n return html;\n}\n\nfunction generateMarkdownReport(sessions: QueryDevelopmentSession[]): string {\n const md = `# Query Development Sessions Report\n\nGenerated: ${new Date().toISOString()}\nTotal Sessions: ${sessions.length}\n\n## Session Summary\n\n| Session ID | Query Path | Status | Language | MCP Calls | Test Executions |\n|------------|-----------|--------|----------|-----------|-----------------|\n${sessions.map(session => \n `| ${session.sessionId} | ${session.queryPath} | ${session.status} | ${session.language} | ${session.mcpCalls.length} | ${session.testExecutions.length} |`\n).join('\\n')}\n\n## Detailed Sessions\n\n${sessions.map(session => `\n### Session: ${session.sessionId}\n\n- **Query Path:** ${session.queryPath}\n- **Status:** ${session.status}\n- **Language:** ${session.language}\n- **Start Time:** ${session.startTime}\n- **End Time:** ${session.endTime || 'N/A'}\n- **MCP Calls:** ${session.mcpCalls.length}\n- **Test Executions:** ${session.testExecutions.length}\n- **Quality Scores:** ${session.qualityScores.length}\n\n${session.recommendations.length > 0 ? `\n**Current Recommendations:**\n${session.recommendations.map(rec => `- ${rec}`).join('\\n')}\n` : ''}\n`).join('\\n')}`;\n\n return md;\n}\n\nfunction _calculateAverageDuration(sessions: QueryDevelopmentSession[]): number {\n const completedSessions = sessions.filter(s => s.endTime);\n if (completedSessions.length === 0) return 0;\n\n const totalDuration = completedSessions.reduce((sum, session) => {\n return sum + (new Date(session.endTime!).getTime() - new Date(session.startTime).getTime());\n }, 0);\n\n return totalDuration / completedSessions.length;\n}\n\nfunction _identifyFailureReasons(results: FunctionalTestResult[]): string[] {\n const failedResults = results.filter(r => !r.passed);\n const reasons: Record = {};\n\n failedResults.forEach(result => {\n Object.entries(result.criteria).forEach(([criterion, passed]) => {\n if (!passed) {\n reasons[criterion] = (reasons[criterion] || 0) + 1;\n }\n });\n });\n\n return Object.entries(reasons)\n .sort(([, a], [, b]) => b - a)\n .map(([reason, count]) => `${reason}: ${count} sessions`);\n}\n\n/**\n * Generate recommendations for MCP tool responses\n * Returns a map of MCP primitive paths to recommendation reasons\n */\nfunction generateRecommendations(\n session: QueryDevelopmentSession | null,\n currentTool: string\n): Record {\n if (!session) {\n return {};\n }\n\n const recommendations: Record = {};\n\n // Session state-based recommendations\n if (session.currentState.compilationStatus === 'failed') {\n recommendations['codeql_query_format'] = 'Format query to fix potential syntax issues';\n recommendations['codeql_query_compile'] = 'Recompile after fixing syntax errors';\n } else if (session.currentState.compilationStatus === 'success') {\n if (session.currentState.testStatus === 'unknown' || session.currentState.testStatus === 'no_tests') {\n recommendations['codeql_test_run'] = 'Run tests to validate query functionality';\n } else if (session.currentState.testStatus === 'failing') {\n recommendations['session_get_test_history'] = 'Review test failures to identify issues';\n recommendations['codeql_query_compile'] = 'Verify query logic matches test expectations';\n } else if (session.currentState.testStatus === 'passing') {\n recommendations['session_calculate_current_score'] = 'Calculate quality score for completed query';\n }\n }\n\n // Tool-specific follow-up recommendations\n switch (currentTool) {\n case 'session_get':\n if (session.mcpCalls.length === 0) {\n recommendations['codeql_query_compile'] = 'Start development by compiling the query';\n }\n break;\n case 'session_end':\n if (session.status === 'completed') {\n recommendations['sessions_export'] = 'Export session data for analysis';\n }\n break;\n case 'session_calculate_current_score': {\n const latestScore = session.qualityScores[session.qualityScores.length - 1];\n if (latestScore && latestScore.overallScore < 80) {\n if (latestScore.dimensions.syntacticCorrectness < 100) {\n recommendations['codeql_query_format'] = 'Improve syntax and formatting';\n }\n if (latestScore.dimensions.testCoverageResults < 70) {\n recommendations['codeql_test_run'] = 'Improve test coverage and results';\n }\n }\n break;\n }\n case 'session_update_state':\n // Recommend next logical step based on updated state\n if (session.currentState.compilationStatus === 'success' && session.currentState.testStatus === 'unknown') {\n recommendations['codeql_test_run'] = 'Run tests now that compilation is successful';\n }\n break;\n }\n\n return recommendations;\n}\n\n/**\n * Generate recommendations for session list results\n */\nfunction generateListRecommendations(sessions: QueryDevelopmentSession[]): Record {\n const recommendations: Record = {};\n\n const activeSessions = sessions.filter(s => s.status === 'active');\n const completedSessions = sessions.filter(s => s.status === 'completed');\n\n if (activeSessions.length > 0) {\n recommendations['session_get'] = `Review details of ${activeSessions.length} active session(s)`;\n }\n\n if (completedSessions.length > 1) {\n recommendations['sessions_compare'] = 'Compare completed sessions to identify patterns';\n recommendations['sessions_aggregate'] = 'Generate aggregate insights from multiple sessions';\n }\n\n if (sessions.length > 5) {\n recommendations['sessions_export'] = 'Export session data for comprehensive analysis';\n }\n\n return recommendations;\n}", "function checkArgs(adapter, defaultData) {\n if (adapter === undefined)\n throw new Error('lowdb: missing adapter');\n if (defaultData === undefined)\n throw new Error('lowdb: missing default data');\n}\nexport class Low {\n adapter;\n data;\n constructor(adapter, defaultData) {\n checkArgs(adapter, defaultData);\n this.adapter = adapter;\n this.data = defaultData;\n }\n async read() {\n const data = await this.adapter.read();\n if (data)\n this.data = data;\n }\n async write() {\n if (this.data)\n await this.adapter.write(this.data);\n }\n async update(fn) {\n fn(this.data);\n await this.write();\n }\n}\nexport class LowSync {\n adapter;\n data;\n constructor(adapter, defaultData) {\n checkArgs(adapter, defaultData);\n this.adapter = adapter;\n this.data = defaultData;\n }\n read() {\n const data = this.adapter.read();\n if (data)\n this.data = data;\n }\n write() {\n if (this.data)\n this.adapter.write(this.data);\n }\n update(fn) {\n fn(this.data);\n this.write();\n }\n}\n", "import { readFileSync, renameSync, writeFileSync } from 'node:fs';\nimport { readFile } from 'node:fs/promises';\nimport path from 'node:path';\nimport { Writer } from 'steno';\nexport class TextFile {\n #filename;\n #writer;\n constructor(filename) {\n this.#filename = filename;\n this.#writer = new Writer(filename);\n }\n async read() {\n let data;\n try {\n data = await readFile(this.#filename, 'utf-8');\n }\n catch (e) {\n if (e.code === 'ENOENT') {\n return null;\n }\n throw e;\n }\n return data;\n }\n write(str) {\n return this.#writer.write(str);\n }\n}\nexport class TextFileSync {\n #tempFilename;\n #filename;\n constructor(filename) {\n this.#filename = filename;\n const f = filename.toString();\n this.#tempFilename = path.join(path.dirname(f), `.${path.basename(f)}.tmp`);\n }\n read() {\n let data;\n try {\n data = readFileSync(this.#filename, 'utf-8');\n }\n catch (e) {\n if (e.code === 'ENOENT') {\n return null;\n }\n throw e;\n }\n return data;\n }\n write(str) {\n writeFileSync(this.#tempFilename, str);\n renameSync(this.#tempFilename, this.#filename);\n }\n}\n", "import { TextFile, TextFileSync } from './TextFile.js';\nexport class DataFile {\n #adapter;\n #parse;\n #stringify;\n constructor(filename, { parse, stringify, }) {\n this.#adapter = new TextFile(filename);\n this.#parse = parse;\n this.#stringify = stringify;\n }\n async read() {\n const data = await this.#adapter.read();\n if (data === null) {\n return null;\n }\n else {\n return this.#parse(data);\n }\n }\n write(obj) {\n return this.#adapter.write(this.#stringify(obj));\n }\n}\nexport class DataFileSync {\n #adapter;\n #parse;\n #stringify;\n constructor(filename, { parse, stringify, }) {\n this.#adapter = new TextFileSync(filename);\n this.#parse = parse;\n this.#stringify = stringify;\n }\n read() {\n const data = this.#adapter.read();\n if (data === null) {\n return null;\n }\n else {\n return this.#parse(data);\n }\n }\n write(obj) {\n this.#adapter.write(this.#stringify(obj));\n }\n}\n", "import { DataFile, DataFileSync } from './DataFile.js';\nexport class JSONFile extends DataFile {\n constructor(filename) {\n super(filename, {\n parse: JSON.parse,\n stringify: (data) => JSON.stringify(data, null, 2),\n });\n }\n}\nexport class JSONFileSync extends DataFileSync {\n constructor(filename) {\n super(filename, {\n parse: JSON.parse,\n stringify: (data) => JSON.stringify(data, null, 2),\n });\n }\n}\n", "/**\n * Session Data Management\n * Provides unified JSON storage and session lifecycle management using lowdb\n */\n\nimport { Low } from 'lowdb';\nimport { JSONFileSync } from 'lowdb/node';\nimport { mkdirSync, writeFileSync } from 'fs';\nimport { join } from 'path';\nimport { randomUUID } from 'crypto';\nimport { getProjectTmpBase } from '../utils/temp-dir';\nimport {\n QueryDevelopmentSession,\n QueryState,\n MCPCallRecord,\n TestExecutionRecord,\n QualityScoreRecord,\n SessionFilter,\n MonitoringConfig,\n MonitoringConfigSchema,\n} from '../types/monitoring';\nimport { logger } from '../utils/logger';\n\n/**\n * Database schema for lowdb - sessions only\n */\ninterface SessionDatabase {\n sessions: QueryDevelopmentSession[];\n}\n\n/**\n * Session Data Manager - handles all session persistence and lifecycle\n */\nexport class SessionDataManager {\n private db: Low;\n private config: MonitoringConfig;\n private storageDir: string;\n\n constructor(configOverrides: Partial = {}) {\n this.config = MonitoringConfigSchema.parse({\n ...MonitoringConfigSchema.parse({}),\n ...configOverrides,\n });\n\n this.storageDir = this.config.storageLocation;\n this.ensureStorageDirectory();\n\n const adapter = new JSONFileSync(join(this.storageDir, 'sessions.json'));\n this.db = new Low(adapter, {\n sessions: [],\n });\n\n this.initializeDatabase();\n }\n\n /**\n * Initialize the database and ensure it's properly set up\n */\n async initialize(): Promise {\n await this.initializeDatabase();\n }\n\n /**\n * Initialize the database and ensure it's properly set up\n */\n private async initializeDatabase(): Promise {\n try {\n await this.db.read();\n \n logger.info(`Session data manager initialized with ${this.db.data.sessions.length} sessions`);\n } catch (error) {\n logger.error('Failed to initialize session database:', error);\n throw error;\n }\n }\n\n /**\n * Ensure storage directory structure exists\n */\n private ensureStorageDirectory(): void {\n try {\n // mkdirSync with recursive: true is a no-op if directories already exist\n mkdirSync(this.storageDir, { recursive: true });\n\n // Create subdirectories\n const subdirs = ['sessions-archive', 'exports'];\n for (const subdir of subdirs) {\n mkdirSync(join(this.storageDir, subdir), { recursive: true });\n }\n\n // Use 'wx' flag (exclusive create) to atomically create config only\n // if it doesn't exist, avoiding TOCTOU race (CWE-367).\n const configPath = join(this.storageDir, 'config.json');\n try {\n writeFileSync(configPath, JSON.stringify(this.config, null, 2), { flag: 'wx' });\n } catch (e: unknown) {\n const err = e as { code?: string };\n if (err.code !== 'EEXIST') throw e;\n }\n\n logger.debug(`Storage directory initialized: ${this.storageDir}`);\n } catch (error) {\n logger.error('Failed to create storage directory:', error);\n throw error;\n }\n }\n\n /**\n * Start a new query development session\n */\n async startSession(\n queryPath: string,\n language?: string,\n queryType?: string,\n description?: string\n ): Promise {\n const sessionId = randomUUID();\n const startTime = new Date().toISOString();\n\n const session: QueryDevelopmentSession = {\n sessionId,\n queryPath,\n language: language || 'unknown',\n queryType,\n description,\n startTime,\n status: 'active',\n mcpCalls: [],\n testExecutions: [],\n qualityScores: [],\n currentState: {\n filesPresent: [],\n compilationStatus: 'unknown',\n testStatus: 'unknown',\n documentationStatus: 'unknown',\n lastActivity: startTime,\n },\n recommendations: [],\n };\n\n await this.db.read();\n this.db.data.sessions.push(session);\n await this.db.write();\n\n logger.info(`Started new session: ${sessionId} for query: ${queryPath}`);\n return sessionId;\n }\n\n /**\n * End a session with final status\n */\n async endSession(\n sessionId: string,\n status: 'completed' | 'failed' | 'abandoned'\n ): Promise {\n await this.db.read();\n \n const session = this.db.data.sessions.find(s => s.sessionId === sessionId);\n if (!session) {\n logger.warn(`Session not found: ${sessionId}`);\n return null;\n }\n\n session.status = status;\n session.endTime = new Date().toISOString();\n session.currentState.lastActivity = session.endTime;\n\n await this.db.write();\n\n // Archive completed session if enabled\n if (this.config.archiveCompletedSessions && status === 'completed') {\n await this.archiveSession(sessionId);\n }\n\n logger.info(`Ended session: ${sessionId} with status: ${status}`);\n return session;\n }\n\n /**\n * Get a specific session by ID\n */\n async getSession(sessionId: string): Promise {\n await this.db.read();\n const session = this.db.data.sessions.find(s => s.sessionId === sessionId);\n return session || null;\n }\n\n /**\n * List sessions with optional filtering\n */\n async listSessions(filters?: SessionFilter): Promise {\n await this.db.read();\n let sessions = [...this.db.data.sessions];\n\n if (filters) {\n if (filters.queryPath) {\n sessions = sessions.filter(s => s.queryPath.includes(filters.queryPath!));\n }\n if (filters.status) {\n sessions = sessions.filter(s => s.status === filters.status);\n }\n if (filters.language) {\n sessions = sessions.filter(s => s.language === filters.language);\n }\n if (filters.queryType) {\n sessions = sessions.filter(s => s.queryType === filters.queryType);\n }\n if (filters.dateRange) {\n const [start, end] = filters.dateRange;\n sessions = sessions.filter(s => \n s.startTime >= start && s.startTime <= end\n );\n }\n }\n\n return sessions;\n }\n\n /**\n * Update session state\n */\n async updateSessionState(\n sessionId: string,\n stateUpdate: Partial\n ): Promise {\n await this.db.read();\n \n const session = this.db.data.sessions.find(s => s.sessionId === sessionId);\n if (!session) {\n logger.warn(`Session not found: ${sessionId}`);\n return null;\n }\n\n session.currentState = {\n ...session.currentState,\n ...stateUpdate,\n lastActivity: new Date().toISOString(),\n };\n\n await this.db.write();\n return session;\n }\n\n /**\n * Add MCP call record to session\n */\n async addMCPCall(sessionId: string, callRecord: MCPCallRecord): Promise {\n await this.db.read();\n \n const session = this.db.data.sessions.find(s => s.sessionId === sessionId);\n if (!session) {\n logger.warn(`Session not found for MCP call: ${sessionId}`);\n return;\n }\n\n session.mcpCalls.push(callRecord);\n session.currentState.lastActivity = callRecord.timestamp;\n\n // Update next suggested tool if provided\n if (callRecord.nextSuggestedTool) {\n session.nextSuggestedTool = callRecord.nextSuggestedTool;\n }\n\n await this.db.write();\n }\n\n /**\n * Add test execution record to session\n */\n async addTestExecution(sessionId: string, testRecord: TestExecutionRecord): Promise {\n await this.db.read();\n \n const session = this.db.data.sessions.find(s => s.sessionId === sessionId);\n if (!session) {\n logger.warn(`Session not found for test execution: ${sessionId}`);\n return;\n }\n\n session.testExecutions.push(testRecord);\n session.currentState.lastActivity = testRecord.timestamp;\n\n // Update compilation/test status based on execution\n if (testRecord.type === 'compilation') {\n session.currentState.compilationStatus = testRecord.success ? 'success' : 'failed';\n } else if (testRecord.type === 'test_run') {\n session.currentState.testStatus = testRecord.success ? 'passing' : 'failing';\n }\n\n await this.db.write();\n }\n\n /**\n * Add quality score record to session\n */\n async addQualityScore(sessionId: string, scoreRecord: QualityScoreRecord): Promise {\n await this.db.read();\n \n const session = this.db.data.sessions.find(s => s.sessionId === sessionId);\n if (!session) {\n logger.warn(`Session not found for quality score: ${sessionId}`);\n return;\n }\n\n session.qualityScores.push(scoreRecord);\n session.currentState.lastActivity = scoreRecord.timestamp;\n session.recommendations = scoreRecord.recommendations;\n\n await this.db.write();\n }\n\n /**\n * Archive a completed session to monthly file\n */\n private async archiveSession(sessionId: string): Promise {\n try {\n const session = await this.getSession(sessionId);\n if (!session) return;\n\n const date = new Date(session.endTime || session.startTime);\n const monthDir = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`;\n const archiveDir = join(this.storageDir, 'sessions-archive', monthDir);\n\n mkdirSync(archiveDir, { recursive: true });\n\n const archiveFile = join(archiveDir, `${sessionId}.json`);\n writeFileSync(archiveFile, JSON.stringify(session, null, 2));\n\n // Remove from active sessions\n await this.db.read();\n this.db.data.sessions = this.db.data.sessions.filter(s => s.sessionId !== sessionId);\n await this.db.write();\n\n logger.info(`Archived session: ${sessionId} to ${archiveFile}`);\n } catch (error) {\n logger.error(`Failed to archive session ${sessionId}:`, error);\n }\n }\n\n /**\n * Get active sessions for a specific query path\n */\n async getActiveSessionsForQuery(queryPath: string): Promise {\n await this.db.read();\n return this.db.data.sessions.filter(s => \n s.queryPath === queryPath && s.status === 'active'\n );\n }\n\n /**\n * Clean up old sessions based on retention policy\n */\n async cleanupOldSessions(): Promise {\n const cutoffDate = new Date();\n cutoffDate.setDate(cutoffDate.getDate() - this.config.retentionDays);\n const cutoffTimestamp = cutoffDate.toISOString();\n\n await this.db.read();\n const sessionsToRemove = this.db.data.sessions.filter(s => \n s.endTime && s.endTime < cutoffTimestamp\n );\n\n if (sessionsToRemove.length > 0) {\n this.db.data.sessions = this.db.data.sessions.filter(s => \n !s.endTime || s.endTime >= cutoffTimestamp\n );\n await this.db.write();\n\n logger.info(`Cleaned up ${sessionsToRemove.length} old sessions`);\n }\n }\n\n /**\n * Get configuration\n */\n getConfig(): MonitoringConfig {\n return this.config;\n }\n\n /**\n * Update configuration\n */\n async updateConfig(configUpdate: Partial): Promise {\n this.config = MonitoringConfigSchema.parse({\n ...this.config,\n ...configUpdate,\n });\n\n // Update config.json file only\n const configPath = join(this.storageDir, 'config.json');\n writeFileSync(configPath, JSON.stringify(this.config, null, 2));\n\n logger.info('Updated monitoring configuration');\n }\n}\n\n/**\n * Parse boolean environment variable\n */\nfunction parseBoolEnv(envVar: string | undefined, defaultValue: boolean): boolean {\n if (envVar === undefined) return defaultValue;\n return envVar.toLowerCase() === 'true' || envVar === '1';\n}\n\n// Export singleton instance with environment variable support\nexport const sessionDataManager = new SessionDataManager({\n storageLocation: process.env.MONITORING_STORAGE_LOCATION || join(getProjectTmpBase(), '.ql-mcp-tracking'),\n enableMonitoringTools: parseBoolEnv(process.env.ENABLE_MONITORING_TOOLS, false),\n});", "import { z } from 'zod';\n\n/**\n * Monitoring and Reporting types for CodeQL Development MCP Server\n * Based on the specification in docs/mcp-server-monitoring-and-reporting.md\n */\n\n/**\n * MCP Call Record - captures individual MCP tool calls\n */\nexport const MCPCallRecordSchema = z.object({\n callId: z.string(),\n timestamp: z.string(), // ISO timestamp\n toolName: z.string(),\n parameters: z.record(z.any()),\n result: z.any(),\n success: z.boolean(),\n duration: z.number(), // milliseconds\n nextSuggestedTool: z.string().optional(),\n});\n\nexport type MCPCallRecord = z.infer;\n\n/**\n * Test Execution Record - captures query compilation and test runs\n */\nexport const TestExecutionRecordSchema = z.object({\n executionId: z.string(),\n timestamp: z.string(),\n type: z.enum(['compilation', 'test_run', 'database_build']),\n success: z.boolean(),\n details: z.record(z.any()),\n metrics: z.object({\n passRate: z.number().optional(),\n coverage: z.number().optional(),\n performance: z.number().optional(),\n }).optional(),\n});\n\nexport type TestExecutionRecord = z.infer;\n\n/**\n * Quality Score Record - multi-dimensional quality assessment\n */\nexport const QualityScoreRecordSchema = z.object({\n scoreId: z.string(),\n timestamp: z.string(),\n overallScore: z.number().min(0).max(100), // 0-100\n dimensions: z.object({\n syntacticCorrectness: z.number().min(0).max(100),\n testCoverageResults: z.number().min(0).max(100),\n documentationQuality: z.number().min(0).max(100),\n functionalCorrectness: z.number().min(0).max(100),\n }),\n grade: z.enum(['A', 'B', 'C', 'D', 'F']),\n recommendations: z.array(z.string()),\n});\n\nexport type QualityScoreRecord = z.infer;\n\n/**\n * Query State - current state of the query development\n */\nexport const QueryStateSchema = z.object({\n filesPresent: z.array(z.string()),\n compilationStatus: z.enum(['unknown', 'success', 'failed']),\n testStatus: z.enum(['unknown', 'passing', 'failing', 'no_tests']),\n documentationStatus: z.enum(['unknown', 'present', 'missing', 'incomplete']),\n lastActivity: z.string(), // ISO timestamp\n});\n\nexport type QueryState = z.infer;\n\n/**\n * Query Development Session - main data structure for tracking\n */\nexport const QueryDevelopmentSessionSchema = z.object({\n // Session Metadata\n sessionId: z.string(),\n queryPath: z.string(),\n language: z.string(),\n queryType: z.string().optional(),\n description: z.string().optional(),\n startTime: z.string(), // ISO timestamp\n endTime: z.string().optional(), // ISO timestamp\n status: z.enum(['active', 'completed', 'failed', 'abandoned']),\n\n // MCP Call History\n mcpCalls: z.array(MCPCallRecordSchema),\n\n // Test Execution Records\n testExecutions: z.array(TestExecutionRecordSchema),\n\n // Quality Metrics\n qualityScores: z.array(QualityScoreRecordSchema),\n\n // Development State\n currentState: QueryStateSchema,\n recommendations: z.array(z.string()),\n nextSuggestedTool: z.string().optional(),\n});\n\nexport type QueryDevelopmentSession = z.infer;\n\n/**\n * Session Filter for listing and searching\n */\nexport const SessionFilterSchema = z.object({\n queryPath: z.string().optional(),\n status: z.string().optional(),\n dateRange: z.tuple([z.string(), z.string()]).optional(),\n language: z.string().optional(),\n queryType: z.string().optional(),\n});\n\nexport type SessionFilter = z.infer;\n\n/**\n * Comparison Report for analyzing multiple sessions\n */\nexport const ComparisonReportSchema = z.object({\n sessionIds: z.array(z.string()),\n dimensions: z.array(z.string()),\n timestamp: z.string(),\n results: z.record(z.any()),\n});\n\nexport type ComparisonReport = z.infer;\n\n/**\n * Aggregate Report for batch analysis\n */\nexport const AggregateReportSchema = z.object({\n filters: SessionFilterSchema,\n timestamp: z.string(),\n totalSessions: z.number(),\n successRate: z.number(),\n averageQualityScore: z.number(),\n commonPatterns: z.array(z.string()),\n recommendations: z.array(z.string()),\n});\n\nexport type AggregateReport = z.infer;\n\n/**\n * Export Result for data export operations\n */\nexport const ExportResultSchema = z.object({\n format: z.enum(['json', 'html', 'markdown']),\n filename: z.string(),\n content: z.string(),\n timestamp: z.string(),\n});\n\nexport type ExportResult = z.infer;\n\n/**\n * Functional Test Result for automated testing\n */\nexport const FunctionalTestResultSchema = z.object({\n sessionId: z.string(),\n queryPath: z.string(),\n passed: z.boolean(),\n criteria: z.record(z.any()),\n details: z.record(z.any()),\n timestamp: z.string(),\n});\n\nexport type FunctionalTestResult = z.infer;\n\n/**\n * Test Report for comprehensive test analysis\n */\nexport const TestReportSchema = z.object({\n sessionIds: z.array(z.string()),\n criteria: z.record(z.any()),\n timestamp: z.string(),\n overallPassRate: z.number(),\n results: z.array(FunctionalTestResultSchema),\n summary: z.record(z.any()),\n});\n\nexport type TestReport = z.infer;\n\n/**\n * Monitoring Configuration\n */\nexport const MonitoringConfigSchema = z.object({\n storageLocation: z.string().default('.ql-mcp-tracking/'),\n autoTrackSessions: z.boolean().default(true),\n retentionDays: z.number().default(90),\n includeCallParameters: z.boolean().default(true),\n includeCallResults: z.boolean().default(true),\n maxActiveSessionsPerQuery: z.number().default(3),\n scoringFrequency: z.enum(['per_call', 'periodic', 'manual']).default('per_call'),\n archiveCompletedSessions: z.boolean().default(true),\n enableRecommendations: z.boolean().default(true),\n enableMonitoringTools: z.boolean().default(false), // Opt-in: session_* tools disabled by default for end-users\n});\n\nexport type MonitoringConfig = z.infer;"], "mappings": "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", "names": ["type", "resolve", "setTimeout", "resolve", "setTimeout", "clearTimeout", "join", "resolve", "spawn", "delimiter", "EventEmitter", "clearTimeout", "setTimeout", "resolve", "spawn", "delimiter", "EventEmitter", "clearTimeout", "setTimeout", "resolve", "mkdirSync", "join", "type", "packageRootDir", "resolve", "existsSync", "delimiter", "dirname", "isAbsolute", "binary", "getServerManager", "resolve", "pathToFileURL", "z", "readFileSync", "dirname", "isAbsolute", "mkdirSync", "mkdirSync", "existsSync", "join", "resolve", "timestamp", "writeFileSync", "existsSync", "mkdirSync", "basename", "dirname", "isAbsolute", "join", "resolve", "logger", "executeCodeQLCommand", "basename", "mkdirSync", "dirname", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "readFile", "z", "exception", "map", "schema", "type", "extend", "str", "string", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "writeFileSync", "readFileSync", "existsSync", "join", "dirname", "basename", "mkdirSync", "z", "z", "z", "z", "z", "z", "join", "resolve", "resolve", "join", "z", "z", "resolve", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "fs", "path", "getLanguageExtension", "z", "readFileSync", "join", "dirname", "fileURLToPath", "__filename", "__dirname", "z", "join", "pathToFileURL", "isAbsolute", "resolve", "pathToFileURL", "getUserWorkspaceDir", "pathToFileURL", "join", "z", "readFile", "isAbsolute", "resolve", "pathToFileURL", "isAbsolute", "resolve", "pathToFileURL", "readFile", "z", "z", "readFileSync", "existsSync", "join", "join", "existsSync", "readFileSync", "z", "basename", "readFileSync", "join", "dirname", "fileURLToPath", "__filename", "__dirname", "z", "basename", "z", "randomUUID", "readFileSync", "writeFileSync", "path", "path", "readFileSync", "str", "writeFileSync", "parse", "mkdirSync", "writeFileSync", "join", "randomUUID", "z", "join", "mkdirSync", "writeFileSync", "randomUUID", "z", "timestamp", "randomUUID", "resolve", "pathToFileURL"] } diff --git a/server/package.json b/server/package.json index f5cd6e4..e971b5a 100644 --- a/server/package.json +++ b/server/package.json @@ -1,6 +1,6 @@ { "name": "@advanced-security/codeql-development-mcp-server", - "version": "2.24.0", + "version": "2.24.1", "description": "An MCP server supporting LLM requests for CodeQL development tools and resources.", "main": "dist/codeql-development-mcp-server.js", "type": "module", diff --git a/server/ql/actions/tools/src/codeql-pack.lock.yml b/server/ql/actions/tools/src/codeql-pack.lock.yml index 2e24b33..51fb63c 100644 --- a/server/ql/actions/tools/src/codeql-pack.lock.yml +++ b/server/ql/actions/tools/src/codeql-pack.lock.yml @@ -2,31 +2,31 @@ lockVersion: 1.0.0 dependencies: codeql/actions-all: - version: 0.4.26 + version: 0.4.27 codeql/concepts: - version: 0.0.14 + version: 0.0.15 codeql/controlflow: - version: 2.0.24 + version: 2.0.25 codeql/dataflow: - version: 2.0.24 + version: 2.0.25 codeql/javascript-all: - version: 2.6.20 + version: 2.6.21 codeql/mad: - version: 1.0.40 + version: 1.0.41 codeql/regex: - version: 1.0.40 + version: 1.0.41 codeql/ssa: - version: 2.0.16 + version: 2.0.17 codeql/threat-models: - version: 1.0.40 + version: 1.0.41 codeql/tutorial: - version: 1.0.40 + version: 1.0.41 codeql/typetracking: - version: 2.0.24 + version: 2.0.25 codeql/util: - version: 2.0.27 + version: 2.0.28 codeql/xml: - version: 1.0.40 + version: 1.0.41 codeql/yaml: - version: 1.0.40 + version: 1.0.41 compiled: false diff --git a/server/ql/actions/tools/src/codeql-pack.yml b/server/ql/actions/tools/src/codeql-pack.yml index 8e72827..8533cdf 100644 --- a/server/ql/actions/tools/src/codeql-pack.yml +++ b/server/ql/actions/tools/src/codeql-pack.yml @@ -1,6 +1,6 @@ name: advanced-security/ql-mcp-actions-tools-src -version: 2.24.0 +version: 2.24.1 description: 'Queries for codeql-development-mcp-server tools for actions language' library: false dependencies: - codeql/actions-all: 0.4.26 + codeql/actions-all: 0.4.27 diff --git a/server/ql/actions/tools/test/codeql-pack.lock.yml b/server/ql/actions/tools/test/codeql-pack.lock.yml index 2e24b33..51fb63c 100644 --- a/server/ql/actions/tools/test/codeql-pack.lock.yml +++ b/server/ql/actions/tools/test/codeql-pack.lock.yml @@ -2,31 +2,31 @@ lockVersion: 1.0.0 dependencies: codeql/actions-all: - version: 0.4.26 + version: 0.4.27 codeql/concepts: - version: 0.0.14 + version: 0.0.15 codeql/controlflow: - version: 2.0.24 + version: 2.0.25 codeql/dataflow: - version: 2.0.24 + version: 2.0.25 codeql/javascript-all: - version: 2.6.20 + version: 2.6.21 codeql/mad: - version: 1.0.40 + version: 1.0.41 codeql/regex: - version: 1.0.40 + version: 1.0.41 codeql/ssa: - version: 2.0.16 + version: 2.0.17 codeql/threat-models: - version: 1.0.40 + version: 1.0.41 codeql/tutorial: - version: 1.0.40 + version: 1.0.41 codeql/typetracking: - version: 2.0.24 + version: 2.0.25 codeql/util: - version: 2.0.27 + version: 2.0.28 codeql/xml: - version: 1.0.40 + version: 1.0.41 codeql/yaml: - version: 1.0.40 + version: 1.0.41 compiled: false diff --git a/server/ql/actions/tools/test/codeql-pack.yml b/server/ql/actions/tools/test/codeql-pack.yml index eb52b77..95d4d80 100644 --- a/server/ql/actions/tools/test/codeql-pack.yml +++ b/server/ql/actions/tools/test/codeql-pack.yml @@ -1,5 +1,5 @@ name: advanced-security/ql-mcp-actions-tools-test -version: 2.24.0 +version: 2.24.1 dependencies: advanced-security/ql-mcp-actions-tools-src: ${workspace} extractor: actions diff --git a/server/ql/cpp/tools/src/codeql-pack.lock.yml b/server/ql/cpp/tools/src/codeql-pack.lock.yml index b391a55..4738075 100644 --- a/server/ql/cpp/tools/src/codeql-pack.lock.yml +++ b/server/ql/cpp/tools/src/codeql-pack.lock.yml @@ -2,27 +2,27 @@ lockVersion: 1.0.0 dependencies: codeql/controlflow: - version: 2.0.24 + version: 2.0.25 codeql/cpp-all: - version: 7.0.0 + version: 7.1.0 codeql/dataflow: - version: 2.0.24 + version: 2.0.25 codeql/mad: - version: 1.0.40 + version: 1.0.41 codeql/quantum: - version: 0.0.18 + version: 0.0.19 codeql/rangeanalysis: - version: 1.0.40 + version: 1.0.41 codeql/ssa: - version: 2.0.16 + version: 2.0.17 codeql/tutorial: - version: 1.0.40 + version: 1.0.41 codeql/typeflow: - version: 1.0.40 + version: 1.0.41 codeql/typetracking: - version: 2.0.24 + version: 2.0.25 codeql/util: - version: 2.0.27 + version: 2.0.28 codeql/xml: - version: 1.0.40 + version: 1.0.41 compiled: false diff --git a/server/ql/cpp/tools/src/codeql-pack.yml b/server/ql/cpp/tools/src/codeql-pack.yml index 8b21d74..750e3f6 100644 --- a/server/ql/cpp/tools/src/codeql-pack.yml +++ b/server/ql/cpp/tools/src/codeql-pack.yml @@ -1,6 +1,6 @@ name: advanced-security/ql-mcp-cpp-tools-src -version: 2.24.0 +version: 2.24.1 description: 'Queries for codeql-development-mcp-server tools for cpp language' library: false dependencies: - codeql/cpp-all: 7.0.0 + codeql/cpp-all: 7.1.0 diff --git a/server/ql/cpp/tools/test/codeql-pack.lock.yml b/server/ql/cpp/tools/test/codeql-pack.lock.yml index b391a55..4738075 100644 --- a/server/ql/cpp/tools/test/codeql-pack.lock.yml +++ b/server/ql/cpp/tools/test/codeql-pack.lock.yml @@ -2,27 +2,27 @@ lockVersion: 1.0.0 dependencies: codeql/controlflow: - version: 2.0.24 + version: 2.0.25 codeql/cpp-all: - version: 7.0.0 + version: 7.1.0 codeql/dataflow: - version: 2.0.24 + version: 2.0.25 codeql/mad: - version: 1.0.40 + version: 1.0.41 codeql/quantum: - version: 0.0.18 + version: 0.0.19 codeql/rangeanalysis: - version: 1.0.40 + version: 1.0.41 codeql/ssa: - version: 2.0.16 + version: 2.0.17 codeql/tutorial: - version: 1.0.40 + version: 1.0.41 codeql/typeflow: - version: 1.0.40 + version: 1.0.41 codeql/typetracking: - version: 2.0.24 + version: 2.0.25 codeql/util: - version: 2.0.27 + version: 2.0.28 codeql/xml: - version: 1.0.40 + version: 1.0.41 compiled: false diff --git a/server/ql/cpp/tools/test/codeql-pack.yml b/server/ql/cpp/tools/test/codeql-pack.yml index dff02fe..5ee333c 100644 --- a/server/ql/cpp/tools/test/codeql-pack.yml +++ b/server/ql/cpp/tools/test/codeql-pack.yml @@ -1,5 +1,5 @@ name: advanced-security/ql-mcp-cpp-tools-test -version: 2.24.0 +version: 2.24.1 dependencies: advanced-security/ql-mcp-cpp-tools-src: ${workspace} extractor: cpp diff --git a/server/ql/csharp/tools/src/PrintCFG/PrintCFG.md b/server/ql/csharp/tools/src/PrintCFG/PrintCFG.md index 7b73069..f6a17e8 100644 --- a/server/ql/csharp/tools/src/PrintCFG/PrintCFG.md +++ b/server/ql/csharp/tools/src/PrintCFG/PrintCFG.md @@ -1,4 +1,4 @@ -# Print CFG for C# +# Print CFG for `csharp` Source Files Produces a representation of a file's Control Flow Graph (CFG) for specified source files. diff --git a/server/ql/csharp/tools/src/codeql-pack.lock.yml b/server/ql/csharp/tools/src/codeql-pack.lock.yml index 8ed53bb..f64889e 100644 --- a/server/ql/csharp/tools/src/codeql-pack.lock.yml +++ b/server/ql/csharp/tools/src/codeql-pack.lock.yml @@ -2,23 +2,23 @@ lockVersion: 1.0.0 dependencies: codeql/controlflow: - version: 2.0.24 + version: 2.0.25 codeql/csharp-all: - version: 5.4.5 + version: 5.4.6 codeql/dataflow: - version: 2.0.24 + version: 2.0.25 codeql/mad: - version: 1.0.40 + version: 1.0.41 codeql/ssa: - version: 2.0.16 + version: 2.0.17 codeql/threat-models: - version: 1.0.40 + version: 1.0.41 codeql/tutorial: - version: 1.0.40 + version: 1.0.41 codeql/typetracking: - version: 2.0.24 + version: 2.0.25 codeql/util: - version: 2.0.27 + version: 2.0.28 codeql/xml: - version: 1.0.40 + version: 1.0.41 compiled: false diff --git a/server/ql/csharp/tools/src/codeql-pack.yml b/server/ql/csharp/tools/src/codeql-pack.yml index 6799531..96a526a 100644 --- a/server/ql/csharp/tools/src/codeql-pack.yml +++ b/server/ql/csharp/tools/src/codeql-pack.yml @@ -1,6 +1,6 @@ name: advanced-security/ql-mcp-csharp-tools-src -version: 2.24.0 +version: 2.24.1 description: 'Queries for codeql-development-mcp-server tools for csharp language' library: false dependencies: - codeql/csharp-all: 5.4.5 + codeql/csharp-all: 5.4.6 diff --git a/server/ql/csharp/tools/test/codeql-pack.lock.yml b/server/ql/csharp/tools/test/codeql-pack.lock.yml index 8ed53bb..f64889e 100644 --- a/server/ql/csharp/tools/test/codeql-pack.lock.yml +++ b/server/ql/csharp/tools/test/codeql-pack.lock.yml @@ -2,23 +2,23 @@ lockVersion: 1.0.0 dependencies: codeql/controlflow: - version: 2.0.24 + version: 2.0.25 codeql/csharp-all: - version: 5.4.5 + version: 5.4.6 codeql/dataflow: - version: 2.0.24 + version: 2.0.25 codeql/mad: - version: 1.0.40 + version: 1.0.41 codeql/ssa: - version: 2.0.16 + version: 2.0.17 codeql/threat-models: - version: 1.0.40 + version: 1.0.41 codeql/tutorial: - version: 1.0.40 + version: 1.0.41 codeql/typetracking: - version: 2.0.24 + version: 2.0.25 codeql/util: - version: 2.0.27 + version: 2.0.28 codeql/xml: - version: 1.0.40 + version: 1.0.41 compiled: false diff --git a/server/ql/csharp/tools/test/codeql-pack.yml b/server/ql/csharp/tools/test/codeql-pack.yml index 6eb238f..24dc7bc 100644 --- a/server/ql/csharp/tools/test/codeql-pack.yml +++ b/server/ql/csharp/tools/test/codeql-pack.yml @@ -1,5 +1,5 @@ name: advanced-security/ql-mcp-csharp-tools-test -version: 2.24.0 +version: 2.24.1 dependencies: advanced-security/ql-mcp-csharp-tools-src: ${workspace} extractor: csharp diff --git a/server/ql/go/tools/src/codeql-pack.lock.yml b/server/ql/go/tools/src/codeql-pack.lock.yml index adafc5a..1f794e5 100644 --- a/server/ql/go/tools/src/codeql-pack.lock.yml +++ b/server/ql/go/tools/src/codeql-pack.lock.yml @@ -2,23 +2,23 @@ lockVersion: 1.0.0 dependencies: codeql/concepts: - version: 0.0.14 + version: 0.0.15 codeql/controlflow: - version: 2.0.24 + version: 2.0.25 codeql/dataflow: - version: 2.0.24 + version: 2.0.25 codeql/go-all: - version: 6.0.0 + version: 6.0.1 codeql/mad: - version: 1.0.40 + version: 1.0.41 codeql/ssa: - version: 2.0.16 + version: 2.0.17 codeql/threat-models: - version: 1.0.40 + version: 1.0.41 codeql/tutorial: - version: 1.0.40 + version: 1.0.41 codeql/typetracking: - version: 2.0.24 + version: 2.0.25 codeql/util: - version: 2.0.27 + version: 2.0.28 compiled: false diff --git a/server/ql/go/tools/src/codeql-pack.yml b/server/ql/go/tools/src/codeql-pack.yml index dc4cd25..41eaf11 100644 --- a/server/ql/go/tools/src/codeql-pack.yml +++ b/server/ql/go/tools/src/codeql-pack.yml @@ -1,6 +1,6 @@ name: advanced-security/ql-mcp-go-tools-src -version: 2.24.0 +version: 2.24.1 description: 'Queries for codeql-development-mcp-server tools for go language' library: false dependencies: - codeql/go-all: 6.0.0 + codeql/go-all: 6.0.1 diff --git a/server/ql/go/tools/test/codeql-pack.lock.yml b/server/ql/go/tools/test/codeql-pack.lock.yml index adafc5a..1f794e5 100644 --- a/server/ql/go/tools/test/codeql-pack.lock.yml +++ b/server/ql/go/tools/test/codeql-pack.lock.yml @@ -2,23 +2,23 @@ lockVersion: 1.0.0 dependencies: codeql/concepts: - version: 0.0.14 + version: 0.0.15 codeql/controlflow: - version: 2.0.24 + version: 2.0.25 codeql/dataflow: - version: 2.0.24 + version: 2.0.25 codeql/go-all: - version: 6.0.0 + version: 6.0.1 codeql/mad: - version: 1.0.40 + version: 1.0.41 codeql/ssa: - version: 2.0.16 + version: 2.0.17 codeql/threat-models: - version: 1.0.40 + version: 1.0.41 codeql/tutorial: - version: 1.0.40 + version: 1.0.41 codeql/typetracking: - version: 2.0.24 + version: 2.0.25 codeql/util: - version: 2.0.27 + version: 2.0.28 compiled: false diff --git a/server/ql/go/tools/test/codeql-pack.yml b/server/ql/go/tools/test/codeql-pack.yml index 10a02e4..ba34d01 100644 --- a/server/ql/go/tools/test/codeql-pack.yml +++ b/server/ql/go/tools/test/codeql-pack.yml @@ -1,5 +1,5 @@ name: advanced-security/ql-mcp-go-tools-test -version: 2.24.0 +version: 2.24.1 dependencies: advanced-security/ql-mcp-go-tools-src: ${workspace} extractor: go diff --git a/server/ql/java/tools/src/codeql-pack.lock.yml b/server/ql/java/tools/src/codeql-pack.lock.yml index fd5ea78..f1f473f 100644 --- a/server/ql/java/tools/src/codeql-pack.lock.yml +++ b/server/ql/java/tools/src/codeql-pack.lock.yml @@ -2,31 +2,31 @@ lockVersion: 1.0.0 dependencies: codeql/controlflow: - version: 2.0.24 + version: 2.0.25 codeql/dataflow: - version: 2.0.24 + version: 2.0.25 codeql/java-all: - version: 7.8.4 + version: 8.0.0 codeql/mad: - version: 1.0.40 + version: 1.0.41 codeql/quantum: - version: 0.0.18 + version: 0.0.19 codeql/rangeanalysis: - version: 1.0.40 + version: 1.0.41 codeql/regex: - version: 1.0.40 + version: 1.0.41 codeql/ssa: - version: 2.0.16 + version: 2.0.17 codeql/threat-models: - version: 1.0.40 + version: 1.0.41 codeql/tutorial: - version: 1.0.40 + version: 1.0.41 codeql/typeflow: - version: 1.0.40 + version: 1.0.41 codeql/typetracking: - version: 2.0.24 + version: 2.0.25 codeql/util: - version: 2.0.27 + version: 2.0.28 codeql/xml: - version: 1.0.40 + version: 1.0.41 compiled: false diff --git a/server/ql/java/tools/src/codeql-pack.yml b/server/ql/java/tools/src/codeql-pack.yml index 005d4ab..1eb0c0a 100644 --- a/server/ql/java/tools/src/codeql-pack.yml +++ b/server/ql/java/tools/src/codeql-pack.yml @@ -1,6 +1,6 @@ name: advanced-security/ql-mcp-java-tools-src -version: 2.24.0 +version: 2.24.1 description: 'Queries for codeql-development-mcp-server tools for java language' library: false dependencies: - codeql/java-all: 7.8.4 + codeql/java-all: 8.0.0 diff --git a/server/ql/java/tools/test/codeql-pack.lock.yml b/server/ql/java/tools/test/codeql-pack.lock.yml index fd5ea78..f1f473f 100644 --- a/server/ql/java/tools/test/codeql-pack.lock.yml +++ b/server/ql/java/tools/test/codeql-pack.lock.yml @@ -2,31 +2,31 @@ lockVersion: 1.0.0 dependencies: codeql/controlflow: - version: 2.0.24 + version: 2.0.25 codeql/dataflow: - version: 2.0.24 + version: 2.0.25 codeql/java-all: - version: 7.8.4 + version: 8.0.0 codeql/mad: - version: 1.0.40 + version: 1.0.41 codeql/quantum: - version: 0.0.18 + version: 0.0.19 codeql/rangeanalysis: - version: 1.0.40 + version: 1.0.41 codeql/regex: - version: 1.0.40 + version: 1.0.41 codeql/ssa: - version: 2.0.16 + version: 2.0.17 codeql/threat-models: - version: 1.0.40 + version: 1.0.41 codeql/tutorial: - version: 1.0.40 + version: 1.0.41 codeql/typeflow: - version: 1.0.40 + version: 1.0.41 codeql/typetracking: - version: 2.0.24 + version: 2.0.25 codeql/util: - version: 2.0.27 + version: 2.0.28 codeql/xml: - version: 1.0.40 + version: 1.0.41 compiled: false diff --git a/server/ql/java/tools/test/codeql-pack.yml b/server/ql/java/tools/test/codeql-pack.yml index 17abb6c..7a2b1d8 100644 --- a/server/ql/java/tools/test/codeql-pack.yml +++ b/server/ql/java/tools/test/codeql-pack.yml @@ -1,5 +1,5 @@ name: advanced-security/ql-mcp-java-tools-test -version: 2.24.0 +version: 2.24.1 dependencies: advanced-security/ql-mcp-java-tools-src: ${workspace} extractor: java diff --git a/server/ql/javascript/tools/src/codeql-pack.lock.yml b/server/ql/javascript/tools/src/codeql-pack.lock.yml index 0bf6f6f..6869bc0 100644 --- a/server/ql/javascript/tools/src/codeql-pack.lock.yml +++ b/server/ql/javascript/tools/src/codeql-pack.lock.yml @@ -2,29 +2,29 @@ lockVersion: 1.0.0 dependencies: codeql/concepts: - version: 0.0.14 + version: 0.0.15 codeql/controlflow: - version: 2.0.24 + version: 2.0.25 codeql/dataflow: - version: 2.0.24 + version: 2.0.25 codeql/javascript-all: - version: 2.6.20 + version: 2.6.21 codeql/mad: - version: 1.0.40 + version: 1.0.41 codeql/regex: - version: 1.0.40 + version: 1.0.41 codeql/ssa: - version: 2.0.16 + version: 2.0.17 codeql/threat-models: - version: 1.0.40 + version: 1.0.41 codeql/tutorial: - version: 1.0.40 + version: 1.0.41 codeql/typetracking: - version: 2.0.24 + version: 2.0.25 codeql/util: - version: 2.0.27 + version: 2.0.28 codeql/xml: - version: 1.0.40 + version: 1.0.41 codeql/yaml: - version: 1.0.40 + version: 1.0.41 compiled: false diff --git a/server/ql/javascript/tools/src/codeql-pack.yml b/server/ql/javascript/tools/src/codeql-pack.yml index 7496914..d474157 100644 --- a/server/ql/javascript/tools/src/codeql-pack.yml +++ b/server/ql/javascript/tools/src/codeql-pack.yml @@ -1,6 +1,6 @@ name: advanced-security/ql-mcp-javascript-tools-src -version: 2.24.0 +version: 2.24.1 description: 'Queries for codeql-development-mcp-server tools for javascript language' library: false dependencies: - codeql/javascript-all: 2.6.20 + codeql/javascript-all: 2.6.21 diff --git a/server/ql/javascript/tools/test/codeql-pack.lock.yml b/server/ql/javascript/tools/test/codeql-pack.lock.yml index 0bf6f6f..6869bc0 100644 --- a/server/ql/javascript/tools/test/codeql-pack.lock.yml +++ b/server/ql/javascript/tools/test/codeql-pack.lock.yml @@ -2,29 +2,29 @@ lockVersion: 1.0.0 dependencies: codeql/concepts: - version: 0.0.14 + version: 0.0.15 codeql/controlflow: - version: 2.0.24 + version: 2.0.25 codeql/dataflow: - version: 2.0.24 + version: 2.0.25 codeql/javascript-all: - version: 2.6.20 + version: 2.6.21 codeql/mad: - version: 1.0.40 + version: 1.0.41 codeql/regex: - version: 1.0.40 + version: 1.0.41 codeql/ssa: - version: 2.0.16 + version: 2.0.17 codeql/threat-models: - version: 1.0.40 + version: 1.0.41 codeql/tutorial: - version: 1.0.40 + version: 1.0.41 codeql/typetracking: - version: 2.0.24 + version: 2.0.25 codeql/util: - version: 2.0.27 + version: 2.0.28 codeql/xml: - version: 1.0.40 + version: 1.0.41 codeql/yaml: - version: 1.0.40 + version: 1.0.41 compiled: false diff --git a/server/ql/javascript/tools/test/codeql-pack.yml b/server/ql/javascript/tools/test/codeql-pack.yml index 37a02cd..a552846 100644 --- a/server/ql/javascript/tools/test/codeql-pack.yml +++ b/server/ql/javascript/tools/test/codeql-pack.yml @@ -1,5 +1,5 @@ name: advanced-security/ql-mcp-javascript-tools-test -version: 2.24.0 +version: 2.24.1 dependencies: advanced-security/ql-mcp-javascript-tools-src: ${workspace} extractor: javascript diff --git a/server/ql/python/tools/src/codeql-pack.lock.yml b/server/ql/python/tools/src/codeql-pack.lock.yml index 9ab8b36..b84c518 100644 --- a/server/ql/python/tools/src/codeql-pack.lock.yml +++ b/server/ql/python/tools/src/codeql-pack.lock.yml @@ -2,29 +2,29 @@ lockVersion: 1.0.0 dependencies: codeql/concepts: - version: 0.0.14 + version: 0.0.15 codeql/controlflow: - version: 2.0.24 + version: 2.0.25 codeql/dataflow: - version: 2.0.24 + version: 2.0.25 codeql/mad: - version: 1.0.40 + version: 1.0.41 codeql/python-all: - version: 6.0.0 + version: 6.1.0 codeql/regex: - version: 1.0.40 + version: 1.0.41 codeql/ssa: - version: 2.0.16 + version: 2.0.17 codeql/threat-models: - version: 1.0.40 + version: 1.0.41 codeql/tutorial: - version: 1.0.40 + version: 1.0.41 codeql/typetracking: - version: 2.0.24 + version: 2.0.25 codeql/util: - version: 2.0.27 + version: 2.0.28 codeql/xml: - version: 1.0.40 + version: 1.0.41 codeql/yaml: - version: 1.0.40 + version: 1.0.41 compiled: false diff --git a/server/ql/python/tools/src/codeql-pack.yml b/server/ql/python/tools/src/codeql-pack.yml index 3d90238..8bcdc22 100644 --- a/server/ql/python/tools/src/codeql-pack.yml +++ b/server/ql/python/tools/src/codeql-pack.yml @@ -1,6 +1,6 @@ name: advanced-security/ql-mcp-python-tools-src -version: 2.24.0 +version: 2.24.1 description: 'Queries for codeql-development-mcp-server tools for python language' library: false dependencies: - codeql/python-all: 6.0.0 + codeql/python-all: 6.1.0 diff --git a/server/ql/python/tools/test/codeql-pack.lock.yml b/server/ql/python/tools/test/codeql-pack.lock.yml index 9ab8b36..b84c518 100644 --- a/server/ql/python/tools/test/codeql-pack.lock.yml +++ b/server/ql/python/tools/test/codeql-pack.lock.yml @@ -2,29 +2,29 @@ lockVersion: 1.0.0 dependencies: codeql/concepts: - version: 0.0.14 + version: 0.0.15 codeql/controlflow: - version: 2.0.24 + version: 2.0.25 codeql/dataflow: - version: 2.0.24 + version: 2.0.25 codeql/mad: - version: 1.0.40 + version: 1.0.41 codeql/python-all: - version: 6.0.0 + version: 6.1.0 codeql/regex: - version: 1.0.40 + version: 1.0.41 codeql/ssa: - version: 2.0.16 + version: 2.0.17 codeql/threat-models: - version: 1.0.40 + version: 1.0.41 codeql/tutorial: - version: 1.0.40 + version: 1.0.41 codeql/typetracking: - version: 2.0.24 + version: 2.0.25 codeql/util: - version: 2.0.27 + version: 2.0.28 codeql/xml: - version: 1.0.40 + version: 1.0.41 codeql/yaml: - version: 1.0.40 + version: 1.0.41 compiled: false diff --git a/server/ql/python/tools/test/codeql-pack.yml b/server/ql/python/tools/test/codeql-pack.yml index cd9f815..d06dbca 100644 --- a/server/ql/python/tools/test/codeql-pack.yml +++ b/server/ql/python/tools/test/codeql-pack.yml @@ -1,5 +1,5 @@ name: advanced-security/ql-mcp-python-tools-test -version: 2.24.0 +version: 2.24.1 dependencies: advanced-security/ql-mcp-python-tools-src: ${workspace} extractor: python diff --git a/server/ql/ruby/tools/src/codeql-pack.lock.yml b/server/ql/ruby/tools/src/codeql-pack.lock.yml index 39f7b2e..35ee6eb 100644 --- a/server/ql/ruby/tools/src/codeql-pack.lock.yml +++ b/server/ql/ruby/tools/src/codeql-pack.lock.yml @@ -2,23 +2,23 @@ lockVersion: 1.0.0 dependencies: codeql/concepts: - version: 0.0.14 + version: 0.0.15 codeql/controlflow: - version: 2.0.24 + version: 2.0.25 codeql/dataflow: - version: 2.0.24 + version: 2.0.25 codeql/mad: - version: 1.0.40 + version: 1.0.41 codeql/regex: - version: 1.0.40 + version: 1.0.41 codeql/ruby-all: - version: 5.1.8 + version: 5.1.9 codeql/ssa: - version: 2.0.16 + version: 2.0.17 codeql/tutorial: - version: 1.0.40 + version: 1.0.41 codeql/typetracking: - version: 2.0.24 + version: 2.0.25 codeql/util: - version: 2.0.27 + version: 2.0.28 compiled: false diff --git a/server/ql/ruby/tools/src/codeql-pack.yml b/server/ql/ruby/tools/src/codeql-pack.yml index 7a9bd08..8e23c04 100644 --- a/server/ql/ruby/tools/src/codeql-pack.yml +++ b/server/ql/ruby/tools/src/codeql-pack.yml @@ -1,6 +1,6 @@ name: advanced-security/ql-mcp-ruby-tools-src -version: 2.24.0 +version: 2.24.1 description: 'Queries for codeql-development-mcp-server tools for ruby language' library: false dependencies: - codeql/ruby-all: 5.1.8 + codeql/ruby-all: 5.1.9 diff --git a/server/ql/ruby/tools/test/codeql-pack.lock.yml b/server/ql/ruby/tools/test/codeql-pack.lock.yml index 39f7b2e..35ee6eb 100644 --- a/server/ql/ruby/tools/test/codeql-pack.lock.yml +++ b/server/ql/ruby/tools/test/codeql-pack.lock.yml @@ -2,23 +2,23 @@ lockVersion: 1.0.0 dependencies: codeql/concepts: - version: 0.0.14 + version: 0.0.15 codeql/controlflow: - version: 2.0.24 + version: 2.0.25 codeql/dataflow: - version: 2.0.24 + version: 2.0.25 codeql/mad: - version: 1.0.40 + version: 1.0.41 codeql/regex: - version: 1.0.40 + version: 1.0.41 codeql/ruby-all: - version: 5.1.8 + version: 5.1.9 codeql/ssa: - version: 2.0.16 + version: 2.0.17 codeql/tutorial: - version: 1.0.40 + version: 1.0.41 codeql/typetracking: - version: 2.0.24 + version: 2.0.25 codeql/util: - version: 2.0.27 + version: 2.0.28 compiled: false diff --git a/server/ql/ruby/tools/test/codeql-pack.yml b/server/ql/ruby/tools/test/codeql-pack.yml index dfaa46e..a8ee615 100644 --- a/server/ql/ruby/tools/test/codeql-pack.yml +++ b/server/ql/ruby/tools/test/codeql-pack.yml @@ -1,5 +1,5 @@ name: advanced-security/ql-mcp-ruby-tools-test -version: 2.24.0 +version: 2.24.1 dependencies: advanced-security/ql-mcp-ruby-tools-src: ${workspace} extractor: ruby diff --git a/server/ql/swift/tools/src/codeql-pack.lock.yml b/server/ql/swift/tools/src/codeql-pack.lock.yml index d25c185..6c5a13f 100644 --- a/server/ql/swift/tools/src/codeql-pack.lock.yml +++ b/server/ql/swift/tools/src/codeql-pack.lock.yml @@ -2,23 +2,23 @@ lockVersion: 1.0.0 dependencies: codeql/concepts: - version: 0.0.14 + version: 0.0.15 codeql/controlflow: - version: 2.0.24 + version: 2.0.25 codeql/dataflow: - version: 2.0.24 + version: 2.0.25 codeql/mad: - version: 1.0.40 + version: 1.0.41 codeql/regex: - version: 1.0.40 + version: 1.0.41 codeql/ssa: - version: 2.0.16 + version: 2.0.17 codeql/swift-all: - version: 6.2.0 + version: 6.2.1 codeql/tutorial: - version: 1.0.40 + version: 1.0.41 codeql/typetracking: - version: 2.0.24 + version: 2.0.25 codeql/util: - version: 2.0.27 + version: 2.0.28 compiled: false diff --git a/server/ql/swift/tools/src/codeql-pack.yml b/server/ql/swift/tools/src/codeql-pack.yml index 0d305d8..5556623 100644 --- a/server/ql/swift/tools/src/codeql-pack.yml +++ b/server/ql/swift/tools/src/codeql-pack.yml @@ -1,6 +1,6 @@ name: advanced-security/ql-mcp-swift-tools-src -version: 2.24.0 +version: 2.24.1 description: 'Queries for codeql-development-mcp-server tools for swift language' library: false dependencies: - codeql/swift-all: 6.2.0 + codeql/swift-all: 6.2.1 diff --git a/server/ql/swift/tools/test/codeql-pack.lock.yml b/server/ql/swift/tools/test/codeql-pack.lock.yml index d25c185..6c5a13f 100644 --- a/server/ql/swift/tools/test/codeql-pack.lock.yml +++ b/server/ql/swift/tools/test/codeql-pack.lock.yml @@ -2,23 +2,23 @@ lockVersion: 1.0.0 dependencies: codeql/concepts: - version: 0.0.14 + version: 0.0.15 codeql/controlflow: - version: 2.0.24 + version: 2.0.25 codeql/dataflow: - version: 2.0.24 + version: 2.0.25 codeql/mad: - version: 1.0.40 + version: 1.0.41 codeql/regex: - version: 1.0.40 + version: 1.0.41 codeql/ssa: - version: 2.0.16 + version: 2.0.17 codeql/swift-all: - version: 6.2.0 + version: 6.2.1 codeql/tutorial: - version: 1.0.40 + version: 1.0.41 codeql/typetracking: - version: 2.0.24 + version: 2.0.25 codeql/util: - version: 2.0.27 + version: 2.0.28 compiled: false diff --git a/server/ql/swift/tools/test/codeql-pack.yml b/server/ql/swift/tools/test/codeql-pack.yml index cb9f0cd..d606c54 100644 --- a/server/ql/swift/tools/test/codeql-pack.yml +++ b/server/ql/swift/tools/test/codeql-pack.yml @@ -1,5 +1,5 @@ name: advanced-security/ql-mcp-swift-tools-test -version: 2.24.0 +version: 2.24.1 dependencies: advanced-security/ql-mcp-swift-tools-src: ${workspace} extractor: swift diff --git a/server/scripts/update-release-version.sh b/server/scripts/update-release-version.sh new file mode 100755 index 0000000..9b565c4 --- /dev/null +++ b/server/scripts/update-release-version.sh @@ -0,0 +1,389 @@ +#!/usr/bin/env bash +set -euo pipefail + +## update-release-version.sh +## Deterministically updates the release version across all version-bearing files +## in the codeql-development-mcp-server repository. +## +## Version-bearing files: +## .codeql-version (vX.Y.Z format) +## package.json (X.Y.Z format) +## client/package.json (X.Y.Z format) +## server/package.json (X.Y.Z format) +## server/ql/*/tools/src/codeql-pack.yml (X.Y.Z format) +## server/ql/*/tools/test/codeql-pack.yml (X.Y.Z format) +## +## The base version (X.Y.Z without any prerelease suffix) must correspond to +## an actual CodeQL CLI release. The script validates this by checking against +## the installed `codeql` CLI or the `.codeql-version` file. +## +## Usage: +## ./server/scripts/update-release-version.sh +## ./server/scripts/update-release-version.sh --check [] +## +## Examples: +## ./server/scripts/update-release-version.sh 2.24.1 +## ./server/scripts/update-release-version.sh 2.24.1-beta +## ./server/scripts/update-release-version.sh v2.24.1-beta +## ./server/scripts/update-release-version.sh --check +## ./server/scripts/update-release-version.sh --check 2.24.1-beta + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)" + +## Supported languages for ql-mcp-* packs +LANGUAGES=("actions" "cpp" "csharp" "go" "java" "javascript" "python" "ruby" "swift") + +usage() { + cat < + $0 --check [] + +Deterministically updates the release version across all version-bearing files. +The base version (X.Y.Z, without prerelease suffixes) must correspond to an +actual CodeQL CLI release. + +ARGUMENTS: + The new version to set (e.g., 2.24.1 or 2.24.1-beta). + The 'v' prefix is optional and will be normalized. + The base version (X.Y.Z) is validated against the + installed CodeQL CLI or .codeql-version file. + +OPTIONS: + --check [] Check version consistency across all files. + If is provided, also validates that all files + match the expected version. + --dry-run Show what would be changed without modifying files. + --skip-cli-validation Skip CodeQL CLI version validation (not recommended). + -h, --help Show this help message. + +EXAMPLES: + $0 2.24.1 Update all files to version 2.24.1 + $0 2.24.1-beta Update all files to version 2.24.1-beta + $0 v2.24.1-beta Same as above (v prefix is stripped automatically) + $0 --check Verify all version-bearing files are consistent + $0 --check 2.24.1 Verify all files contain version 2.24.1 + $0 --dry-run 2.24.1 Preview changes without writing files +EOF +} + +## Collect all version-bearing files and their current versions +collect_versions() { + local versions=() + + ## .codeql-version (stores vX.Y.Z) + local codeql_version_file="${REPO_ROOT}/.codeql-version" + if [[ -f "${codeql_version_file}" ]]; then + local raw_version + raw_version=$(tr -d '[:space:]' < "${codeql_version_file}") + versions+=(".codeql-version|${raw_version#v}") + else + echo "WARNING: .codeql-version not found" >&2 + fi + + ## package.json files + local pkg_files=("package.json" "client/package.json" "server/package.json") + for pkg_file in "${pkg_files[@]}"; do + local full_path="${REPO_ROOT}/${pkg_file}" + if [[ -f "${full_path}" ]]; then + local pkg_version + pkg_version=$(grep -m1 '"version"' "${full_path}" | sed 's/.*"version"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/') + versions+=("${pkg_file}|${pkg_version}") + else + echo "WARNING: ${pkg_file} not found" >&2 + fi + done + + ## codeql-pack.yml files (src and test packs for each language) + for lang in "${LANGUAGES[@]}"; do + for pack_type in "src" "test"; do + local pack_file="server/ql/${lang}/tools/${pack_type}/codeql-pack.yml" + local full_path="${REPO_ROOT}/${pack_file}" + if [[ -f "${full_path}" ]]; then + local pack_version + pack_version=$(grep -m1 "^version:" "${full_path}" | awk '{print $2}') + versions+=("${pack_file}|${pack_version}") + fi + done + done + + printf '%s\n' "${versions[@]}" +} + +## Check version consistency +check_versions() { + local expected_version="${1:-}" + local all_consistent=true + local first_version="" + local file_count=0 + + echo "=== Version Consistency Check ===" + echo "" + + while IFS='|' read -r file version; do + file_count=$((file_count + 1)) + + if [[ -z "${first_version}" ]]; then + first_version="${version}" + fi + + if [[ -n "${expected_version}" ]]; then + if [[ "${version}" == "${expected_version}" ]]; then + echo " ✅ ${file}: ${version}" + else + echo " ❌ ${file}: ${version} (expected ${expected_version})" + all_consistent=false + fi + else + if [[ "${version}" == "${first_version}" ]]; then + echo " ✅ ${file}: ${version}" + else + echo " ❌ ${file}: ${version} (differs from ${first_version})" + all_consistent=false + fi + fi + done < <(collect_versions) + + echo "" + echo "Checked ${file_count} version-bearing files." + + if [[ "${all_consistent}" == true ]]; then + if [[ -n "${expected_version}" ]]; then + echo "✅ All files match expected version: ${expected_version}" + else + echo "✅ All files are consistent at version: ${first_version}" + fi + return 0 + else + echo "❌ Version inconsistency detected!" + return 1 + fi +} + +## Validate version format (X.Y.Z or X.Y.Z-suffix) +validate_version() { + local version="$1" + if [[ ! "${version}" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9._-]+)?$ ]]; then + echo "ERROR: Invalid version format '${version}'" >&2 + echo "Expected format: X.Y.Z or X.Y.Z-suffix (e.g., 2.24.1 or 2.24.1-beta)" >&2 + return 1 + fi +} + +## Extract the base version (X.Y.Z) from a potentially suffixed version (X.Y.Z-beta) +extract_base_version() { + local version="$1" + echo "${version%%-*}" +} + +## Validate that the base version corresponds to an actual CodeQL CLI version. +## Checks in order: +## 1. If 'codeql' CLI is on PATH, compare its version +## 2. Fall back to the .codeql-version file in the repo +validate_cli_version() { + local base_version="$1" + + echo "=== CodeQL CLI Version Validation ===" + echo " Required base version: ${base_version}" + + ## Try the installed CLI first + if command -v codeql >/dev/null 2>&1; then + local installed_version + installed_version=$(codeql version --format=terse 2>/dev/null || echo "unknown") + echo " Installed CLI version: ${installed_version}" + if [[ "${installed_version}" == "${base_version}" ]]; then + echo " ✅ Base version ${base_version} matches installed CodeQL CLI" + return 0 + else + echo " ❌ Base version ${base_version} does not match installed CodeQL CLI (${installed_version})" >&2 + echo "" >&2 + echo " The base version (X.Y.Z, without prerelease suffix) must match an" >&2 + echo " available CodeQL CLI release. Either:" >&2 + echo " - Install the correct CLI: gh codeql set-version ${base_version}" >&2 + echo " - Use a version matching the installed CLI: ${installed_version}" >&2 + echo " - Skip validation with --skip-cli-validation (not recommended)" >&2 + return 1 + fi + fi + + ## Fall back to .codeql-version file + local codeql_version_file="${REPO_ROOT}/.codeql-version" + if [[ -f "${codeql_version_file}" ]]; then + local file_version + file_version=$(tr -d '[:space:]' < "${codeql_version_file}") + file_version="${file_version#v}" ## Strip v prefix + echo " .codeql-version file: ${file_version}" + if [[ "${file_version}" == "${base_version}" ]]; then + echo " ✅ Base version ${base_version} matches .codeql-version" + return 0 + else + echo " ❌ Base version ${base_version} does not match .codeql-version (${file_version})" >&2 + echo "" >&2 + echo " The base version (X.Y.Z, without prerelease suffix) must correspond" >&2 + echo " to the CodeQL CLI version in .codeql-version." >&2 + echo " Current .codeql-version: v${file_version}" >&2 + echo " Skip validation with --skip-cli-validation (not recommended)" >&2 + return 1 + fi + fi + + echo " WARNING: Cannot validate base version — no CodeQL CLI on PATH and no .codeql-version file" >&2 + return 1 +} + +## Update a JSON file's "version" field using sed +update_json_version() { + local file="$1" + local new_version="$2" + ## Match the "version": "..." line and replace the version value + sed -i.bak "s/\"version\"[[:space:]]*:[[:space:]]*\"[^\"]*\"/\"version\": \"${new_version}\"/" "${file}" + rm -f "${file}.bak" +} + +## Update a codeql-pack.yml file's version field using sed +update_pack_version() { + local file="$1" + local new_version="$2" + ## Match the version: line at the start and replace the version value + sed -i.bak "s/^version:[[:space:]]*.*/version: ${new_version}/" "${file}" + rm -f "${file}.bak" +} + +## Update all version-bearing files +update_versions() { + local new_version="$1" + local dry_run="${2:-false}" + local updated_count=0 + + echo "=== Updating Release Version to ${new_version} ===" + echo "" + + ## 1. Update .codeql-version (uses v prefix) + local codeql_version_file="${REPO_ROOT}/.codeql-version" + if [[ -f "${codeql_version_file}" ]]; then + local old_version + old_version=$(tr -d '[:space:]' < "${codeql_version_file}") + if [[ "${dry_run}" == true ]]; then + echo " [DRY RUN] .codeql-version: ${old_version} -> v${new_version}" + else + printf "v%s\n" "${new_version}" > "${codeql_version_file}" + echo " ✅ .codeql-version: ${old_version} -> v${new_version}" + fi + updated_count=$((updated_count + 1)) + fi + + ## 2. Update package.json files + local pkg_files=("package.json" "client/package.json" "server/package.json") + for pkg_file in "${pkg_files[@]}"; do + local full_path="${REPO_ROOT}/${pkg_file}" + if [[ -f "${full_path}" ]]; then + local old_version + old_version=$(grep -m1 '"version"' "${full_path}" | sed 's/.*"version"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/') + if [[ "${dry_run}" == true ]]; then + echo " [DRY RUN] ${pkg_file}: ${old_version} -> ${new_version}" + else + update_json_version "${full_path}" "${new_version}" + echo " ✅ ${pkg_file}: ${old_version} -> ${new_version}" + fi + updated_count=$((updated_count + 1)) + fi + done + + ## 3. Update codeql-pack.yml files (src and test packs for each language) + for lang in "${LANGUAGES[@]}"; do + for pack_type in "src" "test"; do + local pack_file="server/ql/${lang}/tools/${pack_type}/codeql-pack.yml" + local full_path="${REPO_ROOT}/${pack_file}" + if [[ -f "${full_path}" ]]; then + local old_version + old_version=$(grep -m1 "^version:" "${full_path}" | awk '{print $2}') + if [[ "${dry_run}" == true ]]; then + echo " [DRY RUN] ${pack_file}: ${old_version} -> ${new_version}" + else + update_pack_version "${full_path}" "${new_version}" + echo " ✅ ${pack_file}: ${old_version} -> ${new_version}" + fi + updated_count=$((updated_count + 1)) + fi + done + done + + echo "" + if [[ "${dry_run}" == true ]]; then + echo "Would update ${updated_count} files. (Dry run — no files modified)" + else + echo "Updated ${updated_count} files to version ${new_version}." + echo "" + echo "Next steps:" + echo " 1. Run 'npm install' to regenerate package-lock.json" + echo " 2. Run 'npm run build-and-test' to validate the changes" + echo " 3. Commit the changes and tag with 'v${new_version}'" + fi +} + +## Parse arguments +CHECK_MODE=false +DRY_RUN=false +SKIP_CLI_VALIDATION=false +NEW_VERSION="" + +while [[ $# -gt 0 ]]; do + case $1 in + --check) + CHECK_MODE=true + shift + ## Optional expected version argument + if [[ $# -gt 0 && ! "$1" =~ ^-- ]]; then + NEW_VERSION="${1#v}" + shift + fi + ;; + --dry-run) + DRY_RUN=true + shift + ;; + --skip-cli-validation) + SKIP_CLI_VALIDATION=true + shift + ;; + -h|--help) + usage + exit 0 + ;; + -*) + echo "Error: Unknown option $1" >&2 + usage >&2 + exit 1 + ;; + *) + NEW_VERSION="${1#v}" ## Strip optional v prefix + shift + ;; + esac +done + +if [[ "${CHECK_MODE}" == true ]]; then + check_versions "${NEW_VERSION}" + exit $? +fi + +if [[ -z "${NEW_VERSION}" ]]; then + echo "Error: No version specified" >&2 + echo "" >&2 + usage >&2 + exit 1 +fi + +validate_version "${NEW_VERSION}" + +## Validate that the base version matches an actual CodeQL CLI release +if [[ "${SKIP_CLI_VALIDATION}" == false ]]; then + BASE_VERSION=$(extract_base_version "${NEW_VERSION}") + validate_cli_version "${BASE_VERSION}" + echo "" +else + echo "⚠️ CLI version validation skipped (--skip-cli-validation)" + echo "" +fi + +update_versions "${NEW_VERSION}" "${DRY_RUN}" diff --git a/server/src/codeql-development-mcp-server.ts b/server/src/codeql-development-mcp-server.ts index 30c9e9a..c6cf452 100644 --- a/server/src/codeql-development-mcp-server.ts +++ b/server/src/codeql-development-mcp-server.ts @@ -28,7 +28,7 @@ import { logger } from './utils/logger'; dotenv.config({ path: resolve(packageRootDir, '.env') }); const PACKAGE_NAME = 'codeql-development-mcp-server'; -const VERSION = '2.24.0'; +const VERSION = '2.24.1'; /** * Start the MCP server diff --git a/server/src/prompts/document-codeql-query.prompt.md b/server/src/prompts/document-codeql-query.prompt.md index 2832315..43cae92 100644 --- a/server/src/prompts/document-codeql-query.prompt.md +++ b/server/src/prompts/document-codeql-query.prompt.md @@ -1,5 +1,5 @@ --- -mode: agent +agent: agent --- # Document a CodeQL Query @@ -64,8 +64,15 @@ Use the following MCP server tools to gather context before creating documentati - Tool: `validate_codeql_query` - Parameters: `query` = query source code - Gather: Structural validation, suggestions + - Note: This is a heuristic check only — for full validation, use `codeql_query_compile` -- [ ] **Step 5: Run tests** (if tests exist from Step 1) +- [ ] **Step 5: Explore query types** (if deeper understanding needed) + - Tool: `codeql_lsp_definition` — navigate to class/predicate definitions + - Tool: `codeql_lsp_completion` — explore member predicates on types used in the query + - Parameters: `file_path`, `line` (0-based), `character` (0-based), `workspace_uri` (pack root) + - Run `codeql_pack_install` first — LSP tools require resolved dependencies + +- [ ] **Step 6: Run tests** (if tests exist from Step 1) - Tool: `codeql_test_run` - Parameters: `tests` = test directories - Gather: Pass/fail status, confirms query behavior @@ -78,12 +85,12 @@ Based on gathered context, create or update the documentation file. The documentation file (`QueryFileBaseName.md`) should follow this standardized format with these sections: -**Section 1: Title and Description** +### Section 1: Title and Description - H1 heading with the query name from @name metadata - One paragraph description from @description, expanded if needed -**Section 2: Metadata Table** +### Section 2: Metadata Table A table with these rows: @@ -93,15 +100,15 @@ A table with these rows: - Precision: The @precision value - Tags: The @tags values -**Section 3: Overview** +### Section 3: Overview Concise explanation of what vulnerability/issue this query detects and why it matters. 2-4 sentences. -**Section 4: Recommendation** +### Section 4: Recommendation Brief guidance on how developers should fix issues flagged by this query. Include code patterns to use or avoid. -**Section 5: Example** +### Section 5: Example Two subsections: @@ -110,7 +117,7 @@ Two subsections: Use the appropriate language identifier for the code blocks (e.g., `javascript`, `python`, `java`). -**Section 6: References** +### Section 6: References A list of links to: diff --git a/server/src/prompts/explain-codeql-query.prompt.md b/server/src/prompts/explain-codeql-query.prompt.md index b639c93..eab787e 100644 --- a/server/src/prompts/explain-codeql-query.prompt.md +++ b/server/src/prompts/explain-codeql-query.prompt.md @@ -1,5 +1,5 @@ --- -mode: agent +agent: agent --- # Explain a CodeQL Query (Workshop Learning Content) @@ -103,9 +103,11 @@ You MUST use the following MCP server tools in sequence to gather context before ``` - [ ] **Step 8: Quick evaluate specific predicates** (as needed) - - Tool: `quick_evaluate` - - Parameters: Specific QL code snippets to understand predicate behavior + - First, locate the predicate: Tool: `find_predicate_position` with `file` and `name` + - Then evaluate: Tool: `quick_evaluate` with `file`, `db`, and `symbol` - Use when: You need more context on how a specific predicate or class behaves + - Note: `find_class_position` finds `class` definitions only, not `module` definitions + - Note: `find_predicate_position` returns 1-based positions; LSP tools use 0-based ### Phase 5: Generate Explanation diff --git a/server/src/prompts/ql-lsp-iterative-development.prompt.md b/server/src/prompts/ql-lsp-iterative-development.prompt.md index 62738cc..995ebf5 100644 --- a/server/src/prompts/ql-lsp-iterative-development.prompt.md +++ b/server/src/prompts/ql-lsp-iterative-development.prompt.md @@ -4,106 +4,255 @@ agent: agent # Iterative CodeQL Development with LSP Tools -Use this workflow to iteratively develop and refine CodeQL queries using the LSP-powered tools. -These tools let you inspect, validate, and navigate QL code at a granular level — working with -specific classes, predicates, and expressions rather than running full queries. +Use the MCP server tools from this prompt to iteratively develop and refine CodeQL queries using the LSP-powered tools. +These "iterative" tools work entirely through file paths and numeric positions. +Every operation is expressible as a tool call with explicit `file_path`, `line`, and `character` parameters. +Thus, this prompt can be used in any environment where the query files are on disk, and it does not require any special editor integration. -## When to Use This Workflow +## Use This Prompt When - Exploring unfamiliar CodeQL libraries to discover available classes and predicates - Incrementally building a query clause-by-clause with real-time feedback -- Navigating from a class usage to its definition to understand its API -- Finding all usages of a predicate across a codebase +- Navigating from a type usage to its definition to understand its API +- Finding all usages of a predicate to learn patterns from existing queries - Validating query fragments before assembling a complete query +- Debugging individual predicates by evaluating them in isolation against a database ## Prerequisites -- A CodeQL pack with `codeql-pack.yml` and installed dependencies (`codeql_pack_install`) -- Query files must be on disk (LSP tools operate on files, not inline code) -- The `workspace_uri` parameter should point to the pack root or workspace root +1. A CodeQL pack with `codeql-pack.yml` on disk +2. Dependencies installed via `codeql_pack_install` (pointing at the pack directory) +3. Query files saved to disk (LSP tools operate on files, not inline strings) -## Step 1: Discover Available Types +## Key Concept: Positions Are File Path + Line + Character -Use `codeql_lsp_completion` to explore what types are available after an `import` statement. +All LSP tools identify locations using three values: -**Example**: After writing `import javascript` in a `.ql` file, request completions inside the -`from` clause to see all available classes: +- `file_path`: absolute path to a `.ql` or `.qll` file +- `line`: **0-based** line number (line 1 in the file = `line: 0`) +- `character`: **0-based** column offset within that line -- Tool: `codeql_lsp_completion` -- Parameters: `file_path` = your query file, `line` = the `from` line, `character` = after the space -- Tip: Set `workspace_uri` to the pack root for dependency resolution +The `workspace_uri` parameter must point to the **pack root directory** (the folder +containing `codeql-pack.yml`) for import resolution to work. Without it, completions +and definitions for imported libraries will be empty. -The completions include class names with documentation, so you can discover types like -`File`, `Function`, `CallExpr`, etc. without leaving the editor. +> **Critical**: LSP line/character positions are 0-based, but `read_file`, +> `find_predicate_position`, and `find_class_position` return 1-based positions. +> Always subtract 1 when passing their output to LSP tools. + +## Step 1: Discover Available Types with Completions + +Use `codeql_lsp_completion` to explore what types and predicates are available at any +position in a query file. This replaces manual documentation browsing. + +**Example**: To see what classes are available in a `from` clause after `import javascript`: + +```text +Tool: codeql_lsp_completion +Parameters: + file_path: /path/to/your/query.ql + line: 9 # 0-based line of the `from` clause + character: 5 # position after `from ` where you want completions + workspace_uri: /path/to/pack-root # directory containing codeql-pack.yml +``` + +Completions include class names with full documentation. For example, requesting +completions in a JavaScript query's `from` clause returns 150+ types like +`CallNode`, `PropWrite`, `RemoteFlowSource`, etc., each with docstrings. + +**Exploring member predicates**: To see what methods a variable offers, request +completions after the dot. For example, if `pw` is typed as `DataFlow::PropWrite`, +requesting completions at the position after `pw.` returns all member predicates +like `getPropertyName()`, `getRhs()`, `getBase()`, `writes(base, prop, rhs)`, +each with full signature and documentation. ## Step 2: Navigate to Definitions -Once you've chosen a class (e.g., `File`), use `codeql_lsp_definition` to jump to its -definition and understand its member predicates. +Use `codeql_lsp_definition` to find where a class, predicate, or module is defined. +This returns the file URI and line range of the definition — even into library pack +files you haven't opened. -- Tool: `codeql_lsp_definition` -- Parameters: position the cursor on the class name in your query -- Returns: the file URI and line range of the class definition +```text +Tool: codeql_lsp_definition +Parameters: + file_path: /path/to/your/query.ql + line: 11 # 0-based line containing the symbol + character: 30 # 0-based column within the symbol name + workspace_uri: /path/to/pack-root +``` -This is especially useful for understanding what predicates a class offers -(e.g., `File` has `getBaseName()`, `getExtension()`, `getAbsolutePath()`). +**Example**: Navigating to `RemoteFlowSource` at line 12, character 30 returns: + +```text +uri: file:///.../.codeql/packages/codeql/javascript-all/2.6.19/ + semmle/javascript/security/dataflow/RemoteFlowSources.qll +startLine: 14, startCharacter: 17 +``` + +You can then read that file to understand the class's API. This is how you discover +what predicates a type offers without documentation — go to the definition and read +the source. ## Step 3: Find All References -Use `codeql_lsp_references` to find how a class or predicate is used across the pack. +Use `codeql_lsp_references` to find how a symbol is used across the workspace. + +```text +Tool: codeql_lsp_references +Parameters: + file_path: /path/to/your/query.ql + line: 11 # 0-based line + character: 30 # 0-based column within the symbol + workspace_uri: /path/to/pack-root +``` + +> **Scope note**: References are scoped to the pack identified by `workspace_uri`. +> To find usages in library code, point `workspace_uri` to the library pack root. +> For usages within your own pack only, point it to your pack root. + +This is invaluable for learning how experienced query authors use a predicate — +find real usage examples instead of guessing from documentation. -- Tool: `codeql_lsp_references` -- Parameters: position the cursor on the symbol -- Returns: all locations where the symbol is used +## Step 4: Locate Symbols with Position Finders + +Use `find_predicate_position` and `find_class_position` to locate where a specific +symbol is defined in a file. These return **1-based** line/column positions. + +```text +Tool: find_predicate_position +Parameters: + file: /path/to/your/query.ql + name: isSource +Returns: { start_line: 12, start_col: 13, end_line: 12, end_col: 20 } +``` -This helps you find examples of how experienced query authors use a particular -predicate, which is invaluable for learning patterns. +> **Note**: `find_class_position` finds `class` definitions only — it does not find +> `module` definitions. Use `find_predicate_position` for predicates inside modules. + +**Combining with LSP tools**: To navigate to a predicate's definition in library code: + +1. Use `find_predicate_position` to get its 1-based position +2. Subtract 1 from line/col to convert to 0-based +3. Pass to `codeql_lsp_definition` to jump to the underlying type + +## Step 5: Quick-Evaluate Individual Predicates + +Use `quick_evaluate` to evaluate a single predicate or class against a database +without running the full query. This is the fastest way to debug whether a predicate +matches what you expect. + +```text +Tool: quick_evaluate +Parameters: + file: /path/to/your/query.ql + db: /path/to/test-database.testproj + symbol: isSink + output_path: /tmp/quickeval-results # optional, for inspecting bqrs output +``` -## Step 4: Validate Incrementally +The tool evaluates just that symbol (predicate or class) and returns the result path. +Use `codeql_bqrs_decode` on the output to inspect results in CSV or JSON format. -After each modification to your query file: +## Step 6: Validate at Multiple Levels -1. Save the file to disk -2. Use `codeql_lsp_completion` at the cursor position to verify the language server - understands the context (non-empty completions = good parse state) -3. Use `codeql_query_compile` for a full compilation check -4. Use `codeql_test_run` to validate against expected results +Use the right validation tool for each situation: -## Step 5: Quick Evaluate Specific Parts +| Tool | Use When | Input | Resolves Imports? | +| ------------------------ | --------------------------------------- | ------------------ | ------------------- | +| `validate_codeql_query` | Quick structural check | Inline QL string | No (heuristic only) | +| `codeql_lsp_diagnostics` | Syntax/semantic validation of fragments | Inline QL string | No | +| `codeql_query_compile` | Full compilation check | On-disk `.ql` file | Yes | +| `codeql_test_run` | End-to-end result validation | Test directory | Yes | -Use `find_class_position` and `find_predicate_position` to locate specific -symbols, then use `quick_evaluate` to evaluate just that class or predicate -against a database — without running the full query. +**`codeql_lsp_diagnostics`** validates QL syntax and semantics for inline code snippets, +but **cannot resolve `import` statements** (like `import javascript`). Use it for: -- Tool: `find_class_position` — find where a class is defined (line/column) -- Tool: `find_predicate_position` — find where a predicate is defined -- Tool: `quick_evaluate` — evaluate just that symbol against a database +- Checking predicate signatures and QL syntax +- Verifying `from`/`where`/`select` clause structure +- Catching type errors in import-free QL fragments + +For queries with imports, always use `codeql_query_compile` on the saved file. ## Iterative Development Loop ```text -1. Write/modify a clause in the query file +1. Write/modify a clause in the query file (save to disk) + ↓ +2. codeql_lsp_completion → verify context is valid (non-empty = good) ↓ -2. codeql_lsp_completion → verify context is valid +3. codeql_query_compile → check for compilation errors ↓ -3. codeql_query_compile → check for errors +4. codeql_test_run → validate against expected results ↓ -4. codeql_test_run → validate results +5. If unexpected results: quick_evaluate on individual predicates ↓ -5. If failing: use codeql_lsp_definition to understand types +6. If predicate is wrong: codeql_lsp_completion to explore the API ↓ -6. If stuck: use codeql_lsp_references to find usage examples +7. If types are unclear: codeql_lsp_definition to read the source ↓ -7. Repeat from step 1 +8. If stuck: codeql_lsp_references to find usage examples + ↓ +9. Repeat from step 1 +``` + +## Worked Example: Building a Taint-Tracking Query + +This example shows the tools in action for building a JavaScript XSS query. + +### 1. Create the query file and install dependencies + +Write a `.ql` file with `import javascript` and a skeleton `from`/`where`/`select`. +Run `codeql_pack_install` on the pack directory. + +### 2. Explore sink types + +Request completions in the `from` clause to discover `DataFlow::PropWrite`: + +```text +codeql_lsp_completion(file_path=..., line=9, character=5, workspace_uri=pack_root) +→ 155 completions including PropWrite, CallNode, MethodCallNode, ... +``` + +### 3. Explore `PropWrite` member predicates + +After typing `pw.` in the `where` clause, request completions: + +```text +codeql_lsp_completion(file_path=..., line=12, character=9, workspace_uri=pack_root) +→ 43 predicates: getPropertyName(), getRhs(), getBase(), writes(), ... +``` + +### 4. Navigate to `RemoteFlowSource` definition + +```text +codeql_lsp_definition(file_path=..., line=11, character=30, workspace_uri=pack_root) +→ RemoteFlowSources.qll line 14 in codeql/javascript-all +``` + +### 5. Compile and test incrementally + +```text +codeql_query_compile(query=file_path) → check for errors +codeql_test_run(tests=[test_dir], learn=true) → populate .expected files +``` + +### 6. Debug a predicate in isolation + +```text +find_predicate_position(file=..., name="isSink") → line 16, col 13 +quick_evaluate(file=..., db=test.testproj, symbol="isSink") → inspect results ``` ## Important Notes -- **`codeql_lsp_diagnostics` works with inline code** but cannot resolve imports - (like `import javascript`) because the inline code isn't associated with a pack. - For import-dependent validation, use `codeql_query_compile` on the actual file. -- **`codeql_lsp_completion` needs `workspace_uri`** pointing to the pack root for - dependency resolution. Without it, completions will be empty. -- **File-based LSP tools** (`completion`, `definition`, `references`) operate on files - that are part of a pack with resolved dependencies. Always run `codeql_pack_install` - first. +- **All LSP tools use 0-based positions**. `find_class_position` and + `find_predicate_position` return 1-based positions. Convert before combining. +- **`workspace_uri` must be the pack root** (the directory containing + `codeql-pack.yml`). Without it, completions and definitions will be empty. +- **Run `codeql_pack_install` first**. LSP tools require resolved dependencies. +- **`codeql_lsp_diagnostics` cannot resolve imports**. For `import javascript` + and similar, use `codeql_query_compile` on the on-disk file instead. +- **`find_class_position` finds `class` only**, not `module` definitions. + Use grep or `find_predicate_position` for predicates inside modules. +- **`codeql_lsp_references` scope** depends on `workspace_uri`. Point it at + a library pack root to find usages across library code. diff --git a/server/src/prompts/ql-tdd-advanced.prompt.md b/server/src/prompts/ql-tdd-advanced.prompt.md index 7de0a88..1910cf5 100644 --- a/server/src/prompts/ql-tdd-advanced.prompt.md +++ b/server/src/prompts/ql-tdd-advanced.prompt.md @@ -82,6 +82,7 @@ quick_evaluate: { } // Or evaluate a specific class +// NOTE: find_class_position finds `class` definitions only, not `module` definitions find_class_position: { file: "/path/to/Query.ql", name: "ThrowingMethodCall" @@ -102,17 +103,17 @@ Use the LSP tools for real-time code exploration during query development: // Discover available types after `import javascript` codeql_lsp_completion: { file_path: "/path/to/Query.ql", - line: 5, // line with `from` clause - character: 5, // cursor position - workspace_uri: "file:///path/to/pack-root" // REQUIRED for dependency resolution + line: 5, // 0-based line with `from` clause + character: 5, // 0-based column position + workspace_uri: "/path/to/pack-root" // REQUIRED: directory containing codeql-pack.yml } // Navigate to a class definition to see its predicates codeql_lsp_definition: { file_path: "/path/to/Query.ql", - line: 5, // line containing the class name - character: 10, // cursor on the class name - workspace_uri: "file:///path/to/pack-root" + line: 5, // 0-based line containing the class name + character: 10, // 0-based column on the class name + workspace_uri: "/path/to/pack-root" } // Find all usages of a predicate across the pack @@ -120,11 +121,17 @@ codeql_lsp_references: { file_path: "/path/to/Query.ql", line: 8, character: 5, - workspace_uri: "file:///path/to/pack-root" + workspace_uri: "/path/to/pack-root" } ``` -**Important**: Always set `workspace_uri` to the pack or workspace root. Without it, completions for imported library types will be empty. +**Important LSP tool notes**: + +- `workspace_uri` must be a **plain directory path** (not a `file://` URI) pointing to the pack root containing `codeql-pack.yml` +- All LSP tools use **0-based** line/character positions +- `find_predicate_position` and `find_class_position` return **1-based** positions — subtract 1 before passing to LSP tools +- Run `codeql_pack_install` before using LSP tools — they require resolved dependencies +- Request completions **after a dot** (e.g., `pw.`) to see all member predicates with full documentation ### 4. Query File Discovery with `find_codeql_query_files` @@ -184,6 +191,7 @@ Before writing any query logic: - Identify which nodes correspond to your test cases 4. **Generate CFG if analyzing control flow**: + ```typescript codeql_query_run: { queryName: "PrintCFG", @@ -238,6 +246,7 @@ Instead of writing the full query at once: ``` 3. **Use find_codeql_query_files** to track all related files: + ```typescript find_codeql_query_files: { queryPath: '/path/to/Query.ql'; @@ -362,6 +371,7 @@ class ThrowingMethod extends Method { ``` 3. **Profile the query**: + ```typescript profile_codeql_query: { query: "/path/to/Query.ql", @@ -406,11 +416,13 @@ When your query produces correct results but differs from the `.expected` file: 1. **Review the `.actual` file** to verify results are correct 2. **Accept the results** to update the expected baseline: + ```typescript codeql_test_accept: { tests: ['/path/to/test/QueryTest']; } ``` + 3. **Re-run tests** to confirm they now pass **Warning**: Only accept results after careful review. Don't blindly accept to make tests pass. diff --git a/server/src/prompts/ql-tdd-basic.prompt.md b/server/src/prompts/ql-tdd-basic.prompt.md index 2e2b618..d941574 100644 --- a/server/src/prompts/ql-tdd-basic.prompt.md +++ b/server/src/prompts/ql-tdd-basic.prompt.md @@ -52,14 +52,20 @@ For detailed guidance, reference the MCP resource: `codeql://learning/test-drive - Review AST structure and identify relevant classes - [ ] **Explore Available Classes and Predicates** - - Tool: `codeql_lsp_completion` at cursor position in `from` clause - - Set `workspace_uri` to the pack root for dependency resolution - - Browse completions with documentation to discover relevant types + - Tool: `codeql_lsp_completion` at the position in the `from` clause + - Parameters: `file_path`, `line` (0-based), `character` (0-based) + - Set `workspace_uri` to the pack root directory (containing `codeql-pack.yml`) + - Request completions after a dot (e.g., `pw.`) to see member predicates with docs + - Tip: Run `codeql_pack_install` first — LSP tools require resolved dependencies - [ ] **Navigate to Type Definitions** - Tool: `codeql_lsp_definition` on a class or predicate name - - Review the definition to understand available member predicates - - Tool: `codeql_lsp_references` to find usage examples in the pack + - Parameters: `file_path`, `line` (0-based), `character` (0-based), `workspace_uri` + - Returns file URI and line range — even into library pack files + - Read the definition source to understand available member predicates + - Tool: `codeql_lsp_references` to find usage examples across the pack + + For the full iterative LSP workflow, see: `codeql://prompts/ql_lsp_iterative_development` - [ ] **Reference Language Documentation** - Resource: `codeql://languages/{language}/ast` @@ -206,6 +212,7 @@ codeql_test_accept: { - ❌ Skipping compilation step - ❌ Not using PrintAST to understand test code - ❌ Not using `codeql_lsp_completion` to discover available types +- ❌ Not setting `workspace_uri` when using LSP tools (completions will be empty) - ❌ Creating tests that are too complex - ❌ Ignoring false positives in results - ❌ Not refactoring after tests pass diff --git a/server/src/prompts/sarif-rank-false-positives.prompt.md b/server/src/prompts/sarif-rank-false-positives.prompt.md index 22db9b0..4075da0 100644 --- a/server/src/prompts/sarif-rank-false-positives.prompt.md +++ b/server/src/prompts/sarif-rank-false-positives.prompt.md @@ -1,5 +1,5 @@ --- -mode: agent +agent: agent --- # Evaluate SARIF Results for False Positives diff --git a/server/src/prompts/sarif-rank-true-positives.prompt.md b/server/src/prompts/sarif-rank-true-positives.prompt.md index e98b145..904c871 100644 --- a/server/src/prompts/sarif-rank-true-positives.prompt.md +++ b/server/src/prompts/sarif-rank-true-positives.prompt.md @@ -1,5 +1,5 @@ --- -mode: agent +agent: agent --- # Evaluate SARIF Results for True Positives diff --git a/server/src/prompts/tools-query-workflow.prompt.md b/server/src/prompts/tools-query-workflow.prompt.md index 870a7e1..e501986 100644 --- a/server/src/prompts/tools-query-workflow.prompt.md +++ b/server/src/prompts/tools-query-workflow.prompt.md @@ -60,7 +60,7 @@ The PrintAST query outputs a hierarchical tree of AST nodes with labels. **Example AST output structure:** -``` +```text TopLevelFunction ├── FunctionDeclarationEntry ├── Block @@ -91,7 +91,7 @@ The PrintCFG query outputs control flow nodes and edges. **Example CFG output structure:** -``` +```text nodes | node | semmle.label | | ... | entry: processData | @@ -141,7 +141,7 @@ Use the gathered information to inform your query: ### Pattern 1: Finding All Function Calls -``` +```text 1. Run PrintAST on your source file 2. Look for FunctionCall, MethodAccess, or similar nodes 3. Note the parent/child relationships @@ -150,7 +150,7 @@ Use the gathered information to inform your query: ### Pattern 2: Tracing Data Through Functions -``` +```text 1. Run CallGraphFrom on your entry point function 2. Identify which functions are called 3. Run CallGraphTo on sink functions @@ -159,7 +159,7 @@ Use the gathered information to inform your query: ### Pattern 3: Understanding Loop Structures -``` +```text 1. Run PrintAST to find loop constructs (ForStmt, WhileStmt, etc.) 2. Run PrintCFG on the containing function 3. Identify back edges that represent loop iteration @@ -187,3 +187,21 @@ Use the gathered information to inform your query: | `codeql_lsp_completion` | Explore available types after seeing AST class names | | `codeql_lsp_definition` | Navigate to class definitions to see predicates | | `codeql_lsp_references` | Find usage examples of a class or predicate | + +### Using LSP Tools After AST Analysis + +After running PrintAST and identifying relevant AST class names, use the LSP tools +to explore those classes in your query file: + +1. **Write the class name** in your query's `from` clause and save the file +2. **Run `codeql_lsp_completion`** after the dot to see member predicates: + - `file_path`: your query file, `line`/`character`: 0-based position after the dot + - `workspace_uri`: the pack root directory (containing `codeql-pack.yml`) +3. **Run `codeql_lsp_definition`** on an AST class name to see its full API +4. **Run `codeql_lsp_references`** to find usage examples in the pack + +> **Note**: LSP tools use 0-based line/character positions. Run `codeql_pack_install` +> before using them — they require resolved dependencies. Set `workspace_uri` to +> a plain directory path (not a `file://` URI). + +For the full iterative LSP development workflow, see: `codeql://prompts/ql_lsp_iterative_development` diff --git a/server/src/prompts/workshop-creation-workflow.prompt.md b/server/src/prompts/workshop-creation-workflow.prompt.md index 2cced81..81edaef 100644 --- a/server/src/prompts/workshop-creation-workflow.prompt.md +++ b/server/src/prompts/workshop-creation-workflow.prompt.md @@ -97,7 +97,7 @@ Decompose the query into 4-8 incremental stages using these strategies: Standard workshop directory layout: -``` +```text workshop-name/ ├── codeql-workspace.yml # CodeQL workspace configuration ├── README.md # Workshop guide with instructions @@ -143,7 +143,13 @@ workshop-name/ ### Phase 5: Create Solution Queries -For each stage, create a complete solution query: +For each stage, create a complete solution query. Use the iterative LSP tools +for efficient development (see `codeql://prompts/ql_lsp_iterative_development`): + +- Use `codeql_lsp_completion` to explore types and member predicates while writing queries +- Use `codeql_lsp_definition` to navigate to library class definitions +- Use `find_predicate_position` + `quick_evaluate` to test predicates in isolation +- Set `workspace_uri` to the solutions pack root for dependency resolution - [ ] **Stage 1 Solution**: Simplest working version - Basic import statements @@ -261,19 +267,24 @@ When creating workshops outside the MCP server repository: ## MCP Tools and Prompts Reference -| Tool/Prompt | Type | Purpose | -| ------------------------- | ------ | ------------------------------------------------------------ | -| `find_codeql_query_files` | Tool | Locate query and related files | -| `explain_codeql_query` | Prompt | Generate detailed explanations for workshop learning content | -| `document_codeql_query` | Prompt | Create/update query documentation files | -| `codeql_query_run` | Tool | Run tools queries (PrintAST, PrintCFG, etc.) | -| `codeql_test_run` | Tool | Validate tests pass | -| `codeql_test_accept` | Tool | Accept test results as expected baseline | -| `codeql_query_compile` | Tool | Verify queries compile | -| `codeql_pack_install` | Tool | Install pack dependencies | -| `codeql_resolve_metadata` | Tool | Extract query metadata | -| `profile_codeql_query` | Tool | Profile query execution to understand evaluation order | -| `create_codeql_query` | Tool | Scaffold new query structure | +| Tool/Prompt | Type | Purpose | +| ------------------------------ | ------ | ------------------------------------------------------------ | +| `find_codeql_query_files` | Tool | Locate query and related files | +| `explain_codeql_query` | Prompt | Generate detailed explanations for workshop learning content | +| `document_codeql_query` | Prompt | Create/update query documentation files | +| `ql_lsp_iterative_development` | Prompt | Iterative query development with LSP tools | +| `codeql_query_run` | Tool | Run tools queries (PrintAST, PrintCFG, etc.) | +| `codeql_test_run` | Tool | Validate tests pass | +| `codeql_test_accept` | Tool | Accept test results as expected baseline | +| `codeql_query_compile` | Tool | Verify queries compile | +| `codeql_pack_install` | Tool | Install pack dependencies | +| `codeql_resolve_metadata` | Tool | Extract query metadata | +| `codeql_lsp_completion` | Tool | Explore types and member predicates during query writing | +| `codeql_lsp_definition` | Tool | Navigate to class/predicate definitions in library code | +| `find_predicate_position` | Tool | Locate predicate positions for quick_evaluate | +| `quick_evaluate` | Tool | Test individual predicates against a database | +| `profile_codeql_query` | Tool | Profile query execution to understand evaluation order | +| `create_codeql_query` | Tool | Scaffold new query structure | ## Troubleshooting diff --git a/server/test/src/prompts/workflow-prompts.test.ts b/server/test/src/prompts/workflow-prompts.test.ts index 11bcf09..ea44547 100644 --- a/server/test/src/prompts/workflow-prompts.test.ts +++ b/server/test/src/prompts/workflow-prompts.test.ts @@ -1,6 +1,15 @@ /** * Tests for workflow prompts context builders, schema validation, * and registerWorkflowPrompts registration consistency. + * + * This file ensures that: + * 1. Required vs optional parameters are properly enforced per schema. + * 2. VS Code slash-command string inputs are handled correctly (coercion). + * 3. Each schema exposes exactly the expected parameter keys. + * 4. Registered schemas match the exported schema constants. + * 5. Handler outputs reflect user-supplied parameter values. + * 6. Default/derived values behave consistently. + * 7. All SUPPORTED_LANGUAGES are accepted by every schema with a language field. */ import { describe, it, expect, vi, beforeEach } from 'vitest'; @@ -30,6 +39,36 @@ vi.mock('../../../src/prompts/prompt-loader', () => ({ processPromptTemplate: vi.fn(() => '# processed mock template\n'), })); +// --------------------------------------------------------------------------- +// Helper types +// --------------------------------------------------------------------------- + +/** Handler result shape returned by every workflow prompt. */ +interface PromptResult { + messages: Array<{ + content: { text: string; type: string }; + role: string; + }>; +} + +// eslint-disable-next-line no-unused-vars +type PromptHandler = (args: Record) => Promise; + +// --------------------------------------------------------------------------- +// Helper: invoke a registered handler by prompt name +// --------------------------------------------------------------------------- +function getRegisteredHandler( + mockServer: McpServer, + promptName: string +): PromptHandler { + const calls = (mockServer.prompt as ReturnType).mock.calls; + const match = calls.find((c: unknown[]) => c[0] === promptName); + if (!match) { + throw new Error(`Prompt "${promptName}" not registered`); + } + return match[3] as PromptHandler; +} + describe('Workflow Prompts', () => { // ----------------------------------------------------------------------- // SUPPORTED_LANGUAGES @@ -70,6 +109,12 @@ describe('Workflow Prompts', () => { // testDrivenDevelopmentSchema // ----------------------------------------------------------------------- describe('testDrivenDevelopmentSchema', () => { + it('should expose exactly the expected parameter keys', () => { + expect(Object.keys(testDrivenDevelopmentSchema.shape).sort()).toEqual( + ['language', 'queryName'] + ); + }); + it('should accept valid required language', () => { const result = testDrivenDevelopmentSchema.safeParse({ language: 'javascript' }); expect(result.success).toBe(true); @@ -86,21 +131,50 @@ describe('Workflow Prompts', () => { } }); + it('should leave queryName as undefined when omitted', () => { + const result = testDrivenDevelopmentSchema.safeParse({ language: 'java' }); + expect(result.success).toBe(true); + if (result.success) { + expect(result.data.queryName).toBeUndefined(); + } + }); + it('should reject missing language', () => { const result = testDrivenDevelopmentSchema.safeParse({ queryName: 'Q' }); expect(result.success).toBe(false); }); + it('should reject empty object (language is required)', () => { + const result = testDrivenDevelopmentSchema.safeParse({}); + expect(result.success).toBe(false); + }); + it('should reject invalid language', () => { const result = testDrivenDevelopmentSchema.safeParse({ language: 'rust' }); expect(result.success).toBe(false); }); + + it('should reject language with wrong case (enum is case-sensitive)', () => { + const result = testDrivenDevelopmentSchema.safeParse({ language: 'JavaScript' }); + expect(result.success).toBe(false); + }); + + it.each([...SUPPORTED_LANGUAGES])('should accept language "%s"', (lang) => { + const result = testDrivenDevelopmentSchema.safeParse({ language: lang }); + expect(result.success).toBe(true); + }); }); // ----------------------------------------------------------------------- // toolsQueryWorkflowSchema // ----------------------------------------------------------------------- describe('toolsQueryWorkflowSchema', () => { + it('should expose exactly the expected parameter keys', () => { + expect(Object.keys(toolsQueryWorkflowSchema.shape).sort()).toEqual( + ['database', 'language', 'sourceFiles', 'sourceFunction', 'targetFunction'] + ); + }); + it('should accept required database and language', () => { const result = toolsQueryWorkflowSchema.safeParse({ database: '/db', @@ -119,6 +193,11 @@ describe('Workflow Prompts', () => { expect(result.success).toBe(false); }); + it('should reject empty object (both required fields missing)', () => { + const result = toolsQueryWorkflowSchema.safeParse({}); + expect(result.success).toBe(false); + }); + it('should accept all optional parameters', () => { const result = toolsQueryWorkflowSchema.safeParse({ database: '/db', @@ -134,12 +213,36 @@ describe('Workflow Prompts', () => { expect(result.data.targetFunction).toBe('cleanup'); } }); + + it('should leave optional parameters as undefined when omitted', () => { + const result = toolsQueryWorkflowSchema.safeParse({ + database: '/db', + language: 'go', + }); + expect(result.success).toBe(true); + if (result.success) { + expect(result.data.sourceFiles).toBeUndefined(); + expect(result.data.sourceFunction).toBeUndefined(); + expect(result.data.targetFunction).toBeUndefined(); + } + }); + + it.each([...SUPPORTED_LANGUAGES])('should accept language "%s"', (lang) => { + const result = toolsQueryWorkflowSchema.safeParse({ database: '/db', language: lang }); + expect(result.success).toBe(true); + }); }); // ----------------------------------------------------------------------- // workshopCreationWorkflowSchema // ----------------------------------------------------------------------- describe('workshopCreationWorkflowSchema', () => { + it('should expose exactly the expected parameter keys', () => { + expect(Object.keys(workshopCreationWorkflowSchema.shape).sort()).toEqual( + ['language', 'numStages', 'queryPath', 'workshopName'] + ); + }); + it('should accept valid parameters with number numStages', () => { const result = workshopCreationWorkflowSchema.safeParse({ queryPath: '/path/to/query.ql', @@ -168,6 +271,18 @@ describe('Workflow Prompts', () => { } }); + it('should coerce string "0" numStages to number 0', () => { + const result = workshopCreationWorkflowSchema.safeParse({ + queryPath: '/q.ql', + language: 'java', + numStages: '0', + }); + expect(result.success).toBe(true); + if (result.success) { + expect(result.data.numStages).toBe(0); + } + }); + it('should accept optional numStages', () => { const result = workshopCreationWorkflowSchema.safeParse({ queryPath: '/path/to/query.ql', @@ -193,6 +308,18 @@ describe('Workflow Prompts', () => { } }); + it('should leave optional fields undefined when omitted', () => { + const result = workshopCreationWorkflowSchema.safeParse({ + queryPath: '/q.ql', + language: 'ruby', + }); + expect(result.success).toBe(true); + if (result.success) { + expect(result.data.workshopName).toBeUndefined(); + expect(result.data.numStages).toBeUndefined(); + } + }); + it('should reject invalid language', () => { const result = workshopCreationWorkflowSchema.safeParse({ queryPath: '/path/to/query.ql', @@ -218,15 +345,18 @@ describe('Workflow Prompts', () => { expect(result.success).toBe(false); }); - it('should support all SUPPORTED_LANGUAGES', () => { - for (const lang of SUPPORTED_LANGUAGES) { - const result = workshopCreationWorkflowSchema.safeParse({ - queryPath: '/path/to/query.ql', - language: lang - }); + it('should reject empty object (both required fields missing)', () => { + const result = workshopCreationWorkflowSchema.safeParse({}); + expect(result.success).toBe(false); + }); + + it.each([...SUPPORTED_LANGUAGES])('should accept language "%s"', (lang) => { + const result = workshopCreationWorkflowSchema.safeParse({ + queryPath: '/path/to/query.ql', + language: lang + }); - expect(result.success).toBe(true); - } + expect(result.success).toBe(true); }); }); @@ -234,6 +364,12 @@ describe('Workflow Prompts', () => { // qlTddBasicSchema // ----------------------------------------------------------------------- describe('qlTddBasicSchema', () => { + it('should expose exactly the expected parameter keys', () => { + expect(Object.keys(qlTddBasicSchema.shape).sort()).toEqual( + ['language', 'queryName'] + ); + }); + it('should accept empty object (all optional)', () => { const result = qlTddBasicSchema.safeParse({}); expect(result.success).toBe(true); @@ -249,16 +385,36 @@ describe('Workflow Prompts', () => { expect(result.success).toBe(true); }); + it('should leave both fields as undefined when omitted', () => { + const result = qlTddBasicSchema.safeParse({}); + expect(result.success).toBe(true); + if (result.success) { + expect(result.data.language).toBeUndefined(); + expect(result.data.queryName).toBeUndefined(); + } + }); + it('should reject invalid language', () => { const result = qlTddBasicSchema.safeParse({ language: 'fortran' }); expect(result.success).toBe(false); }); + + it.each([...SUPPORTED_LANGUAGES])('should accept language "%s"', (lang) => { + const result = qlTddBasicSchema.safeParse({ language: lang }); + expect(result.success).toBe(true); + }); }); // ----------------------------------------------------------------------- // qlTddAdvancedSchema // ----------------------------------------------------------------------- describe('qlTddAdvancedSchema', () => { + it('should expose exactly the expected parameter keys', () => { + expect(Object.keys(qlTddAdvancedSchema.shape).sort()).toEqual( + ['database', 'language', 'queryName'] + ); + }); + it('should accept empty object (all optional)', () => { const result = qlTddAdvancedSchema.safeParse({}); expect(result.success).toBe(true); @@ -276,16 +432,37 @@ describe('Workflow Prompts', () => { } }); + it('should leave all fields as undefined when omitted', () => { + const result = qlTddAdvancedSchema.safeParse({}); + expect(result.success).toBe(true); + if (result.success) { + expect(result.data.database).toBeUndefined(); + expect(result.data.language).toBeUndefined(); + expect(result.data.queryName).toBeUndefined(); + } + }); + it('should reject invalid language', () => { const result = qlTddAdvancedSchema.safeParse({ language: 'sql' }); expect(result.success).toBe(false); }); + + it.each([...SUPPORTED_LANGUAGES])('should accept language "%s"', (lang) => { + const result = qlTddAdvancedSchema.safeParse({ language: lang }); + expect(result.success).toBe(true); + }); }); // ----------------------------------------------------------------------- // sarifRankSchema // ----------------------------------------------------------------------- describe('sarifRankSchema', () => { + it('should expose exactly the expected parameter keys', () => { + expect(Object.keys(sarifRankSchema.shape).sort()).toEqual( + ['queryId', 'sarifPath'] + ); + }); + it('should accept empty object (all optional)', () => { const result = sarifRankSchema.safeParse({}); expect(result.success).toBe(true); @@ -311,12 +488,27 @@ describe('Workflow Prompts', () => { }); expect(result.success).toBe(true); }); + + it('should leave both fields as undefined when omitted', () => { + const result = sarifRankSchema.safeParse({}); + expect(result.success).toBe(true); + if (result.success) { + expect(result.data.queryId).toBeUndefined(); + expect(result.data.sarifPath).toBeUndefined(); + } + }); }); // ----------------------------------------------------------------------- // explainCodeqlQuerySchema // ----------------------------------------------------------------------- describe('explainCodeqlQuerySchema', () => { + it('should expose exactly the expected parameter keys', () => { + expect(Object.keys(explainCodeqlQuerySchema.shape).sort()).toEqual( + ['databasePath', 'language', 'queryPath'] + ); + }); + it('should accept required queryPath and language', () => { const result = explainCodeqlQuerySchema.safeParse({ language: 'javascript', @@ -337,6 +529,17 @@ describe('Workflow Prompts', () => { } }); + it('should leave databasePath as undefined when omitted', () => { + const result = explainCodeqlQuerySchema.safeParse({ + language: 'go', + queryPath: '/q.ql', + }); + expect(result.success).toBe(true); + if (result.success) { + expect(result.data.databasePath).toBeUndefined(); + } + }); + it('should reject missing queryPath', () => { const result = explainCodeqlQuerySchema.safeParse({ language: 'java' }); expect(result.success).toBe(false); @@ -346,12 +549,31 @@ describe('Workflow Prompts', () => { const result = explainCodeqlQuerySchema.safeParse({ queryPath: '/q.ql' }); expect(result.success).toBe(false); }); + + it('should reject empty object (both required fields missing)', () => { + const result = explainCodeqlQuerySchema.safeParse({}); + expect(result.success).toBe(false); + }); + + it.each([...SUPPORTED_LANGUAGES])('should accept language "%s"', (lang) => { + const result = explainCodeqlQuerySchema.safeParse({ + language: lang, + queryPath: '/q.ql', + }); + expect(result.success).toBe(true); + }); }); // ----------------------------------------------------------------------- // documentCodeqlQuerySchema // ----------------------------------------------------------------------- describe('documentCodeqlQuerySchema', () => { + it('should expose exactly the expected parameter keys', () => { + expect(Object.keys(documentCodeqlQuerySchema.shape).sort()).toEqual( + ['language', 'queryPath'] + ); + }); + it('should accept required queryPath and language', () => { const result = documentCodeqlQuerySchema.safeParse({ language: 'go', @@ -369,12 +591,31 @@ describe('Workflow Prompts', () => { const result = documentCodeqlQuerySchema.safeParse({ queryPath: '/q.ql' }); expect(result.success).toBe(false); }); + + it('should reject empty object (both required fields missing)', () => { + const result = documentCodeqlQuerySchema.safeParse({}); + expect(result.success).toBe(false); + }); + + it.each([...SUPPORTED_LANGUAGES])('should accept language "%s"', (lang) => { + const result = documentCodeqlQuerySchema.safeParse({ + language: lang, + queryPath: '/q.ql', + }); + expect(result.success).toBe(true); + }); }); // ----------------------------------------------------------------------- // qlLspIterativeDevelopmentSchema // ----------------------------------------------------------------------- describe('qlLspIterativeDevelopmentSchema', () => { + it('should expose exactly the expected parameter keys', () => { + expect(Object.keys(qlLspIterativeDevelopmentSchema.shape).sort()).toEqual( + ['language', 'queryPath', 'workspaceUri'] + ); + }); + it('should accept empty object (all optional)', () => { const result = qlLspIterativeDevelopmentSchema.safeParse({}); expect(result.success).toBe(true); @@ -392,10 +633,253 @@ describe('Workflow Prompts', () => { } }); + it('should leave all fields as undefined when omitted', () => { + const result = qlLspIterativeDevelopmentSchema.safeParse({}); + expect(result.success).toBe(true); + if (result.success) { + expect(result.data.language).toBeUndefined(); + expect(result.data.queryPath).toBeUndefined(); + expect(result.data.workspaceUri).toBeUndefined(); + } + }); + it('should reject invalid language', () => { const result = qlLspIterativeDevelopmentSchema.safeParse({ language: 'haskell' }); expect(result.success).toBe(false); }); + + it.each([...SUPPORTED_LANGUAGES])('should accept language "%s"', (lang) => { + const result = qlLspIterativeDevelopmentSchema.safeParse({ language: lang }); + expect(result.success).toBe(true); + }); + }); + + // ----------------------------------------------------------------------- + // Cross-schema: VS Code slash command string-input compatibility + // ----------------------------------------------------------------------- + describe('VS Code slash command string-input compatibility', () => { + it('should accept string language values as-is (already strings)', () => { + // VS Code passes everything as strings — language enums must accept + // the literal string values without coercion. + const result = testDrivenDevelopmentSchema.safeParse({ + language: 'javascript', + }); + expect(result.success).toBe(true); + }); + + it('should reject empty string for required language enum', () => { + // If VS Code sends an empty string for the language input box + const result = testDrivenDevelopmentSchema.safeParse({ language: '' }); + expect(result.success).toBe(false); + }); + + it('should accept empty string for optional string parameters', () => { + // VS Code may send empty strings for optional text inputs + const result = testDrivenDevelopmentSchema.safeParse({ + language: 'python', + queryName: '', + }); + expect(result.success).toBe(true); + if (result.success) { + expect(result.data.queryName).toBe(''); + } + }); + + it('should reject null for required string parameters', () => { + const result = toolsQueryWorkflowSchema.safeParse({ + database: null, + language: 'go', + }); + expect(result.success).toBe(false); + }); + + it('should reject null for required language enum', () => { + const result = toolsQueryWorkflowSchema.safeParse({ + database: '/db', + language: null, + }); + expect(result.success).toBe(false); + }); + + it('should reject non-numeric string for numStages (NaN coercion)', () => { + // z.coerce.number() on a non-numeric string produces NaN, which Zod + // rejects because NaN is not a valid finite number. + const result = workshopCreationWorkflowSchema.safeParse({ + queryPath: '/q.ql', + language: 'java', + numStages: 'abc', + }); + expect(result.success).toBe(false); + }); + }); + + // ----------------------------------------------------------------------- + // Cross-schema: required vs optional classification + // ----------------------------------------------------------------------- + describe('Required vs optional parameter classification', () => { + /** + * Mapping of schema name → { required: string[], optional: string[] } + * This serves as a living specification for the VS Code parameter dialog. + */ + const schemaSpecs: Array<{ + name: string; + + schema: any; + required: string[]; + optional: string[]; + }> = [ + { + name: 'testDrivenDevelopmentSchema', + schema: testDrivenDevelopmentSchema, + required: ['language'], + optional: ['queryName'], + }, + { + name: 'toolsQueryWorkflowSchema', + schema: toolsQueryWorkflowSchema, + required: ['database', 'language'], + optional: ['sourceFiles', 'sourceFunction', 'targetFunction'], + }, + { + name: 'workshopCreationWorkflowSchema', + schema: workshopCreationWorkflowSchema, + required: ['language', 'queryPath'], + optional: ['numStages', 'workshopName'], + }, + { + name: 'qlTddBasicSchema', + schema: qlTddBasicSchema, + required: [], + optional: ['language', 'queryName'], + }, + { + name: 'qlTddAdvancedSchema', + schema: qlTddAdvancedSchema, + required: [], + optional: ['database', 'language', 'queryName'], + }, + { + name: 'sarifRankSchema', + schema: sarifRankSchema, + required: [], + optional: ['queryId', 'sarifPath'], + }, + { + name: 'explainCodeqlQuerySchema', + schema: explainCodeqlQuerySchema, + required: ['language', 'queryPath'], + optional: ['databasePath'], + }, + { + name: 'documentCodeqlQuerySchema', + schema: documentCodeqlQuerySchema, + required: ['language', 'queryPath'], + optional: [], + }, + { + name: 'qlLspIterativeDevelopmentSchema', + schema: qlLspIterativeDevelopmentSchema, + required: [], + optional: ['language', 'queryPath', 'workspaceUri'], + }, + ]; + + it.each(schemaSpecs)( + '$name — should have exactly the expected required and optional keys', + ({ schema, required, optional }) => { + const allKeys = [...required, ...optional].sort(); + expect(Object.keys(schema.shape).sort()).toEqual(allKeys); + } + ); + + it.each(schemaSpecs)( + '$name — required fields must cause rejection when omitted individually', + ({ schema, required, optional: _optional }) => { + // Build a fully-valid object first + const validObj: Record = {}; + for (const key of required) { + if (key === 'language') { + validObj[key] = 'javascript'; + } else if (key === 'numStages') { + validObj[key] = 5; + } else { + validObj[key] = `/test/${key}`; + } + } + // Leave optional keys out — valid object with only required keys + void _optional; + + // Verify the base valid object passes + const baseResult = schema.safeParse(validObj); + expect(baseResult.success).toBe(true); + + // Omit each required key one at a time and verify rejection + for (const reqKey of required) { + const partial = { ...validObj }; + delete partial[reqKey]; + const result = schema.safeParse(partial); + expect(result.success).toBe(false); + } + } + ); + + it.each(schemaSpecs)( + '$name — optional fields should be undefined when omitted from a valid parse', + ({ schema, required, optional }) => { + // Build object with only required fields + const validObj: Record = {}; + for (const key of required) { + if (key === 'language') { + validObj[key] = 'python'; + } else if (key === 'numStages') { + validObj[key] = 3; + } else { + validObj[key] = `/test/${key}`; + } + } + + const result = schema.safeParse(validObj); + expect(result.success).toBe(true); + if (result.success) { + for (const optKey of optional) { + expect(result.data[optKey]).toBeUndefined(); + } + } + } + ); + }); + + // ----------------------------------------------------------------------- + // Cross-schema: every schema has a non-empty Zod description per field + // ----------------------------------------------------------------------- + describe('Schema field descriptions (VS Code placeholder text)', () => { + const allSchemas = { + documentCodeqlQuerySchema, + explainCodeqlQuerySchema, + qlLspIterativeDevelopmentSchema, + qlTddAdvancedSchema, + qlTddBasicSchema, + sarifRankSchema, + testDrivenDevelopmentSchema, + toolsQueryWorkflowSchema, + workshopCreationWorkflowSchema, + }; + + it.each(Object.entries(allSchemas))( + '%s — every field should have a non-empty description', + (_name, schema) => { + for (const [key, zodType] of Object.entries(schema.shape)) { + + const desc = (zodType as any).description ?? (zodType as any)._def?.description; + expect( + desc, + `Field "${key}" must have a description for VS Code placeholder text` + ).toBeDefined(); + expect(typeof desc).toBe('string'); + expect(desc.length).toBeGreaterThan(0); + } + } + ); }); // ----------------------------------------------------------------------- @@ -453,6 +937,49 @@ describe('Workflow Prompts', () => { } }); + // ------------------------------------------------------------------- + // Schema-to-registration consistency: exported schema === registered + // ------------------------------------------------------------------- + describe('schema-to-registration consistency', () => { + /** Map prompt name → exported schema .shape */ + const expectedSchemaShapes: Record> = { + document_codeql_query: documentCodeqlQuerySchema.shape, + explain_codeql_query: explainCodeqlQuerySchema.shape, + ql_lsp_iterative_development: qlLspIterativeDevelopmentSchema.shape, + ql_tdd_advanced: qlTddAdvancedSchema.shape, + ql_tdd_basic: qlTddBasicSchema.shape, + sarif_rank_false_positives: sarifRankSchema.shape, + sarif_rank_true_positives: sarifRankSchema.shape, + test_driven_development: testDrivenDevelopmentSchema.shape, + tools_query_workflow: toolsQueryWorkflowSchema.shape, + workshop_creation_workflow: workshopCreationWorkflowSchema.shape, + }; + + it.each(Object.entries(expectedSchemaShapes))( + 'prompt "%s" should register the exported schema', + (promptName, expectedShape) => { + registerWorkflowPrompts(mockServer); + + const calls = (mockServer.prompt as ReturnType).mock.calls; + const match = calls.find((c: unknown[]) => c[0] === promptName); + expect(match).toBeDefined(); + + const registeredSchema = match![2] as Record; + expect(Object.keys(registeredSchema).sort()).toEqual( + Object.keys(expectedShape).sort() + ); + + // Verify the Zod types are the same object references + for (const key of Object.keys(expectedShape)) { + expect(registeredSchema[key]).toBe(expectedShape[key]); + } + } + ); + }); + + // ------------------------------------------------------------------- + // Handler output structure validation (minimal args) + // ------------------------------------------------------------------- it('each handler should return { messages: [...] } with a user message', async () => { registerWorkflowPrompts(mockServer); @@ -472,12 +999,10 @@ describe('Workflow Prompts', () => { for (const call of (mockServer.prompt as ReturnType).mock.calls) { const promptName = call[0] as string; - - const handler = call[3] as any; - const result = (await handler(minimalArgs[promptName] ?? {})) as { - messages: Array<{ content: { text: string; type: string }; role: string }>; - }; + const handler = call[3] as PromptHandler; + + const result = await handler(minimalArgs[promptName] ?? {}); expect(result).toHaveProperty('messages'); expect(result.messages.length).toBeGreaterThan(0); @@ -489,6 +1014,230 @@ describe('Workflow Prompts', () => { }); }); + // ----------------------------------------------------------------------- + // Handler output content: parameter values reflected in context + // ----------------------------------------------------------------------- + describe('Handler output reflects user-supplied parameters', () => { + let mockServer: McpServer; + + beforeEach(() => { + vi.clearAllMocks(); + mockServer = { prompt: vi.fn() } as unknown as McpServer; + registerWorkflowPrompts(mockServer); + }); + + it('test_driven_development handler should include language in context', async () => { + const handler = getRegisteredHandler(mockServer, 'test_driven_development'); + const result: PromptResult = await handler({ language: 'python' }); + expect(result.messages[0].content.text).toContain('**Language**: python'); + }); + + it('test_driven_development handler should include queryName when provided', async () => { + const handler = getRegisteredHandler(mockServer, 'test_driven_development'); + const result: PromptResult = await handler({ + language: 'java', + queryName: 'SqlInjection', + }); + expect(result.messages[0].content.text).toContain('**Query Name**: SqlInjection'); + }); + + it('test_driven_development handler should omit Query Name line when queryName not provided', async () => { + const handler = getRegisteredHandler(mockServer, 'test_driven_development'); + const result: PromptResult = await handler({ language: 'go' }); + expect(result.messages[0].content.text).not.toContain('**Query Name**'); + }); + + it('tools_query_workflow handler should include language and database', async () => { + const handler = getRegisteredHandler(mockServer, 'tools_query_workflow'); + const result: PromptResult = await handler({ + database: '/path/to/mydb', + language: 'cpp', + }); + const text = result.messages[0].content.text; + expect(text).toContain('**Language**: cpp'); + expect(text).toContain('**Database**: /path/to/mydb'); + }); + + it('tools_query_workflow handler should include optional source parameters', async () => { + const handler = getRegisteredHandler(mockServer, 'tools_query_workflow'); + const result: PromptResult = await handler({ + database: '/db', + language: 'javascript', + sourceFiles: 'app.js,index.js', + sourceFunction: 'handleRequest', + targetFunction: 'validateInput', + }); + const text = result.messages[0].content.text; + expect(text).toContain('**Source Files**: app.js,index.js'); + expect(text).toContain('**Source Function**: handleRequest'); + expect(text).toContain('**Target Function**: validateInput'); + }); + + it('workshop_creation_workflow handler should derive workshop name from queryPath', async () => { + const handler = getRegisteredHandler(mockServer, 'workshop_creation_workflow'); + const result: PromptResult = await handler({ + language: 'python', + queryPath: '/path/to/SqlInjection.ql', + }); + const text = result.messages[0].content.text; + expect(text).toContain('**Workshop Name**: sqlinjection'); + }); + + it('workshop_creation_workflow handler should prefer explicit workshopName', async () => { + const handler = getRegisteredHandler(mockServer, 'workshop_creation_workflow'); + const result: PromptResult = await handler({ + language: 'javascript', + queryPath: '/path/to/Xss.ql', + workshopName: 'custom-workshop', + }); + const text = result.messages[0].content.text; + expect(text).toContain('**Workshop Name**: custom-workshop'); + }); + + it('workshop_creation_workflow handler should show numStages when provided', async () => { + const handler = getRegisteredHandler(mockServer, 'workshop_creation_workflow'); + const result: PromptResult = await handler({ + language: 'go', + queryPath: '/q.ql', + numStages: 7, + }); + const text = result.messages[0].content.text; + expect(text).toContain('**Suggested Stages**: 7'); + }); + + it('workshop_creation_workflow handler should show auto-detect when numStages omitted', async () => { + const handler = getRegisteredHandler(mockServer, 'workshop_creation_workflow'); + const result: PromptResult = await handler({ + language: 'ruby', + queryPath: '/q.ql', + }); + const text = result.messages[0].content.text; + expect(text).toContain('4-8 (auto-detect based on query complexity)'); + }); + + it('ql_tdd_basic handler should include context when parameters provided', async () => { + const handler = getRegisteredHandler(mockServer, 'ql_tdd_basic'); + const result: PromptResult = await handler({ + language: 'csharp', + queryName: 'WeakCrypto', + }); + const text = result.messages[0].content.text; + expect(text).toContain('**Language**: csharp'); + expect(text).toContain('**Query Name**: WeakCrypto'); + }); + + it('ql_tdd_basic handler should still return content with no parameters', async () => { + const handler = getRegisteredHandler(mockServer, 'ql_tdd_basic'); + const result: PromptResult = await handler({}); + expect(result.messages[0].content.text.length).toBeGreaterThan(0); + }); + + it('ql_tdd_advanced handler should include database in context', async () => { + const handler = getRegisteredHandler(mockServer, 'ql_tdd_advanced'); + const result: PromptResult = await handler({ + database: '/my/database', + language: 'swift', + queryName: 'TaintTrack', + }); + const text = result.messages[0].content.text; + expect(text).toContain('**Database**: /my/database'); + expect(text).toContain('**Language**: swift'); + expect(text).toContain('**Query Name**: TaintTrack'); + }); + + it('ql_tdd_advanced handler should still return content with no parameters', async () => { + const handler = getRegisteredHandler(mockServer, 'ql_tdd_advanced'); + const result: PromptResult = await handler({}); + expect(result.messages[0].content.text.length).toBeGreaterThan(0); + }); + + it('sarif_rank_false_positives handler should include queryId and sarifPath', async () => { + const handler = getRegisteredHandler(mockServer, 'sarif_rank_false_positives'); + const result: PromptResult = await handler({ + queryId: 'js/code-injection', + sarifPath: '/results/scan.sarif', + }); + const text = result.messages[0].content.text; + expect(text).toContain('**Query ID**: js/code-injection'); + expect(text).toContain('**SARIF File**: /results/scan.sarif'); + }); + + it('sarif_rank_true_positives handler should include queryId and sarifPath', async () => { + const handler = getRegisteredHandler(mockServer, 'sarif_rank_true_positives'); + const result: PromptResult = await handler({ + queryId: 'py/command-injection', + sarifPath: '/out.sarif', + }); + const text = result.messages[0].content.text; + expect(text).toContain('**Query ID**: py/command-injection'); + expect(text).toContain('**SARIF File**: /out.sarif'); + }); + + it('sarif_rank handlers should return content with no parameters', async () => { + for (const name of ['sarif_rank_false_positives', 'sarif_rank_true_positives']) { + const handler = getRegisteredHandler(mockServer, name); + const result: PromptResult = await handler({}); + expect(result.messages[0].content.text.length).toBeGreaterThan(0); + } + }); + + it('explain_codeql_query handler should include required and optional params', async () => { + const handler = getRegisteredHandler(mockServer, 'explain_codeql_query'); + const result: PromptResult = await handler({ + databasePath: '/db/path', + language: 'python', + queryPath: '/queries/Xss.ql', + }); + const text = result.messages[0].content.text; + expect(text).toContain('**Query Path**: /queries/Xss.ql'); + expect(text).toContain('**Language**: python'); + expect(text).toContain('**Database Path**: /db/path'); + }); + + it('explain_codeql_query handler should omit Database Path when not provided', async () => { + const handler = getRegisteredHandler(mockServer, 'explain_codeql_query'); + const result: PromptResult = await handler({ + language: 'java', + queryPath: '/q.ql', + }); + const text = result.messages[0].content.text; + expect(text).not.toContain('**Database Path**'); + }); + + it('document_codeql_query handler should include queryPath and language', async () => { + const handler = getRegisteredHandler(mockServer, 'document_codeql_query'); + const result: PromptResult = await handler({ + language: 'go', + queryPath: '/queries/SqlInjection.ql', + }); + const text = result.messages[0].content.text; + expect(text).toContain('**Query Path**: /queries/SqlInjection.ql'); + expect(text).toContain('**Language**: go'); + }); + + it('ql_lsp_iterative_development handler should include all optional params', async () => { + const handler = getRegisteredHandler(mockServer, 'ql_lsp_iterative_development'); + const result: PromptResult = await handler({ + language: 'ruby', + queryPath: '/my/query.ql', + workspaceUri: '/pack/root', + }); + const text = result.messages[0].content.text; + expect(text).toContain('**Language**: ruby'); + expect(text).toContain('**Query Path**: /my/query.ql'); + expect(text).toContain('**Workspace URI**: /pack/root'); + }); + + it('ql_lsp_iterative_development handler should omit absent optional params', async () => { + const handler = getRegisteredHandler(mockServer, 'ql_lsp_iterative_development'); + const result: PromptResult = await handler({}); + const text = result.messages[0].content.text; + expect(text).not.toContain('**Language**'); + expect(text).not.toContain('**Query Path**'); + expect(text).not.toContain('**Workspace URI**'); + }); + }); + // ----------------------------------------------------------------------- // buildToolsQueryContext // ----------------------------------------------------------------------- @@ -563,6 +1312,13 @@ describe('Workflow Prompts', () => { expect(result).toContain('Identify key functions for CFG'); expect(result).toContain('Identify target functions'); }); + + it('should not contain optional field labels when those fields are omitted', () => { + const result = buildToolsQueryContext('actions', '/db'); + expect(result).not.toContain('**Source Files**'); + expect(result).not.toContain('**Source Function**'); + expect(result).not.toContain('**Target Function**'); + }); }); // ----------------------------------------------------------------------- @@ -629,5 +1385,15 @@ describe('Workflow Prompts', () => { 'queryPath="experimental/Security/MyQuery.ql"' ); }); + + it('should include language in explain command', () => { + const result = buildWorkshopContext( + 'query.ql', + 'swift', + 'swift-workshop' + ); + + expect(result).toContain('language="swift"'); + }); }); }); \ No newline at end of file