Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Format plain text or markdown files with proper structure (frontmatter, headings, bold, lists, code blocks) without changing content.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
scripts/main.ts
1import { readFileSync, writeFileSync } from "fs";2import { unified } from "unified";3import remarkParse from "remark-parse";4import remarkCjkFriendly from "remark-cjk-friendly";5import remarkGfm from "remark-gfm";6import remarkFrontmatter from "remark-frontmatter";7import remarkStringify from "remark-stringify";8import { visit } from "unist-util-visit";9import YAML from "yaml";10import { replaceQuotes } from "./quotes";11import { applyAutocorrect } from "./autocorrect";1213export interface FormatOptions {14quotes?: boolean;15spacing?: boolean;16emphasis?: boolean;17}1819export interface FormatResult {20success: boolean;21filePath: string;22quotesFixed: boolean;23spacingApplied: boolean;24emphasisFixed: boolean;25error?: string;26}2728const DEFAULT_OPTIONS: Required<FormatOptions> = {29quotes: false,30spacing: true,31emphasis: true,32};3334function decodeHtmlEntities(text: string): string {35return text.replace(/&#x([0-9A-Fa-f]+);/g, (_, hex) =>36String.fromCodePoint(parseInt(hex, 16))37);38}3940function formatFrontmatter(value: string): string | null {41try {42const doc = YAML.parseDocument(value);43return doc.toString({ lineWidth: 0 }).trimEnd();44} catch {45return null;46}47}4849function formatMarkdownContent(50content: string,51options: Required<FormatOptions>52): string {53const processor = unified()54.use(remarkParse)55.use(options.emphasis ? remarkCjkFriendly : [])56.use(remarkGfm)57.use(remarkFrontmatter, ["yaml"])58.use(remarkStringify, {59wrap: false,60});6162const tree = processor.parse(content);6364visit(tree, (node) => {65if (node.type === "text" && options.quotes) {66const textNode = node as { value: string };67textNode.value = replaceQuotes(textNode.value);68return;69}70if (node.type === "yaml") {71const yamlNode = node as { value: string };72const formatted = formatFrontmatter(yamlNode.value);73if (formatted !== null) {74yamlNode.value = formatted;75}76return;77}78});7980let result = processor.stringify(tree);81if (options.emphasis) {82result = decodeHtmlEntities(result);83}84return result;85}8687export function formatMarkdown(88filePath: string,89options?: FormatOptions90): FormatResult {91const opts: Required<FormatOptions> = { ...DEFAULT_OPTIONS, ...options };9293const result: FormatResult = {94success: false,95filePath,96quotesFixed: false,97spacingApplied: false,98emphasisFixed: false,99};100101try {102const content = readFileSync(filePath, "utf-8");103const formattedContent = formatMarkdownContent(content, opts);104105result.quotesFixed = opts.quotes;106result.emphasisFixed = opts.emphasis;107108writeFileSync(filePath, formattedContent, "utf-8");109110if (opts.spacing) {111result.spacingApplied = applyAutocorrect(filePath);112}113114result.success = true;115console.log(`✓ Formatted: ${filePath}`);116} catch (error) {117result.error = error instanceof Error ? error.message : String(error);118console.error(`✗ Format failed: ${result.error}`);119}120121return result;122}123124function parseArgs(args: string[]): { filePath: string; options: FormatOptions } {125const options: FormatOptions = {};126let filePath = "";127128for (let i = 0; i < args.length; i++) {129const arg = args[i];130131if (arg === "--quotes" || arg === "-q") {132options.quotes = true;133} else if (arg === "--no-quotes") {134options.quotes = false;135} else if (arg === "--spacing" || arg === "-s") {136options.spacing = true;137} else if (arg === "--no-spacing") {138options.spacing = false;139} else if (arg === "--emphasis" || arg === "-e") {140options.emphasis = true;141} else if (arg === "--no-emphasis") {142options.emphasis = false;143} else if (arg === "--help" || arg === "-h") {144console.log(`Usage: npx -y bun scripts/main.ts <file.md> [options]145146Options:147-q, --quotes Replace ASCII quotes with fullwidth quotes (default: false)148--no-quotes Do not replace quotes149-s, --spacing Add CJK/English spacing via autocorrect (default: true)150--no-spacing Do not add CJK/English spacing151-e, --emphasis Fix CJK emphasis punctuation issues (default: true)152--no-emphasis Do not fix CJK emphasis issues153-h, --help Show this help message`);154process.exit(0);155} else if (!arg.startsWith("-")) {156filePath = arg;157}158}159160return { filePath, options };161}162163if (import.meta.url === `file://${process.argv[1]}`) {164const { filePath, options } = parseArgs(process.argv.slice(2));165166if (!filePath) {167console.error("Usage: npx -y bun scripts/main.ts <file.md> [options]");168console.error("Use --help for more information.");169process.exit(1);170}171172const result = formatMarkdown(filePath, options);173if (!result.success) {174process.exit(1);175}176}177