Skip to content

Commit 63247cc

Browse files
Copilotdata-douser
andauthored
refactor: address review feedback — centralize skip dirs, extract getEffectiveLanguage helper, add Windows env stubs
- Centralize directory skip list into SKIP_DIRS set used by findFilesByExtension, findDatabaseDirs, and completePackRoot (adds dist/, coverage/, build/, .github/ to completePackRoot) - Extract getEffectiveLanguage() helper to eliminate duplicated language auto-derivation logic across 4 prompt handlers - Add Windows env var stubs (HOMEDRIVE, HOMEPATH, USERPROFILE) to database completion tests for cross-platform compatibility - Add 5 unit tests for getEffectiveLanguage() Agent-Logs-Url: https://github.com/advanced-security/codeql-development-mcp-server/sessions/e315dcb4-ed12-428b-8294-14b398746c4c Co-authored-by: data-douser <70299490+data-douser@users.noreply.github.com>
1 parent 0bf6946 commit 63247cc

File tree

5 files changed

+178
-107
lines changed

5 files changed

+178
-107
lines changed

server/dist/codeql-development-mcp-server.js

Lines changed: 40 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -198130,6 +198130,15 @@ init_package_paths();
198130198130
var MAX_FILE_COMPLETIONS = 50;
198131198131
var MAX_SCAN_DEPTH = 8;
198132198132
var CACHE_TTL_MS = 5e3;
198133+
var SKIP_DIRS3 = /* @__PURE__ */ new Set([
198134+
".git",
198135+
".github",
198136+
".tmp",
198137+
"build",
198138+
"coverage",
198139+
"dist",
198140+
"node_modules"
198141+
]);
198133198142
var scanCache = /* @__PURE__ */ new Map();
198134198143
function getCachedResults(cacheKey2) {
198135198144
const entry = scanCache.get(cacheKey2);
@@ -198157,7 +198166,7 @@ async function findFilesByExtension(dir, baseDir, extensions, maxDepth, results)
198157198166
if (results.length >= MAX_FILE_COMPLETIONS) break;
198158198167
const fullPath = join22(dir, entry.name);
198159198168
if (entry.isDirectory()) {
198160-
if (entry.name === "node_modules" || entry.name === ".git" || entry.name === ".github" || entry.name === ".tmp" || entry.name === "build" || entry.name === "coverage" || entry.name === "dist") {
198169+
if (SKIP_DIRS3.has(entry.name)) {
198161198170
continue;
198162198171
}
198163198172
await findFilesByExtension(fullPath, baseDir, extensions, maxDepth - 1, results);
@@ -198270,7 +198279,7 @@ async function findDatabaseDirs(dir, _baseDir, maxDepth, results) {
198270198279
}
198271198280
for (const entry of entries) {
198272198281
if (results.length >= MAX_FILE_COMPLETIONS) break;
198273-
if (entry.isDirectory() && entry.name !== "node_modules" && entry.name !== ".git" && entry.name !== ".github" && entry.name !== ".tmp" && entry.name !== "dist" && entry.name !== "coverage") {
198282+
if (entry.isDirectory() && !SKIP_DIRS3.has(entry.name)) {
198274198283
await findDatabaseDirs(join22(dir, entry.name), _baseDir, maxDepth - 1, results);
198275198284
}
198276198285
}
@@ -198297,7 +198306,7 @@ async function completePackRoot(value) {
198297198306
}
198298198307
for (const entry of entries) {
198299198308
if (results.length >= MAX_FILE_COMPLETIONS) break;
198300-
if (entry.isDirectory() && entry.name !== "node_modules" && entry.name !== ".git" && entry.name !== ".tmp") {
198309+
if (entry.isDirectory() && !SKIP_DIRS3.has(entry.name)) {
198301198310
await scan(join22(dir, entry.name), depth - 1);
198302198311
}
198303198312
}
@@ -198337,6 +198346,22 @@ async function resolveLanguageFromPack(queryFilePath) {
198337198346
}
198338198347
return void 0;
198339198348
}
198349+
async function getEffectiveLanguage(promptName, explicitLanguage, resolvedQueryPath) {
198350+
if (explicitLanguage) {
198351+
return { language: explicitLanguage };
198352+
}
198353+
if (resolvedQueryPath) {
198354+
const derived = await resolveLanguageFromPack(resolvedQueryPath);
198355+
if (derived) {
198356+
logger.debug(`${promptName}: derived language '${derived}' from pack metadata`);
198357+
return { language: derived };
198358+
}
198359+
}
198360+
return {
198361+
language: void 0,
198362+
warning: "\u26A0 **Language could not be auto-derived.** Please provide the `language` parameter or ensure the query is inside a CodeQL pack with either a `codeql/<lang>-all` or `codeql/<lang>-queries` dependency."
198363+
};
198364+
}
198340198365
var PARAMETER_COMPLETIONS = {
198341198366
database: completeDatabasePath,
198342198367
databasePath: completeDatabasePath,
@@ -198790,16 +198815,9 @@ ${content}`
198790198815
if (qpResult.blocked) return blockedPathError(qpResult, "query path");
198791198816
const resolvedQueryPath = qpResult.resolvedPath;
198792198817
if (qpResult.warning) warnings.push(qpResult.warning);
198793-
let effectiveLanguage = language;
198794-
if (!effectiveLanguage && resolvedQueryPath) {
198795-
effectiveLanguage = await resolveLanguageFromPack(resolvedQueryPath);
198796-
if (effectiveLanguage) {
198797-
logger.debug(`workshop_creation_workflow: derived language '${effectiveLanguage}' from pack metadata`);
198798-
}
198799-
}
198800-
if (!effectiveLanguage) {
198801-
warnings.push("\u26A0 **Language could not be auto-derived.** Please provide the `language` parameter or ensure the query is inside a CodeQL pack with either a `codeql/<lang>-all` or `codeql/<lang>-queries` dependency.");
198802-
}
198818+
const langResult = await getEffectiveLanguage("workshop_creation_workflow", language, resolvedQueryPath);
198819+
const effectiveLanguage = langResult.language;
198820+
if (langResult.warning) warnings.push(langResult.warning);
198803198821
const derivedName = workshopName || basename9(resolvedQueryPath).replace(/\.(ql|qlref)$/, "").toLowerCase().replace(/[^a-z0-9]+/g, "-") || "codeql-workshop";
198804198822
const contextSection = buildWorkshopContext(
198805198823
resolvedQueryPath,
@@ -199019,16 +199037,9 @@ ${content}`
199019199037
if (qpResult.blocked) return blockedPathError(qpResult, "query path");
199020199038
const resolvedQueryPath = qpResult.resolvedPath;
199021199039
if (qpResult.warning) warnings.push(qpResult.warning);
199022-
let effectiveLanguage = language;
199023-
if (!effectiveLanguage && resolvedQueryPath) {
199024-
effectiveLanguage = await resolveLanguageFromPack(resolvedQueryPath);
199025-
if (effectiveLanguage) {
199026-
logger.debug(`explain_codeql_query: derived language '${effectiveLanguage}' from pack metadata`);
199027-
}
199028-
}
199029-
if (!effectiveLanguage) {
199030-
warnings.push("\u26A0 **Language could not be auto-derived.** Please provide the `language` parameter or ensure the query is inside a CodeQL pack with either a `codeql/<lang>-all` or `codeql/<lang>-queries` dependency.");
199031-
}
199040+
const langResult = await getEffectiveLanguage("explain_codeql_query", language, resolvedQueryPath);
199041+
const effectiveLanguage = langResult.language;
199042+
if (langResult.warning) warnings.push(langResult.warning);
199032199043
let resolvedDatabasePath = databasePath;
199033199044
if (databasePath) {
199034199045
const dbResult = await resolvePromptFilePath(databasePath);
@@ -199075,16 +199086,9 @@ ${content}`
199075199086
if (qpResult.blocked) return blockedPathError(qpResult, "query path");
199076199087
const resolvedQueryPath = qpResult.resolvedPath;
199077199088
if (qpResult.warning) warnings.push(qpResult.warning);
199078-
let effectiveLanguage = language;
199079-
if (!effectiveLanguage && resolvedQueryPath) {
199080-
effectiveLanguage = await resolveLanguageFromPack(resolvedQueryPath);
199081-
if (effectiveLanguage) {
199082-
logger.debug(`document_codeql_query: derived language '${effectiveLanguage}' from pack metadata`);
199083-
}
199084-
}
199085-
if (!effectiveLanguage) {
199086-
warnings.push("\u26A0 **Language could not be auto-derived.** Please provide the `language` parameter or ensure the query is inside a CodeQL pack with either a `codeql/<lang>-all` or `codeql/<lang>-queries` dependency.");
199087-
}
199089+
const langResult = await getEffectiveLanguage("document_codeql_query", language, resolvedQueryPath);
199090+
const effectiveLanguage = langResult.language;
199091+
if (langResult.warning) warnings.push(langResult.warning);
199088199092
const contextSection = `## Query to Document
199089199093

199090199094
- **Query Path**: ${resolvedQueryPath}
@@ -199170,16 +199174,9 @@ ${workspaceUri ? `- **Workspace URI**: ${workspaceUri}
199170199174
if (qpResult.blocked) return blockedPathError(qpResult, "query path");
199171199175
const resolvedQueryPath = qpResult.resolvedPath;
199172199176
if (qpResult.warning) warnings.push(qpResult.warning);
199173-
let effectiveLanguage = language;
199174-
if (!effectiveLanguage && resolvedQueryPath) {
199175-
effectiveLanguage = await resolveLanguageFromPack(resolvedQueryPath);
199176-
if (effectiveLanguage) {
199177-
logger.debug(`ql_lsp_iterative_development: derived language '${effectiveLanguage}' from pack metadata`);
199178-
}
199179-
}
199180-
if (!effectiveLanguage) {
199181-
warnings.push("\u26A0 **Language could not be auto-derived.** Please provide the `language` parameter or ensure the query is inside a CodeQL pack with either a `codeql/<lang>-all` or `codeql/<lang>-queries` dependency.");
199182-
}
199177+
const langResult = await getEffectiveLanguage("ql_lsp_iterative_development", language, resolvedQueryPath);
199178+
const effectiveLanguage = langResult.language;
199179+
if (langResult.warning) warnings.push(langResult.warning);
199183199180
let resolvedWorkspaceUri = workspaceUri;
199184199181
if (workspaceUri) {
199185199182
const wsResult = await resolvePromptFilePath(workspaceUri);

server/dist/codeql-development-mcp-server.js.map

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

server/src/prompts/prompt-completions.ts

Lines changed: 65 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,20 @@ const MAX_SCAN_DEPTH = 8;
3434
/** Time-to-live for cached scan results, in milliseconds (5 seconds). */
3535
const CACHE_TTL_MS = 5_000;
3636

37+
/**
38+
* Directories to skip during recursive workspace scans.
39+
* Centralised so that all completion providers use the same list.
40+
*/
41+
const SKIP_DIRS = new Set([
42+
'.git',
43+
'.github',
44+
'.tmp',
45+
'build',
46+
'coverage',
47+
'dist',
48+
'node_modules',
49+
]);
50+
3751
/** Cached scan results keyed by a workspace+type identifier. */
3852
interface CacheEntry {
3953
results: string[];
@@ -105,15 +119,7 @@ async function findFilesByExtension(
105119

106120
if (entry.isDirectory()) {
107121
// Skip common non-CodeQL directories
108-
if (
109-
entry.name === 'node_modules'
110-
|| entry.name === '.git'
111-
|| entry.name === '.github'
112-
|| entry.name === '.tmp'
113-
|| entry.name === 'build'
114-
|| entry.name === 'coverage'
115-
|| entry.name === 'dist'
116-
) {
122+
if (SKIP_DIRS.has(entry.name)) {
117123
continue;
118124
}
119125
await findFilesByExtension(fullPath, baseDir, extensions, maxDepth - 1, results);
@@ -284,15 +290,7 @@ async function findDatabaseDirs(
284290

285291
for (const entry of entries) {
286292
if (results.length >= MAX_FILE_COMPLETIONS) break;
287-
if (
288-
entry.isDirectory()
289-
&& entry.name !== 'node_modules'
290-
&& entry.name !== '.git'
291-
&& entry.name !== '.github'
292-
&& entry.name !== '.tmp'
293-
&& entry.name !== 'dist'
294-
&& entry.name !== 'coverage'
295-
) {
293+
if (entry.isDirectory() && !SKIP_DIRS.has(entry.name)) {
296294
await findDatabaseDirs(join(dir, entry.name), _baseDir, maxDepth - 1, results);
297295
}
298296
}
@@ -330,7 +328,7 @@ export async function completePackRoot(value: string): Promise<string[]> {
330328

331329
for (const entry of entries) {
332330
if (results.length >= MAX_FILE_COMPLETIONS) break;
333-
if (entry.isDirectory() && entry.name !== 'node_modules' && entry.name !== '.git' && entry.name !== '.tmp') {
331+
if (entry.isDirectory() && !SKIP_DIRS.has(entry.name)) {
334332
await scan(join(dir, entry.name), depth - 1);
335333
}
336334
}
@@ -407,6 +405,54 @@ export async function resolveLanguageFromPack(
407405
return undefined;
408406
}
409407

408+
/**
409+
* Result of resolving an effective language for a prompt handler.
410+
*
411+
* `language` is the resolved language (explicit or auto-derived), or
412+
* `undefined` when neither was available.
413+
* `warning` is set when the language could not be auto-derived — the
414+
* caller should add it to the prompt's warnings list.
415+
*/
416+
export interface EffectiveLanguageResult {
417+
language: string | undefined;
418+
warning?: string;
419+
}
420+
421+
/**
422+
* Resolve the effective language for a prompt handler.
423+
*
424+
* If `explicitLanguage` is provided, it is used directly.
425+
* Otherwise, the language is auto-derived from the nearest
426+
* `codeql-pack.yml` via `resolveLanguageFromPack()`.
427+
*
428+
* @param promptName - Name of the prompt (for debug logging).
429+
* @param explicitLanguage - The language value supplied by the user (may be undefined).
430+
* @param resolvedQueryPath - Absolute path to the resolved query file.
431+
* @returns The effective language and an optional warning string.
432+
*/
433+
export async function getEffectiveLanguage(
434+
promptName: string,
435+
explicitLanguage: string | undefined,
436+
resolvedQueryPath: string,
437+
): Promise<EffectiveLanguageResult> {
438+
if (explicitLanguage) {
439+
return { language: explicitLanguage };
440+
}
441+
442+
if (resolvedQueryPath) {
443+
const derived = await resolveLanguageFromPack(resolvedQueryPath);
444+
if (derived) {
445+
logger.debug(`${promptName}: derived language '${derived}' from pack metadata`);
446+
return { language: derived };
447+
}
448+
}
449+
450+
return {
451+
language: undefined,
452+
warning: '⚠ **Language could not be auto-derived.** Please provide the `language` parameter or ensure the query is inside a CodeQL pack with either a `codeql/<lang>-all` or `codeql/<lang>-queries` dependency.',
453+
};
454+
}
455+
410456
// ────────────────────────────────────────────────────────────────────────────
411457
// Shape enhancement: apply completable() to known parameter names
412458
// ────────────────────────────────────────────────────────────────────────────

server/src/prompts/workflow-prompts.ts

Lines changed: 13 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { access } from 'fs/promises';
1111
import { basename, isAbsolute, normalize, relative, resolve, sep } from 'path';
1212
import { fileURLToPath } from 'url';
1313
import { SUPPORTED_LANGUAGES } from './constants';
14-
import { addCompletions, resolveLanguageFromPack } from './prompt-completions';
14+
import { addCompletions, getEffectiveLanguage } from './prompt-completions';
1515
import { loadPromptTemplate, processPromptTemplate } from './prompt-loader';
1616
import { getUserWorkspaceDir } from '../utils/package-paths';
1717
import { logger } from '../utils/logger';
@@ -743,16 +743,9 @@ export function registerWorkflowPrompts(server: McpServer): void {
743743
if (qpResult.warning) warnings.push(qpResult.warning);
744744

745745
// Auto-derive language from pack metadata when not explicitly provided
746-
let effectiveLanguage = language;
747-
if (!effectiveLanguage && resolvedQueryPath) {
748-
effectiveLanguage = await resolveLanguageFromPack(resolvedQueryPath) as typeof language;
749-
if (effectiveLanguage) {
750-
logger.debug(`workshop_creation_workflow: derived language '${effectiveLanguage}' from pack metadata`);
751-
}
752-
}
753-
if (!effectiveLanguage) {
754-
warnings.push('⚠ **Language could not be auto-derived.** Please provide the `language` parameter or ensure the query is inside a CodeQL pack with either a `codeql/<lang>-all` or `codeql/<lang>-queries` dependency.');
755-
}
746+
const langResult = await getEffectiveLanguage('workshop_creation_workflow', language, resolvedQueryPath);
747+
const effectiveLanguage = langResult.language as typeof language;
748+
if (langResult.warning) warnings.push(langResult.warning);
756749

757750
const derivedName =
758751
workshopName ||
@@ -1012,16 +1005,9 @@ export function registerWorkflowPrompts(server: McpServer): void {
10121005
if (qpResult.warning) warnings.push(qpResult.warning);
10131006

10141007
// Auto-derive language from pack metadata when not explicitly provided
1015-
let effectiveLanguage = language;
1016-
if (!effectiveLanguage && resolvedQueryPath) {
1017-
effectiveLanguage = await resolveLanguageFromPack(resolvedQueryPath) as typeof language;
1018-
if (effectiveLanguage) {
1019-
logger.debug(`explain_codeql_query: derived language '${effectiveLanguage}' from pack metadata`);
1020-
}
1021-
}
1022-
if (!effectiveLanguage) {
1023-
warnings.push('⚠ **Language could not be auto-derived.** Please provide the `language` parameter or ensure the query is inside a CodeQL pack with either a `codeql/<lang>-all` or `codeql/<lang>-queries` dependency.');
1024-
}
1008+
const langResult = await getEffectiveLanguage('explain_codeql_query', language, resolvedQueryPath);
1009+
const effectiveLanguage = langResult.language as typeof language;
1010+
if (langResult.warning) warnings.push(langResult.warning);
10251011

10261012
let resolvedDatabasePath = databasePath;
10271013
if (databasePath) {
@@ -1076,16 +1062,9 @@ export function registerWorkflowPrompts(server: McpServer): void {
10761062
if (qpResult.warning) warnings.push(qpResult.warning);
10771063

10781064
// Auto-derive language from pack metadata when not explicitly provided
1079-
let effectiveLanguage = language;
1080-
if (!effectiveLanguage && resolvedQueryPath) {
1081-
effectiveLanguage = await resolveLanguageFromPack(resolvedQueryPath) as typeof language;
1082-
if (effectiveLanguage) {
1083-
logger.debug(`document_codeql_query: derived language '${effectiveLanguage}' from pack metadata`);
1084-
}
1085-
}
1086-
if (!effectiveLanguage) {
1087-
warnings.push('⚠ **Language could not be auto-derived.** Please provide the `language` parameter or ensure the query is inside a CodeQL pack with either a `codeql/<lang>-all` or `codeql/<lang>-queries` dependency.');
1088-
}
1065+
const langResult = await getEffectiveLanguage('document_codeql_query', language, resolvedQueryPath);
1066+
const effectiveLanguage = langResult.language as typeof language;
1067+
if (langResult.warning) warnings.push(langResult.warning);
10891068

10901069
const contextSection = `## Query to Document
10911070
@@ -1193,16 +1172,9 @@ ${workspaceUri ? `- **Workspace URI**: ${workspaceUri}
11931172
if (qpResult.warning) warnings.push(qpResult.warning);
11941173

11951174
// Auto-derive language from pack metadata when not explicitly provided
1196-
let effectiveLanguage = language;
1197-
if (!effectiveLanguage && resolvedQueryPath) {
1198-
effectiveLanguage = await resolveLanguageFromPack(resolvedQueryPath) as typeof language;
1199-
if (effectiveLanguage) {
1200-
logger.debug(`ql_lsp_iterative_development: derived language '${effectiveLanguage}' from pack metadata`);
1201-
}
1202-
}
1203-
if (!effectiveLanguage) {
1204-
warnings.push('⚠ **Language could not be auto-derived.** Please provide the `language` parameter or ensure the query is inside a CodeQL pack with either a `codeql/<lang>-all` or `codeql/<lang>-queries` dependency.');
1205-
}
1175+
const langResult = await getEffectiveLanguage('ql_lsp_iterative_development', language, resolvedQueryPath);
1176+
const effectiveLanguage = langResult.language as typeof language;
1177+
if (langResult.warning) warnings.push(langResult.warning);
12061178

12071179
let resolvedWorkspaceUri = workspaceUri;
12081180
if (workspaceUri) {

0 commit comments

Comments
 (0)