npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@nomadamas/agentdir

v0.1.6

Published

Virtual file tree infrastructure for agent-ready file layouts using CoW reflinks

Downloads

713

Readme

@nomadamas/agentdir

Virtual filesystem for agent-optimized exploration of general-purpose files using CoW reflinks.

Built with NAPI-RS. Prebuilt native binaries are bundled per platform via optionalDependencies, so no compiler is required.

  • GitHub: https://github.com/NomaDamas/agentdir
  • License: MIT
  • Node.js: >= 18

Installation

npm install @nomadamas/agentdir

Installation only installs the native package. After mapping original files, call workspace.refresh() whenever the original file tree may have changed. The Node binding exposes reconciliation through refresh() and refreshWithHashVerification(); it does not start the CLI watch loop for you.

Quick Start

import { Workspace } from '@nomadamas/agentdir'

// init and open are synchronous
const ws = Workspace.init('./workspace')

// everything else is async
const summary = await ws.map('./team-files', '/files')
console.log(`Mapped ${summary.entriesAdded} entries`)

const bytes = await ws.readBytes('/files/q1-report.txt')
console.log(bytes.toString())

await ws.mv('/files/q1-report.txt', '/reports/q1-report.txt')  // original files are untouched

// Reconcile source changes before an agent/session depends on the view.
const sync = await ws.refresh()
console.log(sync)

Both CommonJS and ESM are supported:

const { Workspace } = require('@nomadamas/agentdir')
// or
import { Workspace, SnapshotWorkspace } from '@nomadamas/agentdir'

If you also install the CLI, long-running workflows can run agentdir -w ./workspace watch --interval 60 in a separate process. The watcher combines filesystem events with periodic full rescans; Node applications should otherwise call refresh() on their own schedule or at session boundaries.


API Reference

Workspace

Static methods (synchronous)

These two methods are synchronous and return a Workspace directly, not a Promise.

static init(path: string, strategy?: string): Workspace

Initialize a new workspace at path. The optional strategy controls how files are materialized:

| Value | Behavior | |---|---| | "reflink" | Copy-on-write clone (default) | | "symlink" | Symbolic link | | "virtual" | No materialization |

static open(path: string): Workspace

Open an existing workspace at path.


Instance methods (all async)

Mapping
map(source: string, mount: string): Promise<MapSummary>

Map a source directory to a virtual mount point (e.g. "/files").

unmap(mount: string): Promise<UnmapSummary>

Remove the mapping at the given mount point.

mapBatch(mappings: Array<Array<string>>): Promise<BatchMapSummary>

Map multiple sources in one call. Each element is a [sourcePath, mountPoint] tuple. Note: batch map accepts files only, not directories.


Navigation and structure
mv(from: string, to: string): Promise<void>

Move a virtual entry.

cp(from: string, to: string): Promise<void>

Copy a virtual entry.

mkdir(path: string): Promise<void>

Create a virtual directory.

rmdir(path: string, recursive: boolean): Promise<void>

Remove a virtual directory.

rename(path: string, newName: string): Promise<void>

Rename a virtual entry (last path component only).


Querying
exists(path: string): Promise<boolean>

Check whether a virtual path exists.

stat(path: string): Promise<StatResult>

Get metadata for a virtual path.

readBytes(path: string): Promise<Buffer>

Read the raw bytes of a file at the given virtual path.

rglob(pattern: string): Promise<Array<string>>

Match virtual paths against a glob pattern (e.g. "/files/*.pdf", "/media/**/*.png"). Returns an array of matching virtual paths.

exportMapping(reverse?: boolean, relativeTo?: string): Promise<Record<string, string>>

Export the source-to-virtual path mapping as a plain object. Pass reverse: true to get virtual-to-source instead. relativeTo sets a base path for relativizing source paths.


Sync and status
refresh(): Promise<RefreshSummary>

Detect and apply changes from source directories.

refreshWithHashVerification(verifyHashes: boolean): Promise<RefreshSummary>

Refresh with optional SHA-256 verification. When verifyHashes is true, files whose mtime and size are unchanged are additionally verified via SHA-256 to catch silent modifications.

status(): Promise<StatusResult>

Get a summary of the current workspace state.


Snapshots
snapshot(name: string): Promise<SnapshotWorkspace>

Create a named CoW snapshot of the current workspace.

openSnapshot(name: string): Promise<SnapshotWorkspace>

Open an existing named snapshot.

listSnapshots(): Promise<Array<string>>

List all snapshot names.

destroySnapshot(name: string): Promise<void>

Destroy a named snapshot.


SnapshotWorkspace

A snapshot is a CoW fork of a Workspace. Writes to a snapshot are isolated and do not affect the base workspace.

All methods are async.

exists(path: string): Promise<boolean>
stat(path: string): Promise<StatResult>
readBytes(path: string): Promise<Buffer>
write(path: string, content: Buffer): Promise<void>
exportMapping(reverse?: boolean, relativeTo?: string): Promise<Record<string, string>>
destroy(): Promise<void>

write materializes a copy-on-write file in the snapshot. The base workspace is unaffected.

destroy removes all snapshot files from disk.


Result types

interface MapSummary {
  entriesAdded: number
  reflinked: number
  copied: number
  symlinked: number
  dirsCreated: number
  errors: number
}

interface BatchMapSummary {
  entriesAdded: number
  reflinked: number
  copied: number
  symlinked: number
  dirsCreated: number
  errors: Array<Array<string>>
}

interface UnmapSummary {
  entriesRemoved: number
}

interface RefreshSummary {
  added: number
  refreshed: number
  removed: number
  errors: number
}

interface StatResult {
  virtualPath: string
  sourcePath: string
  sizeBytes: number
  mtimeNs: number
  entryType: string
  materialized: boolean
}

interface StatusResult {
  totalEntries: number
  sourceRoots: number
  materializedRoot: string
  lastUpdatedEpochSecs: number
}

Examples

Map a directory and read files

import { Workspace } from '@nomadamas/agentdir'

const ws = Workspace.init('./workspace')           // sync
const summary = await ws.map('./team-files', '/files') // async
console.log(`Mapped ${summary.entriesAdded} entries`)

const bytes = await ws.readBytes('/files/q1-report.txt')
console.log(bytes.toString())

await ws.mv('/files/q1-report.txt', '/reports/q1-report.txt')       // original files untouched

Snapshots with isolated writes

import { Workspace } from '@nomadamas/agentdir'

const ws = Workspace.init('./workspace')
await ws.map('./team-files', '/files')

const snap = await ws.snapshot('experiment')
await snap.write('/files/q1-report.txt', Buffer.from('snapshot-only draft'))

// The base workspace is unaffected:
const original = await ws.readBytes('/files/q1-report.txt')
const modified  = await snap.readBytes('/files/q1-report.txt')

console.log(original.toString()) // original content
console.log(modified.toString()) // snapshot-only draft

await snap.destroy()

Glob and export mapping

import { Workspace } from '@nomadamas/agentdir'

const ws = Workspace.open('./workspace')

const pdfFiles = await ws.rglob('/files/**/*.pdf')
console.log(pdfFiles) // ['/files/reports/q1.pdf', '/files/contracts/vendor.pdf', ...]

const mapping = await ws.exportMapping()
// { '/files/reports/q1.pdf': '/absolute/path/to/team-files/reports/q1.pdf', ... }

Supported Platforms

Prebuilt binaries are provided for:

| Platform | Architecture | |---|---| | macOS | x86_64 (Intel) | | macOS | aarch64 (Apple Silicon) | | Windows | x86_64 (MSVC) | | Linux | x86_64 (GNU) | | Linux | x86_64 (musl / Alpine) |

On other platforms, you'll need a Rust toolchain to build from source.


Related

This is the official Node.js binding for the agentdir project. The main repository also includes a CLI and a Rust library:

https://github.com/NomaDamas/agentdir


License

MIT