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

freelance-mcp

v1.3.0

Published

Graph-based workflow enforcement for AI coding agents

Readme

Freelance

Graph-based workflow enforcement and persistent memory for AI coding agents.

Define structured workflows in YAML. Enforce them at tool boundaries via MCP. Build a persistent knowledge graph that grows with every query and knows when its sources have changed.

Quick Start

Claude Code (plugin — recommended)

/plugin marketplace add duct-tape-and-markdown/freelance
/plugin install freelance@freelance-plugins

This installs the MCP server, hooks, and skills automatically. Run /freelance:freelance-init to scaffold your first workflow.

Other clients (Cursor, Windsurf, Cline)

npm install -g freelance-mcp
cd /path/to/your/project
freelance init

Workflows

Workflows are directed graphs defined in YAML. The agent calls MCP tools to traverse them — freelance_start to begin, freelance_advance to move between nodes. Gate nodes block advancement until conditions are met. State lives server-side, so it survives context compaction.

id: my-workflow
version: "1.0.0"
name: "My Workflow"
startNode: start

context:
  taskDone: false

nodes:
  start:
    type: action
    description: "Do the work"
    instructions: "Complete the task and set context.taskDone = true."
    edges:
      - target: review
        label: done

  review:
    type: gate
    description: "Review the work"
    validations:
      - expr: "context.taskDone == true"
        message: "Task must be completed before review."
    edges:
      - target: complete
        label: approved

  complete:
    type: terminal
    description: "Workflow complete"

Node types: action (do work), decision (pick a route), gate (enforce conditions), wait (pause for external signals), terminal (end state).

Subgraph composition — Nodes can push into child workflows with scoped context. contextMap passes parent values in, returnMap passes child values back. The engine manages a stack, so subgraphs can nest.

Expression evaluator — Edge conditions and validations use a safe expression language (context.x == 'value', context.count > 0, boolean operators, nested property access). Validated at load time, evaluated at runtime.

onEnter hooks — Any node can declare onEnter: [{ call, args }] hooks that run before the agent sees the node. call resolves to either a built-in hook (memory_status, memory_browse) or a local script path (./scripts/fetch-context.js). Hooks receive resolved args, live context, and the memory store, and return a plain object of context updates. Strict-context enforcement still applies. Per-hook timeout defaults to 5000ms, configurable via hooks.timeoutMs in config.yml.

Trust model for hook scripts. Local script hooks execute with full Node privileges in the host process on graph load and node arrival — treat a workflow file that references a local script like a package.json scripts block: trust it at the same level you trust the rest of the repo. Do not load workflow graphs from untrusted sources.

Memory

Freelance includes a persistent knowledge graph backed by SQLite. The agent reads source files, reasons about them, and writes atomic propositions about 1-2 entities. Every proposition records which source files produced it and their content hashes at the time of compilation.

When you query memory, it checks whether the source files on disk still match. Match = valid knowledge. Mismatch = stale. The knowledge base grows with every query but never serves something that's silently out of date.

How it works

Compilation — The agent reads source files, then emits propositions: self-contained claims in natural prose, each about 1-2 named entities. Propositions are deduplicated by content hash. Entity references are resolved by exact match, normalized match, or creation.

Recollection — When a new question comes in, the agent searches existing memory, reads the provenance sources, and identifies the delta — what the sources say about the question that existing propositions don't cover. Only that gap gets compiled. Each query makes the knowledge base denser from a different angle, without re-deriving what's already there.

Source provenance — Every proposition records the specific source files it was derived from, their content hashes, and their mtime at emit time. Validity is checked per-proposition on read: if any of a prop's source files have drifted, the prop is marked stale. Stale propositions aren't hidden — they're returned with a confidence signal so the agent can decide whether to re-verify.

Git branching for free — Switch branches, files change on disk, different propositions light up as valid or stale. Merge the branch, files converge, knowledge converges. No scope model, no branch tracking — just hash checks on read.

Configuration

Memory is enabled by default with zero configuration. The database is stored at .freelance/memory/memory.db.

To customize, add memory settings to your .freelance/config.yml (see Configuration below).

Collections partition propositions into named buckets. All read tools accept an optional --collection filter. Propositions are deduplicated within a collection — the same claim can exist in multiple collections.

Two sealed workflows are auto-injected: memory:compile (read sources, emit propositions, evaluate coverage) and memory:recall (recall, source, compare, fill delta, evaluate). These can be referenced as subgraphs in your own workflows.

Memory tools

Write tools (gated by an active memory:compile or memory:recall traversal):

| Tool | Description | |------|-------------| | memory_emit | Write propositions with required per-file source attribution |

Read tools (available anytime):

| Tool | Description | |------|-------------| | memory_browse | Find entities by name or kind | | memory_inspect | Full entity details with propositions, neighbors, and deduped source files | | memory_by_source | All propositions derived from a specific source file | | memory_related | Entity graph navigation — co-occurring entities with connection strength | | memory_search | Full-text search across proposition content (FTS5) | | memory_status | Knowledge graph health: total, valid, stale counts |

Workflow Tools

| Tool | Description | |------|-------------| | freelance_list | Discover available workflow graphs and active traversals | | freelance_start | Begin traversing a graph | | freelance_advance | Move to the next node via a labeled edge | | freelance_context_set | Update session context without advancing | | freelance_inspect | Read-only introspection (position, history, or full graph) | | freelance_reset | Clear traversal and start over | | freelance_guide | Authoring guidance for writing graphs | | freelance_distill | Distill a task into a new workflow | | freelance_validate | Validate graph definitions | | freelance_sources_hash | Compute hashes for source binding | | freelance_sources_check | Verify source file availability | | freelance_sources_validate | Validate source integrity against expectations |

Configuration

Freelance uses two config files in .freelance/, both with the same schema:

| File | Purpose | Committed? | |------|---------|-----------| | config.yml | Team-shared settings | Yes | | config.local.yml | Machine-specific overrides (plugin hooks) | No (gitignored) |

General precedence: CLI flags > env vars > config.local.yml > config.yml > defaults. Per-field surface:

| Field | CLI flag | Env var | config.yml | Notes | |---|---|---|---|---| | workflows | --workflows (repeatable) | FREELANCE_WORKFLOWS | ✓ (array, concatenates across files) | User/project dirs cascade automatically | | memory.enabled | --memory / --no-memory | — | ✓ | CLI flag always wins | | memory.dir | --memory-dir | — | ✓ | Default: .freelance/memory/ | | memory.collections | — | — | ✓ (concatenates) | Must be declared before emit | | maxDepth | --max-depth | — | ✓ | Default: 5 | | hooks.timeoutMs | — | — | ✓ | Config-only. Default: 5000 | | sourceRoot | --source-root | — | — | Computed from graphsDir if omitted |

# .freelance/config.yml
workflows:                          # Additional workflow directories
  - ../shared-workflows/

memory:
  enabled: true                     # Default: true. Set false to disable.
  dir: /path/to/persistent/dir      # Override memory.db location (default: .freelance/memory/)
  collections:                      # Partition propositions into named buckets
    - name: default
      description: General project knowledge
      paths: [""]
    - name: spec
      description: Feature specifications
      paths: ["docs/", "specs/"]

maxDepth: 5                         # Max subgraph nesting depth. CLI --max-depth overrides.

hooks:
  timeoutMs: 5000                   # Per-hook timeout for onEnter hooks. Default 5000.

Merge rules: arrays (workflows, collections) concatenate across files. Scalars use highest-precedence value.

Use freelance config show to see the resolved configuration and which files contributed.

Use freelance config set-local <key> <value> to modify config.local.yml programmatically (used by plugin hooks).

Workflow directories

Workflows load automatically from these directories (no flags needed):

  1. ./.freelance/ — project-level workflows
  2. ~/.freelance/ — user-level workflows (shared across projects)
  3. Additional directories listed in config.yml or config.local.yml workflows:

Subdirectories are scanned recursively. Later directories shadow earlier ones by graph ID. You can also specify directories explicitly:

freelance mcp --workflows ./my-workflows/

.freelance/ directory layout

.freelance/
├── config.yml           # team-shared config (committed)
├── config.local.yml     # machine-specific overrides (gitignored)
├── *.workflow.yaml      # source artifacts — your graph definitions
├── .gitignore           # auto-generated; covers runtime dirs below
├── memory/              # runtime (gitignored)
│   ├── memory.db        #   persistent knowledge graph
│   ├── memory.db-shm    #   SQLite shared-memory sidecar
│   └── memory.db-wal    #   SQLite write-ahead log
└── traversals/          # runtime (gitignored)
    └── tr_*.json        #   one file per active traversal

Source artifacts and runtime artifacts coexist as peers; the lifecycle distinction is maintained via .gitignore, not directory nesting. Freelance auto-generates .freelance/.gitignore on first write.

If you're upgrading from a pre-1.3 install that used a .state/ subdirectory, the layout is migrated automatically on the next run — memory.db is moved into memory/, traversals/ moves up one level, the vestigial state.db from the earlier architecture is removed, and the empty .state/ is cleaned up. The migration logs one line to stderr and is best-effort; on failure you'll see an actionable message.

Resetting memory

Memory is content-addressable — everything in memory.db can be rebuilt on demand from source files. If you hit a schema incompatibility after a version bump, or just want a clean slate:

freelance memory reset --confirm

Deletes memory.db and its sidecars without opening the database, so it works even when the current binary refuses to load the old schema. Next run re-initializes a fresh store.

CLI flags:

  • --memory-dir <path> — override memory.db location (highest priority)
  • --no-memory — disable memory entirely

Environment variables:

  • FREELANCE_WORKFLOWS_DIR — colon-separated list of workflow directories (bypasses auto-scan)

MCP setup

Run freelance init to auto-detect your client and generate the config. Supports Claude Code, Cursor, Windsurf, and Cline.

Manual configuration (e.g., .mcp.json for Claude Code):

{
  "mcpServers": {
    "freelance": {
      "command": "freelance",
      "args": ["mcp"]
    }
  }
}

CLI

Every MCP tool has a CLI equivalent. Commands operate directly on the local state store — no daemon or MCP client required.

# Setup
freelance init                            # Interactive project setup
freelance validate <dir>                  # Validate graph definitions
freelance visualize <file>                # Render graph as Mermaid or DOT

# Traversals
freelance status                          # Show loaded graphs and active traversals
freelance start <graphId>                 # Begin a workflow traversal
freelance advance [edge]                  # Move to next node via edge label
freelance context set <key=value...>      # Update traversal context
freelance inspect [traversalId]           # Read-only introspection
freelance reset [traversalId] --confirm   # Clear a traversal

# Memory
freelance memory status                   # Proposition and entity counts
freelance memory browse                   # Find entities by name or kind
freelance memory inspect <entity>         # Full entity details
freelance memory search <query>           # Full-text search
freelance memory related <entity>         # Co-occurring entities
freelance memory by-source <file>         # Propositions from a source file
freelance memory register <file>          # Hash a file (stateless echo)
freelance memory emit <file>              # Write propositions from JSON

# Graph tools
freelance guide [topic]                   # Authoring guidance
freelance distill                         # Get a distill prompt
freelance sources hash <paths...>         # Compute source hashes
freelance sources check <sources...>      # Validate source hashes
freelance sources validate                # Validate all source bindings

# Configuration
freelance config show                     # Display resolved config with sources
freelance config set-local <key> <value>  # Modify config.local.yml

# Server
freelance mcp                             # Start MCP server
freelance completion bash|zsh|fish        # Shell completion script

Run freelance --help for full details and flags.

License

MIT