connect-flow
A minimal IaC toolkit for Amazon Connect contact flows: promote a flow between environments, verify it, and keep its layout readable.
A CLI built around one idea: a contact flow JSON file in your repo should be
an environment-neutral source of truth, and each environment (dev, test,
prod) a rendering of it. copy/normalize translate the environment-specific
IDs and ARNs baked into a flow so one file can be promoted anywhere;
diff/lint/digest verify it along the way; prettify auto-arranges its
block layout without changing its behaviour.
Why this exists
The problem. A contact flow exported from Amazon Connect is full of environment-specific identifiers baked directly into the JSON — Lex bot alias ARNs, Wisdom AI-agent ARNs, Lambda ARNs, sibling contact-flow/ flow-module/queue/prompt ARNs. Promoting a flow from dev to test to prod means finding and swapping every one of them, by hand, in a multi-thousand- line file — a process that's slow, error-prone, and leaves no record of which identifiers changed or why. And once a flow is edited in three places, there's no cheap way to tell whether the copy in git still matches what's actually deployed.
The solution. connect-flow copy/normalize translate a flow's
identifiers against a per-environment map file, so one JSON file in git
becomes the environment-neutral template and each environment a rendering of
it — see Promoting a flow between
environments below.
diff/lint/digest answer "has this drifted" and "is this well-formed"
without a console round trip. And because flows in git get edited by
several people, their layout rots too — crossing edges, error paths
visually competing with the happy path, blocks wherever they happened to
land mid-edit — which is what prettify is for.
Every rewrite this tool makes, whether it's an identifier or a coordinate,
goes through the same discipline: patch only the literals that are supposed
to change, in place, with a targeted regex — never json.dumps over the
whole document, which would rewrite every line and bury any accidental
behavioural change inside an unreadable diff. A semantic digest (a hash of
everything behavioural) is taken before and after every write; if it
doesn't match what the operation is supposed to produce, nothing is written
and the tool reports a safety violation instead. See
docs/safety.md for the full mechanism.
So a layout rewrite's diff is exactly the numbers that moved:
"ActionMetadata": {
"560e67f4-4322-4788-831e-e49c2a0e7dd2": {
- "position": { "x": 1284, "y": 61 },
+ "position": { "x": 240, "y": 120 },
"children": ["d3ee852c-65de-4180-95a6-c8bd1f10b5bf"]
},
And the layout itself goes from this to this:
BEFORE — edited by hand over a year AFTER — one command
[Play prompt] [Disconnect] [Entry]
│ ┌────────────────┘ ▲ │
[Entry] │ [Error handler] │ ▼
└───┼────┐ │ ▲ │ [Get input] ──► [Compare] ──► [Play prompt]
[Compare]│ [Get input]─┘ │ ╷ ╷ │
│ └──────┘ └──────┘ └┄┄┄┄┄┬┄┄┄┄┄┄┄┄┘ ▼
└───────────────────────┘ [Error handler] [Disconnect]
happy path zig-zags, error paths happy path straight and left-to-right,
cross it and each other error paths pushed below, out of the way
What you get
connect-flow uses git-style subcommands. Every one of them takes a
TARGET that is either a local path or a live flow / flow-module
ARN, interchangeably — so diff-ing a file in your repo against what's
actually running in production is one command, not a download step plus a
comparison step.
| subcommand | purpose |
|---|---|
connect-flow copy --from SOURCE --to DEST [--from-map FILE] [--to-map FILE] [--apply] |
promote a flow 1:1, translating identifiers through an intermediate <<...>> token form; a live --to is previewed unless --apply |
connect-flow normalize TARGET... [--reverse] [--map FILE] |
the strictly offline form of the same translation — local files only, no AWS |
connect-flow prettify TARGET... |
rewrite block positions into a readable layout |
connect-flow diff TARGET_A [TARGET_B] |
semantic diff between two targets, local and/or remote |
connect-flow lint TARGET... |
structural checks: dangling transitions, unreachable actions, empty branches |
connect-flow digest TARGET... |
print each target's semantic digest — "is my file still what's live?" |
connect-flow list [--instance ID-OR-ALIAS --flows|--flow-modules] |
list instances, or flows/flow modules within one, or describe one via --flow/--flow-module |
Without --from-map/--to-map, copy is a byte-verbatim transfer — fetch a
remote flow to disk, or upload a local file to a remote flow, with no
identifier translation at all.
--profile/--region/--json/--quiet are accepted either before or after
the subcommand, aws-cli style: connect-flow --profile lab --region us-east-1 list and connect-flow list --profile lab --region us-east-1 are
equivalent.
How a run works
SOURCE (--from) DEST (--to)
─────────────── ───────────
┌─► local file (+ .bak)
local file ─┐ │
├─► parse ─► translate/layout ─► patch text ──┼─► live ARN, preview only
live ARN ──┘ │ (from-map/to-map, (regex, │ (--apply to write)
│ dot) in place) │
└──► digest ── compare ──┘ └─► stdout (--emit-map only)
│
mismatch ───► refuse to write, exit 2
prettify is a different translate/layout step over the same pipeline —
geometry only, no identifier maps — with the same digest safety gate.
Promoting a flow between environments
copy and normalize are two thin argument shapes over one engine: read a
flow, substitute identifiers against a map file, optionally re-layout, write
it somewhere. They differ only in which sources/destinations are legal:
| verb | source | destination | touches AWS |
|---|---|---|---|
copy |
local, live ARN, or INSTANCE/NAME (--from) |
local file, or live (--to, previewed unless --apply) |
reads (+ writes with --apply) |
normalize |
local file only | local only | never |
copy translates in one direction per map: --from-map normalises
--from's real identifiers to <<...>> placeholders, --to-map resolves
those placeholders to --to's real identifiers — both are optional and
independent, so a single copy can do either half, both, or neither
(a verbatim transfer). normalize instead takes one map and a --reverse
flag, since it always has exactly one local file on both ends.
The map file
One JSON file per environment, in the shape
{"<<ARN:Some Name>>": "arn:aws:...", "<<ID:Some Name>>": "22222222-..."}
— flat, or grouped into named sections ({"lexv2": {...}, "others": {...}});
both are accepted, and a grouped file's section names are preserved when a
skeleton is emitted. A plain, non-<<...>> key such as IVR_BOT_AGENT_ID
works too — a placeholder is whatever the map says it is, not only a
<<...>> token.
Bootstrap a map for a flow that doesn't have one yet
--emit-map discovers every ARN-shaped literal and documented bare-id field
in a flow and writes them as a map — nothing is written to the flow itself.
Each entry is named automatically wherever a name is available: a Lambda ARN's
function name is parsed straight out of the ARN (works even from normalize,
which never touches AWS); a ContactFlowId/FlowModuleId/QueueId is
resolved to its live Name via a Describe call against copy's --from
instance (turned off with --no-auto-resolve, same flag as live-to-live
copy). Anything with no name available — like the Lex bot-alias ARN below —
falls back to a <<ARN:RENAME-ME-HASH>>/<<ID:RENAME-ME-HASH>> key, HASH a
short hex digest of the value itself (so the key a given value gets doesn't
depend on the order flows/modules were fed into --emit-map).
--to is still required but never written to in this mode:
$ connect-flow copy --from contact-flows/main-ivr.json --to flows/main-ivr.json \
--emit-map maps/dev.json
$ cat maps/dev.json
{
"discovered": {
"<<ARN:RENAME-ME-8ed6922a>>": "arn:aws:lex:ap-southeast-2:000000000000:bot-alias/22222222-2222-2222-2222-222222222222/33333333-3333-3333-3333-333333333333"
}
}
Rename any remaining RENAME-ME keys by hand to something meaningful — the
edited file is that environment's map. Running --emit-map again against
the same FILE — the natural shape of a loop, once per downloaded flow or
module — merges in, rather than overwrites: existing entries and groups are
kept as-is, and only genuinely new values are appended, so the file
accumulates into one shared map across the loop.
dev → committed template → test
# dev's live flow -> an environment-neutral file, committed to the repo
connect-flow copy --from arn:aws:connect:ap-southeast-2:000000000000:instance/1111.../contact-flow/2222... \
--from-map maps/dev.json --prettify --to flows/main-ivr.json --profile dev
# template -> test instance (maps/test.json holds test's real values for the same keys);
# previewed first, applied once the diff looks right
connect-flow copy --from flows/main-ivr.json --to-map maps/test.json \
--to arn:aws:connect:ap-southeast-2:000000000000:instance/1111.../contact-flow/2222... \
--profile test
connect-flow copy --from flows/main-ivr.json --to-map maps/test.json \
--to arn:aws:connect:ap-southeast-2:000000000000:instance/1111.../contact-flow/2222... \
--profile test --apply --publish --yes
copy --to, whether previewing or applying, refuses (exit 2) if the
destination's live content no longer normalises back to the source's
template form through --to-map — i.e. someone edited it outside this
pipeline since it was last copied — unless --force.
Offline: re-point or CI-gate a local file, no credentials needed
connect-flow normalize flows/main-ivr.json --reverse --map maps/test.json -o /tmp/rendered.json
connect-flow normalize contact-flows/*.json --map maps/dev.json --check # CI gate
normalize never touches AWS on its own code path — no credentials, no
network calls — which is what makes it the right verb for a pre-commit hook
or a CI job that has no AWS credentials at all.
Unresolved identifiers are a hard error
If a template still contains a <<...>> placeholder — or the resolve/
normalise direction leaves any of the map's own literals in place — after
translation, that's refused (exit 2), not silently written: a template with
a leftover placeholder is exactly the failure mode this feature exists to
catch. --force downgrades it to a warning.
See docs/adr/0001-cross-environment-id-mapping.md
for the design decisions behind this (placeholder style, map format, the
duplicate-value and substring-shadowing rules),
docs/adr/0009-copy-verb.md for copy's
two-pass design, and docs/safety.md for how the rewrite
itself is proven safe.
prettify
Run connect-flow prettify --help for all options.
Example uses:
Reformat all contact-flows in a directory in-place
connect-flow prettify contact-flows/*/*.json --in-place
What a reformat actually looks like
Here's tests/fixtures/messy.json in the Connect designer, before and after
connect-flow prettify — same flow, same behaviour, same digest, only the
block positions rewritten:
Before — the happy path doubles back on itself and the error branches cross it:
After — the happy path runs left to right along the top, error branches drop below it:
Use --render to see that before/after without opening the console
--render writes a STEM.before.png / STEM.after.png pair — connect-flow's
own sketch of the graph, drawing the blocks where the rewrite will put them.
It combines with --check/--dry-run, which write nothing at all, so you can
look before letting the tool near the file:
connect-flow prettify contact-flows/my-flow.json --dry-run --render --render-dir /tmp/renders
The report says whether the layout actually got better, in numbers:
$ connect-flow prettify tests/fixtures/messy.json --dry-run --render --render-dir /tmp/renders
CHANGED tests/fixtures/messy.json
read: tests/fixtures/messy.json
wrote: rendered
before: crossings=2 backward_happy_edges=4 overlaps=0 canvas=1670x1020
after : crossings=0 backward_happy_edges=0 overlaps=0 canvas=1050x680
Edge crossings and backward happy-path edges both to zero, on the same flow pictured above.
diff
Run connect-flow diff --help for all options.
diff compares two targets semantically — it answers "does this behave
differently", not "are these files byte-identical". Layout, key order and
designer churn are normalised away first (see Flow normalisation
explained below). Either side can be a local
path, a live ARN, or INSTANCE/NAME-OR-ID addressing (--kind {flow,flow-module}, default flow, disambiguates which resource the name
refers to — see docs/adr/0010-diff-instance-addressing.md),
so file↔file, file↔live and live↔live all work the same way. It exits 1
when the two differ, which makes it usable as a CI check.
Example uses:
See what actually changed between two flows
Here tiny-edited.json is a copy of the fixture with one prompt reworded:
$ connect-flow diff tests/fixtures/tiny.json tiny-edited.json
A: tests/fixtures/tiny.json
B: tiny-edited.json
~ e5565ba7-ebc9-43da-8d9e-94412c33ab4b
--- e5565ba7-ebc9-43da-8d9e-94412c33ab4b (tests/fixtures/tiny.json)
+++ e5565ba7-ebc9-43da-8d9e-94412c33ab4b (tiny-edited.json)
@@ -1,7 +1,7 @@
{
"Identifier": "e5565ba7-ebc9-43da-8d9e-94412c33ab4b",
"Parameters": {
- "Text": "Hello!"
+ "Text": "Hello, welcome to support!"
},
"Transitions": {
"Conditions": [],
A per-Identifier summary (+ added, - removed, ~ changed) followed by a
unified diff of just the affected blocks — rather than of the whole file,
where one edited prompt would be buried among thousands of unchanged lines.
--json emits the same structurally.
Confirm a reformat changed nothing
Two files with different layouts and identical behaviour are identical to
diff, which is the whole point:
$ connect-flow diff tests/fixtures/messy.json tests/fixtures/messy.pretty.json
identical: tests/fixtures/messy.json == tests/fixtures/messy.pretty.json
Has my repo drifted from what's really deployed?
connect-flow diff contact-flows/main-flow.json \
arn:aws:connect:ap-southeast-2:000000000000:instance/1111.../contact-flow/2222... \
--profile prod
Compare the same flow across two environments by name
INSTANCE/NAME-OR-ID addressing (ADR 0008) works on either side, avoiding
the need to look up ARNs by hand:
connect-flow diff \
dev-instance/"CSC Support Flow" \
prod-instance/"CSC Support Flow" \
--profile prod
Did someone leave an unpublished draft in the console?
--both-stages compares one flow's saved draft against its own published
content — a single ARN, no second target:
connect-flow diff arn:aws:connect:ap-southeast-2:000000000000:instance/1111.../contact-flow/2222... \
--both-stages --profile prod
Compare a Terraform template against the deployed flow
A .json.tftpl is full of ${...} placeholders that will never match the
resolved ARNs in the live flow. --normalize-opaque replaces every
placeholder and every ARN-shaped literal with one <opaque> token before
comparing, so the structural comparison still works:
connect-flow diff contact-flows/main-flow.json.tftpl \
arn:aws:connect:ap-southeast-2:000000000000:instance/1111.../contact-flow/2222... \
--normalize-opaque --profile prod
It's off by default because it deliberately hides a real class of bug — the wrong ARN wired into a block — so reach for it only when comparing a template to its rendered result.
Compare the same flow lineage across two instances
A flow copied from one instance to another keeps its own identity, but
every ContactFlowId/FlowModuleId/QueueId it references — an "Invoke
Flow Module" block, for example — gets a fresh id in the new instance, even
when the referenced flow/module/queue is genuinely "the same one" by name.
--normalize-opaque also resolves these against each target's own instance
(a live Describe* call), replacing the id with <<FlowModule:Name>>
(etc.) — so two references that both resolve to the same Name stop looking
like a diff, while a reference that resolves to a different Name still is
one:
connect-flow diff arn:aws:connect:ap-southeast-2:000000000000:instance/1111.../contact-flow/2222... \
arn:aws:connect:ap-southeast-2:000000000000:instance/3333.../contact-flow/4444... \
--normalize-opaque --profile prod
This only applies to a remote (ARN) target — there's no instance to resolve a bare id against for a local file. A reference id that can't be resolved (deleted resource, no permission) is left as-is and a warning is printed, rather than silently hidden.
lint and digest
Run connect-flow lint --help / connect-flow digest --help for all
options. Both are strictly read-only — there is no writer path in either, so
neither can ever modify a flow, local or live.
Example uses:
Catch broken wiring before it reaches Connect
lint reports dangling transitions (a NextAction/Conditions[]/Errors[]
entry pointing at an Identifier that doesn't exist), blocks unreachable from
StartAction, a missing StartAction, and Compare/DistributeByPercentage
blocks with no conditions at all. It exits 1 if any target has findings:
$ connect-flow lint tests/fixtures/unreachable.json
ISSUES tests/fixtures/unreachable.json
unreachable: '4008265c-66ba-4495-b465-9c586352ff0b' is not reachable from StartAction
unreachable: '41b9ba6c-fef3-48dd-849c-425afcd58a7c' is not reachable from StartAction
$ connect-flow lint tests/fixtures/messy.json
OK tests/fixtures/messy.json
These are the mistakes a hand-edited or templated flow makes and the console never shows you, because you only find out when a call falls off the end of a branch at 2am.
Answer "is my file still what's live?" in one line
digest prints the semantic hash of each target — the same normalised form
diff compares — so a whole flow reduces to one comparable string:
$ connect-flow digest tests/fixtures/messy.json tests/fixtures/messy.pretty.json tests/fixtures/tiny.json
2afe14e5f2b6ec64122ab9e03e866d343a96ce43ff4f259ece5d166b988cf1b1 tests/fixtures/messy.json
2afe14e5f2b6ec64122ab9e03e866d343a96ce43ff4f259ece5d166b988cf1b1 tests/fixtures/messy.pretty.json
50c2bbe077f06890311f33d13d9ece8d39290b957d685eeb83c52d8fddfae72d tests/fixtures/tiny.json
The first two are the messy and prettified versions of one flow: different files, different layouts, one digest. That's the property the whole tool rests on.
Mixing local and remote targets in a single invocation is the quick "did anything drift" sweep — no diff to read, just two strings to compare:
connect-flow digest contact-flows/main-flow.json \
arn:aws:connect:ap-southeast-2:000000000000:instance/1111.../contact-flow/2222... \
--profile prod
Both accept --stage {published,saved} (default: published) for remote
targets, and the same --instance/--flow/--flow-module
addressing as prettify.
Flow normalisation explained
diff and digest both work on one canonical form of a flow rather than on
its raw JSON, and prettify uses the digest of that same form as its safety
check. Normalisation is what makes all three trustworthy: without it, a flow
re-saved in the console with no edit at all would look "changed" every time,
and a tool that cries wolf on every save gets ignored.
What gets normalised away, and why none of it is behavioural:
| normalised | why it isn't a behavioural difference |
|---|---|
all geometry — position, entryPointPosition, annotation size |
pure layout; the whole point of prettify is to change exactly this and nothing else |
Actions[] array order |
Connect dispatches by Identifier reference, not array position, so the order blocks appear in the file means nothing |
| JSON key order and formatting | whitespace and key ordering carry no meaning to Connect |
conditionMetadata[].id on Compare/DistributeByPercentage |
designer display decoration with no counterpart in Transitions.Conditions[], and Connect regenerates it on every save even with no edit |
a compound block's hidden child Identifier (diff only) |
e.g. inside a "Create Wisdom Session" block; re-randomised on every save, so diff aliases it to <parent-identifier>#<index> and compares that |
${...}/<<...>> placeholders and ARN literals (diff --normalize-opaque, opt-in) |
lets a Terraform template, or a copy/normalize token-form template, be compared to its rendered result; off by default because it also hides a genuinely wrong ARN |
ContactFlowId/FlowModuleId/QueueId referenced from inside a flow, resolved to the referenced resource's Name (diff --normalize-opaque, remote targets only) |
these ids aren't stable across instances, only the Name is — see ADR 0007 |
the whole designer layer — ActionMetadata, its key order, Annotations, snapToGrid (diff --functional, opt-in) |
affects only how the console draws the flow; it's rewritten on every save, so two snapshots of an unedited flow always differ here — see ADR 0016 |
The last two rows are orthogonal axes and compose — --functional chooses
which fields are compared, --normalize-opaque chooses how values inside the
surviving fields are matched:
# same instance: is the saved draft functionally the published flow?
connect-flow diff 'arn:...:contact-flow/2222...' 'arn:...:contact-flow/2222...:$SAVED' --functional
# across instances: same logic, different queue/flow ids either side
connect-flow diff dev.json 'arn:...:contact-flow/3333...' --functional --normalize-opaque
What is deliberately not normalised, because it is behavioural:
Transitions.Conditions[]order — Connect evaluates conditions first-match, so reordering two branches changes which one runs.ActionMetadatakey order — not behavioural at runtime, but it's tracked anyway in the digest (as a separateActionMetadataOrderfield), because the writer must prove its own rewrite didn't disturb it. Passdiff --functionalto leave it, and the rest of the designer layer, out of a comparison between two independently-saved flows.- Everything else —
Parameters, blockType,StartAction,Settings, annotations' text, and any unrecognised top-levelMetadatakey are all carried into the canonical form verbatim. The digest is sensitive to every one of them by design; seedocs/safety.mdfor how that's tested rather than merely intended.
Working against live Amazon Connect instances
connect-flow extends the same "provably no behavioural change" guarantee
across the network: it can fetch a flow or flow module straight from a live
Connect instance, prettify it, and push it back, with a rollback point and
post-write verification.
AWS credentials
Use the usual --profile and/or --region cli parameters, or AWS_* environment
variables.
connect-flow list on lists the Connect instances visible to those credentials;
add --instance plus --flows or --flow-modules to list resources within one instead:
connect-flow list --region us-east-1 --profile lab
connect-flow list --instance 11111111-1111-1111-1111-111111111111 \
--flow-modules --region us-east-1 --profile lab
--instance accepts either a bare instance id or an alias, resolved via an
exact (case-sensitive) name match — no separate flag for each. Pass
--flow/--flow-module to list instead to describe that one flow — its
ARN, description, version and last-modified time (including the instance it
lives in) — rather than enumerating everything in the instance; each also
accepts either a bare id or a name, resolved the same way:
connect-flow list --instance 11111111-1111-1111-1111-111111111111 \
--flow "Main Menu" --region us-east-1 --profile lab
A remote target can also be addressed via --instance plus a repeatable
--flow/--flow-module, instead of a full ARN, with --region
required in that form:
connect-flow prettify --instance lab-instance \
--flow-module "Main Menu" --region us-east-1 \
--dry-run --diff --profile lab
--flow/--flow-module also accept a full ARN, or a value of the form
INSTANCE/NAME-OR-ID that names its own instance (id or alias) — useful for
addressing a flow in a different instance than --instance, or without
--instance at all:
connect-flow list --flow "lab-instance/Main Menu" --region us-east-1 --profile lab
A remote write always defaults to the unpublished draft (:$SAVED), never
the live content — pass --publish to write live instead:
# writes a draft only, with a rollback snapshot; live content is untouched
connect-flow prettify arn:aws:connect:us-east-1:000000000000:instance/1111.../flow-module/2222... \
--in-place --profile prod
# writes the live content, unattended
connect-flow prettify --instance 1111... --flow-module 2222... --region us-east-1 \
--in-place --publish --yes --profile prod
Every remote write is guarded four ways:
1. version snapshot immutable pre-change copy (--snapshot before|after|both|none)
2. sha256 check taken immediately before the write — refuses on conflict
3. read-back verify re-reads what Connect stored, recomputes its digest
4. confirmation mandatory (--yes) rather than skippable off a TTY
copy --to additionally refuses to upload a file whose digest has drifted
from the destination's live content — a straight digest compare for a
map-less (byte-verbatim) copy, or the reverse-normalisation drift check
described above when --to-map is
given — unless --force. See
docs/safety.md for the full
detail, including the residual race the sha check narrows but can't close.
The pre-change snapshot depends on CreateContactFlow{,Module}Version,
which Connect only actually supports for Campaign-type contact flows — for
anything else it fails outright, and the write is refused with a pointer to
--local-backup DIR (writes the pre-change content to DIR/STEM.json
first, no dependency on that API) or --force (proceeds with no rollback
point at all).
The remote-write guardrail flags (--yes, --force, --publish,
--no-verify, --snapshot, --local-backup) have no short forms — a
remote write is never one keystroke. Plain I/O-destination flags like
--output/-o aren't subject to that: they don't touch a live instance by
themselves, so there's no safety reason to make them harder to type.
Install
pipx install connect-flow
Requires Python 3.11+. The base install only needs boto3, for
live-instance support — boto3 needs no configuration to run
normalize/digest/lint on local files or diff on two local files,
since those never make an AWS call.
prettify, copy --prettify/normalize --prettify, and --render also
need pygraphviz to compute a layout; install it with the graphviz extra:
pipx install "connect-flow[graphviz]"
# or, if connect-flow is already installed:
pipx inject connect-flow pygraphviz
pygraphviz's wheel bundles the Graphviz libraries, so no system Graphviz
install is needed either way. Running a layout command without the extra
installed fails cleanly with an error telling you to install it.
Use it as a pre-commit hook in your flows repo
If you keep exported contact flow JSON in a git repo, connect-flow can run
as a pre-commit hook and fail the commit whenever a flow's layout has
drifted, so the tidy layout is the state the repo is always in, rather than
something someone remembers to run.
This is set up in the repo that holds the flow JSON, not here.
connect-flow ships a .pre-commit-hooks.yaml,
which is the manifest offering the hook to other repos to install.
Add this to your flows repo's own .pre-commit-config.yaml:
- repo: https://gitlab.com/mludvig/connect-flow
rev: master
hooks:
- id: connect-flow
files: ^contact-flows/.*\.json$
## Uncomment to enable in-place re-formatting
# args: [prettify, --in-place]
Set files: to wherever your flow JSON actually lives. The hook's default
pattern matches every .json in the repo, which will also feed it
package.json and friends.
By default the hook runs connect-flow prettify --check: it writes nothing
and fails the commit if a flow file would be reformatted — well suited to a
CI gate too.
Exit codes
| code | meaning |
|---|---|
0 |
success — nothing changed, or a write succeeded |
1 |
--check/diff/lint only: at least one target would change, differs, or has findings |
2 |
flow safety violation (would-be behavioural change), or a target failed to parse / isn't a contact flow |
3 |
bad usage (bad flags, missing args, a directory passed as a target) |
4 |
an AWS/remote failure — credentials, access denied, a sha256 conflict, or a resource that doesn't exist |
Going deeper
docs/design.md— why Graphvizdotrather than a hand-rolled layout engine, with the edge-crossing measurements that settled it.docs/data-model.md— how a flow's ports and happy/error paths are modelled.docs/safety.md— exactly what guarantees "never changes behaviour", and how each one is enforced rather than merely asserted.
License
MIT — see LICENSE.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file connect_flow-0.1.1.tar.gz.
File metadata
- Download URL: connect_flow-0.1.1.tar.gz
- Upload date:
- Size: 204.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.12.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a846eb437fe353feb39443ea8a8585dd6cbe9472de6b90653778321be26011a9
|
|
| MD5 |
f7d994364429d4e05c5bd8e1b8f8540a
|
|
| BLAKE2b-256 |
e6295b7569f20aacdaa2e724e43a491f9a969ed6ffc99aa6508882ee0113e6a5
|
File details
Details for the file connect_flow-0.1.1-py3-none-any.whl.
File metadata
- Download URL: connect_flow-0.1.1-py3-none-any.whl
- Upload date:
- Size: 88.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.12.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8bb3033f7bc80d2f9a29b478301e6186ffe6cfe77eb2e6a945043b582ec9ad43
|
|
| MD5 |
fe3a0fcf4d5777a0a14aa027b62b9209
|
|
| BLAKE2b-256 |
c9daa571d13dfdbda48a4104692cfaf11203a1d61dabb4c242e02661e68c37de
|