balladeer 1.0.16 → 1.0.18
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/dist/anchor-budget.d.ts +40 -0
- package/dist/anchor-budget.js +110 -0
- package/dist/anchoring.d.ts +241 -0
- package/dist/anchoring.js +766 -0
- package/dist/anchors-client.d.ts +62 -0
- package/dist/anchors-client.js +197 -0
- package/dist/anchors-schema.d.ts +85 -0
- package/dist/anchors-schema.js +246 -0
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +24 -7
- package/dist/commands/anchor-usage.d.ts +6 -0
- package/dist/commands/anchor-usage.js +19 -0
- package/dist/commands/anchor.d.ts +127 -0
- package/dist/commands/anchor.js +303 -0
- package/dist/commands/guidance.d.ts +2 -1
- package/dist/commands/guidance.js +55 -0
- package/dist/commands/judge.d.ts +219 -12
- package/dist/commands/judge.js +911 -103
- package/dist/commands/map.d.ts +46 -2
- package/dist/commands/map.js +244 -5
- package/dist/commands/mcp.js +2 -2
- package/dist/guidance-hook.mjs +282 -95
- package/dist/guidance.d.ts +7 -0
- package/dist/guidance.js +10 -1
- package/dist/headless-agent.d.ts +19 -1
- package/dist/headless-agent.js +22 -5
- package/dist/hook-trust.d.ts +41 -9
- package/dist/hook-trust.js +98 -16
- package/dist/judge-brief.d.ts +33 -3
- package/dist/judge-brief.js +39 -2
- package/dist/judge-hook.d.ts +188 -14
- package/dist/judge-hook.js +919 -61
- package/dist/judge-said.d.ts +52 -0
- package/dist/judge-said.js +181 -0
- package/dist/promise-meaning.d.ts +25 -0
- package/dist/promise-meaning.js +24 -7
- package/dist/relay.d.ts +71 -0
- package/dist/relay.js +193 -0
- package/dist/remove-earlier.js +4 -2
- package/dist/risk/git.d.ts +3 -1
- package/dist/risk/git.js +3 -3
- package/dist/risk/graph-cache.d.ts +83 -0
- package/dist/risk/graph-cache.js +291 -0
- package/dist/risk/import-graph.d.ts +43 -0
- package/dist/risk/import-graph.js +88 -34
- package/dist/risk/index.d.ts +1 -1
- package/dist/risk/index.js +1 -1
- package/dist/risk/pipeline.d.ts +13 -0
- package/dist/risk/pipeline.js +15 -4
- package/dist/risk/score.d.ts +10 -0
- package/dist/risk/score.js +11 -2
- package/dist/scratch-worktree.d.ts +5 -1
- package/dist/scratch-worktree.js +9 -2
- package/dist/user-scope.d.ts +74 -3
- package/dist/user-scope.js +270 -9
- package/dist/wire.d.ts +19 -3
- package/dist/wire.js +2 -2
- package/package.json +7 -2
package/dist/judge-hook.d.ts
CHANGED
|
@@ -1,20 +1,30 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Where a judge run starts: a coding host's
|
|
3
|
-
* pre-push hook.
|
|
2
|
+
* Where a judge run starts: a coding host's hook around a shell command, or
|
|
3
|
+
* git's own pre-push hook.
|
|
4
4
|
*
|
|
5
|
-
* The host hook fires on every shell command the agent runs,
|
|
6
|
-
*
|
|
7
|
-
* push
|
|
8
|
-
*
|
|
9
|
-
*
|
|
5
|
+
* The host hook fires on every shell command the agent runs, twice: before it
|
|
6
|
+
* runs (PreToolUse) and after (PostToolUse). The first job each time is to say
|
|
7
|
+
* "not a push" and get out of the way in milliseconds. Only `git push`, `gh pr
|
|
8
|
+
* create` and `gh pr merge` go further. The command is read the way a shell
|
|
9
|
+
* reads it, so a push inside a quoted commit message is not a push and `git
|
|
10
|
+
* stash push` is not one either.
|
|
11
|
+
*
|
|
12
|
+
* Report mode judges after the push, so the push is never held and the check
|
|
13
|
+
* sees exactly what was pushed. Block mode judges before it, because a push
|
|
14
|
+
* can only be held before it runs.
|
|
10
15
|
*/
|
|
11
16
|
export type HookHost = "claude" | "codex" | "git";
|
|
17
|
+
/** The two host events the judge is installed on, in the order a command meets them. */
|
|
18
|
+
export type HookEvent = "PreToolUse" | "PostToolUse";
|
|
19
|
+
export declare const JUDGE_HOOK_EVENTS: readonly HookEvent[];
|
|
12
20
|
export type HookInput = Readonly<{
|
|
13
21
|
eventName?: string;
|
|
14
22
|
toolName?: string;
|
|
15
23
|
command?: string;
|
|
16
24
|
cwd?: string;
|
|
17
25
|
model?: string;
|
|
26
|
+
/** The host's session, from `session_id`, which both Claude Code and Codex send. */
|
|
27
|
+
sessionId?: string;
|
|
18
28
|
}>;
|
|
19
29
|
/** A coding host's hook payload, or nothing when the text is not one. */
|
|
20
30
|
export declare function parseHookInput(text: string): HookInput | undefined;
|
|
@@ -26,17 +36,118 @@ export type PushedRef = Readonly<{
|
|
|
26
36
|
remoteSha: string;
|
|
27
37
|
}>;
|
|
28
38
|
export declare function parsePrePushInput(text: string): PushedRef[];
|
|
39
|
+
/** What separates or groups commands in a shell line. */
|
|
40
|
+
export type ShellOperator = ";" | "&&" | "||" | "|" | "&" | "\n" | "(" | ")" | "`";
|
|
41
|
+
/** One piece of a shell line: a simple command's words, or what comes between commands. */
|
|
42
|
+
export type ShellPiece = Readonly<{
|
|
43
|
+
kind: "command";
|
|
44
|
+
words: readonly string[];
|
|
45
|
+
}> | Readonly<{
|
|
46
|
+
kind: "operator";
|
|
47
|
+
operator: ShellOperator;
|
|
48
|
+
}>;
|
|
29
49
|
/**
|
|
30
|
-
*
|
|
50
|
+
* A shell line in the order a shell reads it: simple commands and the
|
|
51
|
+
* operators between them.
|
|
52
|
+
*
|
|
53
|
+
* Quotes and backslashes are honored, so quoted text stays one word. `;`,
|
|
54
|
+
* `&&`, `||`, `|`, `&`, newlines, parentheses and backquotes come between
|
|
55
|
+
* commands. A comment runs to the end of its line. A redirection (`>`, `2>&1`,
|
|
56
|
+
* `<file`, `&>log`) and its target are left out of the command's words.
|
|
31
57
|
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
58
|
+
* A here-document (`<<WORD`, `<<'WORD'`, `<<"WORD"`, `<<-WORD`) is data: the
|
|
59
|
+
* lines after its command, up to a line that is exactly WORD (leading tabs
|
|
60
|
+
* removed for `<<-`), are not commands. A here-string's word (`<<<`) is data
|
|
61
|
+
* too. Two exceptions keep a push inside one from going unseen: text fed to a
|
|
62
|
+
* shell that reads its script from input (`bash <<EOF`, `cat <<EOF | sh`) is
|
|
63
|
+
* read as that script, and the `$(...)` and backquotes in a here-document
|
|
64
|
+
* whose delimiter is not quoted are read as the commands the shell runs while
|
|
65
|
+
* expanding it. Either is read as a subshell, after the pipeline it feeds. A
|
|
66
|
+
* here-document that never reaches its delimiter line is not taken as one, so
|
|
67
|
+
* the lines after it are still read as commands.
|
|
68
|
+
*/
|
|
69
|
+
export declare function scanShell(line: string, depth?: number): ShellPiece[];
|
|
70
|
+
/**
|
|
71
|
+
* The simple commands a shell line runs, as word lists, in the order
|
|
72
|
+
* `scanShell` reads them: `npm test && git push` is two commands, `$(git
|
|
73
|
+
* push)` is found, and a here-document's text is not a command.
|
|
36
74
|
*/
|
|
37
75
|
export declare function shellCommands(line: string): string[][];
|
|
76
|
+
/** Where one push, pull request creation or merge in a shell line runs. */
|
|
77
|
+
export type PushSite = Readonly<{
|
|
78
|
+
kind: "push" | "create" | "merge";
|
|
79
|
+
/** The absolute directory it runs in; absent when that cannot be told for certain. */
|
|
80
|
+
directory?: string;
|
|
81
|
+
/** The repository a pull request command names with `--repo`, `-R` or `GH_REPO`. */
|
|
82
|
+
repo?: string;
|
|
83
|
+
}>;
|
|
84
|
+
/**
|
|
85
|
+
* Every push, pull request creation and merge in a shell line, with the
|
|
86
|
+
* directory it runs in when that can be told for certain.
|
|
87
|
+
*
|
|
88
|
+
* Starting from `cwd`, each `cd` moves the line along in order, and a git
|
|
89
|
+
* command's own `-C` options apply to that command alone. A `cd` inside
|
|
90
|
+
* parentheses, backquotes or `$(...)` ends with them. Anything that makes the
|
|
91
|
+
* directory uncertain makes it unknown from there on rather than guessed: `cd`
|
|
92
|
+
* with no directory, `cd -`, a variable or pattern, `pushd` or `popd`, a `cd`
|
|
93
|
+
* in a pipeline or in the background, a `cd` inside `if`, a loop, `case` or a
|
|
94
|
+
* function, and a `cd` that ran only when an earlier command succeeded or
|
|
95
|
+
* failed, once the line goes past its `&&` chain. A shell's `-c` script starts
|
|
96
|
+
* where the shell does and moves nothing outside it.
|
|
97
|
+
*/
|
|
98
|
+
export declare function pushSites(line: string, cwd: string | undefined, depth?: number): PushSite[];
|
|
99
|
+
/** Where a shell line's pushes run, when that is one directory that can be told for certain. */
|
|
100
|
+
export type PushPlace = Readonly<{
|
|
101
|
+
known: true;
|
|
102
|
+
directory: string;
|
|
103
|
+
sites: readonly PushSite[];
|
|
104
|
+
}> | Readonly<{
|
|
105
|
+
known: false;
|
|
106
|
+
}>;
|
|
107
|
+
/**
|
|
108
|
+
* The one directory a shell line's pushes run in, starting from `cwd`, or
|
|
109
|
+
* unknown when any of them cannot be told or they run in more than one.
|
|
110
|
+
*/
|
|
111
|
+
export declare function pushPlace(line: string, cwd: string): PushPlace;
|
|
112
|
+
/**
|
|
113
|
+
* A repository as `owner/name` in lower case, from a remote URL or a `--repo`
|
|
114
|
+
* value: `https://github.com/o/r.git`, `git@github.com:o/r.git`,
|
|
115
|
+
* `ssh://git@host/o/r`, `HOST/o/r` or `o/r`. Nothing when there is no owner.
|
|
116
|
+
*/
|
|
117
|
+
export declare function repositoryName(reference: string): string | undefined;
|
|
118
|
+
/** The repositories a checkout's remotes point at, from what `git remote -v` prints. */
|
|
119
|
+
export declare function remoteRepositoryNames(listing: string): Set<string>;
|
|
38
120
|
/** Whether a shell line pushes: `git push`, `gh pr create`, or `gh pr merge`, anywhere in it. */
|
|
39
|
-
export declare function isPushCommand(line: string
|
|
121
|
+
export declare function isPushCommand(line: string): boolean;
|
|
122
|
+
/**
|
|
123
|
+
* Whether a shell line sends this checkout's commits: a `git push`, or a `gh pr
|
|
124
|
+
* create`, which pushes the branch it opens a pull request for. A line whose
|
|
125
|
+
* only push is `gh pr merge` sends none; that pull request's commits were on
|
|
126
|
+
* the remote before the line ran.
|
|
127
|
+
*/
|
|
128
|
+
export declare function sendsCommits(line: string): boolean;
|
|
129
|
+
/**
|
|
130
|
+
* The first command in the line that makes a commit before it pushes, as
|
|
131
|
+
* `git <subcommand>`, or nothing.
|
|
132
|
+
*
|
|
133
|
+
* A host's PreToolUse hook sees the checkout before the line runs, so in
|
|
134
|
+
* `git commit -am x && git push` the check judges the code as it was before
|
|
135
|
+
* the commit, and the commit the push carries is one it never saw. Only a
|
|
136
|
+
* commit made before the push matters: one made after it is not in what was
|
|
137
|
+
* pushed, and `gh pr merge` merges a pull request's head, not this checkout's.
|
|
138
|
+
*/
|
|
139
|
+
export declare function commitBeforePush(line: string): string | undefined;
|
|
140
|
+
/**
|
|
141
|
+
* What a `gh pr merge` names. Neither field means the current branch's pull
|
|
142
|
+
* request, which is the change in this checkout; either one can name a pull
|
|
143
|
+
* request whose change is somewhere else entirely.
|
|
144
|
+
*/
|
|
145
|
+
export type MergeTarget = Readonly<{
|
|
146
|
+
selector?: string;
|
|
147
|
+
repo?: string;
|
|
148
|
+
}>;
|
|
149
|
+
/** Every `gh pr merge` in a shell line, with the pull request and repository it names. */
|
|
150
|
+
export declare function mergeTargets(line: string): MergeTarget[];
|
|
40
151
|
export type HookResponse = Readonly<{
|
|
41
152
|
stdout: string;
|
|
42
153
|
stderr: string;
|
|
@@ -47,11 +158,14 @@ export type HookResponse = Readonly<{
|
|
|
47
158
|
* and the push goes ahead; a block is exit 2 with the reason, which both hosts
|
|
48
159
|
* treat as a refusal of that one command. Claude Code also gets the reason as
|
|
49
160
|
* `permissionDecision: "deny"`, which it prefers to stderr; Codex is given the
|
|
50
|
-
* stderr form its documentation names.
|
|
161
|
+
* stderr form its documentation names. After the command ran (PostToolUse)
|
|
162
|
+
* there is nothing left to hold, so the lines are only ever context, named for
|
|
163
|
+
* the event that asked.
|
|
51
164
|
*/
|
|
52
165
|
export declare function hookResponse(host: HookHost, outcome: Readonly<{
|
|
53
166
|
block: boolean;
|
|
54
167
|
lines: readonly string[];
|
|
168
|
+
event?: HookEvent;
|
|
55
169
|
}>): HookResponse;
|
|
56
170
|
/** The status line both hosts show while the hook runs, and the mark of the entry this command owns. */
|
|
57
171
|
export declare const JUDGE_HOOK_MARKER = "Balladeer judge";
|
|
@@ -78,7 +192,67 @@ export type HookInstallResult = Readonly<{
|
|
|
78
192
|
status: "written" | "unchanged" | "refused";
|
|
79
193
|
reason?: string;
|
|
80
194
|
}>;
|
|
195
|
+
/** The hook file the installer writes for a host, relative to the repository root. */
|
|
196
|
+
export declare function judgeHookFile(host: "claude" | "codex"): string;
|
|
197
|
+
/**
|
|
198
|
+
* Whether the hook file the installer writes has this command's entry for one
|
|
199
|
+
* event. It reads the file rather than guessing, so a checkout installed by an
|
|
200
|
+
* earlier release, which wrote only the PreToolUse entry, is told apart from a
|
|
201
|
+
* current one. Anything unreadable counts as no entry.
|
|
202
|
+
*/
|
|
203
|
+
export declare function hasJudgeEntry(root: string, host: "claude" | "codex", event: HookEvent): boolean;
|
|
204
|
+
/**
|
|
205
|
+
* Whether this checkout has the push check's own project entry for a host,
|
|
206
|
+
* under either event. Where it does, that entry owns the check in this
|
|
207
|
+
* checkout and the one setup writes at the host's user scope stands aside, so
|
|
208
|
+
* a push is judged once. Either event counts: a checkout an earlier release
|
|
209
|
+
* installed has only the entry before the push, and it keeps judging there.
|
|
210
|
+
*/
|
|
211
|
+
export declare function hasProjectJudgeHook(root: string, host: "claude" | "codex"): boolean;
|
|
212
|
+
/**
|
|
213
|
+
* The checkout that holds a folder, found by walking up to its `.git` entry (a
|
|
214
|
+
* directory, or a file in a worktree). No process is started, so a folder that
|
|
215
|
+
* is not in a checkout at all is known in well under a millisecond.
|
|
216
|
+
*/
|
|
217
|
+
export declare function checkoutRoot(start: string): string | undefined;
|
|
218
|
+
/**
|
|
219
|
+
* The push check's switch for this laptop, in Balladeer's own config home.
|
|
220
|
+
* Absent means on. Written only by `balladeer judge --push-check off`, and
|
|
221
|
+
* read by the user-scope entry on each push, so turning it off takes effect in
|
|
222
|
+
* sessions that are already open.
|
|
223
|
+
*/
|
|
224
|
+
export declare const PUSH_CHECK_SETTING_FILE = "push-check.json";
|
|
225
|
+
export declare function pushCheckSettingPath(environment: NodeJS.ProcessEnv): string;
|
|
226
|
+
/** True only when this laptop's switch says off; anything unreadable leaves it on. */
|
|
227
|
+
export declare function pushCheckOff(environment: NodeJS.ProcessEnv): boolean;
|
|
228
|
+
/** Turn the user-scope push check off or back on for this laptop. */
|
|
229
|
+
export declare function setPushCheck(environment: NodeJS.ProcessEnv, on: boolean): void;
|
|
81
230
|
/** Where git looks for hooks in this checkout, which is not `.git/hooks` in a worktree. */
|
|
82
231
|
export declare function gitHooksDirectory(root: string): string;
|
|
83
232
|
/** Install the judge hook for one host. Running it twice changes nothing the second time. */
|
|
84
233
|
export declare function installJudgeHook(root: string, target: HookHost): HookInstallResult;
|
|
234
|
+
/**
|
|
235
|
+
* Whether a commit is on a remote now: reachable from a remote-tracking ref,
|
|
236
|
+
* or else the tip of a branch some remote reports.
|
|
237
|
+
*
|
|
238
|
+
* `git push`, `git push origin HEAD:branch` and the push `gh pr create` makes
|
|
239
|
+
* all move the remote-tracking ref of what they pushed, so the first check is
|
|
240
|
+
* local and immediate. The second is there because a push can land without
|
|
241
|
+
* that ref moving: Codex's workspace sandbox keeps `.git` read-only, so its
|
|
242
|
+
* pushes reach the remote and then fail to update `refs/remotes/...` (hook
|
|
243
|
+
* canary, 29 September 2026). Asking each remote for its branch tips settles
|
|
244
|
+
* it without fetching anything, and a push that failed leaves its commit on
|
|
245
|
+
* neither.
|
|
246
|
+
*/
|
|
247
|
+
export declare function pushLanded(root: string, sha: string): Promise<boolean>;
|
|
248
|
+
/**
|
|
249
|
+
* Where the remote default branch stood before this push, when this push is
|
|
250
|
+
* what moved it to `head`; otherwise nothing, and the ordinary fork-point rule
|
|
251
|
+
* applies.
|
|
252
|
+
*
|
|
253
|
+
* Judged after a push straight to the default branch, the fork point with that
|
|
254
|
+
* branch's remote-tracking ref is `head` itself, so the change would shrink to
|
|
255
|
+
* its last commit. git records the push in the ref's reflog ("update by
|
|
256
|
+
* push"), and the entry before it is where the branch stood.
|
|
257
|
+
*/
|
|
258
|
+
export declare function baseBeforePush(root: string, head: string): Promise<string | undefined>;
|