diff --git a/eslint.config.js b/eslint.config.js index 57a71453..e002aece 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -157,7 +157,11 @@ export default tseslint.config( }, { type: "backend-shared-utils", // shared backend runtime helpers that modules may import directly - pattern: ["server/shared/utils.{js,ts}", "server/shared/claude-cli-path.ts"], // classify the shared utils file so modules can depend on it explicitly + pattern: [ + "server/shared/utils.{js,ts}", + "server/shared/frontmatter.ts", + "server/shared/claude-cli-path.ts", + ], // classify shared utility files so modules can depend on them explicitly mode: "file", }, { diff --git a/server/modules/providers/list/claude/claude-skills.provider.ts b/server/modules/providers/list/claude/claude-skills.provider.ts index d00b27e6..cbb1073a 100644 --- a/server/modules/providers/list/claude/claude-skills.provider.ts +++ b/server/modules/providers/list/claude/claude-skills.provider.ts @@ -2,9 +2,8 @@ import { readFile, readdir, stat } from 'node:fs/promises'; import os from 'node:os'; import path from 'node:path'; -import matter from 'gray-matter'; - import { SkillsProvider } from '@/modules/providers/shared/skills/skills.provider.js'; +import { parseFrontMatter } from '@/shared/frontmatter.js'; import type { ProviderSkill, ProviderSkillListOptions, @@ -216,7 +215,7 @@ export class ClaudeSkillsProvider extends SkillsProvider { commandPath: string, ): Promise<{ name: string; description: string }> { const content = await readFile(commandPath, 'utf8'); - const parsed = matter(content); + const parsed = parseFrontMatter(content); const data = readObjectRecord(parsed.data) ?? {}; return { diff --git a/server/routes/commands.js b/server/routes/commands.js index ac957511..bb61644f 100644 --- a/server/routes/commands.js +++ b/server/routes/commands.js @@ -1,9 +1,11 @@ -import express from 'express'; import { promises as fs } from 'fs'; -import path from 'path'; import os from 'os'; +import path from 'path'; + +import express from 'express'; + import { CLAUDE_MODELS, CURSOR_MODELS, CODEX_MODELS } from '../../shared/modelConstants.js'; -import { parseFrontmatter } from '../utils/frontmatter.js'; +import { parseFrontMatter } from '../shared/frontmatter.js'; import { findAppRoot, getModuleDir } from '../utils/runtime-paths.js'; const __dirname = getModuleDir(import.meta.url); @@ -40,7 +42,7 @@ async function scanCommandsDirectory(dir, baseDir, namespace) { // Parse markdown file for metadata try { const content = await fs.readFile(fullPath, 'utf8'); - const { data: frontmatter, content: commandContent } = parseFrontmatter(content); + const { data: frontmatter, content: commandContent } = parseFrontMatter(content); // Calculate relative path from baseDir for command name const relativePath = path.relative(baseDir, fullPath); @@ -513,7 +515,7 @@ router.post('/execute', async (req, res) => { } } const content = await fs.readFile(commandPath, 'utf8'); - const { data: metadata, content: commandContent } = parseFrontmatter(content); + const { data: metadata, content: commandContent } = parseFrontMatter(content); // Basic argument replacement (will be enhanced in command parser utility) let processedContent = commandContent; diff --git a/server/utils/frontmatter.js b/server/shared/frontmatter.ts similarity index 81% rename from server/utils/frontmatter.js rename to server/shared/frontmatter.ts index 0a4b1eb8..9eb7ddd8 100644 --- a/server/utils/frontmatter.js +++ b/server/shared/frontmatter.ts @@ -9,10 +9,10 @@ const frontmatterOptions = { engines: { js: disabledFrontmatterEngine, javascript: disabledFrontmatterEngine, - json: disabledFrontmatterEngine - } + json: disabledFrontmatterEngine, + }, }; -export function parseFrontmatter(content) { +export function parseFrontMatter(content: string) { return matter(content, frontmatterOptions); } diff --git a/server/shared/utils.ts b/server/shared/utils.ts index 239a951c..2903814e 100644 --- a/server/shared/utils.ts +++ b/server/shared/utils.ts @@ -15,9 +15,9 @@ import os from 'node:os'; import path from 'node:path'; import readline from 'node:readline'; -import matter from 'gray-matter'; import type { NextFunction, Request, RequestHandler, Response } from 'express'; +import { parseFrontMatter } from '@/shared/frontmatter.js'; import type { AnyRecord, ApiSuccessShape, @@ -587,7 +587,7 @@ export async function readProviderSkillMarkdownDefinition( skillPath: string, ): Promise<{ name: string; description: string }> { const content = await readFile(skillPath, 'utf8'); - const parsed = matter(content); + const parsed = parseFrontMatter(content); const data = readObjectRecord(parsed.data) ?? {}; const fallbackName = path.basename(path.dirname(skillPath)); diff --git a/server/utils/commandParser.js b/server/utils/commandParser.js index 56e3f702..0451320d 100644 --- a/server/utils/commandParser.js +++ b/server/utils/commandParser.js @@ -1,9 +1,11 @@ +import { execFile } from 'child_process'; import { promises as fs } from 'fs'; import path from 'path'; -import { execFile } from 'child_process'; import { promisify } from 'util'; + import { parse as parseShellCommand } from 'shell-quote'; -import { parseFrontmatter } from './frontmatter.js'; + +import { parseFrontMatter } from '../shared/frontmatter.js'; const execFileAsync = promisify(execFile); @@ -32,7 +34,7 @@ const BASH_COMMAND_ALLOWLIST = [ */ export function parseCommand(content) { try { - const parsed = parseFrontmatter(content); + const parsed = parseFrontMatter(content); return { data: parsed.data || {}, content: parsed.content || '',