agentsclimarketplace

File browser

Skill vanducng/skills/skills/file-browser

A daily-driver collection of skills for agentic coding — a portable, agent-agnostic catalog managed with the vd CLI.

Install
npx -y skills add vanducng/skills --skill file-browser

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 2 stars2 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Local HTTP server that renders markdown, code, text, JSON, CSV/TSV/XLSX tables, HTML, PDF, images, video, and audio in the browser. Persistent left tree sidebar with vim keybindings (j/k/h/l/gg/G/Enter/o), filter (/), theme toggle (T), and folder open/collapse persistence per tab. Markdown dispatches to a novel-theme reader (Mermaid, plan nav, ToC scroll-spy); code/text dispatches to a highlight.js view with line numbers + copy button; CSV/TSV/XLSX dispatches to a tabular view; HTML renders in a sandboxed iframe; PDF dispatches to a custom minimal viewer built on the pdfjs-dist PDFViewer component (toolbar: prev/next, page input, zoom, search, download); images/video/audio get a gallery + single-view with Range-aware streaming. One server, one port, one CLI.

The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.

SKILL.md

13.4 KB, ~3.2k tokens by cl100k_base, as published. Nobody here has run it

file-browser

One local HTTP server, one port, one CLI - handles markdown, images, video, and audio. Dispatches by file extension at the /view route: markdown goes to the novel-theme reader; image/video/audio go to the media viewer.

Installation

cd $HOME/skills/skills/file-browser && npm install

Dependencies: marked, highlight.js, gray-matter (markdown), pdfjs-dist (PDF viewer), read-excel-file (XLSX tables). Media rendering is zero-dep - browser does the work.

npm install runs a postinstall hook that copies pdfjs-dist runtime deps (worker, cmaps, fonts, wasm helpers) from node_modules/pdfjs-dist/ into assets/pdfjs-viewer/. If you ran npm install --ignore-scripts, populate the assets manually:

node $HOME/skills/skills/file-browser/scripts/install-pdfjs-assets.cjs

Quick Start

# Single file (auto-detects markdown vs media)
node $HOME/skills/skills/file-browser/scripts/server.cjs --file ./plan.md --open
node $HOME/skills/skills/file-browser/scripts/server.cjs --file ./photo.png --open
node $HOME/skills/skills/file-browser/scripts/server.cjs --file ./demo.mp4 --open

# Folder (gallery + Documents section)
node $HOME/skills/skills/file-browser/scripts/server.cjs --dir ~/plans --open

# Background mode (returns JSON envelope on stdout)
node $HOME/skills/skills/file-browser/scripts/server.cjs --file ./README.md --background

# Stop all running file-browser servers
node $HOME/skills/skills/file-browser/scripts/server.cjs --stop

CLI Options

OptionDescriptionDefault
--file <path>Single file (markdown / image / video / audio)-
--dir <path>Folder gallery-
--port <n>Server port (auto-increments if busy)3556
--host <addr>Bind host (0.0.0.0 for LAN)localhost
--open / --no-openAuto-open browseropen by default
--backgroundDetach as child, print JSONfalse
--foregroundStay foreground (Claude Code bg tasks)false
--stopStop all file-browser servers-

HTTP Routes

RouteDescription
/view?file=<abs-path>Dispatches by extension. .md/.markdown/.mdx → novel-theme markdown reader (Mermaid, plan nav, ToC). .csv/.tsv/.xlsx → tabular data view. .pdf → custom PDF.js viewer iframe. Image/video/audio → media single-view with arrow-key prev/next. Append ?raw=1 to fall through to source view.
/browse?dir=<abs-path>Gallery: folders, Documents (markdown + PDF + CSV/TSV/XLSX), Media (image/video/audio), Other files.
/file/<abs-path>Raw byte streaming with HTTP Range support (required for video seeking and Safari audio).
/api/tree?dir=<abs-path>Lazy directory listing (one level) for the sidebar tree. Returns JSON {path, entries[]} where each entry has {name, path, kind: dir|file, fileType?: markdown|pdf|table|image|video|audio|other}.
/assets/*Static assets (theme CSS, reader JS, sidebar JS).
/assets/pdfjs-viewer/*Custom PDF viewer + pdfjs-dist runtime deps (populated by postinstall).
/Welcome / route reference.

Supported Formats

Documents: .md, .markdown, .mdx (rendered with marked + highlight.js + Mermaid), .csv, .tsv, .xlsx (tabular data view; XLSX renders the first worksheet), .pdf (custom minimal viewer wrapping the pdfjs-dist PDFViewer component) Images: PNG, JPEG, GIF, WebP, AVIF, SVG, BMP, ICO, HEIC/HEIF (Safari only), JPEG XL, APNG Video: MP4, WebM, MOV, M4V, MKV*, OGV Audio: MP3, M4A, AAC, OGG, Opus, WAV, FLAC

* MKV codec support depends on browser. For codec-difficult video, fall back to IINA.

Markdown Features (inherited from markdown-render)

  • Novel-theme reader (warm cream / dark gold), Libre Baskerville headings, max 720px width
  • Mermaid v11 diagrams, theme-aware light/dark, click to expand
  • Plan navigation: detects plan.md + phase-XX-*.md siblings, builds accordion sidebar with status badges, prev/next buttons
  • Cross-file links: relative .md links re-route through /view; relative non-md paths through /file/
  • Keyboard shortcuts: ? cheatsheet, T theme, S sidebar, / phase nav, Esc close
  • Auto-hide header, progress bar, mobile FAB

Media Features

  • Gallery grid with lazy-loaded image thumbs, video first-frame previews, audio cards
  • Single-view: arrow keys ( ) for prev/next sibling, Esc back to folder
  • HTTP Range support - required for <video> scrubbing and Safari audio playback
  • Mixed media in same folder, sorted alphabetically

Sidebar Tree (gallery + single-view)

A persistent left sidebar shows the directory tree anchored at the launch folder (--dir) or the file's parent (--file). Lazy-loaded - only the root level loads up front; folders fetch their children on expand.

Vim keybindings

KeyAction
j / kMove cursor down / up
hCollapse open folder, or jump to parent
lExpand folder under cursor
Enter / oOpen file or folder under cursor
OOpen in a new tab
gg / GJump to first / last visible node
/Focus filter input (substring match on visible names)
n / NNext / previous filter match
rReload current folder's children
\Toggle sidebar visibility (state persisted in localStorage)
?Toggle keybinding cheatsheet overlay
EscClear filter / close help

Click also works - clicking a folder toggles expand, clicking a file opens it. The currently-viewed file is highlighted; on page load, the sidebar auto-expands ancestors of the active path so the file is always visible.

The markdown reader has its own ToC + plan-nav sidebar, so the file-browser sidebar is intentionally NOT injected there. Press ← Folder (or browser back) to return to the gallery and use the tree.

Root persistence

The current tree root is saved to localStorage['fb-tree-root'] on every page load. URL ?root=<path> is the canonical setter - when present, the server validates against the allow-list and the rendered value is written to storage. When absent, the client redirects once via location.replace to the same URL with the stored root appended, so refresh keeps the tree where you left it. A stale stored root that the server rejects is automatically replaced with the rendered (safe) value, so storage self-heals. The rebase button keeps working - it updates the URL ?root=, which then flows through the same save path.

Customizing / extending

  • assets/sidebar.js - keybinding map and tree behavior. Add a binding by extending the keydown switch.
  • assets/sidebar.css - layout (CSS grid: var(--sidebar-width) 1fr), colors via --sidebar-* tokens.
  • lib/sidebar.cjs - HTML stub including filter input + cheatsheet markup.
  • lib/tree-api.cjs - server-side directory listing. Add a new file classification by extending classifyFile.

Architecture

scripts/
├── server.cjs                      # CLI entry
├── lib/
│   ├── port-finder.cjs             # 3556-3600 range
│   ├── process-mgr.cjs             # PID prefix file-browser-*
│   ├── http-server.cjs             # Routing + dispatch by extension
│   ├── markdown-page.cjs           # Composes novel-theme HTML for .md
│   ├── markdown-renderer.cjs       # marked + highlight.js + gray-matter
│   ├── plan-navigator.cjs          # Plan detection, sidebar, prev/next
│   ├── plan-table-parser.cjs       # plan.md table → phases
│   ├── media-renderer.cjs          # Gallery + single-view for images/video/audio
│   ├── sidebar.cjs                 # Sidebar HTML stub injected into gallery + single-view
│   └── tree-api.cjs                # /api/tree directory listing (lazy)
└── tests/
    └── server.test.cjs             # Smoke tests for every dispatch path

assets/
├── styles.css                      # Media gallery + single-view theme
├── sidebar.css                     # Sidebar layout + tree styling
├── sidebar.js                      # Sidebar tree + vim keybindings
├── novel-theme.css                 # Markdown theme entry (imports modules)
├── styles/                         # Modular markdown CSS (variables, base, content, mermaid, ...)
├── template.html                   # Markdown viewer template
├── reader.js                       # Markdown client-side: Mermaid, sidebar, shortcuts
└── pdfjs-viewer/                   # Custom viewer.html + viewer.js + viewer.css; pdfjs-dist
                                    # runtime deps (build/, web/, cmaps/, standard_fonts/, wasm/)
                                    # populated by `postinstall` from node_modules/pdfjs-dist

Custom PDF viewer architecture

/assets/pdfjs-viewer/viewer.html is custom - pdfjs-dist (npm) does NOT ship Mozilla's standalone web/viewer.html, only the embeddable PDFViewer component. Our viewer wraps that component with a minimal toolbar (~250 LOC across viewer.html / viewer.js / viewer.css). Settings: enableScripting: false, isEvalSupported: false (defense-in-depth against CVE-2024-4367; pdfjs-dist ≥4.2.67 already disables isEvalSupported at the component level). To extend: edit viewer.html (toolbar markup), viewer.js (component wiring), or viewer.css (styling). Component API: https://github.com/mozilla/pdf.js/wiki/Viewer-options

Upgrading pdfjs-dist

  1. Bump pdfjs-dist in package.json.
  2. Re-run npm install (postinstall repopulates assets/pdfjs-viewer/).
  3. Run tests (node scripts/tests/server.test.cjs).
  4. Re-read exports of assets/pdfjs-viewer/web/pdf_viewer.mjs - verify PDFViewer, EventBus, PDFLinkService, PDFFindController, PDFHistory, DownloadManager, parseQueryString are still present with compatible signatures vs viewer.js.
  5. Manual smoke: open a real multi-page PDF and exercise every toolbar control (prev/next, page input, zoom, search, download).
  6. Check the changelog for security advisories - pin minimum to ≥4.2.67 (CVE-2024-4367).

Integration

Hammerspoon (Hyper+V → smart route to Arc)

dotfiles/hammerspoon/.hammerspoon/init.lua already routes the clipboard:

  • .md/.markdown/.mdx → calls file-browser /view (dispatches to markdown reader)
  • image/video/audio extensions → calls file-browser /view (dispatches to media viewer)
  • code/config files → opens in nvim
  • everything else → open

The Hammerspoon URL always appends &root=<computed> (file's parent dir, or the dir itself when launched on a folder) so each fresh launch overrides any localStorage-saved root from the previous session. Without this, the persistence layer would keep the sidebar anchored at whatever path the server was first started with.

nvim

<leader>mv (in polish.lua) - open current buffer in file-browser. If the file is renderable (markdown / image / video / audio), opens single-view; otherwise opens the parent directory's gallery.

Testing

node $HOME/skills/skills/file-browser/scripts/tests/server.test.cjs

Boots on a free port, hits every dispatch path (welcome, gallery, image view, markdown view, PDF viewer iframe, raw passthrough, file streaming, Range, traversal guard, missing params, 404, tree API, sidebar injection), cleans up. 38 tests.

Security

  • All ?file= / ?dir= / /file/* paths must resolve under one of the allow-list directories ($HOME + cwd + target dir + assets dir).
  • Null-byte and .. traversal rejected.
  • Error messages sanitized to strip absolute paths.
  • Server binds to localhost by default; --host 0.0.0.0 opt-in for LAN.

Open Questions

  • HEIC works in Safari only. Should we add ImageMagick on-the-fly conversion for Chrome? (Requires native dep - breaks the zero-runtime-dep posture for media.)
  • Should the gallery prefetch markdown front matter (title, status) so plan dirs render with badges before clicking in? (Adds I/O cost per gallery hit.)
  • Worth adding a /api/files?dir=<path> JSON endpoint so the gallery could be a SPA later? (YAGNI for now.)
  • Should we render gallery thumbnails for PDFs (page-1 canvas, client-side, lazy via PDF.js)?
  • Should we add custom vim keybindings inside the PDF viewer iframe (postMessage bridge)?
  • Should we add an outline/bookmarks panel to the PDF viewer? (PDFOutlineViewer would need to be added to the component import set.)
  • Should we re-add a thumbnails sidebar to the PDF viewer? (PDFThumbnailViewer is not exported from [email protected]'s pdf_viewer.mjs; would require re-implementing or upgrading to a future major.)

Gives 0 of the 12 instructions most pdf office docs skills give in ~3.2k tokens

Counted across 635 of the 690 authors here whose files we hold, read 2026-08-06

  • extract text using pdfplumberin 92 of 635, across 25 files
  • create PDFs using reportlabin 83 of 635, across 16 files
  • read FORMS.md to fill out PDF formsin 80 of 635, across 13 files
  • OCR scanned PDFs using pytesseractin 77 of 635, across 10 files
  • merge or split PDFs using qpdfin 70 of 635, across 3 files
  • use Excel formulas instead of hardcoded calculated valuesin 68 of 635, across 12 files
  • unpack edit xml and repack existing documentsin 63 of 635, across 8 files
  • document sources for hardcoded valuesin 61 of 635, across 9 files
  • write minimal python code without unnecessary commentsin 59 of 635, across 7 files
  • run the recalculation script after adding or modifying formulasin 58 of 635, across 6 files
  • fix all identified formula errors and recalculatein 58 of 635, across 6 files
  • format years as text stringsin 57 of 635, across 5 files

Said here and by no other author read

  • run the test suite after upgrading pdfjs-dist
  • reject null-byte and traversal paths
  • bind to localhost by default
  • sanitize error messages to strip absolute paths
  • ensure all query paths resolve under allowed directories
  • disable scripting and eval support in the pdf viewer

Grouped from the skills themselves: near-identical wordings counted once, and counted by distinct author, so one author publishing three of these counts once. Length counted with cl100k_base; the agent that loads this file may tokenize it differently.

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.