@jawache/flow 0.1.2 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +52 -271
- package/dist/flow.mjs +12293 -106
- package/dist/index.mjs +7945 -18
- package/dist/packs.mjs +9477 -115
- package/dist/types/adapter/archive.d.ts +8 -1
- package/dist/types/adapter/claude.d.ts +67 -18
- package/dist/types/adapter/domain.d.ts +991 -154
- package/dist/types/adapter/product.d.ts +5 -0
- package/dist/types/checks/domain.d.ts +134 -72
- package/dist/types/engine/domain.d.ts +75 -17
- package/dist/types/engine/state.d.ts +7 -4
- package/dist/types/index.d.ts +4 -3
- package/dist/types/language/domain.d.ts +227 -21
- package/dist/types/packs/docs.d.ts +17 -14
- package/dist/types/packs/flow.d.ts +15 -5
- package/dist/types/packs/git.d.ts +20 -25
- package/dist/types/packs/justfile.d.ts +3 -3
- package/dist/types/packs/secrets.d.ts +32 -4
- package/dist/types/packs/work.d.ts +22 -6
- package/docs/user/migrations/0.1.0.md +142 -0
- package/docs/user/migrations/0.1.1.md +44 -0
- package/docs/user/migrations/0.1.2.md +58 -0
- package/docs/user/migrations/0.2.0.md +113 -0
- package/docs/user/migrations/0.2.1.md +41 -0
- package/docs/user/migrations/0.2.2.md +104 -0
- package/docs/user/migrations/0.2.3.md +42 -0
- package/docs/user/migrations/0.3.0.md +289 -0
- package/docs/user/migrations/index.md +45 -0
- package/package.json +5 -3
package/README.md
CHANGED
|
@@ -1,308 +1,89 @@
|
|
|
1
1
|
# flow
|
|
2
2
|
|
|
3
|
-
**Guardrails
|
|
4
|
-
what must not happen, notes that arrive when they are useful, and a compiler that refuses the config
|
|
5
|
-
before it ever runs.
|
|
3
|
+
**Guardrails for AI coding agents, written as code.** flow checks every change a coding agent makes to your repo against rules you write in one TypeScript file. When a change breaks a rule, flow refuses it: a banned command never runs, a refused file write is put back, and a refused commit does not land. The agent is told why, in your words. flow works with [Claude Code](https://claude.com/claude-code), and its commit rules run for people too.
|
|
6
4
|
|
|
7
|
-
|
|
8
|
-
history, nothing in common but four letters. This flow is a guard for coding agents.
|
|
5
|
+
Instructions in a `CLAUDE.md` file are advice: an agent can misread or skip them, more often as a session grows long. A flow rule runs every time, at the moment it applies, and a refused action does not happen.
|
|
9
6
|
|
|
10
|
-
|
|
11
|
-
is on by default from 24 (the Active LTS) — there is no build step and there will not be one, because
|
|
12
|
-
a guard that compiles its own config is a guard with a build to go wrong. On an older node, flow
|
|
13
|
-
tells you the version it needs instead of dying on a syntax error.
|
|
7
|
+
Not to be confused with Flow, Meta's JavaScript type checker. No relation.
|
|
14
8
|
|
|
15
|
-
|
|
9
|
+
## Why use flow
|
|
16
10
|
|
|
17
|
-
|
|
11
|
+
- **Every change is checked, whichever tool made it.** flow records the files before each tool call and compares them after. A file written by the Edit tool, a shell command (`sed -i`, a heredoc, a script, a code generator), an MCP tool or a subagent is checked the same way, and a refused file is put back.
|
|
12
|
+
- **A banned command is stopped before it runs.** A force-push, a `git reset --hard`, an `npm install` you did not want: the rule refuses the command, and the agent gets the rule's message instead of the command's output.
|
|
13
|
+
- **Your commit rules hold for everyone.** A git pre-commit hook runs the same rules over the staged changes, whether you, an agent or a merge makes the commit, including files the commit deletes or renames.
|
|
14
|
+
- **Rules are tested code.** Each rule lists the inputs it must let through and the inputs it must refuse. A rule with no refusal case does not load, `flow test` runs every case, and your editor flags a mistyped rule, because the rules are TypeScript.
|
|
15
|
+
- **A broken guard is never silent.** If the config does not load, every guardrail and breadcrumb is off, and the agent is given the load error with every write, delete and command. The commit gate refuses every commit until the config loads.
|
|
16
|
+
- **Notes arrive when they matter.** A breadcrumb is a note shown to the agent the first time it reads or changes a file in a given area, instead of one long instruction file read once at the start.
|
|
17
|
+
- **Rules can depend on who is acting.** A rule can apply only to subagents, or let only one named agent write a folder. flow identifies an agent from what Claude Code records about it, never from what the agent says about itself.
|
|
18
|
+
- **Ten packs come with it.** Rules for git, node, TypeScript, secrets, the justfile, docs, tests, code layout, agent workflow and the guard itself, each turned on with one line and adjusted with `override(…)`.
|
|
18
19
|
|
|
19
|
-
|
|
20
|
-
should not — you notice that within a minute. It is a rule that reads as armed, is counted as armed,
|
|
21
|
-
and matches nothing: a glob for a folder that was renamed, a script that no longer imports, a fence
|
|
22
|
-
pointed the wrong way. That config is indistinguishable from a working one until the day it was
|
|
23
|
-
supposed to save you.
|
|
20
|
+
## What a rule looks like
|
|
24
21
|
|
|
25
|
-
|
|
22
|
+
This rule is in the demo config `flow init` writes:
|
|
26
23
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
## Install
|
|
35
|
-
|
|
36
|
-
In the repo you want guarded, as a dev dependency — so every clone and every CI run gets the same
|
|
37
|
-
flow, pinned in your lockfile:
|
|
38
|
-
|
|
39
|
-
```sh
|
|
40
|
-
npm i -D @jawache/flow
|
|
41
|
-
npx flow init
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
Or on your PATH, for a folder with no `package.json` of its own — which is what the five-minute
|
|
45
|
-
walkthrough below uses:
|
|
46
|
-
|
|
47
|
-
```sh
|
|
48
|
-
npm i -g @jawache/flow
|
|
24
|
+
```ts
|
|
25
|
+
noForcePush: guardrail()
|
|
26
|
+
.at(command)
|
|
27
|
+
.check(banCommands({ ban: ["git push --force", "git push -f", "git push --force-with-lease"] }))
|
|
28
|
+
.message("Force-pushing rewrites history everyone else has. Push a correcting commit, or ask first.")
|
|
29
|
+
.test({ pass: ["git push origin main"], block: ["git push --force origin main", "git push --force-with-lease origin main"] }),
|
|
49
30
|
```
|
|
50
31
|
|
|
51
|
-
|
|
52
|
-
does not resolve from the repo — a global install, a bare demo folder — init links the flow that is
|
|
53
|
-
running and says so on the line. Install it as a dependency instead and that step is simply absent.
|
|
54
|
-
|
|
55
|
-
To work *on* flow rather than with it: clone it, `just install`, `just link` — the binary then runs
|
|
56
|
-
from your checkout, undone with `npm unlink -g @jawache/flow`.
|
|
57
|
-
|
|
58
|
-
## Five minutes, from nothing
|
|
32
|
+
`.at(command)` says when the rule runs: before every shell command. `.check(…)` is what it checks, `.message(…)` is what the agent is told, and `.test(…)` lists the cases `flow test` runs. When an agent tries `git push --force origin main`, the command does not run, and the agent is shown:
|
|
59
33
|
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
flow init
|
|
63
|
-
```
|
|
34
|
+
```text
|
|
35
|
+
flow — command blocked before it ran:
|
|
64
36
|
|
|
65
|
-
|
|
37
|
+
✗ demo.noForcePush · git push --force origin main
|
|
38
|
+
Force-pushing rewrites history everyone else has. Push a correcting commit, or ask first.
|
|
39
|
+
matches banned `git push --force`
|
|
66
40
|
|
|
67
|
-
|
|
68
|
-
flow init — created:
|
|
69
|
-
+ flow.config.ts
|
|
70
|
-
+ .githooks/pre-commit
|
|
71
|
-
+ .flow/ — flow's own state, self-ignoring, never committed
|
|
72
|
-
+ node_modules/@jawache/flow → …/flow (a link to the flow that is running — nothing here resolved @jawache/flow)
|
|
73
|
-
+ ~/.claude/settings.json — SessionStart · PreToolUse · PostToolUse · Stop
|
|
74
|
-
+ git config core.hooksPath .githooks
|
|
75
|
-
next: `flow status` — what is bound, and what is not wired yet.
|
|
41
|
+
Adjust the command, then retry.
|
|
76
42
|
```
|
|
77
43
|
|
|
78
|
-
|
|
44
|
+
## Get started
|
|
79
45
|
|
|
80
|
-
|
|
81
|
-
| --- | --- |
|
|
82
|
-
| `flow.config.ts` | flow's own `flow` pack plus a demo guard you can read in one screen — the only file that turns anything on |
|
|
83
|
-
| `.githooks/pre-commit` | the commit gate. Never written over one you already have |
|
|
84
|
-
| `.flow/` | flow's own state and logs. It ignores itself, so your `.gitignore` is untouched |
|
|
85
|
-
| `node_modules/@jawache/flow` | a link to the flow that is running, because this folder resolves no `@jawache/flow` of its own. `npm i -D @jawache/flow` and this row disappears |
|
|
86
|
-
| the host's `settings.json` | four hook registrations, so a coding agent in any repo asks flow first |
|
|
87
|
-
| `core.hooksPath` | what makes git run the gate. It is per-clone, so every fresh checkout runs `flow init` |
|
|
88
|
-
|
|
89
|
-
`flow init --empty` gives you the same wiring with no opinions, for a repo that knows what it wants.
|
|
90
|
-
Run `flow init` again whenever you like — it adds only what is missing and says so.
|
|
91
|
-
|
|
92
|
-
Now look at what you have:
|
|
46
|
+
flow needs Node 24 or later, git, and Claude Code for the session rules. Install it globally, then run it in the repo you want guarded:
|
|
93
47
|
|
|
94
48
|
```sh
|
|
49
|
+
npm i -g @jawache/flow
|
|
50
|
+
cd my-repo
|
|
51
|
+
flow init
|
|
95
52
|
flow status
|
|
96
53
|
```
|
|
97
54
|
|
|
98
|
-
|
|
99
|
-
flow is ON — 4 guardrails · 2 breadcrumbs, every one resolved and able to fire
|
|
100
|
-
config /tmp/flow-demo/flow.config.ts
|
|
101
|
-
state /tmp/flow-demo/.flow
|
|
102
|
-
session no live session has marked this worktree yet
|
|
103
|
-
categories subagent — what the entries below bind to
|
|
104
|
-
session
|
|
105
|
-
🍞 flow.orientation
|
|
106
|
-
This repo is guarded by flow. Guardrails block risky edits before they land (and again at commit); …
|
|
107
|
-
touch
|
|
108
|
-
🍞 flow.editingTheGuardrails — on flow.config.ts .claude/settings.json .claude/settings.local.json .claude/agents/** .claude/skills/**
|
|
109
|
-
You are editing the guardrails themselves. If this edit weakens, disables or removes a guardrail or…
|
|
110
|
-
delete
|
|
111
|
-
✗ flow.noDeleteGuardrails — on flow.config.ts .githooks/pre-commit
|
|
112
|
-
That command would delete this repo's guard surface (flow.config.ts binds every rule; the packs it …
|
|
113
|
-
command
|
|
114
|
-
✗ demo.noForcePush
|
|
115
|
-
Force-pushing rewrites history everyone else has. Push a correcting commit, or ask first.
|
|
116
|
-
commit
|
|
117
|
-
✗ demo.noMarkedFiles
|
|
118
|
-
A file carrying the do-not-commit marker is staged. Take the marker out, or unstage the file.
|
|
119
|
-
✗ demo.chatCommits — for subagent
|
|
120
|
-
A subagent does not commit — hand the change back to the chat that spawned it.
|
|
121
|
-
✓ config: /tmp/flow-demo/flow.config.ts
|
|
122
|
-
✓ commit-gate: .githooks/pre-commit runs `flow commit` over the staged set
|
|
123
|
-
✓ hooks-path: core.hooksPath = .githooks
|
|
124
|
-
✓ hooks: ~/.claude/settings.json — SessionStart · PreToolUse · PostToolUse · Stop
|
|
125
|
-
green — every rule loads, every fitting is in place.
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
Both blocks above are the real thing: the suite runs `flow init` and `flow status` in a throwaway
|
|
129
|
-
repo and pins them against this file, with only the three paths (this repo, your home, the checkout)
|
|
130
|
-
standing in for the ones you will see.
|
|
131
|
-
|
|
132
|
-
Green means guarded. Anything not true yet is a red line carrying its own fix, and `flow status`
|
|
133
|
-
exits non-zero — so a setup script can just ask.
|
|
134
|
-
|
|
135
|
-
Then prove each rule to yourself, in this order. Each step takes seconds.
|
|
136
|
-
|
|
137
|
-
**1 — the notes arrive.** Open a coding session in `/tmp/flow-demo` and say hello. The
|
|
138
|
-
orientation breadcrumb is injected into the session as context, before the agent does anything.
|
|
139
|
-
|
|
140
|
-
**2 — a banned command is refused.** Ask the agent to force-push the branch. It is stopped
|
|
141
|
-
*before the command runs*, with the rule's own sentence, and there is no way around it.
|
|
142
|
-
|
|
143
|
-
**3 — the commit gate holds.** Have the agent write a file containing the literal marker
|
|
144
|
-
`DO`-`NOT`-`COMMIT` (spelled whole), stage it, and commit. Git refuses at the hook, naming the rule
|
|
145
|
-
and the file. Take the marker out and the same commit lands.
|
|
146
|
-
|
|
147
|
-
**4 — a rule bound to WHO, not what.** Ask the main chat to make a small commit: it lands. Ask it to
|
|
148
|
-
delegate the identical commit to a subagent: refused. Same act, different wearer, different answer —
|
|
149
|
-
that is `.for(subagent)`, and the category recognises itself from what the host wrote, never from
|
|
150
|
-
anything the session claimed about itself.
|
|
151
|
-
|
|
152
|
-
**5 — break it, and watch everything stop.** Delete a `.message(…)` from `flow.config.ts` and save.
|
|
153
|
-
Your editor goes red on that line. Ask the agent to edit any file, or to run any command: both are
|
|
154
|
-
refused, with the load error on screen. Undo the edit and the next action passes. There is no
|
|
155
|
-
notice-and-proceed anywhere.
|
|
156
|
-
|
|
157
|
-
The one exception is the repair: while the config is broken, a write to `flow.config.ts` — or to a
|
|
158
|
-
pack it imports, whatever you called that folder — still goes through. The surface is read out of
|
|
159
|
-
the config's own relative import lines, as text, because the module is the thing that will not load.
|
|
160
|
-
Without the exception the rule demanding a fix would also forbid it — you would never notice,
|
|
161
|
-
because your editor has no hooks in front of it, but anything that does is locked out of its own
|
|
162
|
-
repair. Commands stay refused, and so does the commit gate, so nothing written under the exception
|
|
163
|
-
reaches a commit until the config loads green.
|
|
164
|
-
|
|
165
|
-
**6 — run every rule's own cases.**
|
|
166
|
-
|
|
167
|
-
```sh
|
|
168
|
-
flow test
|
|
169
|
-
# flow test — 7 cases over 4 guardrails, all green
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
Delete a rule's `.test(…)` block and the config stops loading at all, naming the entry.
|
|
173
|
-
|
|
174
|
-
## Proving the packs themselves
|
|
175
|
-
|
|
176
|
-
`flow test` proves the rules in *your* config. One command proves the ten this package ships, and it
|
|
177
|
-
is the one to run before you trust a change to any of them:
|
|
178
|
-
|
|
179
|
-
```sh
|
|
180
|
-
just test-packs
|
|
181
|
-
```
|
|
182
|
-
|
|
183
|
-
It builds the bundles, runs `flow init` in a throwaway repo that has never heard of flow, binds every
|
|
184
|
-
shipped pack at once with a stranger's parameters — one shell script for the toolchain, `core/` for
|
|
185
|
-
the pure home, `rules/` for the repo's own pack — and then drives the rails: `flow status` green,
|
|
186
|
-
each pack's cases run pack by pack, and one real refusal at each of write · delete · command ·
|
|
187
|
-
commit · turn-end. It prints what it bound and what refused, and exits non-zero naming the pack the
|
|
188
|
-
moment a count moves.
|
|
189
|
-
|
|
190
|
-
## The config
|
|
191
|
-
|
|
192
|
-
Open `flow.config.ts`. It is ordinary code, and it is the whole guard:
|
|
193
|
-
|
|
194
|
-
```ts
|
|
195
|
-
import { commit, command, defineCategory, defineConfig, definePack, guardrail, pack } from "@jawache/flow";
|
|
196
|
-
import { flow } from "@jawache/flow/packs";
|
|
197
|
-
|
|
198
|
-
const subagent = defineCategory("subagent", (facts) => facts.subagent);
|
|
199
|
-
|
|
200
|
-
export const demo = definePack("demo", {
|
|
201
|
-
noForcePush: guardrail()
|
|
202
|
-
.at(command)
|
|
203
|
-
.check((ctx) => (/--force/.test(ctx.command ?? "") ? ctx.fail("rewrites shared history") : ctx.ok()))
|
|
204
|
-
.message("Force-pushing rewrites history everyone else has.")
|
|
205
|
-
.test({ pass: ["git push origin main"], block: ["git push --force origin main"] }),
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
export default defineConfig([pack(flow, { packs: [] }), pack(demo)]);
|
|
209
|
-
```
|
|
210
|
-
|
|
211
|
-
Six things to know, and then you can write your own:
|
|
212
|
-
|
|
213
|
-
- **A sentence says everything, and defaults nothing.** `.at(…)` when it fires · `.on(…)`/`.ignore(…)`
|
|
214
|
-
which files · `.for(…)` which actor · `.check(…)` the question · `.message(…)` what you are told ·
|
|
215
|
-
`.test({ pass, block })` what proves it. Say a verb twice and the compiler stops you.
|
|
216
|
-
- **A check is a function of `ctx`** and reaches the world only through it — `ctx.file`, `ctx.command`,
|
|
217
|
-
`ctx.staged`, `ctx.exec`, `ctx.fs`, `ctx.git`. That is what lets the same check run live, run in a
|
|
218
|
-
case, and replay from a recording without a repo.
|
|
219
|
-
- **A pack is just code.** `definePack` in a file here, or a package you install: identical shape.
|
|
220
|
-
Publishing one is promotion, not a rewrite.
|
|
221
|
-
- **Packs come with the install, behind their own door.** `@jawache/flow` is the GRAMMAR a rule is
|
|
222
|
-
written in; `@jawache/flow/packs` is the CONTENT — ten opinionated packs (`git` · `justfile` ·
|
|
223
|
-
`node` · `secrets` · `typescript` · `fcis` · `tdd` · `docs` · `flow` · `work`), each bound by one
|
|
224
|
-
`pack(…)` line, and an unbound one costs nothing. A pack names nothing it does not ship: the
|
|
225
|
-
recipe your gate runs is a typed, mandatory parameter, so a config that never says which command
|
|
226
|
-
cuts a release does not compile — and `flow`, flow's own self-protection, takes the folder your
|
|
227
|
-
own packs live in, so it never guesses at a folder your repo does not have. `flow init` binds
|
|
228
|
-
that one for you.
|
|
229
|
-
- **A repo bends a pack it did not write** with `override(pack.entry)`, which speaks only what it
|
|
230
|
-
changes — a different glob, a different message, or `.disabled("why")`, and the reason is what
|
|
231
|
-
`flow status` prints beside it.
|
|
232
|
-
- **A category is a name with its recognizer aboard.** It reads host-written evidence — a sidecar the
|
|
233
|
-
harness wrote, the brief a spawn was given — and never a claim the session made about itself,
|
|
234
|
-
because permissions that rest on a claim rest on a lie.
|
|
235
|
-
|
|
236
|
-
## Moments
|
|
237
|
-
|
|
238
|
-
| moment | when | guardrail | breadcrumb |
|
|
239
|
-
| --- | --- | --- | --- |
|
|
240
|
-
| `session` | a chat starts, resumes, or is compacted | — | ✓ |
|
|
241
|
-
| `touch` | a tool call names a file | — | ✓ |
|
|
242
|
-
| `write` | a file is about to be written | ✓ | — |
|
|
243
|
-
| `delete` | a command is about to remove a file | ✓ | — |
|
|
244
|
-
| `command` | a shell command is about to run | ✓ | — |
|
|
245
|
-
| `commit` | git's pre-commit hook, over the staged set | ✓ | — |
|
|
246
|
-
| `turn-end` | the agent is about to hand back | ✓ | — |
|
|
247
|
-
|
|
248
|
-
A guardrail blocks; a breadcrumb is a note and has no rail to block with. The commit moment is not
|
|
249
|
-
delivered by any harness — git's own hook is — which is why every harness gets the gates for free.
|
|
55
|
+
To guard one repo without a global install, install flow as a dev dependency instead: `npm i -D @jawache/flow`, then `npx flow init`. Git and Claude Code run the repo's copy when it has one, and the global one otherwise. [How to install flow](./docs/user/how-to/install-flow.md) has the details.
|
|
250
56
|
|
|
251
|
-
|
|
57
|
+
`flow init` writes `flow.config.ts`, a git pre-commit hook and flow's hook registrations for Claude Code. `flow status` lists every rule and checks the wiring: green means every rule loads and is in place. Then follow [Getting started](./docs/user/tutorials/getting-started.md), which takes you from an unguarded repo to a refused commit in five minutes.
|
|
252
58
|
|
|
253
|
-
|
|
254
|
-
| --- | --- |
|
|
255
|
-
| `flow init [--empty]` | scaffold a config, arm the gate, register the hooks |
|
|
256
|
-
| `flow status [--json]` | what is bound here, at which moments, and what is not wired yet |
|
|
257
|
-
| `flow test [config]` | every rule's own cases, driven |
|
|
258
|
-
| `flow facts [--json]` | what the record and the conversations say about this repo |
|
|
259
|
-
| `flow replay <file>` | a recorded session, back through the engine — no repo, no harness |
|
|
260
|
-
| `flow hook <event>` | what the harness's registrations call. Not for you |
|
|
261
|
-
| `flow commit <files…>` | what git's pre-commit hook calls. Not for you |
|
|
262
|
-
|
|
263
|
-
Two switches, both files, both one command:
|
|
264
|
-
|
|
265
|
-
```sh
|
|
266
|
-
touch .flow/off # every rail silenced. rm it to turn the guard back on
|
|
267
|
-
touch .flow/record # capture sessions so `flow replay` can run them again
|
|
268
|
-
```
|
|
269
|
-
|
|
270
|
-
## Exit codes
|
|
59
|
+
## Documentation
|
|
271
60
|
|
|
272
|
-
| | |
|
|
61
|
+
| To | Read |
|
|
273
62
|
| --- | --- |
|
|
274
|
-
|
|
|
275
|
-
|
|
|
276
|
-
|
|
|
63
|
+
| Learn flow by doing, in five minutes | [Getting started](./docs/user/tutorials/getting-started.md) |
|
|
64
|
+
| Solve a specific problem: protect files, ban a command, gate a commit on your tests | [The how-to guides](./docs/user/index.md#get-something-done) |
|
|
65
|
+
| Look up the rule grammar, the moments, `ctx` and test cases | [The config](./docs/user/reference/config.md) · [moments](./docs/user/reference/moments.md) · [`ctx`](./docs/user/reference/ctx.md) |
|
|
66
|
+
| Look up a command, a switch or an exit code | [The flow command](./docs/user/reference/cli.md) |
|
|
67
|
+
| Write your own check or pack | [How to write your own check](./docs/user/how-to/write-your-own-check.md) · [how to share a pack](./docs/user/how-to/share-a-pack.md) |
|
|
68
|
+
| See what a shipped pack or check does | [The packs](./docs/user/packs/index.md) · [the checks](./docs/user/index.md#the-stock-checks--one-page-each) |
|
|
69
|
+
| Understand why flow works the way it does | [How flow works](./docs/user/explanation/how-flow-works.md) |
|
|
70
|
+
| Upgrade a repo that already uses flow | [Migration guides](./docs/user/migrations/index.md) |
|
|
71
|
+
| See what changed in each release | [CHANGELOG.md](./CHANGELOG.md) |
|
|
277
72
|
|
|
278
|
-
|
|
279
|
-
a hook has exactly two codes to answer with — so a blocked write and a config that will not load
|
|
280
|
-
are both 2, because from a hook's seat they are one instruction: *this did not happen, read
|
|
281
|
-
stderr*. The commit gate answers the same way, for the same reason.
|
|
73
|
+
## How it works
|
|
282
74
|
|
|
283
|
-
|
|
284
|
-
asked a question, so they have room for a third answer, and "not fully in force" is a genuinely
|
|
285
|
-
different fact from "caught something". A config that will not load collapses even that room: it
|
|
286
|
-
makes both of them answer 2, because there is nothing left to report on.
|
|
287
|
-
|
|
288
|
-
This table is pinned by the suite — `e2e/product.test.ts` drives every row against the binary, so a
|
|
289
|
-
code that moves fails a test rather than quietly making this page wrong.
|
|
75
|
+
`flow init` registers flow with Claude Code, and adds a git pre-commit hook. Claude Code then runs `flow` when a session starts, before and after every tool call, and when the agent finishes. Git runs `flow hook commit` before every commit. Each time, flow loads `flow.config.ts` and runs the rules bound to that moment: `session`, `touch`, `write`, `delete`, `command`, `commit` or `turn-end`. A guardrail refuses by exiting with code 2, which Claude Code and git both treat as "do not go ahead", and its message is shown to the agent. A breadcrumb never refuses: its text is added to the agent's context. [Moments](./docs/user/reference/moments.md) describes each one.
|
|
290
76
|
|
|
291
77
|
## Working on flow
|
|
292
78
|
|
|
293
79
|
```sh
|
|
294
|
-
just
|
|
295
|
-
just
|
|
296
|
-
just
|
|
80
|
+
just install && just link # run `flow` from your checkout
|
|
81
|
+
just # every recipe, with what it does
|
|
82
|
+
just test-commit # what the commit gate runs: the rule cases, the typecheck, the suite
|
|
83
|
+
just test-packs # the ten shipped packs, bound in a throwaway repo
|
|
297
84
|
```
|
|
298
85
|
|
|
299
|
-
flow guards
|
|
300
|
-
`@jawache/flow/packs` — the same import a stranger writes — plus one pack this repo writes,
|
|
301
|
-
`guards/house.ts`. A door that stops exporting, a rule that stops loading or a bundle that stops
|
|
302
|
-
building refuses this repo's own next commit first.
|
|
303
|
-
|
|
304
|
-
Longer documentation is in [`docs/user/`](docs/user/index.html): the guidebook, the five-minute
|
|
305
|
-
quick start, the config reference, how to author a pack, and one page per stock check.
|
|
86
|
+
flow guards its own repo. `flow.config.ts` binds the ten shipped packs, plus one pack this repo writes, `guards/house.ts`, so a change that breaks a rule, an export or the build is refused at this repo's own next commit.
|
|
306
87
|
|
|
307
88
|
## Licence
|
|
308
89
|
|