MCPcopy Create free account
hub / github.com/dyoshikawa/rulesync / collectFeatureFiles

Function collectFeatureFiles

src/lib/fetch.ts:415–514  ·  view source on GitHub ↗

* Collect files from feature directories

(params: {
  client: GitHubClient;
  owner: string;
  repo: string;
  basePath: string;
  ref: string;
  enabledFeatures: Feature[];
  semaphore: Semaphore;
  logger: Logger;
})

Source from the content-addressed store, hash-verified

413 * Collect files from feature directories
414 */
415async function collectFeatureFiles(params: {
416 client: GitHubClient;
417 owner: string;
418 repo: string;
419 basePath: string;
420 ref: string;
421 enabledFeatures: Feature[];
422 semaphore: Semaphore;
423 logger: Logger;
424}): Promise<Array<{ remotePath: string; relativePath: string; size: number }>> {
425 const { client, owner, repo, basePath, ref, enabledFeatures, semaphore, logger } = params;
426
427 // Cache directory listing results to avoid duplicate API calls
428 // File-based features (ignore, mcp, hooks) all list the same basePath directory
429 const dirCache = new Map<string, Promise<GitHubFileEntry[]>>();
430
431 async function getCachedDirectory(path: string): Promise<GitHubFileEntry[]> {
432 let promise = dirCache.get(path);
433 if (promise === undefined) {
434 promise = withSemaphore(semaphore, () => client.listDirectory(owner, repo, path, ref));
435 dirCache.set(path, promise);
436 }
437 return promise;
438 }
439
440 const tasks = enabledFeatures.flatMap((feature) =>
441 FEATURE_PATHS[feature].map((featurePath) => ({ feature, featurePath })),
442 );
443
444 const results = await Promise.all(
445 tasks.map(async ({ featurePath }) => {
446 const fullPath =
447 basePath === "." || basePath === "" ? featurePath : posix.join(basePath, featurePath);
448 const collected: Array<{ remotePath: string; relativePath: string; size: number }> = [];
449
450 try {
451 // Check if it's a file (mcp.json, .aiignore, hooks.json)
452 if (featurePath.includes(".")) {
453 // Try to get the file directly
454 try {
455 const entries = await getCachedDirectory(
456 basePath === "." || basePath === "" ? "." : basePath,
457 );
458 const fileEntry = entries.find((e) => e.name === featurePath && e.type === "file");
459 if (fileEntry) {
460 collected.push({
461 remotePath: fileEntry.path,
462 relativePath: featurePath,
463 size: fileEntry.size,
464 });
465 }
466 } catch (error) {
467 // Only skip 404 errors (file not found), re-throw other errors
468 if (isNotFoundError(error)) {
469 logger.debug(`File not found: ${fullPath}`);
470 } else {
471 throw error;
472 }

Callers 2

fetchFilesFunction · 0.85
fetchAndConvertToolFilesFunction · 0.85

Calls 4

getCachedDirectoryFunction · 0.85
isNotFoundErrorFunction · 0.85
listDirectoryRecursiveFunction · 0.85
debugMethod · 0.45

Tested by

no test coverage detected

Used in the wild real call sites across dependent graphs

searching dependent graphs…