refactor: Improve readability of file search function
This commit is contained in:
80
.cursor/rules/codacy.mdc
Normal file
80
.cursor/rules/codacy.mdc
Normal file
@@ -0,0 +1,80 @@
|
||||
---
|
||||
description: Configuration for AI behavior when interacting with Codacy's MCP Server
|
||||
globs:
|
||||
alwaysApply: true
|
||||
---
|
||||
---
|
||||
# Codacy Rules
|
||||
Configuration for AI behavior when interacting with Codacy's MCP Server
|
||||
|
||||
## using any tool that accepts the arguments: `provider`, `organization`, or `repository`
|
||||
- ALWAYS use:
|
||||
- provider: gh
|
||||
- organization: franklioxygen
|
||||
- repository: MyTube
|
||||
- Avoid calling `git remote -v` unless really necessary
|
||||
|
||||
## CRITICAL: After ANY successful `edit_file` or `reapply` operation
|
||||
- YOU MUST IMMEDIATELY run the `codacy_cli_analyze` tool from Codacy's MCP Server for each file that was edited, with:
|
||||
- `rootPath`: set to the workspace path
|
||||
- `file`: set to the path of the edited file
|
||||
- `tool`: leave empty or unset
|
||||
- If any issues are found in the new edits, propose and apply fixes for them.
|
||||
- > NOTE: Failure to follow this rule is considered a critical error.
|
||||
|
||||
## When you tried to run the `codacy_cli_analyze` tool and the Codacy CLI is not installed
|
||||
- Ask the user 'Codacy CLI is not installed. Would you like me to install it now?'
|
||||
- If the user responds with "yes", run the `codacy_cli_install` tool and then continue with the original task
|
||||
- If the user responds with "no", instruct the user that they can disable automatic analysis in the extension settings
|
||||
- Wait for the user to respond before proceeding with any other actions
|
||||
|
||||
## After every response
|
||||
- If you made any file edits in this conversation, verify you ran `codacy_cli_analyze` tool from Codacy's MCP Server
|
||||
|
||||
## When there are no Codacy MCP Server tools available, or the MCP Server is not reachable
|
||||
- Suggest the user the following troubleshooting steps:
|
||||
- Try to reset the MCP on the extension
|
||||
- If the user is using VSCode, suggest them to review their Copilot > MCP settings in Github, under their organization or personal account. Refer them to Settings > Copilot > Enable MCP servers in Copilot. Suggested URL (https://github.com/settings/copilot/features) or https://github.com/organizations/{organization-name}/settings/copilot/features (This can only be done by their organization admins / owners)
|
||||
- If none of the above steps work, suggest the user to contact Codacy support
|
||||
|
||||
## Trying to call a tool that needs a rootPath as a parameter
|
||||
- Always use the standard, non-URL-encoded file system path
|
||||
|
||||
## CRITICAL: Dependencies and Security Checks
|
||||
- IMMEDIATELY after ANY of these actions:
|
||||
- Running npm/yarn/pnpm install
|
||||
- Adding dependencies to package.json
|
||||
- Adding requirements to requirements.txt
|
||||
- Adding dependencies to pom.xml
|
||||
- Adding dependencies to build.gradle
|
||||
- Any other package manager operations
|
||||
- You MUST run the `codacy_cli_analyze` tool with:
|
||||
- `rootPath`: set to the workspace path
|
||||
- `tool`: set to "trivy"
|
||||
- `file`: leave empty or unset
|
||||
- If any vulnerabilities are found because of the newly added packages:
|
||||
- Stop all other operations
|
||||
- Propose and apply fixes for the security issues
|
||||
- Only continue with the original task after security issues are resolved
|
||||
- EXAMPLE:
|
||||
- After: npm install react-markdown
|
||||
- Do: Run codacy_cli_analyze with trivy
|
||||
- Before: Continuing with any other tasks
|
||||
|
||||
## General
|
||||
- Repeat the relevant steps for each modified file.
|
||||
- "Propose fixes" means to both suggest and, if possible, automatically apply the fixes.
|
||||
- You MUST NOT wait for the user to ask for analysis or remind you to run the tool.
|
||||
- Do not run `codacy_cli_analyze` looking for changes in duplicated code or code complexity metrics.
|
||||
- Complexity metrics are different from complexity issues. When trying to fix complexity in a repository or file, focus on solving the complexity issues and ignore the complexity metric.
|
||||
- Do not run `codacy_cli_analyze` looking for changes in code coverage.
|
||||
- Do not try to manually install Codacy CLI using either brew, npm, npx, or any other package manager.
|
||||
- If the Codacy CLI is not installed, just run the `codacy_cli_analyze` tool from Codacy's MCP Server.
|
||||
- When calling `codacy_cli_analyze`, only send provider, organization and repository if the project is a git repository.
|
||||
|
||||
## Whenever a call to a Codacy tool that uses `repository` or `organization` as a parameter returns a 404 error
|
||||
- Offer to run the `codacy_setup_repository` tool to add the repository to Codacy
|
||||
- If the user accepts, run the `codacy_setup_repository` tool
|
||||
- Do not ever try to run the `codacy_setup_repository` tool on your own
|
||||
- After setup, immediately retry the action that failed (only retry once)
|
||||
---
|
||||
@@ -44,12 +44,15 @@ async function getFileUrlsWithSign(
|
||||
const domain = config.publicUrl || apiBaseUrl;
|
||||
|
||||
// Helper to find file in its directory
|
||||
// Returns { file: fileObject, foundPath: string } where foundPath is the directory where file was found
|
||||
// fullRelativePath can be:
|
||||
// - "video.mp4" (in uploadPath root, or just filename)
|
||||
// - "subdir/video.mp4" (in uploadPath/subdir)
|
||||
// - "a/movies/video.mp4" (in scanPath /a/movies, not in uploadPath)
|
||||
// - "/video.mp4" (absolute path, but might be just filename)
|
||||
const findFileInDir = async (fullRelativePath: string): Promise<any> => {
|
||||
const findFileInDir = async (
|
||||
fullRelativePath: string
|
||||
): Promise<{ file: any; foundPath: string } | undefined> => {
|
||||
// Remove leading slash if present (for consistency)
|
||||
const cleanPath = fullRelativePath.startsWith("/")
|
||||
? fullRelativePath.substring(1)
|
||||
@@ -62,7 +65,9 @@ async function getFileUrlsWithSign(
|
||||
// We need to search in multiple possible locations (including subdirectories)
|
||||
if (dirName === ".") {
|
||||
// Helper function to recursively search for a file
|
||||
const searchRecursively = async (searchPath: string): Promise<any> => {
|
||||
const searchRecursively = async (
|
||||
searchPath: string
|
||||
): Promise<{ file: any; foundPath: string } | undefined> => {
|
||||
try {
|
||||
const files = await getFileList(config, searchPath);
|
||||
// Check current directory
|
||||
@@ -70,7 +75,7 @@ async function getFileUrlsWithSign(
|
||||
(f: any) => f.name === fileName && !f.is_dir
|
||||
);
|
||||
if (foundFile) {
|
||||
return foundFile;
|
||||
return { file: foundFile, foundPath: searchPath };
|
||||
}
|
||||
// Recursively search subdirectories
|
||||
for (const file of files) {
|
||||
@@ -78,9 +83,9 @@ async function getFileUrlsWithSign(
|
||||
const subDirPath = searchPath.endsWith("/")
|
||||
? `${searchPath}${file.name}`
|
||||
: `${searchPath}/${file.name}`;
|
||||
foundFile = await searchRecursively(subDirPath);
|
||||
if (foundFile) {
|
||||
return foundFile;
|
||||
const result = await searchRecursively(subDirPath);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -95,18 +100,18 @@ async function getFileUrlsWithSign(
|
||||
};
|
||||
|
||||
// Try to find file in uploadPath first (recursively)
|
||||
let foundFile = await searchRecursively(baseUploadPath);
|
||||
if (foundFile) {
|
||||
return foundFile;
|
||||
let result = await searchRecursively(baseUploadPath);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// If not found in uploadPath, try scanPaths if configured (recursively)
|
||||
if (config.scanPaths && config.scanPaths.length > 0) {
|
||||
for (const scanPath of config.scanPaths) {
|
||||
const normalizedScanPath = normalizeUploadPath(scanPath);
|
||||
foundFile = await searchRecursively(normalizedScanPath);
|
||||
if (foundFile) {
|
||||
return foundFile;
|
||||
result = await searchRecursively(normalizedScanPath);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -116,115 +121,121 @@ async function getFileUrlsWithSign(
|
||||
}
|
||||
|
||||
// Path contains directory information
|
||||
const absolutePath = "/" + cleanPath;
|
||||
// First, try as relative path from uploadPath
|
||||
// If not found, try as absolute path (scanPath)
|
||||
const absoluteUploadRoot = baseUploadPath.startsWith("/")
|
||||
? baseUploadPath
|
||||
: "/" + baseUploadPath;
|
||||
|
||||
let listPath: string;
|
||||
// Try 1: Treat as relative path from uploadPath
|
||||
const normalizedDir = dirName.replace(/\\/g, "/");
|
||||
let listPath = baseUploadPath.endsWith("/")
|
||||
? `${baseUploadPath}${normalizedDir}`
|
||||
: `${baseUploadPath}/${normalizedDir}`;
|
||||
|
||||
if (absolutePath.startsWith(absoluteUploadRoot)) {
|
||||
// File is in uploadPath - use relative path from uploadPath
|
||||
const normalizedDir = dirName.replace(/\\/g, "/");
|
||||
listPath = baseUploadPath.endsWith("/")
|
||||
? `${baseUploadPath}${normalizedDir}`
|
||||
: `${baseUploadPath}/${normalizedDir}`;
|
||||
} else {
|
||||
// File is NOT in uploadPath - must be from a scanPath
|
||||
// Use the full absolute path (directory of the file)
|
||||
const absoluteDirPath = dirName.startsWith("/")
|
||||
? dirName
|
||||
: "/" + dirName;
|
||||
listPath = absoluteDirPath;
|
||||
try {
|
||||
const files = await getFileList(config, listPath);
|
||||
const foundFile = files.find((f: any) => f.name === fileName);
|
||||
if (foundFile) {
|
||||
return { file: foundFile, foundPath: listPath };
|
||||
}
|
||||
} catch (error) {
|
||||
// If failed, try as absolute path (scanPath)
|
||||
logger.debug(
|
||||
`[CloudStorage] File not found in uploadPath subdirectory ${listPath}, trying as scanPath:`,
|
||||
error
|
||||
);
|
||||
}
|
||||
|
||||
const files = await getFileList(config, listPath);
|
||||
return files.find((f: any) => f.name === fileName);
|
||||
// If failed, try to find in scanPaths
|
||||
if (config.scanPaths && config.scanPaths.length > 0) {
|
||||
// Check if the path is already an absolute path starting with a scanPath
|
||||
const absoluteCleanPath = "/" + cleanPath;
|
||||
for (const scanPath of config.scanPaths) {
|
||||
try {
|
||||
const normalizedScanPath = normalizeUploadPath(scanPath);
|
||||
const absoluteScanPath = normalizedScanPath.startsWith("/")
|
||||
? normalizedScanPath
|
||||
: "/" + normalizedScanPath;
|
||||
|
||||
let listPath: string;
|
||||
|
||||
// Check if the directory path starts with this scanPath
|
||||
const absoluteDirPath = "/" + normalizedDir;
|
||||
if (absoluteDirPath.startsWith(absoluteScanPath)) {
|
||||
// Path is already absolute and starts with this scanPath, use it directly
|
||||
listPath = absoluteDirPath;
|
||||
} else {
|
||||
// Path is relative to this scanPath, append it
|
||||
listPath = normalizedScanPath.endsWith("/")
|
||||
? `${normalizedScanPath}${normalizedDir}`
|
||||
: `${normalizedScanPath}/${normalizedDir}`;
|
||||
}
|
||||
|
||||
const files = await getFileList(config, listPath);
|
||||
const foundFile = files.find((f: any) => f.name === fileName);
|
||||
if (foundFile) {
|
||||
return { file: foundFile, foundPath: listPath };
|
||||
}
|
||||
} catch (error) {
|
||||
// Continue to next scanPath
|
||||
logger.debug(
|
||||
`[CloudStorage] Failed to search in scanPath ${scanPath}:`,
|
||||
error
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Not found in either location
|
||||
return undefined;
|
||||
};
|
||||
|
||||
// Find video file
|
||||
if (videoFilename) {
|
||||
const videoFile = await findFileInDir(videoFilename);
|
||||
const videoResult = await findFileInDir(videoFilename);
|
||||
|
||||
if (videoFile && videoFile.sign) {
|
||||
if (videoResult && videoResult.file.sign) {
|
||||
// Build URL: https://domain/d/path/files/filename?sign=xxx
|
||||
// We need to construct the full web path including subdirectory
|
||||
// If videoFilename is "subdir/video.mp4", the path in URL should include /subdir/
|
||||
// The Alist pattern seems to be /d/mount_path/subdir/filename
|
||||
// Use the actual path where file was found to construct URL
|
||||
const foundPath = videoResult.foundPath;
|
||||
const fileName = path.basename(videoFilename);
|
||||
const fullWebPath = foundPath.endsWith("/")
|
||||
? `${foundPath}${fileName}`
|
||||
: `${foundPath}/${fileName}`;
|
||||
const normalizedWebPath = fullWebPath.replace(/\/+/g, "/");
|
||||
|
||||
// Construct full web path for URL
|
||||
// videoFilename can be "video.mp4", "subdir/video.mp4", or "a/movies/video.mp4"
|
||||
const absolutePath = videoFilename.startsWith("/")
|
||||
? videoFilename
|
||||
: "/" + videoFilename;
|
||||
const absoluteUploadRoot = baseUploadPath.startsWith("/")
|
||||
? baseUploadPath
|
||||
: "/" + baseUploadPath;
|
||||
|
||||
let fullWebPath: string;
|
||||
if (absolutePath.startsWith(absoluteUploadRoot)) {
|
||||
// In uploadPath - use relative path from uploadPath
|
||||
const relativePath = path
|
||||
.relative(absoluteUploadRoot, absolutePath)
|
||||
.replace(/\\/g, "/");
|
||||
fullWebPath = baseUploadPath.endsWith("/")
|
||||
? `${baseUploadPath}${relativePath}`
|
||||
: `${baseUploadPath}/${relativePath}`;
|
||||
} else {
|
||||
// Not in uploadPath - use absolute path directly
|
||||
fullWebPath = absolutePath;
|
||||
}
|
||||
// Cleanup double slashes
|
||||
fullWebPath = fullWebPath.replace(/\/+/g, "/");
|
||||
|
||||
result.videoUrl = `${domain}/d${fullWebPath}?sign=${encodeURIComponent(
|
||||
videoFile.sign
|
||||
result.videoUrl = `${domain}/d${normalizedWebPath}?sign=${encodeURIComponent(
|
||||
videoResult.file.sign
|
||||
)}`;
|
||||
}
|
||||
}
|
||||
|
||||
// Find thumbnail file
|
||||
if (thumbnailFilename) {
|
||||
const thumbnailFile = await findFileInDir(thumbnailFilename);
|
||||
const thumbnailResult = await findFileInDir(thumbnailFilename);
|
||||
|
||||
if (thumbnailFile) {
|
||||
// Construct full web path for URL (same logic as video)
|
||||
// thumbnailFilename can be "video.jpg", "subdir/video.jpg", or "a/movies/video.jpg"
|
||||
const absoluteThumbPath = thumbnailFilename.startsWith("/")
|
||||
? thumbnailFilename
|
||||
: "/" + thumbnailFilename;
|
||||
const absoluteUploadRoot = baseUploadPath.startsWith("/")
|
||||
? baseUploadPath
|
||||
: "/" + baseUploadPath;
|
||||
|
||||
let fullWebPath: string;
|
||||
if (absoluteThumbPath.startsWith(absoluteUploadRoot)) {
|
||||
// In uploadPath - use relative path from uploadPath
|
||||
const relativePath = path
|
||||
.relative(absoluteUploadRoot, absoluteThumbPath)
|
||||
.replace(/\\/g, "/");
|
||||
fullWebPath = baseUploadPath.endsWith("/")
|
||||
? `${baseUploadPath}${relativePath}`
|
||||
: `${baseUploadPath}/${relativePath}`;
|
||||
} else {
|
||||
// Not in uploadPath - use absolute path directly
|
||||
fullWebPath = absoluteThumbPath;
|
||||
}
|
||||
// Cleanup double slashes
|
||||
fullWebPath = fullWebPath.replace(/\/+/g, "/");
|
||||
if (thumbnailResult) {
|
||||
// Use the actual path where file was found to construct URL
|
||||
const foundPath = thumbnailResult.foundPath;
|
||||
const fileName = path.basename(thumbnailFilename);
|
||||
const fullWebPath = foundPath.endsWith("/")
|
||||
? `${foundPath}${fileName}`
|
||||
: `${foundPath}/${fileName}`;
|
||||
const normalizedWebPath = fullWebPath.replace(/\/+/g, "/");
|
||||
|
||||
// Prefer file URL with sign if available
|
||||
if (thumbnailFile.sign) {
|
||||
result.thumbnailUrl = `${domain}/d${fullWebPath}?sign=${encodeURIComponent(
|
||||
thumbnailFile.sign
|
||||
if (thumbnailResult.file.sign) {
|
||||
result.thumbnailUrl = `${domain}/d${normalizedWebPath}?sign=${encodeURIComponent(
|
||||
thumbnailResult.file.sign
|
||||
)}`;
|
||||
}
|
||||
|
||||
// If file doesn't have sign but has thumb URL, use thumb URL
|
||||
// Also check if no thumbnail file exists but video file has thumb
|
||||
if (thumbnailFile.thumb) {
|
||||
if (thumbnailResult.file.thumb) {
|
||||
// ... existing thumb logic ...
|
||||
let thumbUrl = thumbnailFile.thumb;
|
||||
let thumbUrl = thumbnailResult.file.thumb;
|
||||
thumbUrl = thumbUrl.replace(
|
||||
/width=\d+[&\\u0026]height=\d+/,
|
||||
"width=1280&height=720"
|
||||
@@ -251,9 +262,9 @@ async function getFileUrlsWithSign(
|
||||
// This is useful if we generated "cloud:video.jpg" but it doesn't exist yet or failed,
|
||||
// but maybe the video file "cloud:video.mp4" has a generated thumb from the server side.
|
||||
if (videoFilename) {
|
||||
const videoFile = await findFileInDir(videoFilename);
|
||||
if (videoFile && videoFile.thumb) {
|
||||
let thumbUrl = videoFile.thumb;
|
||||
const videoResult = await findFileInDir(videoFilename);
|
||||
if (videoResult && videoResult.file.thumb) {
|
||||
let thumbUrl = videoResult.file.thumb;
|
||||
thumbUrl = thumbUrl.replace(
|
||||
/width=\d+[&\\u0026]height=\d+/,
|
||||
"width=1280&height=720"
|
||||
|
||||
Reference in New Issue
Block a user