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

@ldlework/workmark

v1.4.2

Published

Most workspaces accumulate a graveyard of shell scripts, Makefiles, npm scripts, and "just run this" tribal knowledge. Workmark lets you define these workspace operations in TypeScript and run them from:

Downloads

82

Readme

@ldlework/workmark

Most workspaces accumulate a graveyard of shell scripts, Makefiles, npm scripts, and "just run this" tribal knowledge. Workmark lets you define these workspace operations in TypeScript and run them from:

  • CLI — the wm command, with auto-generated help and argument parsing
  • VS Code — a dashboard extension with auto-generated forms for every command/parameter
  • AI Agents — a built-in MCP server so any MCP client can discover and run your commands

Quick start

Install

pnpm add @ldlework/workmark

Write a command

Create commands in .wm/commands/. Subdirectories become groups in the CLI, dashboard, and MCP server. The filename becomes the command name, a leading JSDoc becomes the description, and a string return is executed as a shell command in the workspace root.

// .wm/commands/art/sprites.ts
/** Pack sprite sheets from raw assets */
import { cmd } from "@ldlework/workmark/define";
import { z } from "zod";

export default cmd({
  args: {
    target: z.enum(["all", "characters", "terrain", "ui"]).default("all"),
  },
  flags: {
    watch: z.boolean().default(false),
  },
  handler: ({ target, watch }) =>
    `./tools/pack-sprites.sh ${target}${watch ? " --watch" : ""}`,
});

For a bare shell alias:

// .wm/commands/build.ts
/** Build the project */
import { cmd } from "@ldlework/workmark/define";
export default cmd({ handler: () => "cargo build" });

Run it

wm sprites                      # pack all sprite sheets
wm sprites characters --watch   # pack characters, rebuild on change
wm --help                       # list all commands
wm sprites --help               # per-command help

Projects

If your workspace contains multiple packages or services, you can define projects so that commands can discover and operate on them. Drop a wm.ts in any project directory:

// packages/api/wm.ts
import { defineProject } from "@ldlework/workmark/define";

export default defineProject({
  name: "api",
  tags: ["backend"],
});
// packages/web/wm.ts
import { defineProject } from "@ldlework/workmark/define";

export default defineProject({
  name: "web",
  tags: ["frontend"],
});

Workmark recursively discovers all wm.ts files from the workspace root (respecting .gitignore). Projects are available to commands via the workspace object — query them by name with workspace.get("api"), by tag with workspace.withTag("backend"), or by capability with workspace.withCapability("deploy").

Capabilities

Capabilities are structured metadata attached to projects. A project declares what it supports, and commands filter by it — this keeps project-specific config out of your command files. For example, marking a project with deploy: true advertises that it has the scripts/deploy.sh script that the deploy command expects.

// packages/api/wm.ts
export default defineProject({
  name: "api",
  tags: ["backend"],
  capabilities: { deploy: true },
});
// packages/web/wm.ts
export default defineProject({
  name: "web",
  tags: ["frontend"],
  capabilities: { deploy: true },
});

workspace.withCapability("deploy") returns both projects. Capabilities can also carry structured data:

export default defineProject({
  name: "api",
  capabilities: {
    deploy: true,
    build: { targets: ["esm", "cjs"] },
  },
});

Commands read this with project.capability<{ targets: string[] }>("build"). This pattern works for anything — build targets, test runners, linting rules.

Dynamic commands

A dynamic command receives the workspace and builds its schema from project metadata. Here, the deploy command finds all projects with the deploy capability and populates its argument from their names:

// .wm/commands/deploy.ts
/** Deploy a project */
import { z } from "zod";
import type { DynamicCommandDef } from "@ldlework/workmark/types";

export default {
  factory: (workspace) => {
    const names = workspace.withCapability("deploy").map((p) => p.name);
    return {
      args: { project: z.enum(names as [string, ...string[]]) },
      handler: ({ project }) => {
        const p = workspace.get(project as string);
        return { content: [{ type: "text", text: `deploying ${p.name}` }] };
        // or: return `./scripts/deploy.sh` (with cwd resolved to workspace root by default)
      },
    };
  },
} satisfies DynamicCommandDef;
wm deploy api
wm deploy web

The CLI help, VS Code dropdowns, and MCP tool schema all show exactly the valid project names — no impossible combinations, no runtime validation needed.

Features

CLI

The wm binary auto-discovers your commands and generates help text, argument parsing, and type coercion.

$ wm --help

Usage: wm <command> [args...]

Commands:
  sprites         Pack sprite sheets from raw assets
  deploy          Deploy a project to an environment
  db:migrate      Run database migrations

Run wm <command> --help for details on a specific command.

Arguments support positional args and named flags:

wm sprites characters              # positional (from args)
wm sprites characters --watch      # positional + flag
wm deploy api                      # dynamic command with project arg
wm deploy --project api            # everything works as flags too

VS Code dashboard

Install the workmark-vsc extension. It adds a Workspace panel to the activity bar showing all your commands grouped by category with auto-generated forms:

  • Enum fields become dropdowns
  • Booleans become checkboxes
  • Numbers get validated inputs with min/max
  • Required fields are enforced before execution
  • Double-click any command to jump to its source file

Commands run in the integrated terminal, so you get full color output and interactivity.

MCP server

Workmark includes a built-in Model Context Protocol server. Every command you define is automatically exposed as an MCP tool, which means AI assistants like Claude can discover and invoke your workspace commands.

// claude_desktop_config.json or .mcp.json
{
  "mcpServers": {
    "workspace": {
      "command": "node",
      "args": ["./node_modules/@ldlework/workmark/dist/index.js"]
    }
  }
}

Once connected, your assistant can run wm deploy api the same way you do — with full schema validation and typed responses.

Project structure

your-workspace/
├── .wm/
│   └── commands/
│       ├── deploy.ts         # Root-level command
│       ├── art/
│       │   └── sprites.ts    # Grouped under "Art"
│       └── db/
│           └── migrate.ts    # Grouped under "Db"
├── packages/
│   ├── api/
│   │   └── wm.ts             # Project definition
│   └── web/
│       └── wm.ts
  • wm.ts — project definitions, discovered recursively
  • .wm/commands/**/*.ts — command files, grouped by directory name

API reference

Defining

import { defineProject } from "@ldlework/workmark/define";

Helpers

import { ok, fail, exec, execAsync } from "@ldlework/workmark/helpers";

ok(data)                     // Wrap data in a success CallToolResult
fail(error)                  // Wrap error in an error CallToolResult
exec(cmd, { cwd })           // Synchronous shell exec, returns CallToolResult
execAsync(cmd, { cwd })      // Async shell exec, returns Promise<CallToolResult>
execRaw(cmd, { cwd })        // Synchronous shell exec, returns string (throws on error)
execAsyncRaw(cmd, { cwd })   // Async shell exec, returns Promise<string> (throws on error)

Loading

import { loadWorkspace } from "@ldlework/workmark/workspace";
import { loadCommands } from "@ldlework/workmark";

const workspace = await loadWorkspace();
const commands = await loadCommands(workspace);

License

MIT