Skip to main content
Glama

wf — WorkFlowy CLI

  ╦ ╦╔═╗╦═╗╦╔═╔═╗╦  ╔═╗╦ ╦╦ ╦
  ║║║║ ║╠╦╝╠╩╗╠╣ ║  ║ ║║║║╚╦╝
  ╚╩╝╚═╝╩╚═╩ ╩╚  ╩═╝╚═╝╚╩╝ ╩

wf is the WorkFlowy command-line and agent toolkit: a local-first CLI, cache, automation layer, and MCP server that lets humans, scripts, and AI agents safely read, search, organize, and edit a WorkFlowy account.

Install in one command

macOS and Linux

Copy and paste this into your terminal:

curl -fsSL https://github.com/rodolfo-terriquez/workflowy-cli/releases/latest/download/install.sh | bash

Windows PowerShell

Copy and paste this into PowerShell:

irm https://github.com/rodolfo-terriquez/workflowy-cli/releases/latest/download/install.ps1 | iex

Then connect your WorkFlowy account:

wf login
wf cache:sync
wf doctor

The interactive login prompt hides the key. For automation, pipe it over stdin instead of placing it in shell history:

printf %s "$WORKFLOWY_API_KEY" | wf login --stdin

That’s it. The installer auto-detects your OS and CPU architecture, downloads the latest prebuilt binary, installs wf, and adds it to your PATH when possible.

This project is WorkFlowy-native:

  • everything is a node in a single tree

  • @targets are WorkFlowy bookmarks or built-in locations

  • reads are cache-first through local SQLite

  • writes go through the WorkFlowy APIs

  • agent mode returns machine-readable JSON and structured errors

Related MCP server: WorkFlowy MCP Server

Status

Current version: 3.3.0

Implemented today:

  • cache-first reads, search, path lookup, and subtree context

  • smart search with FTS, fuzzy fallback, and optional AI reranking

  • todos, tags, history, templates, bulk operations

  • REPL shell, shell completions, clipboard copy support, command aliases

  • multi-account config

  • watch daemon, webhooks, workflows, MCP server

  • compiled binary build via Bun

Install details

To install a specific version or custom location:

curl -fsSL https://github.com/rodolfo-terriquez/workflowy-cli/releases/latest/download/install.sh | \
  WF_VERSION=v3.3.0 WF_INSTALL_DIR="$HOME/.local/bin" bash

Build from source

Requires Bun.

git clone https://github.com/rodolfo-terriquez/workflowy-cli.git
cd workflowy-cli
bun install
bun run build

That produces a standalone binary at dist/wf.

If you installed wf from this git checkout, you can update it later with:

wf self:update

You can also run it directly from source:

bun run cli/wf.ts --help

Quick Start

# authenticate
wf login

# sync the local cache
wf cache:sync

# inspect your tree
wf node:read @inbox
wf search "campaign"
wf targets

Quick Demos

wf login
wf cache:sync
wf search "weekly review"
wf node:read @inbox --depth 2
wf node:add @inbox "Follow up with Jane" --type todo
wf ai:propose "Clean up my inbox and group related items"
wf ai:preview
wf ai:apply
wf mcp

For agent workflows:

wf search "project roadmap" --agent
wf node:read @today --depth 3 --agent
wf doc:edit @inbox < nested-outline.json --agent

Command Surface

Node commands

wf node:read [target]
wf node:add <target> <text>
wf node:move <node> <target>
wf node:complete <node>
wf node:update <node>
wf node:delete <node>
wf node:find <path-or-name>
wf node:context <target>
wf node:todos
wf node:bulk complete|delete|move
wf node:template list|save|apply|delete
wf node:export <target>
wf doc:edit <root> < ops.json

Top-level commands

wf search <query>
wf tags
wf targets
wf history
wf batch
wf doc:edit <root> < ops.json
wf mcp
wf completions install
wf doctor
wf login
wf self:update

Cache, AI, automation, and account commands

wf cache:sync
wf cache:diff

wf ai:propose <instruction>
wf ai:preview [id]
wf ai:apply [id]
wf ai:reject [id]
wf ai:list

wf watch:start
wf watch:stop
wf watch:status

wf webhook:create
wf webhook:list
wf webhook:delete <id>
wf webhook:test <id>

wf workflow:list
wf workflow:create <name>
wf workflow:run <name>

wf config:get <key>
wf config:set <key> <value>
wf config:alias set|list|remove

wf account:list
wf account:switch <name>
wf account:current

Common Usage

Read and navigate

wf node:read @today --depth 4
wf node:read "@inbox/Projects" --live
wf node:find "campaign 94"
wf node:context @today
wf node:export @today --format markdown
wf search "campaign"
wf search "campain 94"
wf search "pricing bug" --smart
wf search "launch" --target "@inbox/Projects"
wf search "q2" --format json

Search behavior:

  • tier 1: SQLite FTS

  • tier 2: fuzzy fallback

  • tier 3: optional AI reranking with --smart

Todos, tags, and history

wf node:todos --target @today
wf node:todos --completed --since 7d
wf tags --target @today
wf history --limit 50

Write operations

wf node:add @inbox "Draft Q3 plan" --type todo
wf node:move "@today/Fix release notes" @inbox
wf node:complete "@inbox/Follow up with vendor"
wf node:update "@inbox/Follow up with vendor" --text "Follow up with billing vendor" --note "Waiting on response"
wf node:delete "Old scratch item"

Templates and bulk operations

wf node:template save standup --from @today
wf node:template apply standup --to @inbox

wf node:bulk complete --filter "type:todo completed:false" --target @today
wf node:bulk move --filter "tag:#archive" --to @inbox --dry-run

Nested outline edits

WorkFlowy is an outline, so content that has sections, subpoints, or related bullets should usually be written as child nodes, not packed into notes. Use notes for metadata or true note fields. For nested outline content, prefer doc:edit with nested c children; it creates the whole shape in one API call and maps directly to the MCP edit_doc tool.

Use batch for flat grouped operations such as several independent adds, moves, completes, or deletes. batch converts Markdown-style text to WorkFlowy rich text, but it does not expand indented markdown into child bullets.

cat > nested-outline.json <<'JSON'
[
  {
    "op": "insert",
    "under": "@inbox",
    "items": [
      {
        "n": "Project brief",
        "l": "h2",
        "c": [
          {
            "n": "Draft outline",
            "c": [
              { "n": "Problem" },
              { "n": "Proposed approach" },
              { "n": "Open questions" }
            ]
          },
          {
            "n": "Source links",
            "c": [
              { "n": "https://workflowy.com" }
            ]
          }
        ]
      }
    ],
    "position": "top"
  }
]
JSON

wf doc:edit @inbox < nested-outline.json
wf doc:edit @inbox < nested-outline.json --agent

doc:edit also supports insert-after, richer line types, layout changes, updates, moves, and deletes.

Batch mode

wf batch reads a JSON array from stdin and executes grouped operations.

echo '[
  {"op":"add","text":"Item 1","to":"@inbox"},
  {"op":"complete","ref":"abc123def456"},
  {"op":"move","ref":"def456abc123","to":"@today"}
]' | wf batch

Targets and Paths

Commands that accept a target support:

  • built-in locations like @inbox, @today, @tomorrow, @calendar, @next-week

  • user bookmarks returned by wf targets

  • raw node IDs

  • path traversal such as @today/Meetings/Launch review

Examples:

wf node:read "@today/Meetings"
wf node:complete "@inbox/Buy groceries"
wf node:move "@today/Fix bug" @inbox

Cache Model

wf cache:sync stores a local SQLite copy of your tree and powers fast reads.

wf cache:sync
wf cache:sync --status
wf cache:sync --watch
wf cache:sync --stop
wf cache:diff --since 30m

Most read commands use the cache automatically. node:read and search can bypass it with --live.

Rate Limiting

wf now self-throttles WorkFlowy API traffic by default:

  • 45 requests per minute across normal API calls

  • 65 seconds minimum between full nodes-export calls such as wf cache:sync and wf watch:start

You can adjust those defaults if needed:

wf config:get api.rateLimit.requestsPerMinute
wf config:get api.rateLimit.exportMinIntervalSeconds

wf config:set api.rateLimit.requestsPerMinute 50
wf config:set api.rateLimit.exportMinIntervalSeconds 70

REPL, Completions, and Clipboard

Run wf with no arguments to enter the interactive shell.

Features:

  • command history

  • tab completion for commands, @targets, and common flags

  • alias expansion

wf
wf> node:read @today
wf> search "campaign"
wf> exit

Shell completions:

wf completions install
wf completions install --shell zsh
wf completions install --shell fish

Git-based install update:

wf self:update
wf self:update --check

Many output commands also support --copy:

wf node:read @today --copy
wf search "launch" --copy
wf tags --copy

AI Commands

ai:propose generates a structured proposal using an LLM. Review it, then apply or reject it.

wf ai:propose "move uncompleted todos from @today to @tomorrow"
wf ai:list
wf ai:preview
wf ai:apply
wf ai:reject

LLM config:

printf %s "$OPENROUTER_API_KEY" | wf config:set llm.apiKey --stdin
wf config:set llm.model google/gemini-flash-2.5

Other providers:

# Any OpenAI-compatible chat-completions provider
wf config:set llm.provider openai-compatible
wf config:set llm.baseUrl https://api.openai.com/v1
printf %s "$PROVIDER_API_KEY" | wf config:set llm.apiKey --stdin
wf config:set llm.model <model-id>

# Anthropic Messages API
wf config:set llm.provider anthropic
printf %s "$ANTHROPIC_API_KEY" | wf config:set llm.apiKey --stdin
wf config:set llm.model <claude-model-id>

Automation and Integration

Watch daemon

wf watch:start --interval 2m
wf watch:status
wf watch:stop

watch:start streams newline-delimited JSON events when running non-interactively. Its interval must be at least the configured export minimum, which defaults to 65s.

Webhooks

wf webhook:create --url https://example.com/hook --filter "tag:#urgent"
wf webhook:list
wf webhook:test <id>

Workflows

Workflows are YAML files stored under ~/.workflowy/workflows/.

wf workflow:create daily-review
wf workflow:list
wf workflow:run daily-review

MCP server

wf mcp
wf mcp --port 3399

wf mcp supports stdio transport by default and loopback-only Streamable HTTP at /mcp when --port is provided, with the legacy SSE endpoints retained for compatibility. HTTP mode binds to 127.0.0.1; set WORKFLOWY_MCP_AUTH_TOKEN to require Authorization: Bearer <token> on every HTTP request. --tools is enforced for both discovery and direct tool calls.

MCP client config example:

{
  "mcpServers": {
    "workflowy": {
      "command": "/absolute/path/to/wf",
      "args": ["mcp"]
    }
  }
}

Run wf login and wf cache:sync first. The MCP server uses the same local config and cache as the CLI.

Start an account-pinned server with wf --account work mcp, or pass the optional account field on any MCP tool call to select a configured account per operation. This allows one agent session to read from one account and write to another while each account keeps its own cache.

MCP exposes friendly tools for common tasks (read, search, add, batch) plus edit_doc for structured nested outline edits. Prefer edit_doc when writing sections, subpoints, or multi-bullet content; use batch for flat grouped changes. edit_doc maps back to the CLI doc:edit command, so the CLI remains the canonical implementation.

MCP instructions

wf mcp always sends built-in bootstrap instructions during MCP initialize so agents know how to use the server well. On first MCP use for an account, it will also try a best-effort cache warmup when the active account has no usable cache yet, or when a configured instructions node cannot be resolved from cache.

For clickable WorkFlowy date chips, agents must use literal time tags in node text or notes, for example <time startYear="2026" startMonth="6" startDay="3">Jun 3, 2026</time>. startYear must be 4 digits; startMonth and startDay must be numeric without leading zeroes. Plain-text dates will not render as chips.

You can also append your own custom instructions from a WorkFlowy node or subtree:

wf config:set mcp.instructionsNode "@inbox/Agent instructions"
# or use a stable node id instead of a path
wf config:set mcp.instructionsNode <node-id>

Those instructions are loaded from the local cache when the MCP session starts, so run wf cache:sync after editing that subtree if you want the latest content reflected in new MCP sessions.

Agent Mode

Use --agent for JSON output. Agent mode is also enabled when:

  • WF_AGENT=1

  • CI=true

  • TERM=dumb

Examples:

wf node:read @inbox --agent
wf search "campaign" --agent
wf node:todos --agent

Typical response shapes:

Read-oriented output

{
  "meta": {
    "command": "node:read",
    "wf_version": "3.3.0"
  },
  "node": {},
  "children": []
}

List/query output

{
  "meta": {
    "command": "search",
    "wf_version": "3.3.0"
  },
  "nodes": []
}

Write/status output

{
  "meta": {
    "command": "node:add",
    "wf_version": "3.3.0"
  },
  "message": "..."
}

Errors

{
  "error": {
    "code": "node_not_found",
    "message": "Node not found",
    "hint": "Run `wf cache:sync` first."
  }
}

Configuration

Config is stored under ~/.workflowy/config.json. On Unix-like systems, wf keeps the directory private (0700) and the config file, which may contain API keys, at 0600. Sensitive values are redacted by config:get unless --show-secret is explicitly provided.

Common keys:

wf config:get llm.provider
wf config:get llm.model
wf config:set llm.provider openrouter
wf config:set llm.model google/gemini-flash-2.5
printf %s "$LLM_API_KEY" | wf config:set llm.apiKey --stdin
wf config:set llm.baseUrl <openai-compatible-base-url>

Aliases:

wf config:alias set today-todos "node:todos --target @today"
wf config:alias list
wf config:alias remove today-todos

Accounts:

wf login --account personal
wf login --account work
wf account:list
wf account:switch work
wf account:current

Each account keeps its own SQLite cache under ~/.workflowy/db/accounts/. Switching accounts no longer discards another account's synced tree or requires an immediate re-sync.

Use --account <name> to select an account for one command without changing the configured default:

wf --account personal cache:sync
wf --account work cache:sync
wf cache:sync --all-accounts

wf --account personal read @projects --format json
wf --account work add @inbox "Follow up from personal account"

This makes cross-account automations explicit: read from one retained cache, then write with the destination account's token. Account-qualified sync and watch daemons also use separate PID files, so multiple accounts can stay warm concurrently.

Development

bun install
bun run typecheck
bun run release:check
bun test
bun run build
bun run smoke

Helpful local checks:

./dist/wf --help
./dist/wf self:update --check
./dist/wf doctor
./dist/wf cache:sync --status --agent

CI runs those core checks on Linux, macOS, and Windows on push and pull request.

Release steps are documented in RELEASE.md.

License

MIT

Project Layout

cli/
  wf.ts                 entry point and command registration
  agent.ts              agent-mode detection
  targets.ts            @target resolution
  commands/             command implementations
  shared/               cache, config, clipboard, history, REPL, API helpers
  output/               JSON and outline formatters
dist/
  wf                    compiled binary after bun run build
A
license - permissive license
-
quality - not tested
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/gijs-epping/workflowy-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server