@devflow-one/cli
v0.1.11
Published
Terminal CLI wrapping the local DevFlow gateway so shell scripts can trigger repo actions.
Maintainers
Readme
devflow — CLI for the local action gateway
Trigger DevFlow repo actions (branch, PR/MR, pipeline, reviewers) from the terminal, so shell scripts and CI-style automations can drive the same actions the extension panel exposes.
It is a thin HTTP client of the local gateway the desktop app runs
(desktop/src/gateway/server.js). It reads the same auto-generated token file,
routes by Jira project key (or issue key), and prints the gateway's result. No
provider API logic lives here — the owning browser profile executes the action.
How it works
- Each command maps to one gateway action. Routing is inferred from the current directory — no flags needed inside a configured repo; the command supplies its own args.
- The gateway routes the action to the connected browser profile that owns the project key, waits for the profile's result, and returns it.
- Discovery:
list-actionsandlist-profilesread straight from the live gateway registry (advertised actions; connected profiles + owned keys).whereshows what the current directory resolves to. - Failures come back as structured errors (
NO_PROFILE,AMBIGUOUS_PROFILE,TIMEOUT,DESTRUCTIVE_BLOCKED, or an unreachable daemon), printed with their code (--jsonfor the machine-readable envelope).
Routing: inferred, flags override
Run a command inside a repo you've configured in the extension and it routes itself — the CLI asks the desktop daemon which project owns this checkout:
cd ~/work/acme-web # a configured repo, on branch ABC-123-login
devflow mr find # → finds the PR for ABC-123, no flags at allInference goes through these sources, highest priority first. A lower one never overrides a value a higher one already supplied:
| # | Source | Supplies |
| - | ------ | -------- |
| 1 | Flags — --project / --issue / --repo / --profile | whichever you pass |
| 2 | Env — DEVFLOW_PROJECT / DEVFLOW_ISSUE / DEVFLOW_REPO / DEVFLOW_PROFILE | whichever are set |
| 3 | Git remote origin, matched against your configured repos | project, repo, profile |
| 4 | The checkout path, matched against a repo's configured local path | project, repo, profile |
| 5 | The current branch name (feature/ABC-123-login → ABC-123) | issue key |
| 6 | A Jira-key-shaped ancestor directory (~/work/ABC-123/src) | issue key |
Every flag is therefore an override, never a requirement:
devflow mr find # fully inferred
devflow mr find --issue ZZ-9 # the flag wins over the branch
devflow branch create x --project OTHER # act on a project other than this repo'sAmbiguity is never guessed. If two configured repos match the checkout, the command fails naming both and tells you which flag settles it:
error [UsageError]: "branch create" could not be routed — this checkout matches
several configured projects (ABC (web via p1), XYZ (web-fork via p2)).
Pass --project (or --repo) to choose one.devflow where
Shows what this directory resolves to, where each value came from, and whether the owning Chrome profile is actually connected — so you can see why a call would fail before making it:
$ devflow where
projectKey ABC (git remote)
issueKey ABC-123 (branch name)
repo repo-1 (git remote)
profile profile-a (git remote) — connected
$ devflow where --json # same thing, machine-readableIt never routes and never acts, so it still reports when nothing matches (or when the desktop app isn't running) instead of failing.
The owning Chrome profile must be open
Provider credentials live in the browser extension, so the profile that owns a
project is what actually executes the action. When the daemon knows the profile
but it isn't connected, the CLI says so by name rather than a bare NO_PROFILE:
error [NO_PROFILE]: Chrome profile "profile-a" owns ABC but is not connected,
so this action cannot run. Open that Chrome profile and retry.devflow where flags the same condition up front.
Prerequisites
- The DevFlow desktop app must be running — it hosts the gateway and writes the token. v1 also needs the browser extension connected so a profile owns project keys.
- Node >= 20.
Install / run
Published to npm as @devflow-one/cli — no clone, no build:
npm i -g @devflow-one/cli # exposes `devflow` on your PATH
devflow --helpOr run it without installing:
npx -y @devflow-one/cli --helpFrom a clone of the monorepo (contributors), the CLI is dependency-free and runs straight from the repo:
node cli/bin/devflow.js --help
cd cli && npm link # or link it onto your PATH as `devflow`Token & port (zero-config)
The CLI discovers the gateway automatically — no setup — by reading the token file the desktop daemon writes on first run, under its userData dir:
| Platform | Token path |
| -------- | -------------------------------------------------------------- |
| macOS | ~/Library/Application Support/devflow/gateway-token |
| Linux | ~/.config/devflow/gateway-token |
| Windows | %APPDATA%\devflow\gateway-token |
The file is written owner-only (0600). Override for CI / non-standard installs:
| Env var | Purpose |
| ----------------------- | --------------------------------------------------- |
| DEVFLOW_GATEWAY_TOKEN | Provide the token directly (skips the file lookup). |
| DEVFLOW_GATEWAY_PORT | Gateway port (default 8787). |
Routing defaults can also come from the environment — handy in CI or a shell
profile. They lose to the equivalent flag and win over anything inferred:
DEVFLOW_PROJECT, DEVFLOW_ISSUE, DEVFLOW_REPO, DEVFLOW_PROFILE.
Usage
# discovery
devflow list-actions
devflow list-profiles # connected profiles + owned keys
devflow where # what this directory resolves to
# inside a configured repo, routing is inferred — no flags
devflow mr find # issue key from the branch
devflow branch create feat/x
# or route explicitly, overriding whatever the directory implies
devflow branch create feat/x --project ABC
devflow mr create "Title" feat/x --body "desc" --project ABC
# target branch: the repo's configured base branch by default (a {year}/{number}
# pattern is expanded to the newest matching remote branch), or pick one
devflow mr create "Title" feat/x --base release/2026.4 --project ABC
# PR type: supplies the description/title templates and the labels the PR is
# stamped with. Omitted or unrecognized, it is auto-selected by matching configured
# type names against the title's conventional prefix, branch prefix, Jira issue type
# and title keywords, falling back to the first type. Pass its id or label to pick one.
devflow mr create "Title" feat/x --type Bugfix --project ABC
devflow mr types --project ABC # configured types: id, label, default flag
# issue key: parsed from the head branch by default; --issue sets it explicitly
# ({parentWithChildIssueKeys}/{issueKey}/{issueTitle} template tokens) for a branch without one.
# {issueTitle} needs the Jira API token set in Settings, else it renders empty.
devflow mr create "Title" feat/x --issue ABC-123
# routed by issue key (project key = prefix before the last '-')
devflow mr find ABC-123
devflow pipeline status 42 deadbeef --issue ABC-9
# commit a managed worktree with the project's commit-message template
devflow commit template /wt/ABC-123 --values '{"summary":"fix login"}' --project ABC
devflow commit template /wt/ABC-123 --no-push --project ABC # commit locally, skip push
# assembled fix-review-comments prompt (read-only; runs no AI, makes no writes)
devflow mr fix-prompt 42 # issue key inferred from the branch
# notify the project's configured Slack channel(s)
devflow slack notify "Review fixes ready on {pr}" --values '{"pr":"#42"}' --project ABC
# force a specific profile when a key is ambiguous
devflow mr ready 12 --profile prof-1Destructive actions
branch delete, mr close and mr merge are destructive. They are refused
unless you confirm with --force (or --yes); the gateway enforces the same
gate server-side, so the confirmation can't be bypassed:
devflow branch delete old --project ABC # refused
devflow branch delete old --project ABC --force # executes
# merge a PR/MR, squashing and cleaning up the source branch
devflow mr merge 42 --method squash --delete-source-branch --forceInference never softens this gate — a destructive command run with fully
inferred routing still refuses without --force. Confirmation is always yours
to give explicitly, precisely because inference makes mis-targeting easier.
Verify
npm test # unit: arg parsing, command resolution, routing, destructive gateReleasing
Publishing this package to npm is a manual, dry-run-first process — see
RELEASING.md at the repo root.
