Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 34 additions & 4 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import fs from 'node:fs/promises';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import type { Plugin } from '@opencode-ai/plugin';
import { tool } from '@opencode-ai/plugin';

Expand Down Expand Up @@ -47,13 +49,41 @@ function parseFrontmatter(content: string): { frontmatter: CommandFrontmatter; b
return { frontmatter, body: body.trim() };
}

function getModuleDir(): string {
// Works in both Bun and Node.js
if (typeof import.meta.dir === 'string') {
return import.meta.dir;
}
// Node.js fallback
return path.dirname(fileURLToPath(import.meta.url));
}

async function scanMdFiles(dir: string): Promise<string[]> {
const files: string[] = [];

async function walk(currentDir: string): Promise<void> {
const entries = await fs.readdir(currentDir, { withFileTypes: true });
for (const entry of entries) {
const fullPath = path.join(currentDir, entry.name);
if (entry.isDirectory()) {
await walk(fullPath);
} else if (entry.isFile() && entry.name.endsWith('.md')) {
files.push(fullPath);
}
}
}

await walk(dir);
return files;
}
Comment on lines +61 to +78

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The scanMdFiles function can be improved for performance, robustness, and readability.

  1. Performance: The current implementation processes subdirectories sequentially. This can be inefficient for directories with many subdirectories. The suggested change uses Promise.all to process directory entries in parallel, speeding up file scanning.
  2. Robustness: The current implementation will throw an error if a directory doesn't exist or can't be read. This is likely a regression from Bun.Glob, which would typically find no files. The suggestion adds error handling to return an empty array in these cases, making the function more resilient.
  3. Readability: The suggested version is more functional and avoids shared mutable state (the files array), which makes the code cleaner and easier to reason about.
async function scanMdFiles(dir: string): Promise<string[]> {
  const entries = await fs.readdir(dir, { withFileTypes: true }).catch(err => {
    // If directory is not readable, treat as empty to mimic glob behavior.
    if (err.code === 'ENOENT' || err.code === 'EACCES') {
      return [];
    }
    throw err;
  });

  const filePromises = entries.map(async (entry) => {
    const fullPath = path.join(dir, entry.name);
    if (entry.isDirectory()) {
      return scanMdFiles(fullPath);
    }
    if (entry.isFile() && entry.name.endsWith('.md')) {
      return [fullPath];
    }
    return [];
  });

  const nestedFiles = await Promise.all(filePromises);
  return nestedFiles.flat();
}


async function loadCommands(): Promise<ParsedCommand[]> {
const commands: ParsedCommand[] = [];
const commandDir = path.join(import.meta.dir, 'command');
const glob = new Bun.Glob('**/*.md');
const commandDir = path.join(getModuleDir(), 'command');

for await (const file of glob.scan({ cwd: commandDir, absolute: true })) {
const content = await Bun.file(file).text();
const files = await scanMdFiles(commandDir);
for (const file of files) {
const content = await fs.readFile(file, 'utf-8');
const { frontmatter, body } = parseFrontmatter(content);
const relativePath = path.relative(commandDir, file);
const name = relativePath.replace(/\.md$/, '').replace(/\//g, '-');
Expand Down
Loading