balladeer 1.0.14 → 1.0.16
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 +2 -0
- package/dist/behavior-map-schema.d.ts +107 -0
- package/dist/behavior-map-schema.js +232 -0
- package/dist/cli.d.ts +5 -0
- package/dist/cli.js +77 -2
- package/dist/commands/guidance.d.ts +1 -1
- package/dist/commands/guidance.js +6 -0
- package/dist/commands/judge.d.ts +271 -0
- package/dist/commands/judge.js +1170 -0
- package/dist/commands/map.d.ts +120 -0
- package/dist/commands/map.js +601 -0
- package/dist/commands/mcp.d.ts +6 -0
- package/dist/commands/mcp.js +53 -0
- package/dist/commands/offers.d.ts +265 -0
- package/dist/commands/offers.js +801 -0
- package/dist/commands/risk.d.ts +29 -0
- package/dist/commands/risk.js +133 -0
- package/dist/copy.d.ts +24 -1
- package/dist/copy.js +91 -0
- package/dist/guidance-hook.mjs +230 -74
- package/dist/guidance.d.ts +2 -0
- package/dist/guidance.js +7 -1
- package/dist/headless-agent.d.ts +166 -0
- package/dist/headless-agent.js +416 -0
- package/dist/hook-trust.d.ts +31 -0
- package/dist/hook-trust.js +63 -0
- package/dist/judge-brief.d.ts +36 -0
- package/dist/judge-brief.js +89 -0
- package/dist/judge-hook.d.ts +84 -0
- package/dist/judge-hook.js +420 -0
- package/dist/map-brief.d.ts +16 -0
- package/dist/map-brief.js +41 -0
- package/dist/offer-brief.d.ts +95 -0
- package/dist/offer-brief.js +217 -0
- package/dist/owned-process.d.ts +65 -0
- package/dist/owned-process.js +146 -0
- package/dist/promise-meaning.d.ts +126 -0
- package/dist/promise-meaning.js +292 -0
- package/dist/risk/contract.d.ts +145 -0
- package/dist/risk/contract.js +74 -0
- package/dist/risk/describe.d.ts +7 -0
- package/dist/risk/describe.js +45 -0
- package/dist/risk/diff.d.ts +26 -0
- package/dist/risk/diff.js +174 -0
- package/dist/risk/extract.d.ts +43 -0
- package/dist/risk/extract.js +336 -0
- package/dist/risk/git.d.ts +30 -0
- package/dist/risk/git.js +118 -0
- package/dist/risk/import-graph.d.ts +41 -0
- package/dist/risk/import-graph.js +487 -0
- package/dist/risk/index.d.ts +20 -0
- package/dist/risk/index.js +20 -0
- package/dist/risk/paths.d.ts +16 -0
- package/dist/risk/paths.js +73 -0
- package/dist/risk/pipeline.d.ts +47 -0
- package/dist/risk/pipeline.js +121 -0
- package/dist/risk/priors.d.ts +18 -0
- package/dist/risk/priors.js +86 -0
- package/dist/risk/resources.d.ts +56 -0
- package/dist/risk/resources.js +374 -0
- package/dist/risk/score.d.ts +85 -0
- package/dist/risk/score.js +543 -0
- package/dist/risk/symbols.d.ts +35 -0
- package/dist/risk/symbols.js +348 -0
- package/dist/risk/text.d.ts +43 -0
- package/dist/risk/text.js +277 -0
- package/dist/risk/validate.d.ts +19 -0
- package/dist/risk/validate.js +154 -0
- package/dist/scratch-worktree.d.ts +51 -0
- package/dist/scratch-worktree.js +153 -0
- package/dist/self-update.d.ts +29 -2
- package/dist/self-update.js +96 -11
- package/dist/user-scope.d.ts +2 -0
- package/dist/user-scope.js +26 -2
- package/dist/wire.d.ts +55 -2
- package/dist/wire.js +5 -1
- package/package.json +1 -1
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
import { type HeadlessAgentOptions, type HeadlessAgentResult, type HeadlessClient } from "../headless-agent.js";
|
|
2
|
+
import type { HookHost } from "../judge-hook.js";
|
|
3
|
+
import { OFFER_BRIEF_REVISION, OFFER_QUESTION, type BoundedDiff, type ExistingPromise } from "../offer-brief.js";
|
|
4
|
+
import { type PromiseDocument, type PromiseReader } from "../promise-meaning.js";
|
|
5
|
+
/**
|
|
6
|
+
* `balladeer offers`: the rules a change establishes that no promise records,
|
|
7
|
+
* for the person pushing it to be offered in that same session.
|
|
8
|
+
*
|
|
9
|
+
* It reads and reports. It stores nothing, records nothing and proposes
|
|
10
|
+
* nothing: a rule becomes a proposal only when the person says yes to the
|
|
11
|
+
* question each offer carries, in the session this hands its findings to, and
|
|
12
|
+
* that session records it. No offer outlives the session it was made in.
|
|
13
|
+
*/
|
|
14
|
+
export declare const OFFERS_SCHEMA_VERSION = "balladeer-offers/v1";
|
|
15
|
+
/** The most existing promises read through the connection for one reading. */
|
|
16
|
+
export declare const MAX_EXISTING_PROMISES = 200;
|
|
17
|
+
/** How long `balladeer offers` gives the reader by hand, unless told otherwise. */
|
|
18
|
+
export declare const DEFAULT_OFFER_TIMEOUT_SECONDS = 240;
|
|
19
|
+
/**
|
|
20
|
+
* What the pushing session is told to do with the offers, stated once after
|
|
21
|
+
* them. It asks for the offers to be shown word for word because the first
|
|
22
|
+
* Codex canary's agent, told only to "offer these", answered that no hook
|
|
23
|
+
* message had appeared: in four sessions it surfaced them once, and with this
|
|
24
|
+
* wording three times in three.
|
|
25
|
+
*/
|
|
26
|
+
export declare const OFFER_INSTRUCTIONS: string;
|
|
27
|
+
export type OfferEvidence = Readonly<{
|
|
28
|
+
file: string;
|
|
29
|
+
shows: string;
|
|
30
|
+
}>;
|
|
31
|
+
/** A rule found in the change, in the shape both `offers` and `held` carry. */
|
|
32
|
+
export type FoundRule = Readonly<{
|
|
33
|
+
rule: string;
|
|
34
|
+
beneficiary: string;
|
|
35
|
+
trigger: string;
|
|
36
|
+
passingExample: string;
|
|
37
|
+
failingExample: string;
|
|
38
|
+
evidence: readonly OfferEvidence[];
|
|
39
|
+
statedBy: "commit" | "test" | "code";
|
|
40
|
+
confidence: "high" | "medium";
|
|
41
|
+
}>;
|
|
42
|
+
/** A rule handed to the pushing session, with the exact question it asks. */
|
|
43
|
+
export type Offer = FoundRule & Readonly<{
|
|
44
|
+
question: typeof OFFER_QUESTION;
|
|
45
|
+
}>;
|
|
46
|
+
export type CoveredRule = Readonly<{
|
|
47
|
+
promiseId: string;
|
|
48
|
+
rule: string;
|
|
49
|
+
}>;
|
|
50
|
+
export type ExistingSource = "connection" | "directory" | "checkout" | "none";
|
|
51
|
+
export type OffersResult = Readonly<{
|
|
52
|
+
schemaVersion: typeof OFFERS_SCHEMA_VERSION;
|
|
53
|
+
brief: typeof OFFER_BRIEF_REVISION;
|
|
54
|
+
base: string;
|
|
55
|
+
head: string;
|
|
56
|
+
offers: readonly Offer[];
|
|
57
|
+
held: readonly FoundRule[];
|
|
58
|
+
covered: readonly CoveredRule[];
|
|
59
|
+
existing: Readonly<{
|
|
60
|
+
known: boolean;
|
|
61
|
+
count: number;
|
|
62
|
+
source: ExistingSource;
|
|
63
|
+
}>;
|
|
64
|
+
notes: string;
|
|
65
|
+
agent: Readonly<{
|
|
66
|
+
client: HeadlessClient;
|
|
67
|
+
model: string;
|
|
68
|
+
durationMs: number;
|
|
69
|
+
inputTokens: number;
|
|
70
|
+
outputTokens: number;
|
|
71
|
+
}>;
|
|
72
|
+
}>;
|
|
73
|
+
/**
|
|
74
|
+
* Keeps what is well formed and whose evidence names a file the change
|
|
75
|
+
* touches, in the reader's own order. The first three are offered, each with
|
|
76
|
+
* the question; the rest are held, in the same shape without it. Covered
|
|
77
|
+
* rules must name a promise the reader was given, and there are none when the
|
|
78
|
+
* existing promises could not be read.
|
|
79
|
+
*/
|
|
80
|
+
export declare function readOfferAnswer(answer: Record<string, unknown> | undefined, input: Readonly<{
|
|
81
|
+
changedFiles: readonly string[];
|
|
82
|
+
existingIds: ReadonlySet<string>;
|
|
83
|
+
existingKnown: boolean;
|
|
84
|
+
}>): {
|
|
85
|
+
offers: Offer[];
|
|
86
|
+
held: FoundRule[];
|
|
87
|
+
covered: CoveredRule[];
|
|
88
|
+
notes: string;
|
|
89
|
+
};
|
|
90
|
+
/**
|
|
91
|
+
* The lines handed to the pushing session, through the hook and as the
|
|
92
|
+
* command's `question`: each rule, what shows it, and the exact question; then
|
|
93
|
+
* what the session may do with them, stated once. Nothing when there is no
|
|
94
|
+
* offer, so a push with nothing to offer hears nothing about it.
|
|
95
|
+
*/
|
|
96
|
+
export declare function offerLines(result: OffersResult): string[];
|
|
97
|
+
/**
|
|
98
|
+
* What a person reads at a terminal: one line per offer, what an existing
|
|
99
|
+
* promise already covers and which, and one sentence saying nothing was
|
|
100
|
+
* recorded. The git pre-push hook prints the same, since whoever ran git
|
|
101
|
+
* reads it.
|
|
102
|
+
*/
|
|
103
|
+
export declare function personLines(result: OffersResult, options?: Readonly<{
|
|
104
|
+
directory?: string;
|
|
105
|
+
quietWhenEmpty?: boolean;
|
|
106
|
+
}>): string[];
|
|
107
|
+
export type ExistingRead = Readonly<{
|
|
108
|
+
known: boolean;
|
|
109
|
+
source: ExistingSource;
|
|
110
|
+
promises: readonly ExistingPromise[];
|
|
111
|
+
notes: readonly string[];
|
|
112
|
+
}>;
|
|
113
|
+
/** One promise as the reader is told it: title, outcome, who, when, and what it forbids. */
|
|
114
|
+
export declare function existingFromDocument(document: PromiseDocument): ExistingPromise;
|
|
115
|
+
/**
|
|
116
|
+
* Every promise in a directory of promise files, or in one file.
|
|
117
|
+
*
|
|
118
|
+
* Each `*.json` holds one promise in `get_promise`'s shape or a sealed
|
|
119
|
+
* package's, read with `readPromiseFile`, or an object with a `promises` array
|
|
120
|
+
* of them, which is how the evaluation's promise sets are laid out. A file
|
|
121
|
+
* holding neither is skipped and named.
|
|
122
|
+
*/
|
|
123
|
+
export declare function readPromisesDirectory(path: string): {
|
|
124
|
+
ok: true;
|
|
125
|
+
promises: PromiseDocument[];
|
|
126
|
+
skipped: string[];
|
|
127
|
+
} | {
|
|
128
|
+
ok: false;
|
|
129
|
+
reason: string;
|
|
130
|
+
};
|
|
131
|
+
/**
|
|
132
|
+
* Every agreed promise through the connection: the ids with `list_promises`,
|
|
133
|
+
* page after page, then each meaning with `get_promise`, which the reader
|
|
134
|
+
* always marks unattended. Any read that fails fails the whole source, so a
|
|
135
|
+
* partial list is never presented as the repository's promises.
|
|
136
|
+
*/
|
|
137
|
+
export declare function readThroughConnection(reader: PromiseReader): Promise<{
|
|
138
|
+
ok: true;
|
|
139
|
+
promises: PromiseDocument[];
|
|
140
|
+
capped: boolean;
|
|
141
|
+
} | {
|
|
142
|
+
ok: false;
|
|
143
|
+
reason: string;
|
|
144
|
+
}>;
|
|
145
|
+
/** The promises this checkout holds: sealed packages first, then behavior-map excerpts. */
|
|
146
|
+
export declare function readCheckoutPromises(root: string): ExistingPromise[];
|
|
147
|
+
/**
|
|
148
|
+
* The promises already recorded, from the first source that answers: a
|
|
149
|
+
* directory named by the caller; else this repository's agent connection;
|
|
150
|
+
* else what the checkout holds. A connection that is missing or fails in any
|
|
151
|
+
* read falls through to the checkout, and the notes say why.
|
|
152
|
+
*/
|
|
153
|
+
export declare function readExistingPromises(input: Readonly<{
|
|
154
|
+
root: string;
|
|
155
|
+
/** Promises already read from `--promises-dir`. */
|
|
156
|
+
directory?: readonly PromiseDocument[];
|
|
157
|
+
/** Undefined: find this repository's connection. Null: there is none. */
|
|
158
|
+
reader?: PromiseReader | null;
|
|
159
|
+
environment: NodeJS.ProcessEnv;
|
|
160
|
+
controlPlane: string;
|
|
161
|
+
}>): Promise<ExistingRead>;
|
|
162
|
+
/** `git diff --no-color <base> <head>`, bounded as the prompt carries it. */
|
|
163
|
+
export declare function readChangeDiff(root: string, base: string, head: string): Promise<BoundedDiff>;
|
|
164
|
+
/** The change's commit messages, oldest first, with trailers that carry an address removed. */
|
|
165
|
+
export declare function readCommitMessages(root: string, base: string, head: string): Promise<string>;
|
|
166
|
+
export type OfferReadInput = Readonly<{
|
|
167
|
+
root: string;
|
|
168
|
+
base: string;
|
|
169
|
+
head: string;
|
|
170
|
+
files: readonly string[];
|
|
171
|
+
existing: ExistingRead;
|
|
172
|
+
client: HeadlessClient | "auto";
|
|
173
|
+
prefer?: HeadlessClient;
|
|
174
|
+
timeoutMs: number;
|
|
175
|
+
environment: NodeJS.ProcessEnv;
|
|
176
|
+
signal?: AbortSignal;
|
|
177
|
+
runAgent?: (options: HeadlessAgentOptions) => Promise<HeadlessAgentResult>;
|
|
178
|
+
}>;
|
|
179
|
+
export type OfferReading = Readonly<{
|
|
180
|
+
ok: true;
|
|
181
|
+
result: OffersResult;
|
|
182
|
+
}> | Readonly<{
|
|
183
|
+
ok: false;
|
|
184
|
+
reason: string;
|
|
185
|
+
message: string;
|
|
186
|
+
}>;
|
|
187
|
+
/** Read one change for rules no promise records. Never throws for the agent or the machine. */
|
|
188
|
+
export declare function readOffers(input: OfferReadInput): Promise<OfferReading>;
|
|
189
|
+
/**
|
|
190
|
+
* Whether a change repairs something, read from what its author wrote: a
|
|
191
|
+
* commit whose subject is a fix, a hotfix or a revert by the usual convention,
|
|
192
|
+
* or says in words that it fixes, restores or reverts, or names a regression.
|
|
193
|
+
*
|
|
194
|
+
* The first measurement (29 September 2026, 24 real regressions) is why this
|
|
195
|
+
* is the default gate. Read at the fix, a change offered the rule that had
|
|
196
|
+
* broken in 20 cases of 24, and as its first offer in 18. Read at the change
|
|
197
|
+
* that first built the behavior, it offered that rule in 3 of 23, while
|
|
198
|
+
* offering two rules or more on almost every feature. A repair is where a rule
|
|
199
|
+
* has just shown what losing it costs; everywhere else the reader is mostly
|
|
200
|
+
* guessing which of many rules will matter, and the person is asked too often.
|
|
201
|
+
* No model is asked: the subject line is what the author said.
|
|
202
|
+
*/
|
|
203
|
+
export declare function isRepairChange(messages: string): boolean;
|
|
204
|
+
/** The first `max` offers stay offers; the rest join what was held, without their question. */
|
|
205
|
+
export declare function limitOffers(result: OffersResult, max: number): OffersResult;
|
|
206
|
+
/**
|
|
207
|
+
* The same reading, for the push hook: the change is already identified, and
|
|
208
|
+
* what comes back is the lines the pushing session or person is handed. Any
|
|
209
|
+
* failure, a missing login or a reading still running at the deadline is no
|
|
210
|
+
* lines at all: finding rules never holds or fails a push.
|
|
211
|
+
*/
|
|
212
|
+
export declare function offersForPush(input: Readonly<{
|
|
213
|
+
root: string;
|
|
214
|
+
base: string;
|
|
215
|
+
head: string;
|
|
216
|
+
files: readonly string[];
|
|
217
|
+
host: HookHost;
|
|
218
|
+
environment: NodeJS.ProcessEnv;
|
|
219
|
+
controlPlane: string;
|
|
220
|
+
client: HeadlessClient | "auto";
|
|
221
|
+
/** When the hook's budget ends, as a time in milliseconds. */
|
|
222
|
+
deadline: number;
|
|
223
|
+
signal: AbortSignal;
|
|
224
|
+
/** Which pushes are read. Absent reads every change, as the command by hand does. */
|
|
225
|
+
policy?: "fixes" | "all";
|
|
226
|
+
/** How many offers are handed over. Absent hands over every offer read. */
|
|
227
|
+
max?: number;
|
|
228
|
+
reader?: PromiseReader | null;
|
|
229
|
+
runAgent?: (options: HeadlessAgentOptions) => Promise<HeadlessAgentResult>;
|
|
230
|
+
now?: () => number;
|
|
231
|
+
}>): Promise<string[]>;
|
|
232
|
+
export type OffersArguments = Readonly<{
|
|
233
|
+
base?: string;
|
|
234
|
+
head?: string;
|
|
235
|
+
json: boolean;
|
|
236
|
+
repo?: string;
|
|
237
|
+
promisesDir?: string;
|
|
238
|
+
client: HeadlessClient | "auto";
|
|
239
|
+
timeoutSeconds: number;
|
|
240
|
+
}>;
|
|
241
|
+
export declare const OFFERS_USAGE = " npx -y balladeer@latest offers [--base <ref>] [--head <ref>] [--json] [--repo <root>]\n [--promises-dir <dir>] [--client auto|claude|codex] [--timeout <seconds>]\n Read this change for rules it establishes that no promise records yet,\n on your own Claude Code or Codex login, and list at most three, each with\n the files in the change that show it. It records nothing and stores\n nothing: to keep one, ask your coding agent to record it as a promise for\n your review. The promises already recorded come from --promises-dir,\n else this repository's Balladeer connection, else the promises sealed or\n mapped in this checkout. The change runs from the fork point with the\n remote default branch to HEAD unless named. --json prints the result.\n";
|
|
242
|
+
export declare function parseOffersArguments(argv: readonly string[]): OffersArguments;
|
|
243
|
+
export type OffersDependencies = Readonly<{
|
|
244
|
+
runAgent?: (options: HeadlessAgentOptions) => Promise<HeadlessAgentResult>;
|
|
245
|
+
/** Null means "no connection", without trying to find one. */
|
|
246
|
+
reader?: PromiseReader | null;
|
|
247
|
+
}>;
|
|
248
|
+
export type OffersRunOptions = Readonly<{
|
|
249
|
+
args: OffersArguments;
|
|
250
|
+
cwd: string;
|
|
251
|
+
environment: NodeJS.ProcessEnv;
|
|
252
|
+
controlPlane: string;
|
|
253
|
+
write: (text: string) => void;
|
|
254
|
+
error: (text: string) => void;
|
|
255
|
+
deps?: OffersDependencies;
|
|
256
|
+
}>;
|
|
257
|
+
/** `balladeer offers`, from arguments already parsed. */
|
|
258
|
+
export declare function runOffers(options: OffersRunOptions): Promise<number>;
|
|
259
|
+
/** `balladeer offers ...` from raw arguments, as `cli.ts` hands them over. */
|
|
260
|
+
export declare function offersCommand(argv: readonly string[], io: Readonly<{
|
|
261
|
+
cwd: string;
|
|
262
|
+
environment: NodeJS.ProcessEnv;
|
|
263
|
+
write: (text: string) => void;
|
|
264
|
+
error: (text: string) => void;
|
|
265
|
+
}>): Promise<number>;
|