@fml-inc/fml
v0.8.11
Published
FML CLI and agent tools
Readme
FML CLI
@fml-inc/fml is the single published FML command-line package. It provides
the fml command, account and organization authentication, agent MCP
integration, cloud-backed product commands, and raw artifact sync.
Panopticon is an internal workspace engine under packages/panopticon. Its
raw-sync implementation is bundled into this package at build time; users do
not install or run a second Panopticon package.
Install
Node.js 22.19 or newer and npm are required.
On macOS, Linux, and compatible Bash environments, install with:
curl -fsSL https://fml.inc/install.sh | bashFor Windows or manual installation, run:
npm install --global @fml-inc/fml
fml setupFor a customer-hosted deployment, pass the address you use to open FML in the
browser to setup instead. The CLI asks that app for its backend (Convex HTTP
actions) origin, signs in against it, and only then saves both as the active
custom environment and restarts the observer, so later CLI and observer
traffic uses the same deployment and a failed login never switches
environments:
fml setup https://fml.example.com
# Sign in again later, or headless:
fml login https://fml.example.com
fml login https://fml.example.com --deviceThe backend origin itself (for example https://actions.fml.example.com) is
also accepted. Either URL must be an HTTPS origin with no path, query,
credentials, or fragment. Plain HTTP is accepted only for loopback development
servers.
Run fml update to check for and install a newer global package with npm. It
reports the version installed and refreshes FML setup automatically. Stable
releases stay on npm's latest channel; prerelease versions stay on next.
Installing the package does not change agent configuration. Run fml install
to register the FML Claude Code plugin, install the agent-native FML skill,
configure the FML MCP server, register capture-only lifecycle hooks for
supported agents, and install the FML extension for Pi. fml setup performs
this installation, signs in when needed, and restarts the observer. Invoke the
skill as /fml review in Claude, Gemini, or Grok, and as $fml review in
Codex. Review runs inside the agent from the packaged skill; it is not an
fml review CLI command. Start a new agent session after installing so it
picks up the integration. FML pre-approves only the isolated command hooks it
installs for Codex; use /hooks to review or disable them.
The Claude plugin is MCP-only. FML installs the workflow through each host's
native skill directory so it keeps the bare fml name without a second,
plugin-namespaced skill.
For a coding agent operating in a sandbox, use the agent installation guide.
mini-swe-agent v2
FML supports native v2 DefaultAgent/InteractiveAgent with LocalEnvironment.
Install mini and its dependencies yourself (Python >=3.10, Pydantic >=2), activate
that environment, and retain your native model credentials:
fml install
mini -t "Your task" -o ./mini-run.traj.jsonSuccessful Mini setup uses the shared installation summary; Mini-specific output is reserved for warnings.
Native mini-swe-agent and python -m minisweagent entrypoints also work.
No wrapper, PYTHONPATH export, or adapter flag is required. Installation probes
package metadata without importing mini or loading its .env; activation needs
writable site-packages. An absent Mini installation or unrelated executable named
mini is optional, not a warning. An existing activation is retained when Mini
is unavailable. Recognized Mini prerequisite failures and activation conflicts
still warn without blocking general FML setup.
An ASCII-only owned .pth loads a guarded bootstrap bound to the selected Python
prefix, including paths with non-ASCII characters.
Hook delivery uses owned absolute Node and hook-handler paths recorded at
installation; it does not resolve fml or Node from the runtime PATH.
Automatic capture leaves ordinary Python commands and other environments native,
including environments inheriting that site-packages directory. Native YAML,
.env, model/mode/limit/output settings, and configuration edits remain
authoritative. fml doctor silently checks prerequisites, activation, and ownership
of the managed adapter and hook-command assets. It shows a Mini-specific warning
only when a recognized installation needs attention; missing or modified assets
warn without changing files. Installation does not guarantee capture for custom
configurations.
Explicit -c/--config (including short-option clusters such as -yc) and
--agent-class opt out unless they select an FML adapter. Supplying -c replaces
mini's default config list: include the native base first, such as
-c mini.yaml -c local-overlay.yaml. CLI class selection wins
over YAML. To capture an explicit configuration, select
--agent-class fml_mini_agent.FmlInteractiveAgent or
--agent-class fml_mini_agent.FmlDefaultAgent.
Native -o overrides YAML agent.output_path, including its default when omitted.
Custom agents, remote environments, mini v1, and batch/SWE-bench are unsupported.
The adapter preserves native confirmation, human/yolo modes, interruptions,
completion, and exceptions. FML requests run through bash using the CLI-only
workflow; there is no native /fml, $fml, MCP client, or fml review command.
The bundled workflow is model-visible without being printed as a user message in
the interactive terminal. Capture-only interruptions after execution preserve
the native result or pending exception, including during observation snapshots,
before interrupting further native work.
Capture interruptions before native execution discard the pending execution
record, so the final snapshot does not report a command that never ran.
Each run() receives a UUID; interactive continuation keeps it. Native output is
unchanged. Atomic snapshots retain the complete native trajectory plus FML
identity, actual execution, context, cwd, and message-observation metadata under
<FML data directory>/mini-swe-agent/trajectories/<UUID>.traj.json.
Managed snapshots omit native info.config.model.model_kwargs and
info.config.environment.env; native configuration and output retain both.
Messages and tool output remain raw, not generally credential-redacted.
Sync replaces the session projection as this full JSON snapshot changes, rather
than appending JSONL. Rejected proposals are not executed bash calls, and injected
context is not a human prompt. Usage views count reasoning/cache tokens once,
including native OpenRouter cache-write details; unknown usage remains unknown.
Native cumulative costs are scoped to each run.
Snapshots require fml.human_message_indices, recorded at native input boundaries
and tied to retained message objects. Blank interruption and mode-switch feedback
do not create human turns; genuine comments and human commands still do, even
when their text matches generated feedback. Projection does not infer provenance
from message text or accept snapshots missing this metadata.
Provenance is recorded before interruptible hook delivery, so accepted native
input keeps its human role if capture is interrupted.
Human-mode shell commands retain their raw whitespace, including mode-like text
that native Mini treats as a command. Confirmation and interruption responses
follow their native caller's input decision instead of a global trim heuristic.
Capture uses the bounded, fail-open hook inbox and authenticated destination
binding. Dynamic server context is supported, but disabled for mini by default.
Admins can opt an organization in through the existing dynamic-context policy
with the Convex-safe override {"harnesses":{"miniSweAgent":true}}; organization
override lookup failures leave mini disabled. Other harness defaults are unchanged,
and the public target/source remains mini-swe-agent.
The hook's JSON transport uses explicit UTF-8 encoding, independent of Python's locale, so Unicode context is preserved at the native model boundary.
The initial native task and actual interactive human prompts arm a BeforeModel
request immediately before the next provider call. Native slash/mode controls,
generated feedback, and tool continuations do not rearm it. Returned context is
inserted quietly into native messages without printing a user message; snapshots
record its position in fml.context_message_indices, so it is not classified as
human input. Both requests and insertion honor pause state, including a pause
while a request is in flight. Deferred capture interruptions are delivered before
the next provider call or tool execution, without replacing completed native
results or exceptions.
fml stop pauses hooks, context, and snapshots, not native history; resuming
publishes the complete cumulative run, including its paused interval.
Reinstall repairs a missing owned .pth after environment revalidation.
When Mini resolves to a different interpreter, reinstall moves the unchanged
owned activation to that environment; no prior uninstall is required.
Modified hooks and destination conflicts are preserved with a warning.
Updates retain activation if mini is unavailable. Uninstall removes unchanged
owned files without editing native YAML or .env; modified/conflicting files
retain their dependencies with a warning. Trajectories remain unless --purge
is explicit, and unrelated or modified files are preserved even during purge.
Resolve reported ownership conflicts manually.
Upgrading from standalone Panopticon
Earlier releases shipped as a separate @fml-inc/panopticon package that
installed its own Claude plugin, agent hooks, MCP server, OpenTelemetry
settings, and shell environment. fml install does not migrate that install.
Retire it first, from the machine that has it, before installing FML:
panopticon uninstall --purgepanopticon uninstall --purge stops the standalone daemon and removes its
plugin, hooks, MCP registrations, shell block, and generated data directory.
Then follow the Install steps above for a clean setup. If the old
panopticon command is already gone, reinstall @fml-inc/panopticon just to
run its uninstall, or clean up by hand: remove the # >>> panopticon >>> block
from your shell profile, delete the data directory (macOS:
~/Library/Application Support/panopticon; Linux: ~/.local/share/panopticon;
Windows: %APPDATA%\panopticon), and remove any leftover panopticon entries
from the Claude plugin registration (~/.claude/settings.json and
~/.claude/plugins/), agent hook configs (~/.codex/config.toml,
~/.codex/hooks.json, ~/.gemini/settings.json, ~/.grok/hooks/), and MCP
server registrations in those same agent config files. Also remove the legacy
Pi extension at ~/.pi/agent/extensions/panopticon.js.
Raw artifact sync
Sync uploads original, file-backed agent artifacts to FML. It does not parse them into local session, message, tool-call, or scanner tables. Server-side scanners own parsing and projections.
Discovery covers supported local stores across Claude Code, Codex, Gemini, Grok, Cursor, Pi, mini-swe-agent, Copilot CLI, and Factory Droid, including archived Codex sessions and managed mini full-trajectory snapshots. Availability depends on which harnesses have written artifacts on the machine.
Sync has one persistent setting: enabled or disabled. It defaults to enabled. The backend environment, login, and selected organization determine the upload destination; there is no user-configurable sync-target list.
fml sync # Discover and drain pending raw artifacts
fml sync --dry-run # Show what would be discovered without changing the queue
fml sync status # Show enabled state, queue counts, and byte watermarks
fml sync disable # Stop discovery and upload
fml sync enable # Allow discovery and upload again
fml sync reset # Clear watermarks and replay every local artifact from byte zero
fml start # Start the observer and continuously run enabled raw sync
fml restart # Stop and start the observer
fml stop # Stop the observer and disable raw syncfml sync reset preserves the enabled/disabled setting and source files. It
clears local upload progress and gives tracked artifacts fresh generations, so
the next enabled fml sync performs a full replay. Reset refuses to race an
active sync.
For automation, the sync commands support --json. fml start enables raw
sync and launches a detached observer with a loopback-only HTTP endpoint. The
observer runs sync immediately, retries in the background, and follows later
fml env and fml org changes. fml sync disable pauses only raw discovery
and upload. Capture-only hooks continue writing to their durable local outbox
and uploading in the background. fml stop disables raw sync, records an
explicit hook-capture pause, and stops the complete observer runtime. Either
fml start or fml sync enable clears that pause. A crash or reboot does not
pause capture: handler inbox files remain durable until the observer returns.
The observer keeps a SQLite database for runtime bookkeeping and the durable hook outbox. It is operational state, not a local copy of session, message, scanner, or analytics tables. Hermes discovery and sync remain deferred.
The database is panopticon.db under FML's platform-native operational-state
root:
| Platform | Account/configuration (FML_DATA_DIR) | Operational database |
| -------- | -------------------------------------- | ----------------------------------------------------- |
| macOS | ~/Library/Application Support/fml | ~/Library/Application Support/fml/panopticon.db |
| Linux | ${XDG_DATA_HOME:-~/.local/share}/fml | ${XDG_STATE_HOME:-~/.local/state}/fml/panopticon.db |
| Windows | %APPDATA%\fml | %LOCALAPPDATA%\fml\panopticon.db |
The cutover is forward-only: the observer starts with a clean database at the
new path and does not migrate or delete the former
~/.fml/panopticon/observer.db. fml start reports that retained path whenever
the file still exists; when replacing a live legacy observer, it also reports
the old observer's pending row count. The PID state, lease/lock, endpoint,
auth-token files, hook inbox, and raw-sync watermarks remain under the
separately configurable observer directory.
Use FML_STATE_DIR to relocate the operational database and FML_DATA_DIR to
relocate account and environment configuration. For compatibility, an explicit
FML_DATA_DIR also relocates the database when FML_STATE_DIR is absent. It
does not relocate logs or observer coordination state: those retain their
platform defaults unless FML_LOG_DIR or an observer override is set.
FML_OBSERVER_DIR directly relocates observer coordination state;
PANOPTICON_HOME is a legacy alias for that directory and FML_HOME is a
legacy parent whose panopticon child is used. These observer-only aliases do
not relocate the database or account configuration. Because that pattern used
to isolate the pre-cutover database as well, fml start and the observer warn
at startup when an observer-only alias is set without FML_STATE_DIR or
FML_DATA_DIR; set one of those to choose the database root explicitly.
Every explicit FML directory override must be absolute; relative values are rejected so processes launched from different working directories cannot silently select different state. Relative XDG and Windows shell-directory variables are ignored in favor of the platform fallback.
The filesystems containing FML_STATE_DIR and FML_OBSERVER_DIR must support
same-directory hard links. FML uses that atomic primitive to publish complete
observer lease records without exposing a partially written database lock;
standard APFS, ext4, and NTFS volumes support it.
For multiple fully isolated FML instances, configure absolute, dedicated
FML_DATA_DIR, FML_LOG_DIR, and FML_OBSERVER_DIR roots per instance.
FML_DATA_DIR also isolates the database unless a separate FML_STATE_DIR is
set. Configuring a distinct FML_OBSERVER_PORT is also recommended. A
database-scoped runtime lease rejects a second observer that resolves to the
same panopticon.db; give that instance a different data or state root and
start it again.
Hook capture
FML installs capture-only hooks for Claude Code, Codex, Gemini CLI, and Grok CLI. It also configures direct MCP entries for Codex, Gemini, Grok, and Claude Desktop. The hook handler reads the agent's JSON event, first commits an owner-only per-delivery file, then sends it only to the authenticated loopback observer, and always returns an empty JSON object. It does not make permission decisions, inject context, or contact FML directly.
The observer commits each event to SQLite before removing its handler-side
delivery file. Files left by a stopped, restarting, busy, or unavailable
observer are replayed with the same sync ID, so a timeout cannot create a
duplicate or silently drop the event. A separate uploader sends stable,
idempotent batches through /v2/sync to the selected FML environment and
account, then removes local rows only after the server acknowledges them.
Repository authorization is evaluated per row across every organization the
account can access; hook delivery does not depend on the currently selected
organization. fml status reports both the handler inbox and SQLite outbox.
Each capture with known login state is durably bound to its exact FML URL, and stable account identity; switching environments or accounts cannot upload or acknowledge another destination's rows. Captures made while logged out remain unbound until the first authenticated destination atomically claims that backlog.
The handler-side inbox is a short-lived durability bridge for times when the
observer is stopped or unavailable. It is bounded independently from the
SQLite spool: by default it accepts at most 512 MiB or 262,144 delivery files,
whichever is reached first; the entry cap is derived from the byte budget at
2 KiB per event, so bytes are the effective bound for any realistic event.
Raising FML_HOOK_INBOX_MAX_BYTES alone re-derives the entry cap. At a limit
it preserves older committed files, refuses the newest inbox write, and still
fails open for the invoking agent; a running observer can still accept that
event directly. fml status reports the active limits, cumulative capacity
refusals, and the subset of refusals that no observer took either, which is
real loss; fml start warns about any such loss recorded since the last
start.
To keep the bridge short, a hook that finds the observer's recorded endpoint
no longer answering relaunches the observer in the background, at most once
every ten minutes. An explicit fml stop pauses capture entirely and is never
undone by a hook; a crash, reboot, or upgrade is. A machine where fml start
never ran has no recorded endpoint and gets no daemon.
Set FML_HOOK_INBOX_MAX_BYTES and FML_HOOK_INBOX_MAX_ENTRIES in the coding
agent's environment to change these limits. Bytes accept the same B, KiB,
MiB, and GiB suffixes described below; entries are positive integers. An
explicit unbounded opts out of either limit. These settings affect only the
per-delivery file inbox and do not change normal SQLite spool retention.
The hook spool is unbounded by default. To bound it, set
FML_HOOK_SPOOL_MAX_BYTES when starting the observer:
FML_HOOK_SPOOL_MAX_BYTES=512MiB fml startThe value is a positive integer in bytes with an optional B, KiB, MiB,
or GiB suffix. Unset it, or set it to unbounded, to retain the full spool
without a size limit. Invalid values (including 0) stop observer startup
instead of silently disabling the limit. Restart a running observer after
changing the setting.
This is a logical-content limit: it counts the UTF-8 bytes retained in hook
fields and their destination or rejection metadata across pending and
quarantined rows, not SQLite page, index, or WAL overhead. When the limit is
exceeded, the observer evicts the oldest quarantined rows first and then the
oldest pending rows. It never fails a hook invocation merely because the spool
is full. fml status reports current logical bytes, the configured maximum,
cumulative eviction counts and bytes, and the last eviction time.
Terminally rejected rows are retained for 30 days by default, independently
of the pending spool's unbounded default. Set
FML_HOOK_QUARANTINE_MAX_AGE_DAYS to a positive integer to change that window,
or to unbounded to retain quarantine rows until the byte bound evicts them.
Expired quarantine rows contribute to the same persisted eviction counters.
Retryable pending rows are never removed by this age policy.
The spool retains complete hook payloads and extracted fields, which can include prompts, commands, file paths, plans, and tool output. FML-created directories request owner-only permissions, and the SQLite database and sidecars are tightened to owner-only modes on platforms that support POSIX modes. An existing caller-supplied state directory retains its directory mode.
Automatic updates
While the observer is running it checks npm for a newer FML release about once
a day (with jitter). A session-start hook also requests a check, limited to one
session-start check per hour. When a newer release exists, the observer applies
it by running fml update in the background: npm install --global followed by
fml setup, which restarts the observer onto the new version. Output from that
background update is written to update.log in the FML log directory.
Auto-update is on by default. To disable it, set FML_AUTO_UPDATE=off and
restart the observer so it picks up the change:
FML_AUTO_UPDATE=off fml restartThe variable is read from the observer's environment at startup, so exporting it in a shell has no effect until the observer is relaunched. Development builds and non-release versions never auto-update.
MCP tools
The MCP server exposes FML tools for session analytics, cost tracking, integrations, team workflows, and account context, including:
| Tool | Description |
| ----------------------------- | ----------------------------------------------------------- |
| get_engineering_activity | Activity summary across sessions, prompts, tools, and costs |
| list_engineering_sessions | List recent sessions with stats |
| search_engineering_sessions | Search across sessions |
| get_session_timeline | Messages and tool calls for a session |
| get_session_turns | Per-turn token usage for a session |
| get_ai_spending | Token usage and cost breakdowns |
| whoami | Current auth and organization status |
| fml_list_integrations | Connected integrations |
| fml_query_* | Query connected integrations |
| fml_list_messages | Conversation messages |
| fml_search_analysis | Search codebase analysis results |
| fml_run_analysis_workflow | Run analysis workflows |
| fml_list_skills | Browse and load skills |
| list_repo_configs | Team configuration snapshots |
For backend tools not yet exposed as dedicated MCP tools, use the dynamic catalog:
fml tools list --json
fml tools describe <tool-name> --json
fml tools call <tool-name> --args '{"key":"value"}'CLI overview
fml setup [backend-url] Install integrations, sign in if needed, and start FML
fml install Register FML agent integrations and the MCP server
fml uninstall Remove agent integration; add --purge to remove local state
fml update Update @fml-inc/fml with npm (stable: latest; prerelease: next)
fml start Start the local observer and continuous raw sync
fml restart Stop and start the local observer
fml stop Stop the local observer and disable raw sync
fml login [backend-url] Sign in to hosted FML or a customer-hosted backend
fml logout Clear stored credentials
fml org [slug] Show or select an organization
fml env [name-or-url] Show or select a backend environment
fml status Show auth, environment, and raw-sync status
fml doctor Check auth, environment, organization, raw sync, and API access
fml open Open the FML dashboard
fml sync Upload raw local artifacts
fml sync enable Enable raw artifact sync
fml sync disable Disable raw artifact sync
fml sync status Show queue counts and byte watermarks
fml sync reset Replay all raw artifacts from byte zero
fml tools Browse and invoke backend tools
fml activity Show an activity summary
fml sessions List recent sessions
fml timeline <id> Show events for a session
fml spending Show token usage and cost breakdowns
fml search <query> Search sessionsRun fml --help or fml commands for the complete command inventory.
Development
Run from the repository root:
pnpm install
pnpm --filter @fml-inc/fml type-check
pnpm --filter @fml-inc/fml test
pnpm --filter @fml-inc/fml buildBuild, pack, and install the same artifact that will be published:
pnpm --filter @fml-inc/fml pack:local
pnpm add --global ./apps/cli/fml-inc-fml-*.tgz
fml --version
fml installThe packed manifest must not contain runtime dependencies on
@fml/panopticon or @fml/agent-targets; both private workspace packages are
bundled into the CLI.
Release
The FML CLI release workflow
publishes the exact package that passes the packed-install smoke test. Create a
tag named fml-v<semver> on a commit reachable from main, then push the tag:
git tag -a fml-v0.8.0 -m "Release FML CLI 0.8.0"
git push origin fml-v0.8.0Stable versions use npm's default latest tag. SemVer prereleases such as
fml-v0.9.0-beta.1 publish under next. Each release must advance the version
currently assigned to its npm channel; the workflow rejects stable versions
that do not advance latest and prereleases that do not advance next.
Release tags may not contain SemVer build metadata. Keep
apps/cli/package.json at 0.0.0-dev on main; the workflow stamps the
release version before building.
The @fml-inc/fml package must have one npm trusted publisher configured with
these exact values:
- Provider: GitHub Actions
- Organization:
fml-inc - Repository:
fml - Workflow:
publish-fml-cli.yml - Environment:
npm - Allowed action:
npm publish
The GitHub npm environment should allow only fml-v* tags and may require a
reviewer. The publish job has no npm token, source checkout, dependency
installation, or build step; it receives only the smoke-tested tarball and its
checksum. npm provenance is disabled because the source repository is private,
while OIDC trusted publishing remains enabled.
License
Proprietary — see LICENSE.
