Flowmind CLI
The package installs two equal commands, flowmind and fm. On macOS, /usr/bin/fm can be
Apple's Foundation Models tool, so agents and hooks call flowmind.
Set up coding agents
npm install -g @flowmind/cli
flowmind auth login
flowmind hooks install
hooks install copies the Flowmind skill to Codex and Claude Code and adds session hooks to
~/.codex/hooks.json and ~/.claude/settings.json. Other hooks in those files stay as they are.
When the Flowmind Claude Code plugin (flowmind@flowmind from afgonullu/flowmind-skill) is
installed and enabled, the plugin provides the skill in Claude Code: hooks install writes no
copy to ~/.claude/skills, removes an unmodified copy it wrote earlier, and still adds the hooks.
The hooks run agent hook through the absolute paths of the installing Node binary and CLI file,
because hooks start without your shell profile and a version manager may not put node on PATH.
Codex on Windows gets a PowerShell command (& '<node>' '<cli>' agent hook --agent codex).
After you upgrade Node or move the CLI, hooks status shows outdated; run hooks install again
and trust the changed hooks. The hook records each agent turn under the CLI config folder.
In a linked repository, the session-start hook tells the agent which project it works on. Once 20 or more minutes of
finished agent turns are unstaged, each prompt also carries a reminder with the minutes and the
staging command. A successful agent stage resets the count.
In a git repository without .flowmind.json, the session-start hook first reads .flowmind.json from the
local remote-tracking refs: the current branch's upstream, then origin/HEAD, origin/main and
origin/master. It does not fetch. If a remote ref links the repository, the agent learns the project and
is told to run git pull and never to create a new project. Otherwise the agent gets the steps to link
the repository: pull, find the existing project, create one only for a new product, link, and commit
.flowmind.json, or mark a third-party clone or scratch folder with link --ignore. Once 20 minutes of
agent turns are recorded in such a repository, one prompt per session repeats this as a short reminder.
Ignored repositories and folders outside git get no context and no reminder.
Codex runs new hooks only after you trust them once in /hooks. hooks status shows both installs,
and hooks uninstall removes only the Flowmind entries.
Then ask your agent to "set up Flowmind". The skill has it inventory your repositories, propose projects, and create and link them after you approve the list.
Projects and repository links
flowmind projects --json
flowmind projects add "Bead Focus" --description "Focus timer for iOS" --json
flowmind link "Bead Focus" # writes .flowmind.json at the repository root
flowmind link <project-id> --dir ~/projects/bead-focus
flowmind link # shows the current link
flowmind link --ignore --reason "Third-party clone"
flowmind link --scan ~/projects ~/code --depth 2
A link names the project by id and keeps its name as a label. link accepts an id or an exact
project name and checks it against the projects visible to agents. Commit .flowmind.json to share
the link with other machines and agents.
One product is one project on every machine. The API owns that rule: projects add sends one request, and the
API refuses a name that an existing project already has. The CLI prints the API's message and suggests
--allow-duplicate (which creates the second project anyway) or flowmind link <project> for the existing one.
link --ignore writes {"ignore": true, "reason": "..."} as .flowmind.json for a third-party clone or
a scratch folder. link then prints Ignored for Flowmind, the hooks stay silent there, and
agent stage fails unless you pass --project. Commit the marker so other machines skip it too.
link --scan <dir...> finds git repositories in each directory and up to --depth folder levels below
it (default 2). It skips hidden folders and node_modules, and does not search inside a repository.
Each repository gets a status: linked, ignored, remote-linked (a local remote-tracking ref has the
link; run git pull) or unlinked, with the project and the origin URL without credentials.
--json prints { "repositories": [...] }. The scan exits 0 and does not use the network.
Log completed work by project name and duration:
fm log --project Flowmind --hours 1.5 --outcome "Shipped the reader"
Without a start time, the session ends when saved: 90 minutes saved at 10:00 records 08:30–10:00. To supply the actual start, use an ISO 8601 timestamp with a timezone:
fm log --project Flowmind --hours 1.5 --started-at 2026-10-02T08:30:00+02:00 --outcome "Shipped the reader"
Future session ends are rejected before contacting the API. The API enforces
the seven-day logging window in your stored timezone. --started-at applies to
structured project/hours logging. Natural language logging uses its reviewed
AI confirmation time. Run fm log --help for all options.
Stage an agent proof
Agents stage completed work for explicit human approval. In a linked repository with the session
hooks installed, the intention and outcome are enough. Write the outcome as one line of at most
120 characters: what changed for the project or its users. Add up to three optional --detail
values for proof or open items. Each detail must be one line of at most 100 characters.
Keep commits, runs, paths, and open items in details or use --artifact-url for the proof link.
For example:
flowmind agent stage --intention "Fix CLI sign-in" \
--outcome "Signed-out CLI sign-in now returns to Authorize CLI" --detail "Cause: Supabase rejected the www callbacks" --detail "Still needs: deploy approval" \
--json
If a proof breaks these rules, the CLI rejects it before any request. The error says exactly
what to shorten or split and includes the worked example above. In --json mode, stderr contains
an error message and an issues list. API rejections retain the server's exact message.
Existing long notes remain readable; this rule applies to new proofs. agent proposals shows
details as indented bullets, and --json includes the API's details unchanged.
The CLI reads the project from .flowmind.json and the session from CODEX_THREAD_ID or the one
session with a turn in progress in the repository. The duration is the main agent's recorded turn
time since the session's last staged proof, in whole minutes; the remaining seconds carry over to
the next proof. A stop closes only its matching turn; an unfinished turn counts only through its last recorded activity. Idle time between turns does not count, and neither does sleep: the hooks read the
awake clock that Codex uses for "Worked for". Subagent prompts never start a turn, because the main
agent's turn already contains their work. Codex runs no prompt hook for a turn that another chat
starts (send_message_to_thread, create_thread). For such a turn the CLI reads the start, the end
and Codex's own awake time from the Codex session file. It places a time on the awake clock only where
the record shows no sleep in between; on macOS that record is the kernel's last wake time. The JSON
output gives the recorded unstagedSeconds. When nothing new is recorded, or when a Codex subagent
calls the command, it stages nothing, prints "staged": false with a reason and a message,
and exits 0. The source kind is codex or claude_code from the recording host.
--dry-run prints the request and its retry key without staging anything.
Add --suggest-share only for a shipped outcome with an artifact link in --artifact-url
(merged PR, release, deployed page, or published document) that a builder would want to show others.
Never suggest routine, private, in-progress, failed, or security-sensitive work, or suggest just to pad.
The flag only adds a hint in the review desk. The person decides; agents can never share.
--dry-run shows suggestShare: true in the request when the flag is set. Saved stages keep the
flag and retry with the exact same body; stages without the flag omit it.
A saved stage made with the flag needs CLI 0.3.13 or later to retry; an older CLI reports it as unreadable.
Without hooks, pass observed values:
fm agent stage --project <project-id> --minutes 45 \
--intention "Finish the reader" --outcome "Shipped the reader" \
--started-at 2026-10-02T08:30:00+02:00 --source-kind claude_code \
--artifact-url https://example.com/proof --session-id <stable-session-id> --json
Source kinds: calendar, claude_code, cli, codex, github, linear,
manual, session_log, other. claude is not an alias. Use --source-label
to override the Claude Code display label. Run fm agent stage --help for all options.
Staging adds no hours; approve in Activity individually or confirm Approve all with links (N).
The retry key derives from the session id, project id, and start time, so each work unit in a
session gets its own key. agent capabilities, agent projects, agent routines, agent proposals,
and agent cancel accept --json. auth status checks the credential with the API and accepts --json. A failed connection reports an error; a revoked key reports authenticated: false.
Saved stages
After login the CLI saves every stage for that API and account before it sends it, so a crash, a timeout or a lost answer
never loses it. When Flowmind does not take the stage, because the API is unreachable, answers too
slowly (30 seconds), fails, limits the request, or sends an answer the CLI cannot read, the stage
stays saved and the command exits 0 with "staged": false, "reason": "saved", the error, and a
pendingStageId. The session counts its time as staged, so the next proof does not repeat it. A
refused credential for a known account, or an unexpected CLI error, keeps the stage saved but exits 1, because
someone must act. A stage that the API refuses for its content is removed, and its time stays unstaged
for a corrected proof.
Saved stages are sent with the same retry key and body. The API keeps one proof per account and
retry key for the credential that sent it, also after its 24-hour receipt expires, so a stage that
reached the server before its answer was lost comes back as the same proposal. After
flowmind auth login replaces the stored key, the API answers a stage that the old key already delivered
with This stage key is already in use. That key can hold a different body, so the CLI keeps the request,
its original credential fingerprint (not the key itself), and its time in conflict. No hook or later stage
sends it. Check Activity, then use flowmind agent retry with the original credential or explicitly
discard it with flowmind agent discard <id>. Retry reports unresolved items in conflicts; only a
returned proposal confirms delivery.
Each saved file is checked before sending, including its name, key/body hash, timestamps, source, details and
evidence. An unreadable reservation is kept. It blocks measured staging for this account until you repair
and retry it, or discard it after checking Activity. Saved stages go out after the next accepted stage
(savedStagesSent in the JSON output) and from the session hooks, which start agent retry --due
in the background when one is due: 1, 2, 4 … minutes after each failed attempt, at most an hour.
agent pending lists them and agent retry sends them now; both accept --json. A saved stage that
the API later refuses for its content stays listed as rejected and is not sent again.
If a process ends between saving a stage and marking its session, the next stage on the machine
restores the mark first. Only one process stages or sends for an account at a time. Returning to the same account after login restores its queue. Signing in to another account never adopts it.
The CLI keeps each account's sign-in periods on this machine, including earlier periods after logout. A whole turn belongs to the account signed in then, even if its first hook in the session came later. A turn crossing a sign-in or sign-out counts only its part within that period, and only when the awake clock places the boundary within the turn. Without that evidence, the uncertain part counts nothing. On hosts without turn IDs, overlapping prompts count nothing until all their stops have arrived. An ordinary prompt and stop still count; a restart or a gap longer than 12 hours adds no unverified time.
Upgrading from 0.3.7 needs no new sign-in. A never-bound profile asks the API which account owns its
stored key (GET /v1/me, once). A previously bound key whose credential or origin no longer matches
is refused locally. While a profile has only ever been signed in to one account, which is the normal case
of one person on their own machine, the session time and saved proofs that 0.3.7 recorded become that
account's on the first command or hook after the upgrade: an agent session that was running during the
upgrade stages its whole time as before, and saved proofs are sent with the others. On a profile that has
switched accounts, 0.3.7 proofs stay in their original location and the unowned list in agent pending
shows only neutral metadata. Check Activity before adopting a proof that is yours with
flowmind agent adopt <id>, or delete it unsent with flowmind agent discard <id>. A revoked key asks you to
sign in; an unreachable API reports that the account could not be verified and changes nothing. On POSIX the
config directory is 0700 and its credential file is 0600, including when repairing an older config.
Momentum brief
fm momentum
fm momentum --week 2026-10-12
fm momentum --json
See eight weeks of outcomes, personal and agent hours, project trends, and signals with source outcomes. Brackets mark the selected week; each sparkline uses its own peak. Quiet projects show the age of their last outcome. Commitment ideas are suggestions only and save nothing.
--week requires YYYY-MM-DD. The API checks that it is a Monday and is not in
the future. Without it, the API selects your current personal week.
--json prints only the response data as pretty JSON. Errors go to stderr and
exit with status 1. Colors require a terminal and an unset NO_COLOR variable.
Project ship log
fm ship-log "My Project"
fm ship-log <project-id> --json
Use a project id or an exact project name, ignoring case. Duplicate names require an id; unknown names list the available projects. Both errors exit with status 1. Archived projects remain available.
The text view shows publication status, public title and summary, and the API's public, new, and changed counts. Existing publications include page, card, and Atom feed links, plus README badge Markdown. Unpublished links remain listed, but public readers receive 404 until the log has published outcomes.
Public counts include only selected outcomes that still match their saved digest. Changed counts include edited selections and eligible corrections of selected outcomes. New counts include other eligible, unselected outcomes created after the last save. New is zero before the first publication. Saving clears new counts for omitted outcomes. When new or changed outcomes exist, the command points you to review in the web app.
This command is read-only. A project id accepts a read_only API key; resolving
a name uses the existing project list, which requires full_personal. Publish,
update, or unpublish in the web app. --json prints the API data unchanged as pretty JSON.
Errors go to stderr and exit with status 1.
Response failures
src/api.ts exposes apiRequest(descriptor, args, options) through the shared response decoder.
src/contracts.ts binds command workflows to generated descriptors and @flowmind/contracts/wire types.
Every API request validates the media type, envelope, status agreement, and response schema.
An installed CLI can be older than the API, so it accepts one kind of difference: a response field that its contracts do not declare, inside a closed object.
It drops that field before the command reads the response, so the field never reaches the output or --json.
Every other difference still fails: a missing, retyped or renamed field, an unknown enum value, a union that matches no branch, and an unknown top-level envelope field.
src/response-additions.ts owns this rule; the decision gives the reasons and the limits.
The rule is sound only for some schema forms, so the build runs response-forms.mjs and fails when a response that the CLI reads uses another form.
See the contract generation flow and endpoint procedure.
Do not cast response data, write payload parsers, or add generic request helpers.
Commands exit with status 1 when transport, JSON, media type, envelope, or payload checks fail.
A valid API rejection also exits with status 1.
One diagnostic names the action, operation, schema location, and request ID when available.
Diagnostics omit response values, credentials, query values, and server messages.
A personal log with a transport failure, timeout, or unreadable response is not confirmed. The CLI does not retry it automatically. Run the same log command to replay its saved fields and key. A natural log reuses what you already reviewed. After the 24-hour receipt window the same command is refused, because the server may no longer recognize the key. Check Activity, then close the saved log with flowmind pending-logs done <id> when the entry is there, or flowmind pending-logs discard <id> to log the work again. flowmind pending-logs lists the saved logs; neither action posts anything. --hours takes hours or minutes: 1.5, 1,5, 2h, 90m or 1h30m, rounded to the nearest minute. Anything else is refused before a request.
Authentication stops immediately on a failed poll. Pending authorization continues polling.
Expired authorization exits with status 1. No failed authorization saves a credential.
Use fm auth login --no-browser to open the printed authorization URL yourself.
Verify the CLI
Run pnpm --filter @flowmind/cli test from the repository root.
This builds the CLI, runs command tests against loopback HTTP fixtures, and tests an installed package.
The package check uses npm pack and a local npm install into a temporary prefix.
A loopback package fixture serves the runtime dependencies already installed in this workspace.
The install uses an empty npm cache and never contacts a public registry.
Installed commands run outside the repository.
Reports are saved under tools/cli/test-results/:
response-contracts-packed.json: installed command inputs, build hash, results, and request counts.pack-install.txt: package installation and hook lifecycle results.
The response matrix also checks credential persistence, diagnostic redaction, and write counts. Fixtures are synthetic. These tests do not prove production API availability.