Ferret CLI

August 14, 2026 ยท View on GitHub

Go Report Status Build Status Discord Chat Ferret release Apache-2.0 License

Ferret

This branch contains the CLI for Ferret v2. For the stable v1 CLI, see the v1 branch.

What is this?

Ferret CLI is the command-line interface for Ferret, a declarative-first, expression-oriented embedded language and runtime for data automation.

FQL combines a declarative core with domain orchestration through expressions such as match, for, filter, query, and waitfor. When a workflow needs state, var and while provide constrained mutation without turning FQL into a general-purpose language.

Use it to run FQL scripts, format and check source files, inspect compiled bytecode, manage browser sessions, and debug local scripts.

Full documentation lives at ferretlang.org.

Installation

Download a release from the releases page, or install from source:

go install github.com/MontFerret/cli/v2/ferret@latest

Shell installer:

curl https://raw.githubusercontent.com/MontFerret/cli/master/install.sh | sh

Quick start

Run the REPL:

ferret repl

Run an inline expression:

ferret run --eval 'return "Hello, Ferret!"'

Run a script:

ferret run example.fql

Pass parameters:

ferret run example.fql --param url=https://example.com --param limit=10

Parameter values are parsed as JSON when possible. Values that are not valid JSON are passed as strings.

ferret run example.fql --param active=true
ferret run example.fql --param tags='["news","tech"]'
ferret run example.fql --param code='"123"'

Use parameters in FQL with @name:

let page = DOCUMENT(@url)

return ELEMENT(page, "title").innerText

Common commands

ferret run script.fql       # Run a script
ferret exec script.fql      # Alias for run
ferret repl                 # Start the interactive shell
ferret check script.fql     # Check syntax and semantics
ferret fmt script.fql       # Format source
ferret build script.fql     # Compile to a bytecode artifact
ferret inspect script.fql   # Print compiled program details
ferret debug script.fql     # Start the interactive debugger
ferret migrate              # Migrate supported Ferret v1 Go and FQL source behavior
ferret browser open         # Start a managed browser
ferret config list          # Show configuration
ferret mod search sqlite    # Search the Ferret module registry
ferret mod install montferret/archive # Install a module into a Go application
ferret mod publish          # Submit a tagged module release to the Registry
ferret version              # Show version information

Run ferret [command] --help for command-specific options.

Migrating embedded Ferret applications

Run ferret migrate from anywhere inside a Go module that embeds Ferret v1:

ferret migrate

The command rewrites documented Ferret v1 imports to their Ferret v2 compatibility packages and updates go.mod and go.sum only when an import was rewritten. It also finds lowercase .fql files in the containing Go module and preserves v1's implicit result for a final top-level FOR by returning it explicitly:

// Before
FOR item IN 1..3
    RETURN item
// After
return for item in 1..3 {
    return item
}

Only a structurally recognized final top-level FOR without an explicit terminal return is changed. Nested, assigned, expression-contained, function-contained, non-final, and already-returned loops remain untouched. Changed FQL is canonically formatted; files needing only formatting remain byte-for-byte unchanged. A project with only FQL migrations does not run go get or change Go module dependencies.

Preview the affected paths without changing the project, or print a unified diff for review:

ferret migrate --dry-run
ferret migrate --print

The source scan excludes vendor, testdata, node_modules, hidden and underscore-prefixed directories, and nested Go modules. Malformed FQL is left unchanged and reported with its first useful diagnostic and line while other files continue to migrate. Unsupported and generated-file imports are also reported as manual follow-up.

Migration is intentionally limited to documented mechanical changes. It does not translate arbitrary v1 APIs or application logic, rewrite generated Go files or files under excluded directories, or guess replacements for unsupported v1 packages such as the former drivers packages. If a project vendors dependencies, run go mod vendor after reviewing and applying the migration.

Module lifecycle

Ferret CLI handles module discovery, installation into Go applications, project scaffolding, and preparation of registry publication records. Installation uses the Go module toolchain and the package path published by the Ferret registry.

Search the public registry by canonical module ID or description, or inspect one registered module:

ferret mod search sqlite
ferret mod info montferret/sqlite

Install a compatible registered release into an existing Go application:

ferret mod install montferret/archive
ferret mod install montferret/archive@1.0.0-rc.3
ferret mod install --yes montferret/archive # Approve safe missing prerequisites automatically

If the application is missing github.com/MontFerret/ferret/v2 or an active ferret.New(...) composition, an interactive install shows the complete setup before changing the project. It can add the exact Ferret version embedded in the CLI and create an exported NewFerret(options ...ferret.Option) helper in ferret.go when the destination package is unambiguous. Empty modules derive the package name from the module path; projects with one package use that package. Projects with multiple packages must add a composition manually.

Use -y or --yes to approve safe missing prerequisites in automation. Non-interactive installs without that flag fail with equivalent manual steps instead of reading stdin. The application must already have a go.mod.

The installer updates go.mod, go.sum, and the composition, then builds only its owning package before committing all changes. It does not modify the Ferret CLI runtime or create Ferret-specific project state. Installed modules are Go code compiled into the application and execute with the application's process permissions.

Initialize a new module project with the guided flow. It explains each value, offers editable defaults, and shows the resolved configuration before creating files:

ferret mod init

You can also provide any known values up front; the wizard asks only for what is missing. For non-interactive use, provide the module name and Go import path. The directory and namespace retain their module-name defaults:

ferret mod init acme/sqlite \
  --go-module github.com/acme/ferret-sqlite \
  --dir sqlite \
  --namespace DB::SQLITE

The scaffold contains schema-valid TODO metadata. Replace it before publishing a release. The manifest must identify a public repository that supports anonymous HTTPS Git access. Commit the module files, create and push the release tag, then run the publication command from the module root:

git tag v1.0.0
git push origin v1.0.0
ferret mod publish

By default, the tag is v<version> for a standalone module or <repository.directory>/v<version> for a monorepo module. For non-standard release tags, pass --tag.

To publish a nested monorepo module while staying in the repository root, pass the local module directory explicitly:

ferret mod publish --dir modules/widget

--dir selects the local module directory containing ferret.yaml; it does not override the manifest's repository.directory. The flag can also be combined with --dry-run or --print.

Publication validates ferret.yaml, resolves the public tag and pinned commit, checks the adjacent README.md and go.mod, consults the current Registry, and prepares only the immutable Barn source records needed for the release. The CLI then uses the GitHub API to create or reuse your personal Barn fork, create a focused publication branch, and open a pull request against MontFerret/barn. You do not need a local Barn checkout or knowledge of its record layout.

Set GH_TOKEN or GITHUB_TOKEN before publishing. If neither is set, Ferret uses the token from gh auth token --hostname github.com; authenticate it with gh auth login --hostname github.com when needed. The credential must be able to write to your personal Barn fork and open a pull request against the public Barn repository.

Validate the complete release without authenticating or mutating GitHub:

ferret mod publish --dry-run

Print the deterministic Barn-relative records as a versioned JSON document for inspection or unusual manual automation:

ferret mod publish --print

--dry-run and --print cannot be combined, and neither mode submits records. Publication retries are safe: an already-published version exits successfully, and an exact open pull request or publication branch is reused. Ferret never overwrites an immutable Registry record or a divergent branch; delete a stale publication branch from your fork before retrying if its contents differ.

Browser usage

Ferret can use Chrome or Chromium through the Chrome DevTools Protocol.

Open a managed browser:

ferret browser open

Run a script with a visible browser:

ferret run --browser-open script.fql

Run with a headless browser:

ferret run --browser-headless script.fql

Use an existing browser endpoint:

ferret run --browser-address http://127.0.0.1:9222 script.fql

Debugging

Start the debugger for a local source file:

ferret debug script.fql

Useful debugger commands:

break 12        Set a breakpoint
breakpoints     List breakpoints
continue        Resume execution
step            Step into
next            Step over
out             Step out
where           Show stack trace
locals          Show local variables
print <expr>    Evaluate a safe debug expression
quit            Exit

The debugger currently supports local source scripts with the builtin runtime. Compiled artifacts, remote debugging, DAP, conditional breakpoints, hit-count breakpoints, and logpoints are not supported yet.

Filesystem policy

Ferret's builtin runtime exposes filesystem functions through a writable sandbox rooted at the CLI's current working directory. Select a narrower relative or absolute root, and optionally make it read-only:

ferret run \
  --policy-fs-root=./fixtures \
  --policy-fs-read-only \
  script.fql

Filesystem policy options are available on run, repl, and debug and apply only to the builtin runtime. Supplying one with a remote runtime is a configuration error.

Flag and config keyEnvironment variableDefaultBehavior
policy-fs-rootFERRET_POLICY_FS_ROOTCurrent working directoryFilesystem sandbox root
policy-fs-read-onlyFERRET_POLICY_FS_READ_ONLYfalseReject writes, directory changes, and removals

HTTP policy

Ferret's builtin runtime blocks localhost, loopback, private-network, and link-local HTTP access by default. Grant only the access a script needs; for example, a script that intentionally calls a local development service requires an explicit opt-in:

ferret run \
  --policy-http-allow-localhost \
  --policy-http-default-headers='{"X-Trace":"local"}' \
  script.fql

HTTP policy options are available on run, repl, and debug and apply only to the builtin runtime. Supplying one with a remote runtime is a configuration error. They configure Ferret HTTP integrations such as IO::NET::HTTP and NET::REST; the existing --proxy and --user-agent options continue to configure HTML/browser drivers.

List values accept repeated flags or comma-separated values. Default headers use a JSON object with string values. Only values explicitly supplied through a flag, environment variable, or config file override Ferret's secure defaults. Numeric zero retains the Ferret default; use the dedicated no-timeout or unlimited-* option to disable a limit.

Flag and config keyEnvironment variableDefaultBehavior
policy-http-allowed-schemesFERRET_POLICY_HTTP_ALLOWED_SCHEMEShttp,httpsAllowed URL schemes
policy-http-allowed-methodsFERRET_POLICY_HTTP_ALLOWED_METHODSGET,HEAD,POST,PUT,PATCH,DELETE,OPTIONSAllowed HTTP methods
policy-http-allowed-hostsFERRET_POLICY_HTTP_ALLOWED_HOSTSunrestrictedExact allowed hosts or host:port values
policy-http-blocked-hostsFERRET_POLICY_HTTP_BLOCKED_HOSTSnoneExact blocked hosts or host:port values
policy-http-allow-localhostFERRET_POLICY_HTTP_ALLOW_LOCALHOSTfalseAllow localhost and loopback addresses
policy-http-allow-private-networksFERRET_POLICY_HTTP_ALLOW_PRIVATE_NETWORKSfalseAllow private-network addresses
policy-http-allow-link-localFERRET_POLICY_HTTP_ALLOW_LINK_LOCALfalseAllow link-local addresses
policy-http-default-headersFERRET_POLICY_HTTP_DEFAULT_HEADERSnoneDefault request headers as a JSON string map
policy-http-blocked-request-headersFERRET_POLICY_HTTP_BLOCKED_REQUEST_HEADERSnoneBlock requests containing these header names
policy-http-timeoutFERRET_POLICY_HTTP_TIMEOUT30sOverall HTTP timeout
policy-http-no-timeoutFERRET_POLICY_HTTP_NO_TIMEOUTfalseExplicitly disable the overall timeout
policy-http-max-request-sizeFERRET_POLICY_HTTP_MAX_REQUEST_SIZE16777216Maximum request-body size in bytes
policy-http-unlimited-request-sizeFERRET_POLICY_HTTP_UNLIMITED_REQUEST_SIZEfalseExplicitly disable the request-body limit
policy-http-max-response-sizeFERRET_POLICY_HTTP_MAX_RESPONSE_SIZE16777216Maximum response-body size in bytes
policy-http-unlimited-response-sizeFERRET_POLICY_HTTP_UNLIMITED_RESPONSE_SIZEfalseExplicitly disable the response-body limit
policy-http-max-response-header-sizeFERRET_POLICY_HTTP_MAX_RESPONSE_HEADER_SIZE1048576Maximum response-header size in bytes
policy-http-follow-redirectsFERRET_POLICY_HTTP_FOLLOW_REDIRECTStrueFollow HTTP redirects
policy-http-max-redirectsFERRET_POLICY_HTTP_MAX_REDIRECTS10Maximum redirects to follow

Configuration

Configuration values can come from command-line flags, environment variables, or the config file.

Priority order:

  1. Command-line flags
  2. Environment variables, for example FERRET_RUNTIME
  3. Config file
  4. Defaults

Config file locations:

  • Linux/macOS: ~/.config/ferret/config.yaml
  • Windows: %APPDATA%\ferret\config.yaml

Examples:

ferret config set runtime builtin
ferret config set browser-address http://127.0.0.1:9222
ferret config set policy-fs-root ./fixtures
ferret config set policy-fs-read-only true
ferret config set policy-http-allow-localhost true
ferret config set policy-http-allowed-hosts api.example.com,cdn.example.com
ferret config set policy-http-default-headers '{"X-Trace":"local"}'
ferret config get browser-address
ferret config list
ferret config unset policy-http-allowed-hosts

config set validates the complete persisted filesystem and HTTP policy before writing it. Invalid policy values and conflicting controls, such as setting both policy-http-timeout and policy-http-no-timeout=true, leave the existing config unchanged. List values use comma-separated strings, and default headers use a JSON object with string values.

config unset removes only the value stored in the config file and restores the implicit default for that key. Command-line flags and environment variables remain unaffected.

Development

Build and test locally:

git clone https://github.com/MontFerret/cli.git
cd cli

make compile
make test

Common development commands:

make fmt
make lint
make build

Contributing

Issues and pull requests are welcome. Before opening a pull request, run the formatter, linter, and test suite.

License

Apache-2.0