| name | markit-markdown-converter |
| description | Convert files, URLs, and media to markdown using the markit-ai CLI and SDK with pluggable converters and LLM support. |
| triggers | ["convert PDF to markdown","turn a DOCX into markdown","extract markdown from a URL","convert images or audio to markdown with AI","use markit to convert files","install markit CLI","write a markit plugin","use markit as a library in TypeScript"] |
markit-markdown-converter
Skill by ara.so — Daily 2026 Skills collection.
markit converts almost anything to markdown: PDFs, Word docs, PowerPoint, Excel, HTML, EPUB, Jupyter notebooks, RSS feeds, CSV, JSON, YAML, images (with EXIF + AI description), audio (with metadata + AI transcription), ZIP archives, URLs, Wikipedia pages, and source code files. It works as a CLI tool and as a TypeScript/Node.js library, supports pluggable converters, and integrates with OpenAI, Anthropic, and any OpenAI-compatible LLM API.
Installation
npm install -g markit-ai
npm install markit-ai
CLI Quick Reference
markit report.pdf
markit document.docx
markit slides.pptx
markit data.xlsx
markit notebook.ipynb
markit https://example.com/article
markit https://en.wikipedia.org/wiki/Markdown
markit photo.jpg
markit recording.mp3
markit diagram.png -p "Describe the architecture and data flow"
markit receipt.jpg -p "List all line items with prices as a table"
markit report.pdf -o report.md
markit report.pdf -q
markit report.pdf --json
cat file.pdf | markit -
markit report.pdf | pbcopy
markit data.xlsx -q | some-other-tool
markit formats
markit init
markit config show
markit config get llm.model
markit config set llm.provider anthropic
markit config set llm.model claude-haiku-4-5
markit plugin install npm:markit-plugin-dwg
markit plugin install git:github.com/user/markit-plugin-ocr
markit plugin install ./my-plugin.ts
markit plugin list
markit plugin remove dwg
markit onboard
AI / LLM Configuration
Images and audio always get free metadata extraction. AI-powered description and transcription requires an API key.
export OPENAI_API_KEY=sk-...
markit photo.jpg
export ANTHROPIC_API_KEY=sk-ant-...
markit config set llm.provider anthropic
markit photo.jpg
markit config set llm.apiBase http://localhost:11434/v1
markit config set llm.model llama3.2-vision
markit photo.jpg
.markit/config.json (created by markit init):
{
"llm": {
"provider": "openai",
"apiBase": "https://api.openai.com/v1",
"model": "gpt-4.1-nano",
"transcriptionModel": "gpt-4o-mini-transcribe"
}
}
Environment variables always override config file values. Never store API keys in the config file — use env vars.
| Provider | Env Vars | Default Vision Model |
|---|
openai | OPENAI_API_KEY, MARKIT_API_KEY | gpt-4.1-nano |
anthropic | ANTHROPIC_API_KEY, MARKIT_API_KEY | claude-haiku-4-5 |
SDK Usage
Basic File and URL Conversion
import { Markit } from "markit-ai";
const markit = new Markit();
const { markdown } = await markit.convertFile("report.pdf");
console.log(markdown);
const { markdown: webMd } = await markit.convertUrl("https://example.com/article");
import { readFileSync } from "fs";
const buffer = readFileSync("document.docx");
const { markdown: docMd } = await markit.convert(buffer, { extension: ".docx" });
With OpenAI for Vision + Transcription
import OpenAI from "openai";
import { Markit } from "markit-ai";
const openai = new OpenAI();
const markit = new Markit({
describe: async (image: Buffer, mime: string) => {
const res = await openai.chat.completions.create({
model: "gpt-4.1-nano",
messages: [
{
role: "user",
content: [
{ type: "text", text: "Describe this image in detail." },
{
type: "image_url",
image_url: {
url: `data:${mime};base64,${image.toString("base64")}`,
},
},
],
},
],
});
return res.choices[0].message.content ?? "";
},
transcribe: (: , : ) => {
res = openai...({
: ,
: ([audio], , { : mime }),
});
res.;
},
});
{ markdown } = markit.();
With Anthropic for Vision
import Anthropic from "@anthropic-ai/sdk";
import OpenAI from "openai";
import { Markit } from "markit-ai";
const anthropic = new Anthropic();
const openai = new OpenAI();
const markit = new Markit({
describe: async (image: Buffer, mime: string) => {
const res = await anthropic.messages.create({
model: "claude-haiku-4-5",
max_tokens: 1024,
messages: [
{
role: "user",
content: [
{
type: "image",
source: {
type: "base64",
media_type: mime as "image/jpeg" | "image/png" | | ,
: image.(),
},
},
{ : , : },
],
},
],
});
(res.[] { : }).;
},
: (: , : ) => {
res = openai...({
: ,
: ([audio], , { : mime }),
});
res.;
},
});
Using Built-in Providers via Config
import { Markit, createLlmFunctions, loadConfig, loadAllPlugins } from "markit-ai";
const config = loadConfig();
const plugins = await loadAllPlugins();
const markit = new Markit(createLlmFunctions(config), plugins);
const { markdown } = await markit.convertFile("report.pdf");
Writing a Plugin
Plugins let you add new formats or override built-in converters. Plugin converters run before built-ins.
Basic Converter Plugin
import type { MarkitPluginAPI } from "markit-ai";
export default function (api: MarkitPluginAPI) {
api.setName("my-format");
api.setVersion("1.0.0");
api.registerConverter(
{
name: "myformat",
accepts: (info) => [".myf", ".myfmt"].includes(info.extension ?? ""),
convert: async (input: Buffer, info) => {
const text = input.toString("utf-8");
const markdown = `# Converted\n\n\`\`\`\n${text}\n\`\`\``;
return { markdown };
},
},
{ name: "My Format", extensions: [".myf", ".myfmt"] },
);
}
Override a Built-in Converter
import type { MarkitPluginAPI } from "markit-ai";
export default function (api: MarkitPluginAPI) {
api.setName("better-pdf");
api.setVersion("1.0.0");
api.registerConverter({
name: "pdf",
accepts: (info) => info.extension === ".pdf",
convert: async (input: Buffer, info) => {
const markdown = `# PDF Content\n\n(extracted with custom logic)`;
return { markdown };
},
});
}
Register a Custom LLM Provider
import type { MarkitPluginAPI } from "markit-ai";
export default function (api: MarkitPluginAPI) {
api.setName("gemini-provider");
api.registerProvider({
name: "gemini",
envKeys: ["GOOGLE_API_KEY"],
defaultBase: "https://generativelanguage.googleapis.com/v1beta",
defaultModel: "gemini-2.0-flash",
create: (config, prompt) => ({
describe: async (image: Buffer, mime: string) => {
return "Image description from Gemini";
},
}),
});
}
Install and Use a Plugin
markit plugin install ./my-plugin.ts
markit plugin install npm:markit-plugin-dwg
markit plugin install git:github.com/user/markit-plugin-ocr
markit plugin list
markit plugin remove my-format
Common Patterns
Batch Convert a Directory
import { Markit } from "markit-ai";
import { readdirSync, readFileSync, writeFileSync } from "fs";
import { extname, basename, join } from "path";
const markit = new Markit();
const inputDir = "./docs";
const outputDir = "./docs-md";
const files = readdirSync(inputDir);
for (const file of files) {
const ext = extname(file);
if (![".pdf", ".docx", ".html"].includes(ext)) continue;
const buffer = readFileSync(join(inputDir, file));
const { markdown } = await markit.convert(buffer, { extension: ext });
const outName = basename(file, ext) + ".md";
writeFileSync(join(outputDir, outName), markdown);
console.log(`Converted: ${file} → ${outName}`);
}
Convert URL List
import { Markit } from "markit-ai";
const markit = new Markit();
const urls = [
"https://example.com/article-1",
"https://example.com/article-2",
"https://en.wikipedia.org/wiki/TypeScript",
];
const results = await Promise.all(
urls.map(async (url) => {
const { markdown } = await markit.convertUrl(url);
return { url, markdown };
})
);
for (const { url, markdown } of results) {
console.log(`\n## ${url}\n\n${markdown.slice(0, 200)}...`);
}
CLI in Agent/Automation Scripts
markit report.pdf --json
markit report.pdf -q > report.md
markit https://example.com/article -q | wc -w
for f in docs/*.pdf; do
markit "$f" -o "out/$(basename "$f" .pdf).md"
done
Supported Formats Reference
| Format | Extensions |
|---|
| PDF | .pdf |
| Word | .docx |
| PowerPoint | .pptx |
| Excel | .xlsx |
| HTML | .html, .htm |
| EPUB | .epub |
| Jupyter | .ipynb |
| RSS/Atom | .rss, .atom, .xml |
| CSV/TSV | .csv, .tsv |
| JSON | .json |
| YAML | .yaml, .yml |
| XML/SVG | .xml, .svg |
| Images | .jpg, .png, .gif, .webp |
| Audio | .mp3, .wav, .m4a, .flac |
| ZIP | .zip |
| Code | .py, .ts, .go, .rs, and more |
| Plain text | .txt, .md, .rst, .log |
| URLs | http://, https:// |
Run markit formats to see the full live list including any installed plugins.
Troubleshooting
AI description/transcription not working
- Ensure the correct env var is set:
OPENAI_API_KEY or ANTHROPIC_API_KEY
- Run
markit config show to verify the resolved provider and model
- For custom API bases (Ollama, etc.), confirm the server is running and the model supports vision
Plugin not loading
- Run
markit plugin list to confirm it's installed
- Check the plugin exports a default function matching
(api: MarkitPluginAPI) => void
- Try reinstalling:
markit plugin remove <name> then markit plugin install <source>
PDF returns empty or garbled markdown
- The built-in converter uses text extraction (not OCR). Scanned PDFs need an OCR plugin.
- Try a custom plugin or pre-process with an OCR tool first.
Stdin (markit -) not working
- Pipe content directly:
cat file.pdf | markit -
- Ensure the file type can be detected from content; use explicit hints if needed.
Config not being read
- Config is loaded from
.markit/config.json relative to the current working directory
- Run
markit init to create it, then markit config show to verify