@jawache/flow 0.2.1 → 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 -277
- package/dist/flow.mjs +12290 -124
- package/dist/index.mjs +7945 -24
- package/dist/packs.mjs +9468 -141
- package/dist/types/adapter/claude.d.ts +23 -15
- package/dist/types/adapter/domain.d.ts +630 -153
- package/dist/types/adapter/product.d.ts +5 -0
- package/dist/types/checks/domain.d.ts +111 -106
- package/dist/types/engine/domain.d.ts +65 -15
- package/dist/types/engine/state.d.ts +7 -4
- package/dist/types/index.d.ts +3 -2
- package/dist/types/language/domain.d.ts +204 -21
- package/dist/types/packs/docs.d.ts +17 -13
- 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 +4 -4
- 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,57 +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`.
|
|
36
37
|
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
* commands.
|
|
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
42
|
*
|
|
43
|
-
* `
|
|
44
|
-
*
|
|
45
|
-
*
|
|
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.
|
|
46
53
|
*/
|
|
47
54
|
export declare const banCommands: (options: {
|
|
48
55
|
ban: readonly string[];
|
|
49
|
-
|
|
56
|
+
wholeLine?: boolean;
|
|
50
57
|
}) => Check;
|
|
51
58
|
/**
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
* `
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
|
|
74
|
-
|
|
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;
|
|
75
88
|
/**
|
|
76
89
|
* What a blocked prose-writing command is told, and it is the same sentence whichever pack caught
|
|
77
|
-
* it: the mechanism, the absence of an undo,
|
|
90
|
+
* it: the mechanism, the absence of an undo, and the fix.
|
|
78
91
|
*
|
|
79
92
|
* ONE constant rather than a message per pack. The sentence never named the verb that was blocked
|
|
80
93
|
* — it names the shape — so two copies would be two places for the same advice to drift, and the
|
|
@@ -226,7 +239,12 @@ export declare const jsonInvariant: (options: {
|
|
|
226
239
|
/** Of some exported names, which are never mentioned in the sibling test's text? */
|
|
227
240
|
export declare function unreferenced(names: readonly string[], siblingText: string): string[];
|
|
228
241
|
/**
|
|
229
|
-
* 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.
|
|
230
248
|
*
|
|
231
249
|
* A real parser rather than a regex, because `export const x = …` inside a string or a comment is
|
|
232
250
|
* exactly the false positive that teaches people to ignore a rule. Parsing is not reaching the
|
|
@@ -235,7 +253,11 @@ export declare function unreferenced(names: readonly string[], siblingText: stri
|
|
|
235
253
|
* milliseconds to load and no other check needs it.
|
|
236
254
|
*/
|
|
237
255
|
export declare function exportedNames(content: string): Promise<string[]>;
|
|
238
|
-
/**
|
|
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
|
+
*/
|
|
239
261
|
export declare const symbolsInSibling: (options: {
|
|
240
262
|
sibling: string;
|
|
241
263
|
}) => Check;
|
|
@@ -270,69 +292,35 @@ export declare const changeTogether: (options: {
|
|
|
270
292
|
* aimed at a directory called HEAD.
|
|
271
293
|
*/
|
|
272
294
|
export declare const GIT_VALUE_OPTS: ReadonlySet<string>;
|
|
273
|
-
/**
|
|
274
|
-
|
|
275
|
-
readonly delim: string;
|
|
276
|
-
readonly tabs: boolean;
|
|
277
|
-
readonly end: number;
|
|
278
|
-
}
|
|
279
|
-
/**
|
|
280
|
-
* The first heredoc opener anywhere in a line, quoted context or not — the one definition of "this
|
|
281
|
-
* line carries a heredoc". A here-string (`<<<`) is stepped over whole, so its tail is not read as
|
|
282
|
-
* one.
|
|
283
|
-
*/
|
|
284
|
-
export declare function firstHeredoc(s: string): HeredocOpen | null;
|
|
295
|
+
/** What a command reader is handed: the line, and its parse when the engine made one. */
|
|
296
|
+
type CommandFacts = Pick<Ctx, "command" | "commands">;
|
|
285
297
|
/**
|
|
286
|
-
*
|
|
287
|
-
*
|
|
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".
|
|
288
301
|
*/
|
|
289
|
-
export declare function
|
|
290
|
-
/**
|
|
291
|
-
export
|
|
292
|
-
/**
|
|
293
|
-
* THE COMMAND LINE WITHOUT ITS HEREDOC BODIES — what a command ban matches unless the rule sets
|
|
294
|
-
* `matchHeredocs`.
|
|
295
|
-
*
|
|
296
|
-
* A heredoc is text fed to a program on its standard input: a python script, a file being written,
|
|
297
|
-
* a note. The words of a banned command inside it are not a use of that command, and matching
|
|
298
|
-
* them refused `python3 - <<'PY'` scripts for naming a recipe in a string. Every body is removed
|
|
299
|
-
* (its terminator line stays, so the opener still ends), whether the delimiter is quoted or not,
|
|
300
|
-
* and wherever the heredoc sits —
|
|
301
|
-
* inside a `$(…)` or a quoted argument too. EVERYTHING ELSE IS LEFT AS WRITTEN — newlines
|
|
302
|
-
* included, because the patterns that police a commit message span them.
|
|
303
|
-
*
|
|
304
|
-
* One exception keeps a body: a heredoc fed to a SHELL (`bash`, `sh`, `zsh`, `eval`, or a pipeline
|
|
305
|
-
* into one), whose body is commands, and a banned one in it will run.
|
|
306
|
-
*
|
|
307
|
-
* Best-effort, and it errs toward matching: from a word whose quote never closes, the line is
|
|
308
|
-
* returned as written, so a ban still sees everything it saw before.
|
|
309
|
-
*/
|
|
310
|
-
export declare function elideHeredocs(command: string): string;
|
|
311
|
-
/**
|
|
312
|
-
* A shell-ish tokeniser: quotes respected (so a commit message mentioning `git commit` is not a
|
|
313
|
-
* second command), operators emitted as their own tokens.
|
|
314
|
-
*
|
|
315
|
-
* Null on unparseable input — an unclosed quote — and null means "cannot tell", which every caller
|
|
316
|
-
* must read as "do not judge". A guard that guesses at a line it cannot parse is worse than one
|
|
317
|
-
* that stands aside.
|
|
318
|
-
*/
|
|
319
|
-
export declare function tokenizeCommand(command: string): string[] | null;
|
|
320
|
-
/** One `git …` in a command line, split where git stops and its subcommand starts. */
|
|
321
|
-
export interface GitInvocation {
|
|
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 {
|
|
322
305
|
/** git's OWN options, before the subcommand — where `-C` counts and only there. */
|
|
323
306
|
readonly globals: readonly string[];
|
|
324
|
-
/** The subcommand word (`commit`, `status`), or null when the
|
|
307
|
+
/** The subcommand word (`commit`, `status`), or null when the command carries none. */
|
|
325
308
|
readonly subcommand: string | null;
|
|
326
309
|
/** Everything after the subcommand — its own flags and arguments. */
|
|
327
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;
|
|
328
315
|
}
|
|
329
316
|
/**
|
|
330
|
-
*
|
|
331
|
-
*
|
|
332
|
-
*
|
|
333
|
-
* operator — so a `git` inside a quoted message or as an argument to something else is not one.
|
|
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.
|
|
334
320
|
*/
|
|
335
|
-
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[];
|
|
336
324
|
/**
|
|
337
325
|
* The message a `git commit` in this command line carries, or null when there is no judgeable
|
|
338
326
|
* commit in it at all. Null is the "not our business" answer and every caller passes on it.
|
|
@@ -350,7 +338,12 @@ export declare function gitInvocations(command: string): GitInvocation[] | null;
|
|
|
350
338
|
*
|
|
351
339
|
* git commit -m MSG · -mMSG · --message MSG · --message=MSG judged
|
|
352
340
|
* git commit -m SUBJECT -m BODY (repeated, any mix of forms) judged, joined as git joins them
|
|
353
|
-
* 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
|
|
354
347
|
* python3 - <<'PY' … PY; git commit -F msg.txt NOT judged — the heredoc is
|
|
355
348
|
* another command's script, and
|
|
356
349
|
* the message is on disk
|
|
@@ -364,7 +357,7 @@ export declare function gitInvocations(command: string): GitInvocation[] | null;
|
|
|
364
357
|
* The boundary is on the record: this rail polices AGENT-typed commits, which are its audience. A
|
|
365
358
|
* human committing from an editor is not its subject and never was.
|
|
366
359
|
*/
|
|
367
|
-
export declare function commitMessage(
|
|
360
|
+
export declare function commitMessage(facts: CommandFacts): string | null;
|
|
368
361
|
/** One shell argument, single-quoted so nothing in it can be expanded or re-parsed. */
|
|
369
362
|
export declare function quoteArg(value: string): string;
|
|
370
363
|
/**
|
|
@@ -392,15 +385,16 @@ export declare function quoteArg(value: string): string;
|
|
|
392
385
|
* options exactly as a shell chains directory changes: `cd /a && git -C b status` runs in `/a/b`,
|
|
393
386
|
* and `-C '/a' -C 'b'` says precisely that. Nothing has to decide which of the two wins.
|
|
394
387
|
*
|
|
395
|
-
* ONLY A LEADING `cd`, and only when
|
|
396
|
-
*
|
|
397
|
-
*
|
|
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.
|
|
398
392
|
*
|
|
399
393
|
* A path the SHELL would have expanded — `~`, a variable — is quoted here and so reaches git
|
|
400
394
|
* literally, which makes the read fail rather than lie. Every caller treats a failed read as "not
|
|
401
395
|
* a repo, git owns that error" and passes: a false negative, which is this pack's stated trade.
|
|
402
396
|
*/
|
|
403
|
-
export declare function gitDirPrefix(
|
|
397
|
+
export declare function gitDirPrefix(facts: CommandFacts): string;
|
|
404
398
|
/**
|
|
405
399
|
* Does the message carry the token, on a LINE OF ITS OWN?
|
|
406
400
|
*
|
|
@@ -655,7 +649,7 @@ export interface Layer {
|
|
|
655
649
|
readonly not?: readonly string[];
|
|
656
650
|
}
|
|
657
651
|
export interface Dialect {
|
|
658
|
-
/** What to cruise
|
|
652
|
+
/** What to cruise: a directory or a glob. `src` and `src/**` cruise the same modules. */
|
|
659
653
|
readonly scan: string;
|
|
660
654
|
/** Each layer, as globs — a bare list, or `{ path, not }` when it has holes in it. */
|
|
661
655
|
readonly layers: Readonly<Record<string, readonly string[] | Layer>>;
|
|
@@ -667,8 +661,6 @@ export interface Dialect {
|
|
|
667
661
|
readonly require?: readonly RequireEdge[];
|
|
668
662
|
/** Carve-outs — `type-only` above all, since a type import is erased at compile time. */
|
|
669
663
|
readonly except?: readonly string[];
|
|
670
|
-
/** Informational rules that never block. `sdp` = the Stable Dependencies Principle. */
|
|
671
|
-
readonly warn?: readonly string[];
|
|
672
664
|
/** dependency-cruiser's own options, passed through. */
|
|
673
665
|
readonly options?: Readonly<Record<string, unknown>>;
|
|
674
666
|
}
|
|
@@ -755,6 +747,15 @@ export declare function nonGlobLayers(layers: Readonly<Record<string, readonly s
|
|
|
755
747
|
* at load: use plain globs for a layer that an `only` names, or express the exclusion as a `forbid`.
|
|
756
748
|
*/
|
|
757
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[];
|
|
758
759
|
/**
|
|
759
760
|
* The generic import-fence runner.
|
|
760
761
|
*
|
|
@@ -847,6 +848,10 @@ export declare function readCase(c: Case): {
|
|
|
847
848
|
readonly dialect: CaseDialect;
|
|
848
849
|
readonly facts: Partial<Ctx>;
|
|
849
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;
|
|
850
855
|
};
|
|
851
856
|
/** Which moment's dialect a case speaks. */
|
|
852
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
|
/**
|
|
@@ -359,15 +383,42 @@ export interface SessionMarker {
|
|
|
359
383
|
* delete. Absent on a marker written before this field existed, which reads as "unknown actor".
|
|
360
384
|
*/
|
|
361
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;
|
|
362
388
|
readonly ts?: unknown;
|
|
363
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;
|
|
364
415
|
/** A commit long after the last edit is not this session's. */
|
|
365
416
|
export declare const MARKER_MAX_AGE_MS: number;
|
|
366
417
|
/** Who a commit-gate run belongs to: the live session, and which of its agents was working. */
|
|
367
418
|
export interface Attribution {
|
|
368
419
|
readonly session: string;
|
|
369
420
|
/** Null when the marker named no agent — an old marker, or a run with nobody to attribute to. */
|
|
370
|
-
readonly
|
|
421
|
+
readonly who: AgentOf | null;
|
|
371
422
|
}
|
|
372
423
|
/**
|
|
373
424
|
* Which session × agent a commit-gate run belongs to, given the marker it found.
|
|
@@ -647,12 +698,11 @@ export declare const RECORDING_VERSION = 1;
|
|
|
647
698
|
export type RecordedStep = {
|
|
648
699
|
readonly rail: "guard";
|
|
649
700
|
readonly moment: GuardrailMoment;
|
|
650
|
-
readonly file?:
|
|
651
|
-
readonly path: string;
|
|
652
|
-
readonly content: string;
|
|
653
|
-
} | undefined;
|
|
701
|
+
readonly file?: Ctx["file"];
|
|
654
702
|
readonly command?: string | undefined;
|
|
655
703
|
readonly staged?: readonly string[] | undefined;
|
|
704
|
+
readonly added?: readonly string[] | undefined;
|
|
705
|
+
readonly deleted?: GuardEvent["deleted"];
|
|
656
706
|
readonly turn?: readonly TurnAction[] | undefined;
|
|
657
707
|
readonly wearing: readonly string[];
|
|
658
708
|
readonly world?: CaseWorld | undefined;
|
|
@@ -736,8 +786,8 @@ export interface RowDiff {
|
|
|
736
786
|
* Only the two MECHANISM rows are compared, and that is not a softening: a replay has no tool
|
|
737
787
|
* calls to record and no session to stamp a meta row for, so `tool`, `run` and `meta` are facts
|
|
738
788
|
* about the live process rather than about the guard's judgement. What the recording claims is
|
|
739
|
-
* that the same rules refuse the same subjects and the same notes show for the same causes
|
|
740
|
-
* 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.
|
|
741
791
|
*/
|
|
742
792
|
export declare function diffRows(replayed: readonly Row[], live: readonly Row[]): RowDiff[];
|
|
743
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
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";
|