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

@arcmantle/forge

v0.5.2

Published

Universal repo script runner

Readme

forge

Universal repo script runner. Write scripts in Go, TypeScript, or C# — forge compiles, caches, and runs them with zero config.

Install

# Global install
npm install -g @arcmantle/forge

# Or run directly
npx @arcmantle/forge

Quick Start

# Scaffold a new project
forge init

# Add a script
forge add hello

# Run it
forge hello

forge init creates a forge.yaml manifest and .forge/ directory with helpers and schema. Scripts go in .forge/scripts/<name>/.

forge.yaml

Commands are defined in forge.yaml at the root of your project. Each command either points to a script file or composes other commands.

# yaml-language-server: $schema=.forge/forge-schema.json
commands:
  install:
    description: "Install all dependencies"
    script: .forge/scripts/install/install.go

  build:
    description: "Build the project"
    script: .forge/scripts/build/build.ts

  deploy:
    description: "Build then deploy"
    run:
      - build
      - deploy-prod

  ci:
    description: "Run linting and tests in parallel"
    run:
      - parallel: [lint, test]

The schema comment on line 1 enables IDE autocompletion and validation.

Command Properties

| Property | Type | Description | | --- | --- | --- | | description | string | Shown in forge --list | | script | string | Path to .go, .ts, or .cs script file | | run | array | Sequential steps — strings or parallel: [...] blocks |

A command must have either script or run, not both.

Composite Commands

Sequential steps run one after another. Parallel blocks run concurrently with prefixed, color-coded output:

commands:
  pipeline:
    run:
      - clean                           # step 1: sequential
      - parallel: [build-api, build-ui] # step 2: parallel
      - deploy                          # step 3: sequential

Forge detects cycles in composite commands and errors before execution.

Step Arguments

Steps can carry arguments — either inline in the string or via the explicit object form:

commands:
  ci:
    run:
      - "lint --fix"                     # inline args (string form)
      - command: test                    # explicit object form
        args: [--coverage, --reporter, junit]
      - parallel:
          - "build:api --production"     # inline args in parallel entries
          - build:ui

Forwarding rules:

  • Steps with explicit or inline args use those — no forwarding from the composite invocation.
  • Steps without args receive whatever CLI args were passed to the composite command (backward compatible).
  • Parallel entries follow the same rule independently — each entry either uses its own inline args or receives the composite's forwarded args.

Nested Commands

Commands with : in their name form groups. On the CLI, spaces are resolved as nested command names using greedy longest-match:

commands:
  deploy:staging:
    description: "Deploy to staging"
    script: .forge/scripts/deploy-staging/deploy-staging.go

  deploy:prod:
    description: "Deploy to production"
    script: .forge/scripts/deploy-prod/deploy-prod.go
forge deploy staging --dryrun   # resolves to deploy:staging with args [--dryrun]
forge deploy prod               # resolves to deploy:prod
forge help deploy               # lists available subcommands (staging, prod)
forge help deploy staging       # shows help for deploy:staging

forge --list groups nested commands under their prefix:

Available commands:

  clean          Remove all node_modules
  deploy
    staging      Deploy to staging
    prod         Deploy to production
  hello          Say hello

Nesting is unlimited — infra:aws:deploy is invoked as forge infra aws deploy. A group prefix (e.g. deploy) is not itself runnable unless a deploy command also exists in the manifest. Composite commands can reference nested commands by their full colon key: run: [deploy:staging].

To scaffold a nested command:

forge add deploy:staging --go   # creates .forge/scripts/deploy-staging/deploy-staging.go

Command Builder

Arguments are defined in code using the command builder pattern — no YAML args needed. Each script declares its own arguments with type safety, auto-generated --help, and introspection via forge help <cmd>.

Given a command invoked as:

forge greet world --shout --count 3

The CLI concepts map as follows:

| Concept | What it is | In the example | | --- | --- | --- | | Command | The top-level verb registered in forge.yaml. Forge resolves it and runs the associated script. | greet | | Arg | A required positional value — no -- prefix, identified by position. Must be provided or the script errors. | world (1st positional) | | Flag | A boolean switch — presence means true, absence means false. Never takes a value after it. | --shouttrue | | Option | A named key-value pair. The name is prefixed with -- and the next token is its value. Supports a default if omitted. | --count 3 |

Go:

package main

import "github.com/arcmantle/forge/helpers"

func main() {
    cmd := helpers.Command("greet", "Greet someone")
    name := cmd.Arg("name", "Name to greet")
    shout := cmd.Flag("shout", "Uppercase the greeting")
    count := cmd.Option("count", "Number of times", "1")
    cmd.Parse()

    helpers.Info("Hello, %s! (x%s, shout=%v)", name.Value, count.Value, shout.Value)
}

TypeScript:

import { command, info } from '#helpers';

const cmd = command('greet', 'Greet someone');
const name = cmd.arg('name', 'Name to greet');
const shout = cmd.flag('shout', 'Uppercase the greeting');
const count = cmd.option('count', 'Number of times', '1');
cmd.parse();

info(`Hello, ${name.value}! (x${count.value}, shout=${shout.value})`);

C#:

using Forge.Helpers;

var cmd = Cmd.Create("greet", "Greet someone");
var name = cmd.Arg("name", "Name to greet");
var shout = cmd.Flag("shout", "Uppercase the greeting");
var count = cmd.Option("count", "Number of times", "1");
cmd.Parse();

Log.Info($"Hello, {name.Value}! (x{count.Value}, shout={shout.Value})");

The builder provides three argument types:

| Method | Description | Parsed as | | --- | --- | --- | | Arg(name, desc) | Required positional argument | StringValue | | Option(name, desc [, default]) | Named string option (--name value) | StringValue | | Flag(name, desc) | Boolean flag (--name) | BoolValue |

Parse() handles --help / -h automatically, printing a formatted help screen and exiting. forge help <command> also works — it invokes the script with --forge-meta to retrieve argument metadata.

$ forge help greet
greet — Greet someone

Usage:
  forge greet <name> [flags]

Args:
  name    Name to greet

Flags:
  --shout           Uppercase the greeting
  --count <value>   Number of times (default: 1)

Multi-Language Scripts

All languages use top-level code — no special interface or wrapper needed.

Go

package main

import "github.com/arcmantle/forge/helpers"

func main() {
    helpers.Info("Hello from Go!")
    helpers.Exec("echo", []string{"done"}, helpers.RunOpts{})
}

Go scripts are compiled to .forge/cache/ with content-hash caching — only recompiled when the source changes.

TypeScript

import { info, exec } from '#helpers';

info('Hello from TypeScript!');
await exec('echo', ['done']);

TypeScript scripts run natively via `node` (requires Node 23.6+). The `#helpers` import maps to the generated helpers file via `package.json` subpath imports.

### C\#

```csharp
using Forge.Helpers;

Log.Info("Hello from C#!");
await Exec.Run("echo", ["done"]);
return 0;

C# scripts are compiled via dotnet publish with content-hash caching. Top-level statements — args is available as a built-in variable.

CLI Reference

forge <command> [args...]    Run a command
forge --list, -l             List available commands
forge --help, -h             Show help
forge --version, -v          Show version
forge init                   Scaffold forge.yaml and .forge/
forge add <name> [--lang]    Add a new script (go, ts, cs)
forge setup <runtime>        Add scaffolding for a runtime (go, ts, cs)
forge help <command>         Show detailed help for a command

forge init

Creates forge.yaml, .forge/ directory with helpers, schema, and project files. Scaffolding is conditional — only sets up runtimes that are installed on your system:

  • Go: go.mod, Go helpers
  • TypeScript: package.json, tsconfig.json, TS helpers, installs dependencies
  • C#: ForgeScripts.csproj, ForgeScripts.slnx, C# helpers

forge add <name>

Adds a new script with the correct boilerplate. Defaults to Go. If no forge.yaml exists in the current directory, forge bootstraps the full setup automatically — creating forge.yaml, .forge/ directory, and language support files so intellisense works immediately.

forge add deploy            # Go script
forge add deploy --ts       # TypeScript script
forge add deploy --cs       # C# script

This makes it easy to set up subdirectory-specific scripts in monorepos:

cd apps/frontend
forge add dev --ts          # creates forge.yaml, .forge/, and the dev script
forge dev                   # runs the local script
forge install               # still works — inherited from parent forge.yaml

forge setup <runtime>

Add support for a runtime to enable intellisense. If the current directory has a .forge/ directory, support files are created there. Otherwise, targets the closest manifest's project root. Idempotent — safe to run multiple times.

forge setup ts              # Add TypeScript support
forge setup cs              # Add C# support
forge setup go              # Add Go support

Fuzzy Matching

Forge uses prefix matching for command names. If deploy-prod is the only command starting with dep, running forge dep will match it.

Helpers API

All three languages provide a consistent API surface for common operations.

Args

Access raw command-line arguments passed to the script. For structured argument parsing, prefer the Command Builder instead.

// Go
args := helpers.Args()
// TypeScript
import { args } from '#helpers';
const a = args();
// C# — use Environment.GetCommandLineArgs().Skip(1)

Exec

Run commands with streaming output, optional prefixed tags, and environment variables.

Go:

helpers.Exec("pnpm", []string{"install"}, helpers.RunOpts{
    Dir:   "/path/to/workspace",
    Tag:   "frontend",
    Color: helpers.ColorCyan,
    Env:   map[string]string{"NODE_ENV": "production"},
})

output, err := helpers.ExecSimple("git", []string{"rev-parse", "HEAD"}, ".")

TypeScript:

await exec('pnpm', ['install'], {
    dir:   '/path/to/workspace',
    tag:   'frontend',
    color: ColorCyan,
    env:   { NODE_ENV: 'production' },
});

const sha = execSimple('git', ['rev-parse', 'HEAD']);

C#:

await Exec.Run("pnpm", ["install"], new RunOpts {
    Dir   = "/path/to/workspace",
    Tag   = "frontend",
    Color = Colors.Cyan,
    Env   = new() { ["NODE_ENV"] = "production" },
});

var sha = Exec.RunSimple("git", ["rev-parse", "HEAD"]);

Filesystem

// Go
helpers.FileExists("package.json")
helpers.FindDirs(".", "src/*")
helpers.FindFiles(".", "*.go")
helpers.FindDirsContaining(".", "package.json")
// TypeScript
fileExists('package.json')
findDirs('.', 'src/*')
findFiles('.', '*.go')
findDirsContaining('.', 'package.json')
// C#
Fs.FileExists("package.json")
Fs.FindDirs(".", "src/*")
Fs.FindFiles(".", "*.go")
Fs.FindDirsContaining(".", "package.json")

Logging

All languages provide info, warn, error, and success with colored prefixes.

helpers.Info("Installing %d packages", count)
helpers.Warn("Skipping %s", name)
helpers.Error("Build failed: %v", err)
helpers.Success("Deployed to %s", env)

Colors

A palette of ANSI color constants is available for tagged output:

ColorReset, ColorRed, ColorGreen, ColorYellow, ColorBlue, ColorMagenta, ColorCyan, ColorGray, ColorBrightRed

Plus a Colors array for cycling through colors in multi-stream output.

Documentation Site

Forge can serve an interactive documentation site for your project — generated from your manifest and script metadata:

forge --docs

This opens a browser with a single-page reference showing all commands, their arguments, flags, options, and composite step sequences. The data is gathered by running --forge-meta on all script commands in parallel.

Features:

  • Grouped sidebar — nested commands (:) displayed under their group prefix
  • Full argument docs — positionals, flags, and options with types and defaults
  • Composite step visualization — sequential and parallel steps with clickable cross-references
  • Search — filter commands by name or description (/ to focus)
  • Auto-close — the server shuts down when you close the browser tab

No external dependencies — the HTML is embedded in the forge binary.

Manifest Discovery

Forge walks up from the current directory looking for forge.yaml files. Commands from child manifests override parent ones, allowing hierarchical command definitions across monorepos.

repo/
  forge.yaml          # repo-wide commands
  apps/
    frontend/
      forge.yaml      # frontend-specific commands (can override parent)

Running forge build inside apps/frontend/ will use the frontend manifest's build command if defined, falling back to the repo-wide one.

Auto-Discovered Scripts

In addition to explicit forge.yaml entries, forge auto-discovers scripts from .forge/scripts/ directories. Any subdirectory in .forge/scripts/ that contains a script file matching <name>.{go,ts,cs} is automatically available as a command — no forge.yaml entry required.

repo/
  forge.yaml              # repo-wide commands
  .forge/scripts/
    install/install.go    # auto-discovered as "install"
  apps/
    frontend/
      .forge/scripts/
        dev/dev.ts        # auto-discovered as "dev" (only from apps/frontend/)

Running forge dev inside apps/frontend/ will find the auto-discovered dev script. Running forge install from anywhere under repo/ will find the repo-wide install script.

Priority rules:

  1. Explicit forge.yaml commands always override auto-discovered scripts at any level
  2. Closer directories override further ones (same as manifest discovery)
  3. Among auto-discovered scripts, the first matching extension wins (.go.ts.cs)

This is useful for monorepos where subdirectories need local scripts without cluttering the root forge.yaml.

To add scripts to a subdirectory:

cd apps/frontend
forge add dev --ts           # bootstraps forge.yaml, .forge/, and the dev script

Compilation & Caching

  • Go: Compiled to .forge/cache/<name> using go build. A SHA-256 hash of the source file is stored alongside the binary — recompilation only happens when the hash changes.
  • TypeScript: Executed directly via node with no compilation step. Node's native TypeScript support (23.6+) handles it.
  • C#: Compiled via dotnet publish -c Release to .forge/cache/cs/<name>/. Same hash-based caching as Go.

Building from Source

cd forge
go run build.go 0.1.0

This cross-compiles binaries for Linux, macOS, and Windows (amd64 + arm64) into dist/ and generates checksums.txt.

License

MIT