@mutagent/evaluator 0.1.0-alpha.2
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/.claude/skills/mutagent-evaluator/SKILL.md +538 -0
- package/.claude/skills/mutagent-evaluator/assets/agents/audit-executor.md +169 -0
- package/.claude/skills/mutagent-evaluator/assets/agents/dataset-builder.md +160 -0
- package/.claude/skills/mutagent-evaluator/assets/agents/discovery.md +425 -0
- package/.claude/skills/mutagent-evaluator/assets/agents/evaluator.md +1044 -0
- package/.claude/skills/mutagent-evaluator/assets/brand/theme.css +213 -0
- package/.claude/skills/mutagent-evaluator/assets/brand/wordmark.html +9 -0
- package/.claude/skills/mutagent-evaluator/lenses/context-flow-lens.md +85 -0
- package/.claude/skills/mutagent-evaluator/lenses/data-lens.md +47 -0
- package/.claude/skills/mutagent-evaluator/lenses/decision-lens.md +48 -0
- package/.claude/skills/mutagent-evaluator/lenses/methodology-critic-lens.md +53 -0
- package/.claude/skills/mutagent-evaluator/lenses/trajectory-lens.md +44 -0
- package/.claude/skills/mutagent-evaluator/references/build-review-interface.md +83 -0
- package/.claude/skills/mutagent-evaluator/references/edd-loop.md +134 -0
- package/.claude/skills/mutagent-evaluator/references/error-analysis.md +113 -0
- package/.claude/skills/mutagent-evaluator/references/eval-audit.md +154 -0
- package/.claude/skills/mutagent-evaluator/references/eval-stage.md +168 -0
- package/.claude/skills/mutagent-evaluator/references/generate-synthetic-data.md +81 -0
- package/.claude/skills/mutagent-evaluator/references/grounded-adjudication.md +221 -0
- package/.claude/skills/mutagent-evaluator/references/memory-format.md +65 -0
- package/.claude/skills/mutagent-evaluator/references/methodology.md +201 -0
- package/.claude/skills/mutagent-evaluator/references/operation-inventory.md +196 -0
- package/.claude/skills/mutagent-evaluator/references/validate-evaluator.md +125 -0
- package/.claude/skills/mutagent-evaluator/references/workflows/orchestrator-protocol.md +287 -0
- package/.claude/skills/mutagent-evaluator/references/write-judge-prompt.md +123 -0
- package/.claude/skills/mutagent-evaluator/schemas/behavior-tree.schema.yaml +73 -0
- package/.claude/skills/mutagent-evaluator/schemas/dataset.schema.yaml +66 -0
- package/.claude/skills/mutagent-evaluator/schemas/edd-change-request.schema.yaml +114 -0
- package/.claude/skills/mutagent-evaluator/schemas/eval-matrix.schema.yaml +74 -0
- package/.claude/skills/mutagent-evaluator/schemas/flow-graph.schema.yaml +69 -0
- package/.claude/skills/mutagent-evaluator/schemas/flow-profile.schema.yaml +49 -0
- package/.claude/skills/mutagent-evaluator/schemas/methodology-review.schema.yaml +40 -0
- package/.claude/skills/mutagent-evaluator/schemas/scorecard.schema.yaml +85 -0
- package/.claude/skills/mutagent-evaluator/scripts/agent-dispatch.ts +0 -0
- package/.claude/skills/mutagent-evaluator/scripts/aggregate-discover.ts +543 -0
- package/.claude/skills/mutagent-evaluator/scripts/artifact-paths.ts +99 -0
- package/.claude/skills/mutagent-evaluator/scripts/assemble-scorecard.ts +172 -0
- package/.claude/skills/mutagent-evaluator/scripts/build-dataset.ts +186 -0
- package/.claude/skills/mutagent-evaluator/scripts/build-evals.ts +93 -0
- package/.claude/skills/mutagent-evaluator/scripts/build-review-ui.ts +393 -0
- package/.claude/skills/mutagent-evaluator/scripts/check-method-router.ts +170 -0
- package/.claude/skills/mutagent-evaluator/scripts/cli/aggregate.ts +112 -0
- package/.claude/skills/mutagent-evaluator/scripts/cli/audit-run.ts +175 -0
- package/.claude/skills/mutagent-evaluator/scripts/cli/doctor.ts +211 -0
- package/.claude/skills/mutagent-evaluator/scripts/cli/dogfood.ts +133 -0
- package/.claude/skills/mutagent-evaluator/scripts/cli/init.ts +601 -0
- package/.claude/skills/mutagent-evaluator/scripts/cli/methodology-review.ts +122 -0
- package/.claude/skills/mutagent-evaluator/scripts/cli/prep.ts +279 -0
- package/.claude/skills/mutagent-evaluator/scripts/cli/profile-subject.ts +165 -0
- package/.claude/skills/mutagent-evaluator/scripts/cli/run.sh +56 -0
- package/.claude/skills/mutagent-evaluator/scripts/cli/variance-check.ts +105 -0
- package/.claude/skills/mutagent-evaluator/scripts/code-eval.ts +248 -0
- package/.claude/skills/mutagent-evaluator/scripts/codegen-evals.ts +173 -0
- package/.claude/skills/mutagent-evaluator/scripts/cold-start-project.ts +151 -0
- package/.claude/skills/mutagent-evaluator/scripts/cold-start-sampler.ts +267 -0
- package/.claude/skills/mutagent-evaluator/scripts/config/load.ts +307 -0
- package/.claude/skills/mutagent-evaluator/scripts/config/schema.ts +325 -0
- package/.claude/skills/mutagent-evaluator/scripts/contracts/agentspec-evals.ts +85 -0
- package/.claude/skills/mutagent-evaluator/scripts/contracts/dataset.ts +149 -0
- package/.claude/skills/mutagent-evaluator/scripts/contracts/eval-engine.ts +118 -0
- package/.claude/skills/mutagent-evaluator/scripts/contracts/eval-matrix.ts +577 -0
- package/.claude/skills/mutagent-evaluator/scripts/contracts/eval-types.ts +1074 -0
- package/.claude/skills/mutagent-evaluator/scripts/contracts/flow-graph.ts +194 -0
- package/.claude/skills/mutagent-evaluator/scripts/contracts/types.ts +303 -0
- package/.claude/skills/mutagent-evaluator/scripts/contracts/validation.ts +193 -0
- package/.claude/skills/mutagent-evaluator/scripts/derive-dataset.ts +152 -0
- package/.claude/skills/mutagent-evaluator/scripts/determine-outcome.ts +219 -0
- package/.claude/skills/mutagent-evaluator/scripts/diff-discriminate.ts +160 -0
- package/.claude/skills/mutagent-evaluator/scripts/discover-criteria.ts +348 -0
- package/.claude/skills/mutagent-evaluator/scripts/edd/change-request.ts +232 -0
- package/.claude/skills/mutagent-evaluator/scripts/edd/edd-types.ts +210 -0
- package/.claude/skills/mutagent-evaluator/scripts/edd/variance-gate.ts +186 -0
- package/.claude/skills/mutagent-evaluator/scripts/emit-completeness.ts +111 -0
- package/.claude/skills/mutagent-evaluator/scripts/eval-engine.ts +160 -0
- package/.claude/skills/mutagent-evaluator/scripts/evaluate.ts +333 -0
- package/.claude/skills/mutagent-evaluator/scripts/flow-graph.ts +230 -0
- package/.claude/skills/mutagent-evaluator/scripts/judge-prompt-template.ts +253 -0
- package/.claude/skills/mutagent-evaluator/scripts/judge-provider.ts +135 -0
- package/.claude/skills/mutagent-evaluator/scripts/lint-grounding.ts +229 -0
- package/.claude/skills/mutagent-evaluator/scripts/lint-uniformity.ts +168 -0
- package/.claude/skills/mutagent-evaluator/scripts/living-suite.ts +109 -0
- package/.claude/skills/mutagent-evaluator/scripts/load-bundle.ts +203 -0
- package/.claude/skills/mutagent-evaluator/scripts/load-profile-vocab.ts +64 -0
- package/.claude/skills/mutagent-evaluator/scripts/load-profile.ts +106 -0
- package/.claude/skills/mutagent-evaluator/scripts/mask.ts +138 -0
- package/.claude/skills/mutagent-evaluator/scripts/materialize-dataset.ts +113 -0
- package/.claude/skills/mutagent-evaluator/scripts/matrix-judge.ts +750 -0
- package/.claude/skills/mutagent-evaluator/scripts/memory/append.ts +215 -0
- package/.claude/skills/mutagent-evaluator/scripts/memory/read.ts +168 -0
- package/.claude/skills/mutagent-evaluator/scripts/prep-tasks.ts +125 -0
- package/.claude/skills/mutagent-evaluator/scripts/profile-subject.ts +310 -0
- package/.claude/skills/mutagent-evaluator/scripts/publish-report.ts +131 -0
- package/.claude/skills/mutagent-evaluator/scripts/read-unitf-traces.ts +81 -0
- package/.claude/skills/mutagent-evaluator/scripts/render-build-cards.ts +195 -0
- package/.claude/skills/mutagent-evaluator/scripts/render-discover-report.ts +1640 -0
- package/.claude/skills/mutagent-evaluator/scripts/render-eval-report.ts +3823 -0
- package/.claude/skills/mutagent-evaluator/scripts/render-report.ts +212 -0
- package/.claude/skills/mutagent-evaluator/scripts/resolve-credential.ts +110 -0
- package/.claude/skills/mutagent-evaluator/scripts/resolve-ref.ts +98 -0
- package/.claude/skills/mutagent-evaluator/scripts/result-verify.ts +129 -0
- package/.claude/skills/mutagent-evaluator/scripts/route-failures.ts +320 -0
- package/.claude/skills/mutagent-evaluator/scripts/run-deterministic.ts +271 -0
- package/.claude/skills/mutagent-evaluator/scripts/run-evaluate.ts +715 -0
- package/.claude/skills/mutagent-evaluator/scripts/run-judge.ts +155 -0
- package/.claude/skills/mutagent-evaluator/scripts/run-pipeline.ts +175 -0
- package/.claude/skills/mutagent-evaluator/scripts/sample-traces.ts +210 -0
- package/.claude/skills/mutagent-evaluator/scripts/self-audit.ts +387 -0
- package/.claude/skills/mutagent-evaluator/scripts/source-map.ts +106 -0
- package/.claude/skills/mutagent-evaluator/scripts/subject-profile.ts +134 -0
- package/.claude/skills/mutagent-evaluator/scripts/substrate.ts +162 -0
- package/.claude/skills/mutagent-evaluator/scripts/ui-slots.ts +119 -0
- package/.claude/skills/mutagent-evaluator/scripts/unitf-to-evaltrace.ts +284 -0
- package/.claude/skills/mutagent-evaluator/scripts/validate-judge.ts +358 -0
- package/.claude/skills/mutagent-evaluator/scripts/variance-compare.ts +177 -0
- package/.claude/skills/mutagent-evaluator/subjects/mutagent-diagnostics/behavior-tree.yaml +140 -0
- package/.claude/skills/mutagent-evaluator/subjects/mutagent-diagnostics/eval-matrix.yaml +1270 -0
- package/.claude/skills/mutagent-evaluator/subjects/mutagent-diagnostics/methodology-review.yaml +105 -0
- package/.claude/skills/mutagent-evaluator/workflows/audit.workflow.js +82 -0
- package/.claude/skills/mutagent-evaluator/workflows/data-leak.workflow.js +236 -0
- package/.claude/skills/mutagent-evaluator/workflows/variance.workflow.js +163 -0
- package/LICENSE +201 -0
- package/NOTICE +23 -0
- package/README.md +90 -0
- package/bin/mutagent-cli.mjs +9263 -0
- package/bin/mutagent-evaluator.mjs +96 -0
- package/package.json +52 -0
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* scripts/cold-start-sampler.ts — EV-5.3 the balanced COLD-START sampler.
|
|
3
|
+
* ---------------------------------------------------------------------------
|
|
4
|
+
* PRE-SUITE, there is NO LLM judge yet: the EV-042 determiner (`determine-
|
|
5
|
+
* outcome.ts`) needs a criteria suite to run, and the suite doesn't exist yet.
|
|
6
|
+
* So the ONLY ✓/✗ labels available at cold start are:
|
|
7
|
+
* (a) the agent/skill DEFINITION's success ORACLE, and
|
|
8
|
+
* (b) deterministic MECHANICAL signals — the four cold-label families
|
|
9
|
+
* ERROR · FEEDBACK · SCORE · TERMINAL (a subset of `ext.signals` plus the
|
|
10
|
+
* §9.4.2 terminal/`incomplete` marker). No provider, no judge tokens.
|
|
11
|
+
*
|
|
12
|
+
* This is DISTINCT from `sample-traces.ts` `balancedSample`, which balances over
|
|
13
|
+
* the LLM-determiner's `OutcomeVerdict` labels (a WARM, post-judge input). This
|
|
14
|
+
* module is the COLD front-door: it manufactures a balanced ✓/✗ bootstrap suite
|
|
15
|
+
* from mechanical labels alone so the first judge pass is trained on BOTH classes.
|
|
16
|
+
*
|
|
17
|
+
* The pipeline (in order):
|
|
18
|
+
* stratify → SL-5 prevalence floor → worthiness/scenario dedup →
|
|
19
|
+
* MIN-BOTH (a guaranteed minimum from BOTH pools — never a success-only suite) →
|
|
20
|
+
* oversample the ✗-pool when rare (and flag the suite LOW-CONFIDENCE).
|
|
21
|
+
*
|
|
22
|
+
* PURE + deterministic: no `Math.random`, no clock, no network. A given
|
|
23
|
+
* (traces, opts) always yields the identical suite (reproducible cold suites —
|
|
24
|
+
* C-PIN-adjacent). Never fabricates a trace: a scarcer class contributes all it
|
|
25
|
+
* has and no more.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
// ── Mechanical cold labels (NO LLM judge) ───────────────────────────────────
|
|
29
|
+
|
|
30
|
+
/** The cold outcome — the ONLY two pools a balanced suite is built from. */
|
|
31
|
+
export const ColdOutcome = { Pass: "pass", Fail: "fail" } as const;
|
|
32
|
+
export type ColdOutcomeValue = (typeof ColdOutcome)[keyof typeof ColdOutcome];
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* The four MECHANICAL cold-label signal families available pre-suite. Mirrors the
|
|
36
|
+
* subset of UniTF `ext.signals` (`hasError`/`hasApiErrors`/`hasChatFeedback`/
|
|
37
|
+
* `negativeReaction`/`hasScore`/`minScore`) plus the §9.4.2 terminal marker
|
|
38
|
+
* (`incomplete`). All optional/additive: an absent field is UNKNOWN, never a
|
|
39
|
+
* false — the oracle reads only what is present.
|
|
40
|
+
*/
|
|
41
|
+
export interface MechanicalSignals {
|
|
42
|
+
/** ERROR family — a run/tool error was recorded. */
|
|
43
|
+
hasError?: boolean;
|
|
44
|
+
/** ERROR family — provider/API errors were recorded. */
|
|
45
|
+
hasApiErrors?: boolean;
|
|
46
|
+
/** FEEDBACK family — a user chat reaction exists on the trace. */
|
|
47
|
+
hasChatFeedback?: boolean;
|
|
48
|
+
/** FEEDBACK family — that reaction was negative (frustration/swear hit). */
|
|
49
|
+
negativeReaction?: boolean;
|
|
50
|
+
/** SCORE family — a numeric score exists. */
|
|
51
|
+
hasScore?: boolean;
|
|
52
|
+
/** SCORE family — the lowest score seen (compared to `scoreThreshold`). */
|
|
53
|
+
minScore?: number;
|
|
54
|
+
/** TERMINAL family — the session was cut off / reached no terminal event. */
|
|
55
|
+
incomplete?: boolean;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** One candidate trace at cold start: its id + mechanical signals + dedup keys. */
|
|
59
|
+
export interface ColdTrace {
|
|
60
|
+
id: string;
|
|
61
|
+
signals: MechanicalSignals;
|
|
62
|
+
/** `ext.classification.scenario` — the dedup key (one rep per scenario). */
|
|
63
|
+
scenario?: string;
|
|
64
|
+
/** `ext.signals.worthinessScore` [0,1] — dedup keeps the worthiest rep. */
|
|
65
|
+
worthiness?: number;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* The DEFINITION success oracle: maps a cold trace → ✓/✗, or `null` when the
|
|
70
|
+
* mechanical signals are insufficient to decide. A `null` (UNKNOWN) trace is kept
|
|
71
|
+
* for coverage but NEVER forced into a pool — it can't help balance and must not
|
|
72
|
+
* fabricate a label. The default is {@link defaultMechanicalOracle}; a subject
|
|
73
|
+
* whose DEFINITION encodes a richer success rule injects its own here.
|
|
74
|
+
*/
|
|
75
|
+
export type SuccessOracle = (t: ColdTrace) => ColdOutcomeValue | null;
|
|
76
|
+
|
|
77
|
+
/** Default SCORE-family pass/fail cut. */
|
|
78
|
+
export const DEFAULT_SCORE_THRESHOLD = 0.5;
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* The default MECHANICAL oracle over the four cold-label families. A trace is ✗
|
|
82
|
+
* when ANY negative mechanical signal fired — an error, a NEGATIVE user reaction,
|
|
83
|
+
* a below-threshold score, or a non-terminal/truncated session — and ✓ otherwise.
|
|
84
|
+
* PURE + total (never returns `null`): a subject that needs an UNKNOWN verdict
|
|
85
|
+
* injects its own oracle. No LLM, no provider.
|
|
86
|
+
*/
|
|
87
|
+
export function defaultMechanicalOracle(
|
|
88
|
+
scoreThreshold: number = DEFAULT_SCORE_THRESHOLD,
|
|
89
|
+
): SuccessOracle {
|
|
90
|
+
return (t) => {
|
|
91
|
+
const s = t.signals;
|
|
92
|
+
const errored = s.hasError === true || s.hasApiErrors === true;
|
|
93
|
+
const negFeedback = s.hasChatFeedback === true && s.negativeReaction === true;
|
|
94
|
+
const lowScore =
|
|
95
|
+
s.hasScore === true && typeof s.minScore === "number" && s.minScore < scoreThreshold;
|
|
96
|
+
const truncated = s.incomplete === true;
|
|
97
|
+
return errored || negFeedback || lowScore || truncated ? ColdOutcome.Fail : ColdOutcome.Pass;
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// ── stratify ────────────────────────────────────────────────────────────────
|
|
102
|
+
|
|
103
|
+
/** The ✓/✗ pools plus the UNKNOWN remainder (oracle abstained). Order-preserving. */
|
|
104
|
+
export interface Stratified {
|
|
105
|
+
pass: ColdTrace[];
|
|
106
|
+
fail: ColdTrace[];
|
|
107
|
+
unknown: ColdTrace[];
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Partition traces into the ✓/✗ pools via the oracle (UNKNOWN → `unknown`).
|
|
112
|
+
* PURE + deterministic; preserves input order within each pool.
|
|
113
|
+
*/
|
|
114
|
+
export function stratify(traces: ColdTrace[], oracle: SuccessOracle): Stratified {
|
|
115
|
+
const pass: ColdTrace[] = [];
|
|
116
|
+
const fail: ColdTrace[] = [];
|
|
117
|
+
const unknown: ColdTrace[] = [];
|
|
118
|
+
for (const t of traces) {
|
|
119
|
+
const v = oracle(t);
|
|
120
|
+
if (v === ColdOutcome.Pass) pass.push(t);
|
|
121
|
+
else if (v === ColdOutcome.Fail) fail.push(t);
|
|
122
|
+
else unknown.push(t);
|
|
123
|
+
}
|
|
124
|
+
return { pass, fail, unknown };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// ── worthiness / scenario dedup ─────────────────────────────────────────────
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Keep ONE representative per `scenario` — the highest `worthiness` (ties → the
|
|
131
|
+
* first seen). Traces with NO scenario have no dedup key, so they are all kept
|
|
132
|
+
* (they can't be proven duplicates). Deterministic + order-preserving over the
|
|
133
|
+
* surviving representatives. Never reorders by worthiness — only DROPS lower-
|
|
134
|
+
* worthiness same-scenario duplicates.
|
|
135
|
+
*/
|
|
136
|
+
export function dedupByScenario(pool: ColdTrace[]): ColdTrace[] {
|
|
137
|
+
// First pass: pick the winning id per scenario (highest worthiness, first-seen tie).
|
|
138
|
+
const winner = new Map<string, ColdTrace>();
|
|
139
|
+
for (const t of pool) {
|
|
140
|
+
if (t.scenario === undefined) continue;
|
|
141
|
+
const cur = winner.get(t.scenario);
|
|
142
|
+
if (cur === undefined || (t.worthiness ?? 0) > (cur.worthiness ?? 0)) {
|
|
143
|
+
winner.set(t.scenario, t);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
// Second pass: emit in original order, keeping non-scenario traces + each
|
|
147
|
+
// scenario's winner exactly once.
|
|
148
|
+
const emittedScenario = new Set<string>();
|
|
149
|
+
const out: ColdTrace[] = [];
|
|
150
|
+
for (const t of pool) {
|
|
151
|
+
if (t.scenario === undefined) {
|
|
152
|
+
out.push(t);
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
if (winner.get(t.scenario) === t && !emittedScenario.has(t.scenario)) {
|
|
156
|
+
emittedScenario.add(t.scenario);
|
|
157
|
+
out.push(t);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return out;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// ── coldStartSample — the balanced cold suite ───────────────────────────────
|
|
164
|
+
|
|
165
|
+
export interface ColdStartOptions {
|
|
166
|
+
/** Target suite size (the sampler stays at or under this, MIN-BOTH excepted). */
|
|
167
|
+
size: number;
|
|
168
|
+
/**
|
|
169
|
+
* MIN-BOTH — the guaranteed minimum drawn from EACH non-empty pool (default 1).
|
|
170
|
+
* When honoring both minimums would exceed `size`, MIN-BOTH WINS: a balanced
|
|
171
|
+
* bootstrap suite is worth more than a hard size cap, and a success-only suite
|
|
172
|
+
* is never acceptable.
|
|
173
|
+
*/
|
|
174
|
+
minBoth?: number;
|
|
175
|
+
/**
|
|
176
|
+
* SL-5 PREVALENCE FLOOR — the minimum number of ✗-pool traces retained even when
|
|
177
|
+
* the ✗ class is scarce (default = `minBoth`). The fail budget never drops below
|
|
178
|
+
* `min(prevalenceFloor, availableFails)`, so a rare-but-present failure signal is
|
|
179
|
+
* never balanced away.
|
|
180
|
+
*/
|
|
181
|
+
prevalenceFloor?: number;
|
|
182
|
+
/** SCORE-family pass/fail cut for the default oracle (default 0.5). */
|
|
183
|
+
scoreThreshold?: number;
|
|
184
|
+
/** The DEFINITION success oracle (default `defaultMechanicalOracle(scoreThreshold)`). */
|
|
185
|
+
oracle?: SuccessOracle;
|
|
186
|
+
/** Worthiness/scenario dedup (default true). */
|
|
187
|
+
dedup?: boolean;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export interface ColdStartSuite {
|
|
191
|
+
/** The selected cold suite (deduped, balanced): ✓ representatives then ✗. */
|
|
192
|
+
selected: ColdTrace[];
|
|
193
|
+
passCount: number;
|
|
194
|
+
failCount: number;
|
|
195
|
+
/** true when the ✗-pool was too rare to fill its balanced half → all fails taken. */
|
|
196
|
+
oversampledFailPool: boolean;
|
|
197
|
+
/**
|
|
198
|
+
* true when the suite rests on TOO-FEW negatives — the ✗-pool was oversampled or
|
|
199
|
+
* couldn't fill its balanced half (or is empty). Downstream MUST treat the first
|
|
200
|
+
* cold judge pass as LOW-CONFIDENCE and re-balance once real verdicts exist.
|
|
201
|
+
*/
|
|
202
|
+
lowConfidence: boolean;
|
|
203
|
+
/**
|
|
204
|
+
* The MIN-BOTH invariant: BOTH non-empty pools are represented in `selected`.
|
|
205
|
+
* A success-only suite (or a fail-only suite) ⇒ false. When both pools have
|
|
206
|
+
* members this is ALWAYS true.
|
|
207
|
+
*/
|
|
208
|
+
minBothHeld: boolean;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Build a balanced cold-start suite from mechanical labels. Runs the full EV-5.3
|
|
213
|
+
* pipeline: stratify → SL-5 prevalence floor → scenario/worthiness dedup →
|
|
214
|
+
* MIN-BOTH → oversample-when-rare. PURE + deterministic; never fabricates.
|
|
215
|
+
*/
|
|
216
|
+
export function coldStartSample(traces: ColdTrace[], opts: ColdStartOptions): ColdStartSuite {
|
|
217
|
+
const size = Math.max(0, Math.floor(opts.size));
|
|
218
|
+
const minBoth = Math.max(0, Math.floor(opts.minBoth ?? 1));
|
|
219
|
+
const oracle = opts.oracle ?? defaultMechanicalOracle(opts.scoreThreshold);
|
|
220
|
+
|
|
221
|
+
const strat = stratify(traces, oracle);
|
|
222
|
+
const pass = opts.dedup === false ? strat.pass : dedupByScenario(strat.pass);
|
|
223
|
+
const fail = opts.dedup === false ? strat.fail : dedupByScenario(strat.fail);
|
|
224
|
+
|
|
225
|
+
const perSide = Math.floor(size / 2);
|
|
226
|
+
// SL-5 prevalence floor: fails we must keep even when scarce (capped at availability).
|
|
227
|
+
const prevalenceFloor = Math.max(0, Math.floor(opts.prevalenceFloor ?? minBoth));
|
|
228
|
+
const failFloor = Math.min(Math.max(prevalenceFloor, minBoth), fail.length);
|
|
229
|
+
|
|
230
|
+
let takeFail: number;
|
|
231
|
+
let takePass: number;
|
|
232
|
+
let oversampledFailPool = false;
|
|
233
|
+
|
|
234
|
+
if (fail.length > 0 && fail.length < perSide) {
|
|
235
|
+
// ✗-pool RARE — oversample: take EVERY available fail, fill the rest with ✓.
|
|
236
|
+
oversampledFailPool = true;
|
|
237
|
+
takeFail = fail.length;
|
|
238
|
+
takePass = Math.min(pass.length, Math.max(0, size - takeFail));
|
|
239
|
+
} else {
|
|
240
|
+
// Balanced take, but never below the SL-5 floor for fails.
|
|
241
|
+
takeFail = Math.min(fail.length, Math.max(perSide, failFloor));
|
|
242
|
+
takePass = Math.min(pass.length, Math.max(0, size - takeFail));
|
|
243
|
+
// If ✓ is scarce and budget remains, let ✗ backfill up to `size`.
|
|
244
|
+
if (takePass + takeFail < size) {
|
|
245
|
+
takeFail = Math.min(fail.length, size - takePass);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// MIN-BOTH enforcement — never drop a NON-EMPTY pool below min(minBoth, available).
|
|
250
|
+
// This is the "never a success-only suite" guarantee; it can push the total over
|
|
251
|
+
// `size` only in the degenerate small-`size` case, which is intentional.
|
|
252
|
+
if (fail.length > 0) takeFail = Math.max(takeFail, Math.min(minBoth, fail.length));
|
|
253
|
+
if (pass.length > 0) takePass = Math.max(takePass, Math.min(minBoth, pass.length));
|
|
254
|
+
|
|
255
|
+
const selectedPass = pass.slice(0, takePass);
|
|
256
|
+
const selectedFail = fail.slice(0, takeFail);
|
|
257
|
+
const selected = [...selectedPass, ...selectedFail];
|
|
258
|
+
|
|
259
|
+
const passCount = selectedPass.length;
|
|
260
|
+
const failCount = selectedFail.length;
|
|
261
|
+
const minBothHeld = passCount > 0 && failCount > 0;
|
|
262
|
+
// Low-confidence when negatives are thin: oversampled, or the ✗-pool couldn't
|
|
263
|
+
// fill its balanced half, or there are no negatives at all.
|
|
264
|
+
const lowConfidence = oversampledFailPool || fail.length === 0 || failCount < perSide;
|
|
265
|
+
|
|
266
|
+
return { selected, passCount, failCount, oversampledFailPool, lowConfidence, minBothHeld };
|
|
267
|
+
}
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* scripts/config/load.ts — the evaluator's config-loader (v0.2.0).
|
|
3
|
+
* ---------------------------------------------------------------------------
|
|
4
|
+
* Read + YAML-parse the UNIFIED local `.mutagent/config.yaml` and RESOLVE the
|
|
5
|
+
* two things the evaluator needs to run:
|
|
6
|
+
*
|
|
7
|
+
* source — bound BY ROLE. The evaluator is a source-CONSUMER, so it binds
|
|
8
|
+
* `global.sources`. A SINGLE entry auto-binds; MULTIPLE ⇒
|
|
9
|
+
* disambiguation deferred (Fork A) → returned as `multiple` (NOT a
|
|
10
|
+
* hard fail — DISCOVER needs a source, code/dataset runs don't).
|
|
11
|
+
* judge — the pinned judge MODEL, resolved from `global.models.judge_model`
|
|
12
|
+
* ?? `global.models.default`. `judge_runtime` (renamed from
|
|
13
|
+
* `substrate` in v0.2.0) selects HOW judges run.
|
|
14
|
+
*
|
|
15
|
+
* These feed the CLI's config→flag precedence (an explicit `--model` /
|
|
16
|
+
* `--traces` flag WINS; the config fills the gap). This module does NOT apply
|
|
17
|
+
* that precedence — it exposes the resolved config values; the caller (cli/prep.ts)
|
|
18
|
+
* merges them under flag precedence.
|
|
19
|
+
*
|
|
20
|
+
* LEGACY (Fork B — hard-cut): a pre-v0.2.0 config (wrong `config_version` OR a
|
|
21
|
+
* legacy top-level key `shared`/`stages`/top-level `diagnostics`/`evaluator`) is
|
|
22
|
+
* DETECTED, never parsed at runtime — the loader returns
|
|
23
|
+
* `{ status: "migration-required", … }` so the caller routes to the migration
|
|
24
|
+
* DIRECTIVE. No old-shape parse ever happens.
|
|
25
|
+
*
|
|
26
|
+
* Determinism (coding-rules): `loadEvaluatorConfig` reads an INJECTED path —
|
|
27
|
+
* never resolved here. Path resolution (the LOCAL `.mutagent/`) happens only in
|
|
28
|
+
* the thin CLI, so tests stay deterministic against committed fixtures. Pure:
|
|
29
|
+
* one fs read + a YAML parse; no clock, no random, no network. Never throws.
|
|
30
|
+
*
|
|
31
|
+
* SECRETS: config holds only credential REF names (never values); resolution is
|
|
32
|
+
* a separate use-time step (resolve-credential.ts).
|
|
33
|
+
*/
|
|
34
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
35
|
+
import { resolve } from "node:path";
|
|
36
|
+
import { parse as parseYaml } from "yaml";
|
|
37
|
+
import {
|
|
38
|
+
CONFIG_VERSION,
|
|
39
|
+
LEGACY_CONFIG_VERSIONS,
|
|
40
|
+
type GlobalSource,
|
|
41
|
+
type GlobalTarget,
|
|
42
|
+
type ContextLink,
|
|
43
|
+
validateGlobal,
|
|
44
|
+
validateLifecycleEvaluator,
|
|
45
|
+
} from "./schema.ts";
|
|
46
|
+
|
|
47
|
+
/** The unified config path under a project root: `<root>/.mutagent/config.yaml`. */
|
|
48
|
+
export function configPathFor(projectRoot: string): string {
|
|
49
|
+
return resolve(projectRoot, ".mutagent", "config.yaml");
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** The v0.1.0 top-level keys that must NOT appear in a v0.2.0 config. */
|
|
53
|
+
const LEGACY_TOP_LEVEL_KEYS = [
|
|
54
|
+
"shared",
|
|
55
|
+
"stages",
|
|
56
|
+
"diagnostics",
|
|
57
|
+
"evaluator",
|
|
58
|
+
] as const;
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Detect a legacy (pre-v0.2.0) config WITHOUT parsing its old shape. Two signals,
|
|
62
|
+
* either one fires: a wrong/absent `config_version`, or a legacy top-level key
|
|
63
|
+
* (`shared`/`stages`/a top-level `diagnostics`/`evaluator` skill section). Pure;
|
|
64
|
+
* tolerant of a non-object input (returns []). Mirrors the orchestrator's
|
|
65
|
+
* detectLegacyConfig (parity port — no cross-import).
|
|
66
|
+
*/
|
|
67
|
+
export function detectLegacyConfig(obj: unknown): string[] {
|
|
68
|
+
if (obj === null || typeof obj !== "object") return [];
|
|
69
|
+
const o = obj as Record<string, unknown>;
|
|
70
|
+
const markers: string[] = [];
|
|
71
|
+
|
|
72
|
+
const version = o.config_version;
|
|
73
|
+
if (version !== CONFIG_VERSION) {
|
|
74
|
+
if (
|
|
75
|
+
typeof version === "string" &&
|
|
76
|
+
(LEGACY_CONFIG_VERSIONS as readonly string[]).includes(version)
|
|
77
|
+
) {
|
|
78
|
+
markers.push(
|
|
79
|
+
`config_version: "${version}" (legacy — expected "${CONFIG_VERSION}")`,
|
|
80
|
+
);
|
|
81
|
+
} else if (version === undefined) {
|
|
82
|
+
markers.push(`config_version: absent (expected "${CONFIG_VERSION}")`);
|
|
83
|
+
}
|
|
84
|
+
// A non-legacy, non-current version (e.g. a future "0.3.0") is NOT a legacy
|
|
85
|
+
// marker — structural validation reports it instead.
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
for (const key of LEGACY_TOP_LEVEL_KEYS) {
|
|
89
|
+
if (key in o) markers.push(`legacy top-level key '${key}'`);
|
|
90
|
+
}
|
|
91
|
+
return markers;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* How the evaluator's source was bound (or why it couldn't be). The SELECTION
|
|
96
|
+
* contract (v0.2.1 — parity with orchestrator + diagnostics):
|
|
97
|
+
* bound — exactly one source resolved (single entry · `default:true` · `--source` hit).
|
|
98
|
+
* none — zero sources (DISCOVER needs a pick; code/dataset runs don't need a source).
|
|
99
|
+
* needs-selection — >1 source, none `default:true`, no `--source` ⇒ the caller must PROMPT the
|
|
100
|
+
* operator to pick (NON-fatal — the source EXISTS, only the pick is missing).
|
|
101
|
+
* multiple-defaults— >1 source with `default:true` — an AMBIGUOUS config the operator must fix.
|
|
102
|
+
* unknown-name — an explicit `--source <name>` that matched no catalog entry.
|
|
103
|
+
*/
|
|
104
|
+
export type SourceBinding =
|
|
105
|
+
| { kind: "bound"; source: GlobalSource }
|
|
106
|
+
| { kind: "none" }
|
|
107
|
+
| { kind: "needs-selection"; sources: GlobalSource[] }
|
|
108
|
+
| { kind: "multiple-defaults"; sources: GlobalSource[] }
|
|
109
|
+
| { kind: "unknown-name"; name: string };
|
|
110
|
+
|
|
111
|
+
/** How the judge model resolved (model-intent-sacred: never a silent swap). */
|
|
112
|
+
export type JudgeModelResolution =
|
|
113
|
+
| { kind: "resolved"; model: string; from: "judge_model" | "default" }
|
|
114
|
+
| { kind: "unresolved" };
|
|
115
|
+
|
|
116
|
+
export interface EvaluatorConfigResolved {
|
|
117
|
+
/** the raw config_version literal read from the file. */
|
|
118
|
+
configVersion: string;
|
|
119
|
+
/** source bound BY ROLE from `global.sources` (single auto-binds). */
|
|
120
|
+
source: SourceBinding;
|
|
121
|
+
/** the pinned judge model from `global.models.{judge_model,default}`. */
|
|
122
|
+
judgeModel: JudgeModelResolution;
|
|
123
|
+
/** `lifecycle.evaluator.judge_runtime` (renamed from `substrate`), if set. */
|
|
124
|
+
judgeRuntime: string | undefined;
|
|
125
|
+
/** `global.context[]` — PROJECT-WIDE context links (every run loads these). */
|
|
126
|
+
globalContext: ContextLink[];
|
|
127
|
+
/** `lifecycle.evaluator.context[]` — the evaluator's stage-specific links. */
|
|
128
|
+
evaluatorContext: ContextLink[];
|
|
129
|
+
/** present targets (evaluator is judge-only — carried for completeness only). */
|
|
130
|
+
targets: GlobalTarget[];
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export type LoadEvaluatorConfigResult =
|
|
134
|
+
| { status: "ok"; exists: true; config: EvaluatorConfigResolved }
|
|
135
|
+
| { status: "absent"; exists: false }
|
|
136
|
+
| { status: "migration-required"; exists: true; markers: string[]; error: string }
|
|
137
|
+
| { status: "invalid"; exists: true; errors: string[] };
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Bind a source by role from `global.sources`. Precedence (SELECTION contract —
|
|
141
|
+
* EXACT parity with orchestrator + diagnostics):
|
|
142
|
+
*
|
|
143
|
+
* 1. explicit `selectName` (`--source`) WINS — find by name; miss ⇒ `unknown-name`.
|
|
144
|
+
* 2. 0 sources ⇒ `none`.
|
|
145
|
+
* 3. 1 source ⇒ `bound` (auto-bind).
|
|
146
|
+
* 4. >1 sources:
|
|
147
|
+
* exactly one `default:true` ⇒ `bound` (silent default).
|
|
148
|
+
* >1 `default:true` ⇒ `multiple-defaults` (ambiguous — operator fixes config).
|
|
149
|
+
* 0 `default:true` ⇒ `needs-selection` (caller PROMPTS the operator).
|
|
150
|
+
*
|
|
151
|
+
* Pure; no clock/random/network. NON-fatal on `needs-selection` — the source
|
|
152
|
+
* exists, only the disambiguation pick is deferred to the run-start ASK.
|
|
153
|
+
*/
|
|
154
|
+
function bindSourceByRole(
|
|
155
|
+
sources: GlobalSource[],
|
|
156
|
+
selectName?: string,
|
|
157
|
+
): SourceBinding {
|
|
158
|
+
// (1) Explicit override — `--source <name>` WINS over any default / auto-bind.
|
|
159
|
+
if (selectName !== undefined && selectName !== "") {
|
|
160
|
+
const hit = sources.find((s) => s.name === selectName);
|
|
161
|
+
return hit !== undefined
|
|
162
|
+
? { kind: "bound", source: hit }
|
|
163
|
+
: { kind: "unknown-name", name: selectName };
|
|
164
|
+
}
|
|
165
|
+
// (2) / (3) — zero or a single entry.
|
|
166
|
+
if (sources.length === 0) return { kind: "none" };
|
|
167
|
+
if (sources.length === 1) return { kind: "bound", source: sources[0] as GlobalSource };
|
|
168
|
+
// (4) — multiple entries; disambiguate by `default:true`.
|
|
169
|
+
const defaults = sources.filter((s) => s.default === true);
|
|
170
|
+
if (defaults.length === 1) return { kind: "bound", source: defaults[0] as GlobalSource };
|
|
171
|
+
if (defaults.length > 1) return { kind: "multiple-defaults", sources };
|
|
172
|
+
return { kind: "needs-selection", sources };
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Resolve the judge model (model-intent-sacred): `judge_model` wins, else
|
|
177
|
+
* `default`, else unresolved. The CALLER refuses (or falls back to `--model`)
|
|
178
|
+
* on `unresolved` — this pure resolver never invents a model.
|
|
179
|
+
*/
|
|
180
|
+
function resolveJudgeModel(models: {
|
|
181
|
+
default?: string;
|
|
182
|
+
judge_model?: string;
|
|
183
|
+
}): JudgeModelResolution {
|
|
184
|
+
if (models.judge_model !== undefined && models.judge_model !== "") {
|
|
185
|
+
return { kind: "resolved", model: models.judge_model, from: "judge_model" };
|
|
186
|
+
}
|
|
187
|
+
if (models.default !== undefined && models.default !== "") {
|
|
188
|
+
return { kind: "resolved", model: models.default, from: "default" };
|
|
189
|
+
}
|
|
190
|
+
return { kind: "unresolved" };
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/** Options for {@link loadEvaluatorConfig}. */
|
|
194
|
+
export interface LoadEvaluatorConfigOptions {
|
|
195
|
+
/**
|
|
196
|
+
* Run-time source override (`--source <name>`). WINS over `default:true` /
|
|
197
|
+
* auto-bind; a miss ⇒ `{ kind: "unknown-name" }`. Absent ⇒ default precedence.
|
|
198
|
+
*/
|
|
199
|
+
selectSourceName?: string;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Load + resolve the evaluator's config from an INJECTED path. Guarded (mirrors
|
|
204
|
+
* the orchestrator loadConfig): a missing file → `absent`; malformed YAML or a
|
|
205
|
+
* failed structural check → `invalid`; a LEGACY config → `migration-required`
|
|
206
|
+
* (detected BEFORE structural validation — never parse the old shape). Otherwise
|
|
207
|
+
* `ok` with the resolved source + judge model + context links. Never throws.
|
|
208
|
+
*
|
|
209
|
+
* `opts.selectSourceName` (`--source`) threads the run-time source override into
|
|
210
|
+
* `bindSourceByRole` (SELECTION contract) — it WINS over `default:true`.
|
|
211
|
+
*/
|
|
212
|
+
export function loadEvaluatorConfig(
|
|
213
|
+
configPath: string,
|
|
214
|
+
opts: LoadEvaluatorConfigOptions = {},
|
|
215
|
+
): LoadEvaluatorConfigResult {
|
|
216
|
+
if (!existsSync(configPath)) {
|
|
217
|
+
return { status: "absent", exists: false };
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
let raw: string;
|
|
221
|
+
try {
|
|
222
|
+
raw = readFileSync(configPath, "utf8");
|
|
223
|
+
} catch (err) {
|
|
224
|
+
return { status: "invalid", exists: true, errors: [`cannot read ${configPath}: ${String(err)}`] };
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
let parsed: unknown;
|
|
228
|
+
try {
|
|
229
|
+
parsed = parseYaml(raw);
|
|
230
|
+
} catch (err) {
|
|
231
|
+
return { status: "invalid", exists: true, errors: [`malformed YAML in ${configPath}: ${String(err)}`] };
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Legacy detection FIRST — never parse the old shape at runtime (Fork B).
|
|
235
|
+
const legacyMarkers = detectLegacyConfig(parsed);
|
|
236
|
+
if (legacyMarkers.length > 0) {
|
|
237
|
+
return {
|
|
238
|
+
status: "migration-required",
|
|
239
|
+
exists: true,
|
|
240
|
+
markers: legacyMarkers,
|
|
241
|
+
error:
|
|
242
|
+
`legacy config detected in ${configPath} — migration required ` +
|
|
243
|
+
`(see references/config-migration.md): ${legacyMarkers.join("; ")}`,
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const root = (parsed ?? {}) as Record<string, unknown>;
|
|
248
|
+
const globalBlock = (root.global ?? {}) as Record<string, unknown>;
|
|
249
|
+
const lifecycle = (root.lifecycle ?? {}) as Record<string, unknown>;
|
|
250
|
+
const evaluatorSection = (lifecycle.evaluator ?? {}) as Record<string, unknown>;
|
|
251
|
+
|
|
252
|
+
// Structural validation of the two blocks the evaluator reads.
|
|
253
|
+
const gRes = validateGlobal(globalBlock);
|
|
254
|
+
if (!gRes.ok) return { status: "invalid", exists: true, errors: gRes.errors.map((e) => `global${e}`) };
|
|
255
|
+
const eRes = validateLifecycleEvaluator(evaluatorSection);
|
|
256
|
+
if (!eRes.ok) {
|
|
257
|
+
return { status: "invalid", exists: true, errors: eRes.errors.map((e) => `lifecycle.evaluator${e}`) };
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const sources = (globalBlock.sources as GlobalSource[] | undefined) ?? [];
|
|
261
|
+
const targets = (globalBlock.targets as GlobalTarget[] | undefined) ?? [];
|
|
262
|
+
const models = (globalBlock.models as { default?: string; judge_model?: string } | undefined) ?? {};
|
|
263
|
+
const globalContext = (globalBlock.context as ContextLink[] | undefined) ?? [];
|
|
264
|
+
const evaluatorContext = (evaluatorSection.context as ContextLink[] | undefined) ?? [];
|
|
265
|
+
const judgeRuntime =
|
|
266
|
+
typeof evaluatorSection.judge_runtime === "string" && evaluatorSection.judge_runtime !== ""
|
|
267
|
+
? evaluatorSection.judge_runtime
|
|
268
|
+
: undefined;
|
|
269
|
+
|
|
270
|
+
return {
|
|
271
|
+
status: "ok",
|
|
272
|
+
exists: true,
|
|
273
|
+
config: {
|
|
274
|
+
configVersion: typeof root.config_version === "string" ? root.config_version : "",
|
|
275
|
+
source: bindSourceByRole(sources, opts.selectSourceName),
|
|
276
|
+
judgeModel: resolveJudgeModel(models),
|
|
277
|
+
judgeRuntime,
|
|
278
|
+
globalContext,
|
|
279
|
+
evaluatorContext,
|
|
280
|
+
targets,
|
|
281
|
+
},
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// ── CLI — thin wrapper (resolve the LOCAL config; print the resolution) ────────
|
|
286
|
+
// bun scripts/config/load.ts [project-root] [--source <name>] (root defaults to cwd)
|
|
287
|
+
// Exit 0 = ok / absent; exit 1 = invalid / migration-required.
|
|
288
|
+
|
|
289
|
+
function runCli(argv: string[]): number {
|
|
290
|
+
const sourceIdx = argv.indexOf("--source");
|
|
291
|
+
const selectSourceName =
|
|
292
|
+
sourceIdx >= 0 && sourceIdx + 1 < argv.length ? argv[sourceIdx + 1] : undefined;
|
|
293
|
+
// First positional after the script path that isn't the --source value.
|
|
294
|
+
const positional = argv[2] !== undefined && argv[2] !== "--source" ? argv[2] : undefined;
|
|
295
|
+
const projectRoot = positional ?? process.cwd();
|
|
296
|
+
const result = loadEvaluatorConfig(configPathFor(projectRoot), { selectSourceName });
|
|
297
|
+
process.stdout.write(JSON.stringify(result, null, 2) + "\n");
|
|
298
|
+
return result.status === "invalid" || result.status === "migration-required" ? 1 : 0;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
const isMain =
|
|
302
|
+
typeof import.meta !== "undefined" &&
|
|
303
|
+
(import.meta as unknown as { main?: boolean }).main === true;
|
|
304
|
+
if (isMain) {
|
|
305
|
+
const argv = typeof Bun !== "undefined" ? Bun.argv : process.argv;
|
|
306
|
+
process.exit(runCli(argv));
|
|
307
|
+
}
|