@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
|
@@ -6,5 +6,10 @@ export interface VerbResult {
|
|
|
6
6
|
}
|
|
7
7
|
/** `flow init [--empty]` — make this repo drivable, and say exactly what was created. */
|
|
8
8
|
export declare function runInit(cwd: string, args: readonly string[]): VerbResult;
|
|
9
|
+
/**
|
|
10
|
+
* `flow uninstall [--machine]` — remove what `flow init` wrote in this repo, but the config, and say
|
|
11
|
+
* exactly what went. `--machine` takes flow's hooks out of the machine-wide settings file too.
|
|
12
|
+
*/
|
|
13
|
+
export declare function runUninstall(cwd: string, args: readonly string[]): VerbResult;
|
|
9
14
|
/** `flow status` — is flow working here, and what will meet me. Green means guarded. */
|
|
10
15
|
export declare function runStatus(cwd: string, args: readonly string[]): Promise<VerbResult>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Case, CaseWorld, Check, Ctx, ExecResult, Grammar, LoadedEntry, Moment, Unanswered } from "../language/domain.ts";
|
|
1
|
+
import type { Case, CaseWorld, Check, Ctx, ExecResult, Grammar, LoadedEntry, Moment, ShellCommand, Unanswered } from "../language/domain.ts";
|
|
2
2
|
/** One pattern that matched, and the 1-based line it matched on. */
|
|
3
3
|
export interface PatternHit {
|
|
4
4
|
readonly pattern: string;
|
|
@@ -24,46 +24,70 @@ export declare const textBan: (options: {
|
|
|
24
24
|
/**
|
|
25
25
|
* Ban a dangerous command before it runs.
|
|
26
26
|
*
|
|
27
|
-
* A DETERRENT: the
|
|
28
|
-
* a rule watching the wrong place.
|
|
27
|
+
* A DETERRENT: the commands it names ARE its scope, so matching nothing in a repo is the rule
|
|
28
|
+
* working, not a rule watching the wrong place.
|
|
29
29
|
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
30
|
+
* EACH ENTRY IS A COMMAND IN WORDS — `"git push --force"` — read by the same parse as the line: a
|
|
31
|
+
* program, then the arguments that make it dangerous. A command in `ctx.commands` matches when its
|
|
32
|
+
* program, less its path, is the entry's, and its arguments include every one of the entry's, in
|
|
33
|
+
* any order. Arguments are compared whole, so `"work task"` is not `work task-list` and a quoted
|
|
34
|
+
* `'we tick later'` is not the word `tick`; a bundle of short flags counts letter by letter on both
|
|
35
|
+
* sides, so `"rm -rf"` is `rm -fr` and `rm -r -f`. A regex over a command's text could say none of
|
|
36
|
+
* that: it read `/usr/local/bin/work plan tick` as no tick and `rm -fr` as no `-rf`.
|
|
37
|
+
*
|
|
38
|
+
* One command at a time, so `git push origin main; ls -f` is no force push, and a heredoc's body
|
|
39
|
+
* is text fed to a program, not arguments — unless a shell reads it, when the parse has already
|
|
40
|
+
* made its lines commands of their own. A line that does not parse is refused when an entry's words
|
|
41
|
+
* all appear in it as whole words: a deterrent that cannot read a line errs toward refusing it.
|
|
42
|
+
*
|
|
43
|
+
* `wholeLine: true` is the escape hatch for a pattern that spans two commands on purpose
|
|
44
|
+
* (`curl … | sh`): each entry is then a regex, compiled when the rule loads and tried anywhere in
|
|
45
|
+
* the line as written, less its heredoc bodies. One that does not compile refuses to load.
|
|
46
|
+
*
|
|
47
|
+
* An entry that is not one command in words — a line that does not parse, two commands, a
|
|
48
|
+
* backslash, the mark of a regex written before words, or a wrapper such as `sudo` or a `FOO=1`
|
|
49
|
+
* assignment in front — refuses to load, rather than arming a ban that matches nothing, or every
|
|
50
|
+
* use of its program.
|
|
51
|
+
*
|
|
52
|
+
* docs/user/checks/ban-commands.md is the promise, case by case.
|
|
36
53
|
*/
|
|
37
54
|
export declare const banCommands: (options: {
|
|
38
55
|
ban: readonly string[];
|
|
56
|
+
wholeLine?: boolean;
|
|
39
57
|
}) => Check;
|
|
40
58
|
/**
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
* `
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
|
|
63
|
-
|
|
59
|
+
* Refuse a command that records permanent prose when the shell would run a BACKTICK substitution
|
|
60
|
+
* while building it — a commit message, a PR or issue body, a journal entry.
|
|
61
|
+
*
|
|
62
|
+
* `heads` are the commands that record prose, each in words, `"gh pr"`, matched as `banCommands`
|
|
63
|
+
* matches an entry: the same program, less its path, and every argument of the head among the
|
|
64
|
+
* command's, compared whole. A head's words are found anywhere among the arguments, so a bare word
|
|
65
|
+
* passed as an option's value counts too: `"gh pr"` refuses `gh run list --branch pr`. A head that
|
|
66
|
+
* is not one command in words refuses to load, as an entry does. `gitSubcommands` names git's own,
|
|
67
|
+
* `commit`, and is read through `gitCommand`: git's options sit between `git` and its subcommand,
|
|
68
|
+
* so `-C '/a b' commit` is `commit` there, where the containment of `"git commit"` would also take
|
|
69
|
+
* `git log --grep commit`.
|
|
70
|
+
*
|
|
71
|
+
* The verdict reads the parse, never the line: a backtick the shell runs is one the parse lists as
|
|
72
|
+
* the command's substitution — in a double-quoted or bare argument, or in an unquoted heredoc the
|
|
73
|
+
* command is fed. A single-quoted argument, an escaped backtick and a QUOTED heredoc run nothing,
|
|
74
|
+
* so none is refused, and neither is a backtick in another command on the same line. A `$( … )`
|
|
75
|
+
* substitution is the sanctioned way to feed a heredoc into an argument, so it is not refused
|
|
76
|
+
* itself; what it runs is, when that holds a backtick — the unquoted `"$(cat <<EOF … EOF)"`
|
|
77
|
+
* message. A line that does not parse is not judged.
|
|
78
|
+
*
|
|
79
|
+
* A CORE CHECK because TWO PACKS SHARE IT: `git` polices `git commit` and the `gh` verbs, which
|
|
80
|
+
* every repo binds, and `work` the lifecycle verbs, which only a repo running the work lifecycle
|
|
81
|
+
* binds. Anything two packs share is a core check; a copy in each is how the two would drift
|
|
82
|
+
* while both still read as armed.
|
|
83
|
+
*/
|
|
84
|
+
export declare const substitutionInProse: (options: {
|
|
85
|
+
heads: readonly string[];
|
|
86
|
+
gitSubcommands?: readonly string[];
|
|
87
|
+
}) => Check;
|
|
64
88
|
/**
|
|
65
89
|
* What a blocked prose-writing command is told, and it is the same sentence whichever pack caught
|
|
66
|
-
* it: the mechanism, the absence of an undo,
|
|
90
|
+
* it: the mechanism, the absence of an undo, and the fix.
|
|
67
91
|
*
|
|
68
92
|
* ONE constant rather than a message per pack. The sentence never named the verb that was blocked
|
|
69
93
|
* — it names the shape — so two copies would be two places for the same advice to drift, and the
|
|
@@ -86,6 +110,24 @@ export declare const SUBSTITUTION_MESSAGE: string;
|
|
|
86
110
|
export declare const protectedPath: (options: {
|
|
87
111
|
existingOnly?: boolean;
|
|
88
112
|
}) => Check;
|
|
113
|
+
/**
|
|
114
|
+
* Only the listed categories may change a file: a write or a delete by any other actor is refused.
|
|
115
|
+
*
|
|
116
|
+
* It reads `ctx.actor` — the categories the acting session wears, recognised from what the host
|
|
117
|
+
* wrote about it, never from a claim it made — and nothing about the file. A session wearing none
|
|
118
|
+
* of `writers` is refused whatever it wrote, and one wearing none at all is refused too: "no
|
|
119
|
+
* category" is not "every category".
|
|
120
|
+
*
|
|
121
|
+
* `.for(…)` on an entry says which actors a RULE applies to; this says which actors may WRITE. Both
|
|
122
|
+
* read the same categories, and they answer different questions — a rule for a checker says what a
|
|
123
|
+
* checker may not do, this says that everybody but a builder may not write.
|
|
124
|
+
*
|
|
125
|
+
* An empty `writers` is refused at load: it would refuse every change by everyone, which is a
|
|
126
|
+
* frozen worktree, not a rule about who writes it.
|
|
127
|
+
*/
|
|
128
|
+
export declare const oneWriter: (options: {
|
|
129
|
+
writers: readonly string[];
|
|
130
|
+
}) => Check;
|
|
89
131
|
/**
|
|
90
132
|
* A file must have a required companion on disk — every `src/domain/**` needs its `.test.ts`.
|
|
91
133
|
*
|
|
@@ -197,7 +239,12 @@ export declare const jsonInvariant: (options: {
|
|
|
197
239
|
/** Of some exported names, which are never mentioned in the sibling test's text? */
|
|
198
240
|
export declare function unreferenced(names: readonly string[], siblingText: string): string[];
|
|
199
241
|
/**
|
|
200
|
-
* The names a TypeScript source exports — functions, classes and consts.
|
|
242
|
+
* The names a TypeScript source exports — functions, classes and consts, in source order.
|
|
243
|
+
*
|
|
244
|
+
* An `export const` is read as every name it binds: each declarator of `export const a = 1, b = 2`,
|
|
245
|
+
* and each name a destructuring pattern binds. A declarator with a type annotation is left out,
|
|
246
|
+
* destructured or not, and so is an ambient `export declare const`: the declaration sits under an
|
|
247
|
+
* `ambient_declaration`, not directly under the export, so it is never matched.
|
|
201
248
|
*
|
|
202
249
|
* A real parser rather than a regex, because `export const x = …` inside a string or a comment is
|
|
203
250
|
* exactly the false positive that teaches people to ignore a rule. Parsing is not reaching the
|
|
@@ -206,7 +253,11 @@ export declare function unreferenced(names: readonly string[], siblingText: stri
|
|
|
206
253
|
* milliseconds to load and no other check needs it.
|
|
207
254
|
*/
|
|
208
255
|
export declare function exportedNames(content: string): Promise<string[]>;
|
|
209
|
-
/**
|
|
256
|
+
/**
|
|
257
|
+
* Every exported symbol is referenced in its sibling test.
|
|
258
|
+
*
|
|
259
|
+
* A refusal is one line: the sibling test once, then every export it never mentions.
|
|
260
|
+
*/
|
|
210
261
|
export declare const symbolsInSibling: (options: {
|
|
211
262
|
sibling: string;
|
|
212
263
|
}) => Check;
|
|
@@ -241,44 +292,35 @@ export declare const changeTogether: (options: {
|
|
|
241
292
|
* aimed at a directory called HEAD.
|
|
242
293
|
*/
|
|
243
294
|
export declare const GIT_VALUE_OPTS: ReadonlySet<string>;
|
|
295
|
+
/** What a command reader is handed: the line, and its parse when the engine made one. */
|
|
296
|
+
type CommandFacts = Pick<Ctx, "command" | "commands">;
|
|
244
297
|
/**
|
|
245
|
-
*
|
|
246
|
-
*
|
|
247
|
-
*
|
|
248
|
-
* Null on unparseable input — an unclosed quote — and null means "cannot tell", which every caller
|
|
249
|
-
* must read as "do not judge". A guard that guesses at a line it cannot parse is worse than one
|
|
250
|
-
* that stands aside.
|
|
298
|
+
* The command line parsed as bash: `ctx.commands`, or the line parsed here when the ctx carries no
|
|
299
|
+
* parse at all — which is what an engine older than the parse hands these checks. Null when the
|
|
300
|
+
* line does not parse, and every reader takes null as "cannot tell".
|
|
251
301
|
*/
|
|
252
|
-
export declare function
|
|
253
|
-
/** One `git
|
|
254
|
-
export interface
|
|
302
|
+
export declare function shellCommands(facts: CommandFacts): readonly ShellCommand[] | null;
|
|
303
|
+
/** One `git` command in the parse, split where git's own options stop and its subcommand starts. */
|
|
304
|
+
export interface GitCommand {
|
|
255
305
|
/** git's OWN options, before the subcommand — where `-C` counts and only there. */
|
|
256
306
|
readonly globals: readonly string[];
|
|
257
|
-
/** The subcommand word (`commit`, `status`), or null when the
|
|
307
|
+
/** The subcommand word (`commit`, `status`), or null when the command carries none. */
|
|
258
308
|
readonly subcommand: string | null;
|
|
259
309
|
/** Everything after the subcommand — its own flags and arguments. */
|
|
260
310
|
readonly args: readonly string[];
|
|
311
|
+
/** Where `args` starts in `command.args`: `args[i]` is `command.args[argsAt + i]`. */
|
|
312
|
+
readonly argsAt: number;
|
|
313
|
+
/** The parsed command it was read from: its heredocs are what `git commit -F -` reads. */
|
|
314
|
+
readonly command: ShellCommand;
|
|
261
315
|
}
|
|
262
316
|
/**
|
|
263
|
-
*
|
|
264
|
-
*
|
|
265
|
-
*
|
|
266
|
-
* operator — so a `git` inside a quoted message or as an argument to something else is not one.
|
|
267
|
-
*/
|
|
268
|
-
export declare function gitInvocations(command: string): GitInvocation[] | null;
|
|
269
|
-
/**
|
|
270
|
-
* The body of the first heredoc in the raw command, or null.
|
|
271
|
-
*
|
|
272
|
-
* `git commit -F -` reads its message on stdin, and a QUOTED heredoc is the safe way to write one
|
|
273
|
-
* containing backticks — a form this codebase's own rules push commits toward. The argument scan
|
|
274
|
-
* cannot see it: there is no `-m` token, so a message written that way goes unjudged, and every
|
|
275
|
-
* rule that reads a commit message is inert on it.
|
|
276
|
-
*
|
|
277
|
-
* Deliberately last-resort and deliberately dumb — it runs only when no inline message was found,
|
|
278
|
-
* and reads the FIRST heredoc, because a commit command carrying two is not a shape anyone writes
|
|
279
|
-
* and guessing between them would be worse than the gap.
|
|
317
|
+
* One parsed command read as git, or null when its program is not git. The parse has already
|
|
318
|
+
* found the program, so a `git` inside a quoted message, a heredoc body or another command's
|
|
319
|
+
* arguments is never one, and a `git` inside `$( … )` or a `bash -c` string is.
|
|
280
320
|
*/
|
|
281
|
-
export declare function
|
|
321
|
+
export declare function gitCommand(command: ShellCommand): GitCommand | null;
|
|
322
|
+
/** Every git command the line runs, in the order the parse lists them; none when it does not parse. */
|
|
323
|
+
export declare function gitCommands(facts: CommandFacts): GitCommand[];
|
|
282
324
|
/**
|
|
283
325
|
* The message a `git commit` in this command line carries, or null when there is no judgeable
|
|
284
326
|
* commit in it at all. Null is the "not our business" answer and every caller passes on it.
|
|
@@ -296,7 +338,15 @@ export declare function heredocBody(command: string): string | null;
|
|
|
296
338
|
*
|
|
297
339
|
* git commit -m MSG · -mMSG · --message MSG · --message=MSG judged
|
|
298
340
|
* git commit -m SUBJECT -m BODY (repeated, any mix of forms) judged, joined as git joins them
|
|
299
|
-
* git commit -
|
|
341
|
+
* git commit -m "$(cat <<'EOF' … EOF)" judged, via that heredoc's body,
|
|
342
|
+
* which is what the shell passes
|
|
343
|
+
* git commit -F - <<'EOF' … EOF (the commit reads stdin) judged, via ITS heredoc's body
|
|
344
|
+
* python3 - <<'PY' … PY; git commit -F - <<'EOF' … EOF judged, via the commit's own
|
|
345
|
+
* heredoc — the first one is the
|
|
346
|
+
* python's script
|
|
347
|
+
* python3 - <<'PY' … PY; git commit -F msg.txt NOT judged — the heredoc is
|
|
348
|
+
* another command's script, and
|
|
349
|
+
* the message is on disk
|
|
300
350
|
* git commit -F path/to/file NOT judged — the message is on
|
|
301
351
|
* disk, and reading it is a read
|
|
302
352
|
* of the world this layer does not
|
|
@@ -307,7 +357,7 @@ export declare function heredocBody(command: string): string | null;
|
|
|
307
357
|
* The boundary is on the record: this rail polices AGENT-typed commits, which are its audience. A
|
|
308
358
|
* human committing from an editor is not its subject and never was.
|
|
309
359
|
*/
|
|
310
|
-
export declare function commitMessage(
|
|
360
|
+
export declare function commitMessage(facts: CommandFacts): string | null;
|
|
311
361
|
/** One shell argument, single-quoted so nothing in it can be expanded or re-parsed. */
|
|
312
362
|
export declare function quoteArg(value: string): string;
|
|
313
363
|
/**
|
|
@@ -335,15 +385,16 @@ export declare function quoteArg(value: string): string;
|
|
|
335
385
|
* options exactly as a shell chains directory changes: `cd /a && git -C b status` runs in `/a/b`,
|
|
336
386
|
* and `-C '/a' -C 'b'` says precisely that. Nothing has to decide which of the two wins.
|
|
337
387
|
*
|
|
338
|
-
* ONLY A LEADING `cd`, and only when
|
|
339
|
-
*
|
|
340
|
-
*
|
|
388
|
+
* ONLY A LEADING `cd`, and only when a command follows it. A `cd` in the middle of a chain can be
|
|
389
|
+
* preceded by another that already moved, and a command line that walks two trees is beyond what
|
|
390
|
+
* one answer can honestly describe — the same reason the FIRST git command decides below. Both are
|
|
391
|
+
* read from the parse, so a `cd` or a `git` inside a quoted argument or a heredoc body is neither.
|
|
341
392
|
*
|
|
342
393
|
* A path the SHELL would have expanded — `~`, a variable — is quoted here and so reaches git
|
|
343
394
|
* literally, which makes the read fail rather than lie. Every caller treats a failed read as "not
|
|
344
395
|
* a repo, git owns that error" and passes: a false negative, which is this pack's stated trade.
|
|
345
396
|
*/
|
|
346
|
-
export declare function gitDirPrefix(
|
|
397
|
+
export declare function gitDirPrefix(facts: CommandFacts): string;
|
|
347
398
|
/**
|
|
348
399
|
* Does the message carry the token, on a LINE OF ITS OWN?
|
|
349
400
|
*
|
|
@@ -598,7 +649,7 @@ export interface Layer {
|
|
|
598
649
|
readonly not?: readonly string[];
|
|
599
650
|
}
|
|
600
651
|
export interface Dialect {
|
|
601
|
-
/** What to cruise
|
|
652
|
+
/** What to cruise: a directory or a glob. `src` and `src/**` cruise the same modules. */
|
|
602
653
|
readonly scan: string;
|
|
603
654
|
/** Each layer, as globs — a bare list, or `{ path, not }` when it has holes in it. */
|
|
604
655
|
readonly layers: Readonly<Record<string, readonly string[] | Layer>>;
|
|
@@ -610,8 +661,6 @@ export interface Dialect {
|
|
|
610
661
|
readonly require?: readonly RequireEdge[];
|
|
611
662
|
/** Carve-outs — `type-only` above all, since a type import is erased at compile time. */
|
|
612
663
|
readonly except?: readonly string[];
|
|
613
|
-
/** Informational rules that never block. `sdp` = the Stable Dependencies Principle. */
|
|
614
|
-
readonly warn?: readonly string[];
|
|
615
664
|
/** dependency-cruiser's own options, passed through. */
|
|
616
665
|
readonly options?: Readonly<Record<string, unknown>>;
|
|
617
666
|
}
|
|
@@ -698,6 +747,15 @@ export declare function nonGlobLayers(layers: Readonly<Record<string, readonly s
|
|
|
698
747
|
* at load: use plain globs for a layer that an `only` names, or express the exclusion as a `forbid`.
|
|
699
748
|
*/
|
|
700
749
|
export declare function holesInAnOnlyList(opts: Dialect): string[];
|
|
750
|
+
/**
|
|
751
|
+
* A `warn` option — refused, because it is gone and its rule would silently go with it.
|
|
752
|
+
*
|
|
753
|
+
* `warn: ["sdp"]` compiled a Stable Dependencies rule that nothing bound, and it is removed with no
|
|
754
|
+
* replacement. The type no longer has the key, so a literal config fails to compile; but options
|
|
755
|
+
* built in a variable carry it past the compiler, and dropped quietly the rule would report armed
|
|
756
|
+
* and fence nothing. It is read off the raw object because the type cannot name it.
|
|
757
|
+
*/
|
|
758
|
+
export declare function removedWarnOption(opts: Dialect): string[];
|
|
701
759
|
/**
|
|
702
760
|
* The generic import-fence runner.
|
|
703
761
|
*
|
|
@@ -790,6 +848,10 @@ export declare function readCase(c: Case): {
|
|
|
790
848
|
readonly dialect: CaseDialect;
|
|
791
849
|
readonly facts: Partial<Ctx>;
|
|
792
850
|
readonly world: CaseWorld;
|
|
851
|
+
/** commit: the staged paths the last commit does not have, when the case says. */
|
|
852
|
+
readonly added?: readonly string[] | undefined;
|
|
853
|
+
/** commit: the staged deletions, by path, with their content as the last commit has it. */
|
|
854
|
+
readonly deleted?: Readonly<Record<string, string>> | undefined;
|
|
793
855
|
};
|
|
794
856
|
/** Which moment's dialect a case speaks. */
|
|
795
857
|
export declare function caseMoment(c: Case): Moment;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { inScope, type Scope, type BreadcrumbMoment, type CaseWorld, type Category, type EntrySpec, type GuardrailMoment, type LoadResult, type LoadedEntry, type Moment, type SessionFacts, type Settings, type TurnAction, type Unanswered, type World } from "../language/domain.ts";
|
|
1
|
+
import { inScope, type Scope, type BreadcrumbMoment, type CaseWorld, type Category, type Ctx, type EntrySpec, type GuardrailMoment, type LoadResult, type LoadedEntry, type Moment, type SessionFacts, type Settings, type TurnAction, type Unanswered, type World } from "../language/domain.ts";
|
|
2
2
|
/**
|
|
3
3
|
* How a category recognises its members from host-written evidence.
|
|
4
4
|
*
|
|
@@ -9,7 +9,7 @@ import { inScope, type Scope, type BreadcrumbMoment, type CaseWorld, type Catego
|
|
|
9
9
|
*/
|
|
10
10
|
export interface SpawnRecipe {
|
|
11
11
|
/**
|
|
12
|
-
*
|
|
12
|
+
* Host-sent agent types that ARE this category, decisively. Brief text is never consulted for a
|
|
13
13
|
* session whose type is named here — nor for one whose type is named anywhere but `generic`.
|
|
14
14
|
*/
|
|
15
15
|
readonly types?: readonly string[];
|
|
@@ -21,7 +21,7 @@ export interface SpawnRecipe {
|
|
|
21
21
|
/** Substrings that recover this category from a generic spawn's brief. */
|
|
22
22
|
readonly brief?: readonly string[];
|
|
23
23
|
/**
|
|
24
|
-
* True for the top-level session only —
|
|
24
|
+
* True for the top-level session only — not spawned, no agent type. Structural, and exclusive: a
|
|
25
25
|
* recipe is either about spawns or about the parent, never both.
|
|
26
26
|
*/
|
|
27
27
|
readonly parent?: boolean;
|
|
@@ -64,21 +64,33 @@ export declare function categoriesIn(entries: readonly LoadedEntry[]): Category[
|
|
|
64
64
|
* accident — a transcript since compacted, a brief scrolled away — and a rule that fired for the
|
|
65
65
|
* first half of a session and not the second is worse than either outcome alone. The unit is
|
|
66
66
|
* session × agent because that is what the evidence is about: each subagent has its own transcript
|
|
67
|
-
* and its own
|
|
67
|
+
* and its own agent id.
|
|
68
68
|
*/
|
|
69
69
|
export declare function identify(stored: readonly string[] | undefined, facts: SessionFacts, categories: readonly Category[]): Identity;
|
|
70
70
|
/** One event the guardrail rails carry. Facts only — who it happened to is `wearing`. */
|
|
71
71
|
export interface GuardEvent {
|
|
72
72
|
readonly moment: GuardrailMoment;
|
|
73
73
|
/** write/delete: the would-be file, in memory, before disk. */
|
|
74
|
-
readonly file?:
|
|
75
|
-
readonly path: string;
|
|
76
|
-
readonly content: string;
|
|
77
|
-
} | undefined;
|
|
74
|
+
readonly file?: Ctx["file"];
|
|
78
75
|
/** command: the line about to run. */
|
|
79
76
|
readonly command?: string | undefined;
|
|
77
|
+
/** command: the line's parse, when the caller already made it. Absent, the engine parses the line. */
|
|
78
|
+
readonly commands?: Ctx["commands"];
|
|
80
79
|
/** commit: the staged paths. */
|
|
81
80
|
readonly staged?: readonly string[] | undefined;
|
|
81
|
+
/**
|
|
82
|
+
* commit: the staged paths the last commit does not have, which become each file's `existed`.
|
|
83
|
+
* Absent when the shell could not ask git, and then `existed` is left unset.
|
|
84
|
+
*/
|
|
85
|
+
readonly added?: readonly string[] | undefined;
|
|
86
|
+
/**
|
|
87
|
+
* commit: the staged deletions an entry will judge (`judgedDeletions`), each with its content as
|
|
88
|
+
* the last commit has it. A rename is a deletion of its old path plus an add of its new one.
|
|
89
|
+
*/
|
|
90
|
+
readonly deleted?: readonly {
|
|
91
|
+
readonly path: string;
|
|
92
|
+
readonly content: string;
|
|
93
|
+
}[] | undefined;
|
|
82
94
|
/** turn-end: what the actor did this turn, in order. */
|
|
83
95
|
readonly turn?: readonly TurnAction[] | undefined;
|
|
84
96
|
/** Every category this session × agent wears. Empty means it wears none, not "all of them". */
|
|
@@ -123,6 +135,12 @@ export declare function covers(scope: Scope, path: string): boolean;
|
|
|
123
135
|
* guarded on one page and abandoned on the next.
|
|
124
136
|
*/
|
|
125
137
|
export declare function watching(entries: readonly Bound[]): Bound[];
|
|
138
|
+
/**
|
|
139
|
+
* The staged deletions some entry will judge — the ones whose content is worth reading from the
|
|
140
|
+
* last commit. A commit that removes a vendored folder is thousands of deletions, and none of them
|
|
141
|
+
* costs a read unless a rule is about it.
|
|
142
|
+
*/
|
|
143
|
+
export declare function judgedDeletions(entries: readonly LoadedEntry[], deleted: readonly string[]): string[];
|
|
126
144
|
/** A rail refusing. `entry` is null when it is the guard itself that is broken, not a rule. */
|
|
127
145
|
export interface Block {
|
|
128
146
|
readonly do: "block";
|
|
@@ -146,6 +164,12 @@ export interface Notice {
|
|
|
146
164
|
/** The file the prose lives in, when it did not. The shell resolves it; a pure home cannot. */
|
|
147
165
|
readonly file: string | undefined;
|
|
148
166
|
readonly cause: Cause;
|
|
167
|
+
/**
|
|
168
|
+
* What the event that showed it was about: the repo-relative path, or the command line, as a
|
|
169
|
+
* guardrail row's subject is. Absent when the event carries no file or command, such as a
|
|
170
|
+
* session note or a fault.
|
|
171
|
+
*/
|
|
172
|
+
readonly subject: string | undefined;
|
|
149
173
|
}
|
|
150
174
|
export type Effect = Block | Notice;
|
|
151
175
|
/**
|
|
@@ -266,8 +290,16 @@ export declare const FLOW_DIR = ".flow";
|
|
|
266
290
|
export declare const FLOW_GITIGNORE = "# flow's own state \u2014 telemetry and session marks, never committed.\n*\n";
|
|
267
291
|
/** The log schema this build writes, stamped into every session file's first row. */
|
|
268
292
|
export declare const LOG_VERSION = 1;
|
|
269
|
-
/**
|
|
270
|
-
|
|
293
|
+
/**
|
|
294
|
+
* Every kind a log row may be. Closed — a reader that meets another word has met corruption.
|
|
295
|
+
*
|
|
296
|
+
* `read` and `write` are what went ROUND the tool rows: a file a shell command read by name, and a
|
|
297
|
+
* file a call changed that the call did not name — a heredoc, a `sed -i`, a script. A `tool` row
|
|
298
|
+
* says a call happened; these say what it did to the tree, so the coverage and lead numbers count
|
|
299
|
+
* work done through the shell as well as through Edit. `mismatch` is the host's own list of the
|
|
300
|
+
* files a command changed disagreeing with flow's diff: a fact to read, never an input to a rule.
|
|
301
|
+
*/
|
|
302
|
+
export declare const ROW_KINDS: readonly ["meta", "tool", "breadcrumb", "guardrail", "compaction", "run", "read", "write", "mismatch"];
|
|
271
303
|
export type RowKind = (typeof ROW_KINDS)[number];
|
|
272
304
|
export declare function flowDir(root: string): string;
|
|
273
305
|
/** A session id becomes a filename, so it is held to a filename's charset. */
|
|
@@ -351,15 +383,42 @@ export interface SessionMarker {
|
|
|
351
383
|
* delete. Absent on a marker written before this field existed, which reads as "unknown actor".
|
|
352
384
|
*/
|
|
353
385
|
readonly agent?: unknown;
|
|
386
|
+
/** The type that agent was started as, for the commit gate's log rows. Absent on `main`. */
|
|
387
|
+
readonly agentType?: unknown;
|
|
354
388
|
readonly ts?: unknown;
|
|
355
389
|
}
|
|
390
|
+
/**
|
|
391
|
+
* The agent one row, marker or call came from: the key its state is stored under, and its type.
|
|
392
|
+
*
|
|
393
|
+
* One value rather than two loose ones, because the type means nothing without the id it belongs to.
|
|
394
|
+
*/
|
|
395
|
+
export interface AgentOf {
|
|
396
|
+
/** `main` for the session itself, a subagent's `agent_id`, or `commit`: see `UNNAMED_COMMIT`. */
|
|
397
|
+
readonly agent: string;
|
|
398
|
+
/** The type the subagent was started as, when the host said. Never beside `main` or `commit`. */
|
|
399
|
+
readonly agentType?: string;
|
|
400
|
+
}
|
|
401
|
+
/**
|
|
402
|
+
* The agent a commit-gate run's rows name when no agent can be named: no marker, a stale one, or
|
|
403
|
+
* one written before markers held the agent. `commit` is also the stream such a commit's rows land
|
|
404
|
+
* in when no session claims it.
|
|
405
|
+
*/
|
|
406
|
+
export declare const UNNAMED_COMMIT: AgentOf;
|
|
407
|
+
/**
|
|
408
|
+
* An agent named by an id and, maybe, a type — the one reading of the pair, wherever it is read
|
|
409
|
+
* from: a hook message's `agent_id` and `agent_type`, or a marker's `agent` and `agentType`.
|
|
410
|
+
*
|
|
411
|
+
* Null when the id is not a non-empty string, and the type counts only beside such an id, because a
|
|
412
|
+
* type means nothing without the agent it belongs to.
|
|
413
|
+
*/
|
|
414
|
+
export declare function namedAgent(id: unknown, type: unknown): AgentOf | null;
|
|
356
415
|
/** A commit long after the last edit is not this session's. */
|
|
357
416
|
export declare const MARKER_MAX_AGE_MS: number;
|
|
358
417
|
/** Who a commit-gate run belongs to: the live session, and which of its agents was working. */
|
|
359
418
|
export interface Attribution {
|
|
360
419
|
readonly session: string;
|
|
361
420
|
/** Null when the marker named no agent — an old marker, or a run with nobody to attribute to. */
|
|
362
|
-
readonly
|
|
421
|
+
readonly who: AgentOf | null;
|
|
363
422
|
}
|
|
364
423
|
/**
|
|
365
424
|
* Which session × agent a commit-gate run belongs to, given the marker it found.
|
|
@@ -639,12 +698,11 @@ export declare const RECORDING_VERSION = 1;
|
|
|
639
698
|
export type RecordedStep = {
|
|
640
699
|
readonly rail: "guard";
|
|
641
700
|
readonly moment: GuardrailMoment;
|
|
642
|
-
readonly file?:
|
|
643
|
-
readonly path: string;
|
|
644
|
-
readonly content: string;
|
|
645
|
-
} | undefined;
|
|
701
|
+
readonly file?: Ctx["file"];
|
|
646
702
|
readonly command?: string | undefined;
|
|
647
703
|
readonly staged?: readonly string[] | undefined;
|
|
704
|
+
readonly added?: readonly string[] | undefined;
|
|
705
|
+
readonly deleted?: GuardEvent["deleted"];
|
|
648
706
|
readonly turn?: readonly TurnAction[] | undefined;
|
|
649
707
|
readonly wearing: readonly string[];
|
|
650
708
|
readonly world?: CaseWorld | undefined;
|
|
@@ -728,8 +786,8 @@ export interface RowDiff {
|
|
|
728
786
|
* Only the two MECHANISM rows are compared, and that is not a softening: a replay has no tool
|
|
729
787
|
* calls to record and no session to stamp a meta row for, so `tool`, `run` and `meta` are facts
|
|
730
788
|
* about the live process rather than about the guard's judgement. What the recording claims is
|
|
731
|
-
* that the same rules refuse the same subjects and the same notes show for the same causes
|
|
732
|
-
* those are exactly the rows named here.
|
|
789
|
+
* that the same rules refuse the same subjects and the same notes show for the same causes and
|
|
790
|
+
* files, and those are exactly the rows named here.
|
|
733
791
|
*/
|
|
734
792
|
export declare function diffRows(replayed: readonly Row[], live: readonly Row[]): RowDiff[];
|
|
735
793
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Category, SessionFacts } from "../language/domain.ts";
|
|
2
|
-
import { type Attribution, type Identity, type RecordedStep, type Recording, type Row, type SessionRows, type SessionState } from "./domain.ts";
|
|
2
|
+
import { type AgentOf, type Attribution, type Identity, type RecordedStep, type Recording, type Row, type SessionRows, type SessionState } from "./domain.ts";
|
|
3
3
|
/**
|
|
4
4
|
* Create `.flow/`, self-ignored, and answer whether it is there.
|
|
5
5
|
*
|
|
@@ -35,11 +35,14 @@ export interface LogContext {
|
|
|
35
35
|
readonly session: string;
|
|
36
36
|
/** The branch this worktree has out, for the stream's meta row. The adapter knows it; this does not. */
|
|
37
37
|
readonly branch: string | null;
|
|
38
|
+
/** The agent every row came from, stamped beside the time. */
|
|
39
|
+
readonly who: AgentOf;
|
|
38
40
|
/** Injected, because a clock inside a decision is a decision nobody can test. */
|
|
39
41
|
readonly now?: () => Date;
|
|
40
42
|
}
|
|
41
43
|
/**
|
|
42
|
-
* Append rows to this session's stream, stamping each with the time
|
|
44
|
+
* Append rows to this session's stream, stamping each with the time, the next event index and the
|
|
45
|
+
* agent it came from.
|
|
43
46
|
*
|
|
44
47
|
* The FIRST write creates the file with its meta row, which is the fail-loud "armed" marker — a
|
|
45
48
|
* present-but-quiet log and a missing one are different facts, and a reader that cannot tell them
|
|
@@ -99,6 +102,6 @@ export declare function loadRecording(file: string): Recording;
|
|
|
99
102
|
* marker tracks the session's latest activity and its timestamp is what `attribution` judges
|
|
100
103
|
* freshness by.
|
|
101
104
|
*/
|
|
102
|
-
export declare function writeMarker(root: string, session: string,
|
|
103
|
-
/** Who this worktree's commit belongs to — the fresh marker's session × agent, or `commit`. */
|
|
105
|
+
export declare function writeMarker(root: string, session: string, who: AgentOf, branch: string | null, at?: Date): boolean;
|
|
106
|
+
/** Who this worktree's commit belongs to — the fresh marker's session × agent and its type, or `commit`. */
|
|
104
107
|
export declare function commitAttribution(root: string, branch: string | null, nowMs?: number): Attribution;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
export { guardrail, breadcrumb, type Sentence, type EntrySpec, type GuardrailSpec, type BreadcrumbSpec, type GuardrailSentence, type BreadcrumbSentence, type EntryGroup, type DisabledMark, } from "./language/domain.ts";
|
|
2
2
|
export { definePack, pack, override, defineConfig, type Pack, type PackDefinition, type EntryRef, type RefTarget, type FlowConfig, type Binding, type PackBinding, type OverrideBinding, type ConfigSentence, } from "./language/domain.ts";
|
|
3
|
-
export { defineCheck, withFaults, faultsOf, type Check, type Ctx, type World, type Verdict, type ExecResult, type TurnAction, type Case, type CaseWorld, type Cases, } from "./language/domain.ts";
|
|
3
|
+
export { defineCheck, withFaults, faultsOf, type Check, type Ctx, type World, type Verdict, type ExecResult, type TurnAction, type ShellCommand, type ShellHeredoc, type ShellRedirect, type ShellSubstitution, type Case, type CaseWorld, type Cases, } from "./language/domain.ts";
|
|
4
4
|
export { defineCategory, type Category, type Classifier, type SessionFacts } from "./language/domain.ts";
|
|
5
|
-
export { astGrep, banCommands, canonicalFiles, changeTogether, commitReason, depcruise, execPasses, jsonInvariant, protectedPath, ranSinceEdit, siblingExists, symbolsInSibling, textBan, type CanonOptions, type ChangeGroup, type Dialect, type Layer, type DiffAdds, type ForbidEdge, type JsonAssert, type RequireEdge, } from "./checks/domain.ts";
|
|
5
|
+
export { astGrep, banCommands, canonicalFiles, changeTogether, commitReason, depcruise, execPasses, jsonInvariant, oneWriter, protectedPath, ranSinceEdit, siblingExists, symbolsInSibling, textBan, type CanonOptions, type ChangeGroup, type Dialect, type Layer, type DiffAdds, type ForbidEdge, type JsonAssert, type RequireEdge, } from "./checks/domain.ts";
|
|
6
6
|
export { substitutionInProse, SUBSTITUTION_MESSAGE } from "./checks/domain.ts";
|
|
7
|
-
export {
|
|
7
|
+
export { programName } from "./language/domain.ts";
|
|
8
|
+
export { commitMessage, gitCommand, gitCommands, gitDirPrefix, givesReason, quoteArg, shellCommands, type GitCommand, } from "./checks/domain.ts";
|
|
8
9
|
export { spawnedAs, type SpawnRecipe } from "./engine/domain.ts";
|
|
9
10
|
export { write, command, commit, deletion, turnEnd, session, touch, type Moment, type GuardrailMoment, type BreadcrumbMoment, } from "./language/domain.ts";
|
|
10
11
|
export { loadConfig, refusalText, REFUSAL_CODES, type Refusal, type RefusalCode, type LoadResult, type LoadedEntry, type Settings, } from "./language/domain.ts";
|