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

@lightward/mechanic-cli

v0.1.15

Published

Develop, preview, diff, and publish Mechanic Shopify automation tasks from local files

Downloads

2,154

Readme

Mechanic CLI

Develop Mechanic tasks locally: pull Shopify automation tasks from Mechanic, edit Liquid and docs in normal files, preview changes safely, review diffs, and publish intentionally.

Mechanic is a Shopify automation platform for teams that need flexible, code-backed workflows. Install Mechanic from the Shopify App Store at https://apps.shopify.com/mechanic, read the docs at https://learn.mechanic.dev/, or start with the CLI guide at https://learn.mechanic.dev/platform/mechanic-cli.

This CLI is built around the same task export shape used by Mechanic, so tasks move cleanly between the app, local files, version control, and pull requests.

Use it to:

  • pull tasks from Mechanic into a local repository
  • edit Liquid, Markdown, and task configuration in normal files
  • preview task changes before saving them to Mechanic
  • diff local files against the current Mechanic task
  • publish one task intentionally, with dry-run and conflict protection
  • automate single-shop task updates with optional GitHub Actions workflows

Mechanic is made by Lightward. You can also meet Lightward AI at https://lightward.com.

Requirements

  • Node 22 or newer
  • A Mechanic shop with an API token created in the Mechanic app

Install

Install from npm:

npm install -g @lightward/mechanic-cli

In interactive terminals, Mechanic occasionally checks npm and prints the update command when a newer CLI is available.

To check your installed CLI version against the latest published version:

mechanic version

Quick Start

Create an API token in Mechanic:

  1. Open the shop in Mechanic.
  2. Go to Settings -> API tokens.
  3. Create a token named for the place it will be used, like Laptop or GitHub Actions.
  4. Copy the token immediately. Mechanic only shows it once.

Initialize a local project for that Shopify shop, then paste the token when prompted:

mechanic init --shop example.myshopify.com

Choose how to start.

To bring one existing task into local files, find its remote task ID, then pull that task:

mechanic tasks list --verbose
mechanic tasks pull <remote-task-id>

To intentionally bootstrap a repo with every task in the shop:

mechanic tasks pull

To start from scratch instead, create a new blank local task. This writes a starter JSON file and matching helper directory. You can use this in any initialized CLI project; it does not require a fresh repository:

mechanic tasks new order-tagger

For most Liquid or documentation edits, use the helper directory beside the JSON file. Existing pulled tasks can be unbundled first; tasks created with tasks new already have the helper directory.

# for an existing pulled task only
mechanic tasks unbundle order-tagger

# edit tasks/order-tagger/script.liquid, docs.md, or task.json
mechanic tasks bundle order-tagger
mechanic tasks status
mechanic tasks preview order-tagger
mechanic tasks diff order-tagger
mechanic tasks publish order-tagger --dry-run
mechanic tasks publish order-tagger

For example, edit tasks/order-tagger/docs.md or tasks/order-tagger/script.liquid, bundle it, preview it, then publish only when the diff and dry-run plan look right.

For normal setup, paste the token into the masked prompt or run mechanic auth login after mechanic init. In CI, store the token as MECHANIC_API_TOKEN. The --token flag exists for controlled automation, but avoid typing secrets into shared shells because shell history or process listings can expose them.

To check whether the shop is currently busy or backlogged:

mechanic shop status

Most commands that editor integrations or agents would call support --json. Use JSON output for automation instead of parsing tables or colored text.

When a --json command fails before producing its normal output (auth problems, network failures, rate limits), it prints a JSON error envelope to stdout instead, with exit codes unchanged:

{ "error": { "message": "Mechanic API rate limit exceeded.", "status": 429, "retry_after_seconds": 7 } }

status is present when the Mechanic API rejected the request, and retry_after_seconds is present when a rate limit response includes Retry-After. With --json, stdout is always a single JSON document: the command's normal output, or this error envelope.

Commands

mechanic init --shop example.myshopify.com [--token <token>] [--api-base-url <url>] [--app-url <url>] [--force]
mechanic help [command]
mechanic version [--json]
mechanic doctor
mechanic auth login [--token <token>]
mechanic auth logout
mechanic github init [--force]
mechanic shop status [--json]
mechanic shop deprecations [task] [--json]
mechanic tasks list [--verbose] [--json]
mechanic tasks new <name> [--force] [--json]
mechanic tasks open <task>
mechanic tasks status [task] [--local] [--json]
mechanic tasks pull [--force]
mechanic tasks pull <task> [--force]
mechanic tasks pull --all [--force]
mechanic tasks preview [task] [--stdin] [--remote] [--verbose] [--json]
mechanic tasks diff <task> [--exit-code] [--json]
mechanic tasks diff --all [--exit-code] [--json]
mechanic tasks publish <task> [--force] [--dry-run] [--json]
mechanic tasks publish --all [--force] [--dry-run] [--json]
mechanic tasks unbundle <task> [--out <dir>] [--json]
mechanic tasks bundle <task|dir|file> [--out <file>] [--json]
mechanic tasks validate <task|dir> [--json]
mechanic globals list [--json]
mechanic globals pull [--out <file>] [--force]
mechanic globals push [--file <file>] [--dry-run] [--force]
mechanic globals set <key> --json <value>
mechanic globals delete <key> --force
mechanic secrets list [--json]
mechanic secrets set <key> [--from-stdin | --value-env <env>] [--force]
mechanic secrets delete <key> --force

Most task commands accept a <task> selector. In day-to-day use, prefer the short local task slug:

mechanic tasks preview order-tagger

The CLI also accepts a full task JSON path, matching helper directory, or linked remote task ID:

mechanic tasks preview tasks/order-tagger.json
mechanic tasks preview tasks/order-tagger
mechanic tasks preview 171578bf-79e2-46af-857a-dbd71c6b7b2b

The local file is the working copy. The remote task ID is the Mechanic app's address for that task. tasks list shows linked local files when the CLI knows them. If a short slug matches more than one local file, the CLI asks for the full file path; that is the main reason to use tasks/<name>.json directly.

Task names and local file names are related, but they are not the same identity. When the CLI first pulls or creates a task, it uses the task name to choose a readable local slug like order-tagger, which becomes tasks/order-tagger.json. After that, the remote task ID stored in .mechanic/links.json is the sync identity. If you rename the task in Mechanic, the next pull keeps the existing local file name and updates the name field inside the JSON. If you rename the local JSON file or helper directory by hand, the CLI treats that as a new local slug and the task may appear unlinked. Keep local filenames stable unless you also intentionally update .mechanic/links.json and verify with tasks status and tasks publish --dry-run.

tasks pull pulls every task when you run it without arguments. Pass a task selector when you only want one task. tasks diff and tasks publish operate on one task when you pass a selector. They operate on every task only when you explicitly pass --all.

tasks new creates a new blank local starter task. It writes both tasks/<slug>.json and tasks/<slug>/, so you can edit the helper files first and bundle them into the JSON file before publishing. It can be used any time in an initialized CLI project, and it refuses to overwrite existing local task files unless you pass --force. It does not create anything in Mechanic until you run tasks publish; new published tasks are created disabled.

shop status shows the current Mechanic run queue for the configured shop: running runs, waiting runs, queue lag, and the largest backlog groups by task, action, and event topic.

shop deprecations shows unresolved Shopify API deprecations reported by tasks in the configured shop. Pass a task slug, task file, helper directory, or linked remote task ID to focus on one task:

mechanic shop deprecations
mechanic shop deprecations order-tagger

The output includes the task's configured Shopify API version and the Shopify API version seen in the deprecated request. Use --json for monitoring scripts, agents, or dashboards.

tasks status shows whether local task JSON files are linked, ready to publish, and in sync with their remote Mechanic tasks. Use --local when you only want the fast offline check for JSON validity, link state, and helper-folder drift. Add --json when an editor integration needs task readiness, link state, and remote sync state without parsing table output.

tasks diff compares current Mechanic with your local file, groups output by changed field, and only shows nearby changed lines. In diff output, - means current Mechanic and + means local file. Differences are informational by default; add --exit-code when a script or CI job should treat differences as a nonzero result.

tasks preview is the confidence check before publishing. It sends one local task to Mechanic's preview engine without saving it, then reports whether the sample events passed, which actions would run, validation errors, and Shopify permissions detected by the previewed paths. Add --remote to preview the current task already in Mechanic instead of your local draft. Add --verbose to show event, task run, and action run result details in the terminal. Add --json when an agent, script, or CI job needs the raw preview response. Missing Shopify permissions are approved in the Mechanic app after publishing or enabling the task. Preview exits 0 when the preview passes, 1 when sample runs fail, and 2 when the task is invalid.

tasks preview --stdin reads task JSON from stdin instead of local files, so editors and other tools can preview in-memory task content without writing it to disk first:

cat build/order-tagger.json | mechanic tasks preview order-tagger --stdin
generate-task | mechanic tasks preview --stdin --json

Pass a task selector alongside --stdin to preview the piped content in the context of that linked Mechanic task, or omit the selector to preview the content as a new unlinked task. --stdin trusts the piped content as the source of truth, so it skips the stale helper directory check. Previews are rate limited per token; tools that preview on every edit should debounce and respect 429 Retry-After responses. With --json, a rate limited preview prints the JSON error envelope, including retry_after_seconds.

tasks publish sends local task JSON back to Mechanic.

tasks publish --dry-run is a publish preflight. It checks whether publishing would be safe, including helper-folder drift, linked remote task changes, and unlinked files that look like existing remote tasks. It then prints what would create, update, stay unchanged, or stop as a conflict. It does not write to Mechanic, .mechanic/links.json, or local task JSON. New tasks created by tasks publish are created disabled; review and enable them in Mechanic when they are ready to run.

For automation or editor integrations, add --json to tasks list, tasks status, tasks preview, tasks diff, tasks validate, tasks bundle, tasks unbundle, tasks publish, or shop status.

tasks unbundle and tasks bundle operate on one task at a time. By default, mechanic tasks unbundle order-tagger writes to tasks/order-tagger/, and mechanic tasks bundle order-tagger writes back to tasks/order-tagger.json. You can pass the full JSON path when you want to be explicit.

When a task has subscriptions_template, the editable helper file is subscriptions.liquid. Mechanic renders that template into subscriptions, so the CLI treats subscriptions as generated state instead of something to edit or publish by hand.

mechanic init accepts --token, reads MECHANIC_API_TOKEN, or prompts for an API token in an interactive terminal. It verifies the token and stores it outside the project. mechanic auth login does the same thing later if you skip token setup during init. For automation, set MECHANIC_API_TOKEN; it takes precedence over any locally stored token for authenticated commands.

mechanic doctor checks the local project config, token source, verified shop, and task API access. Run it after mechanic auth login or when a project stops syncing cleanly.

mechanic init defaults to https://api.mechanic.dev for the API and refuses to overwrite an existing project unless you pass --force. The CLI only sends API tokens to trusted Mechanic API hosts by default. Use --api-base-url or MECHANIC_API_BASE_URL only for a staging or dedicated Mechanic API host you control; set MECHANIC_TRUST_API_BASE_URL=1 only when you intentionally trust that host. Task IDs in command output link back to the Mechanic embedded app in terminals that support hyperlinks; use mechanic tasks open when you want to open a task explicitly. Use --app-url or MECHANIC_APP_URL if your shop uses a non-production Shopify app alias.

Use mechanic tasks open <task> to open a task in the Mechanic app from its local task slug, linked local task JSON file, helper directory, or remote ID.

Advanced: GitHub Actions Task Workflows

Local CLI usage works without GitHub Actions. If you want optional single-shop Git sync automation, start from a populated Mechanic CLI project, then generate the recommended workflows:

mechanic tasks pull
mechanic github init

This creates:

.github/workflows/mechanic-validate.yml
.github/workflows/mechanic-deploy.yml
.github/workflows/mechanic-sync-from-app.yml

Re-run with --force only when you intentionally want to replace those workflow files.

Add one repository or organization secret named MECHANIC_API_TOKEN containing a Mechanic API token for the shop in mechanic.json.

From the task repository, the safest repository-secret setup is:

gh secret set MECHANIC_API_TOKEN

Paste the token when prompted. If you are not running the command from the task repository checkout, add --repo OWNER/REPO.

The generated workflows install the same @lightward/mechanic-cli version that generated them and use the api_base_url committed in mechanic.json.

Only enable the deploy and sync workflows in repos whose maintainers you trust. Those workflows use the Mechanic API token, and the sync workflow also needs repository write permissions so it can open or update sync-back pull requests.

The generated workflows do three jobs:

  • mechanic-validate.yml runs on pull requests and manually, with no Mechanic token, validates tasks/, and fails if helper task directories need bundling.
  • mechanic-deploy.yml runs manually. It requires either task_path or deploy_all=true, always runs mechanic tasks publish ... --dry-run, and only writes to Mechanic when you choose mode=deploy. The default is dry-run; inspect that plan first, then rerun with mode=deploy when you mean to publish. After a deploy, the workflow opens or updates a sync-state PR so .mechanic/links.json and task hashes stay current.
  • mechanic-sync-from-app.yml runs manually, pulls app updates with mechanic tasks pull --all --force, validates the result, and opens or updates a PR from mechanic-sync/from-app when files changed. The PR body includes a changed-file summary and the V1 deletion caveat. Add a schedule to this workflow later only if you want automatic app-to-Git sync PRs.

The pull-from-app workflow is intentionally update-only in V1: it does not prune local files or links for tasks that were deleted in Mechanic.

Generated GitHub workflows do not sync mechanic.globals.json. Add a separate workflow step later if you intentionally want globals sync in automation.

Project Layout

mechanic.json
.mechanic/
  links.json
tasks/
  order-tagger.json

mechanic.json stores the Shopify shop domain, API base URL, embedded app URL for task links, and tasks directory. Because this file is repo-controlled, the CLI validates configured API and app URLs before using them with a token.

.mechanic/links.json maps local task slugs to remote Mechanic task IDs and the last known remote content hash. Commit this file when the repo represents a shared source of truth for that shop.

API tokens are stored outside the project under the local user config directory, not in mechanic.json. Token files are written with owner-only permissions.

Task Files

Canonical task files are JSON files at tasks/<slug>.json. They use Mechanic's task export fields, including:

  • name
  • script
  • docs
  • subscriptions_template
  • subscriptions
  • options
  • preview_event_definitions
  • tags
  • shopify_api_version
  • online_store_javascript
  • order_status_javascript

Remote IDs are never written into task JSON. mechanic tasks publish also removes enabled from the API payload so local sync cannot accidentally enable or disable a production task.

Direct-subscription tasks may use null for script, docs, and subscriptions_template.

Bundle And Unbundle

JSON is the canonical file format, but editing long Liquid and Markdown strings inside JSON is unpleasant. The helper workflow splits a task into separate editable files:

mechanic tasks unbundle order-tagger --out order-tagger

tasks unbundle also accepts the same local task selectors as preview, diff, and publish:

mechanic tasks unbundle order-tagger
mechanic tasks unbundle tasks/order-tagger.json
mechanic tasks unbundle tasks/order-tagger
mechanic tasks unbundle 171578bf-79e2-46af-857a-dbd71c6b7b2b
order-tagger/
  task.json
  script.liquid
  docs.md
  subscriptions.liquid
  online_store_javascript.js.liquid
  order_status_javascript.js.liquid

After editing, bundle the helper directory back to canonical JSON:

mechanic tasks bundle order-tagger
mechanic tasks validate order-tagger

tasks bundle accepts the local task slug or either side of the pair. mechanic tasks bundle order-tagger and mechanic tasks bundle tasks/order-tagger both write tasks/order-tagger.json. mechanic tasks bundle tasks/order-tagger.json reads tasks/order-tagger/ and writes back to that JSON file.

When unbundling, helper files are written only for string fields. null fields stay in task.json, and stale helper files for non-string fields are removed.

When publishing a JSON file, the CLI checks for a matching helper directory. If tasks/order-tagger/ has changes that are not bundled into tasks/order-tagger.json, publishing stops before making any API request.

mechanic init adds Mechanic Liquid helper files to .prettierignore. Formatter rewrites can change task behavior, especially for newline-delimited subscriptions.liquid and exact Liquid string quotes in script.liquid.

Sync Safety

The CLI uses remote content hashes to avoid overwriting task edits made in the Mechanic app or by another local checkout.

  • tasks pull records the latest remote content hash in .mechanic/links.json.
  • tasks pull <task> can refresh a linked local file without --force when the local file still matches the last synced hash. If local edits would be lost, it stops and asks for an explicit --force.
  • tasks publish sends that hash as previous_content_hash.
  • The API rejects stale publishes with a conflict.
  • tasks diff warns when Mechanic changed since the file was last synced, then compares current Mechanic with your local file. If only Mechanic changed, pull normally to update your local file. If both Mechanic and your local file changed, choose explicitly: pull with --force to replace local with Mechanic, or publish with --force to overwrite Mechanic with local.
  • tasks publish --all checks every linked remote task before writing anything, so one stale task blocks the whole publish instead of partially publishing earlier files first.
  • tasks publish --force bypasses the hash guard when you intentionally want the local file to win.

Creating a new remote task sends a deterministic idempotency key for the local shop and task slug, so retrying after a timeout returns the same remote task instead of creating duplicates.

Before creating a task from an unlinked local file, the CLI checks existing remote task names for the same normalized slug. If a likely match already exists in Mechanic, publishing stops and asks you to pull/link the remote task first.

tasks publish --all rejects local files whose names normalize to the same task slug, for example foo-bar.json and foo_bar.json.

Globals and Secrets

Shop globals are visible JSON configuration shared across tasks. The mechanic globals pull command writes mechanic.globals.json, which is repo-safe and may be committed. By default it refuses to overwrite an existing file whose contents differ; pass --force when the remote globals should replace the local file. The mechanic globals push command validates the whole file and checks remote globals before sending any writes. By default it creates new globals but refuses to overwrite existing remote globals whose values differ; use mechanic globals push --dry-run to review the plan, then pass --force when the local file should replace those remote values.

For one-off global changes:

mechanic globals list
mechanic globals set warehouse_id --json '"main"'
mechanic globals set shipping_rules --json '{"regions":["CA","US"]}'
mechanic globals delete warehouse_id --force

Shop secrets are write-only string values. The CLI never creates a secrets file, and task pull, preview, and publish never write secret values into task JSON. Use mechanic secrets set <key> --from-stdin or --value-env <env> for non-interactive use; --from-stdin preserves exact stdin content, including trailing newlines. Secret values are not printed after save. Replacing or deleting a secret can break tasks until they are updated, so existing secret values and secret deletes require --force.

For one-off secret changes:

mechanic secrets list
mechanic secrets set api_token
printf %s "$API_TOKEN" | mechanic secrets set api_token --from-stdin
mechanic secrets set api_token --value-env API_TOKEN --force
mechanic secrets delete api_token --force

Task Sync API

This package targets Mechanic's v1 task sync API. The CLI is the recommended client; direct HTTP use is intended for trusted, CLI-compatible automation.

  • GET /v1/auth/verify
  • GET /v1/shop/status
  • GET /v1/tasks
  • GET /v1/tasks/:id
  • POST /v1/tasks/preview
  • POST /v1/tasks/:id/preview
  • POST /v1/tasks
  • PUT /v1/tasks/:id
  • GET /v1/globals
  • PUT /v1/globals/:key
  • DELETE /v1/globals/:key
  • GET /v1/secrets
  • PUT /v1/secrets/:key
  • DELETE /v1/secrets/:key

Preview endpoints run local or remote task content through Mechanic's preview engine without creating, updating, or saving a task.

Requests use Bearer token auth with API tokens created in the Mechanic app. The CLI does not make separate telemetry calls. Authenticated API requests include a MechanicCLI/<version> user agent so Mechanic can observe CLI usage from server-side operational logs.

This API surface is intentionally narrow: task sync, task preview, shop status, and shop-level globals/secrets for the authenticated shop. It is not a general Mechanic API for arbitrary integrations.