@mutagent/evaluator 0.1.0-alpha.4 → 0.1.0-alpha.6

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.
Files changed (38) hide show
  1. package/.claude/skills/mutagent-evaluator/SKILL.md +6 -5
  2. package/.claude/skills/mutagent-evaluator/assets/agents/evaluator.md +76 -12
  3. package/.claude/skills/mutagent-evaluator/assets/code-quality-criteria.yaml +75 -0
  4. package/.claude/skills/mutagent-evaluator/lenses/methodology-critic-lens.md +1 -1
  5. package/.claude/skills/mutagent-evaluator/references/edd-loop.md +6 -6
  6. package/.claude/skills/mutagent-evaluator/references/eval-stage.md +36 -3
  7. package/.claude/skills/mutagent-evaluator/references/memory-format.md +1 -1
  8. package/.claude/skills/mutagent-evaluator/references/operation-inventory.md +1 -0
  9. package/.claude/skills/mutagent-evaluator/schemas/methodology-review.schema.yaml +1 -1
  10. package/.claude/skills/mutagent-evaluator/scripts/agent-dispatch.ts +0 -0
  11. package/.claude/skills/mutagent-evaluator/scripts/build-review-ui.ts +320 -6
  12. package/.claude/skills/mutagent-evaluator/scripts/cli/audit-run.ts +6 -3
  13. package/.claude/skills/mutagent-evaluator/scripts/cli/prep.ts +32 -2
  14. package/.claude/skills/mutagent-evaluator/scripts/cli/profile-subject.ts +1 -1
  15. package/.claude/skills/mutagent-evaluator/scripts/code-quality-verdict.ts +277 -0
  16. package/.claude/skills/mutagent-evaluator/scripts/config/schema.ts +3 -3
  17. package/.claude/skills/mutagent-evaluator/scripts/contracts/eval-matrix.ts +39 -0
  18. package/.claude/skills/mutagent-evaluator/scripts/determine-outcome.ts +8 -0
  19. package/.claude/skills/mutagent-evaluator/scripts/edd/change-request.ts +1 -1
  20. package/.claude/skills/mutagent-evaluator/scripts/edd/edd-types.ts +2 -2
  21. package/.claude/skills/mutagent-evaluator/scripts/judge-prompt-template.ts +144 -15
  22. package/.claude/skills/mutagent-evaluator/scripts/memory/append.ts +1 -1
  23. package/.claude/skills/mutagent-evaluator/scripts/memory/ratify.ts +165 -0
  24. package/.claude/skills/mutagent-evaluator/scripts/persist-eval-criteria.ts +232 -0
  25. package/.claude/skills/mutagent-evaluator/scripts/prep-tasks.ts +15 -4
  26. package/.claude/skills/mutagent-evaluator/scripts/read-manifest.ts +120 -0
  27. package/.claude/skills/mutagent-evaluator/scripts/read-unitf-traces.ts +34 -2
  28. package/.claude/skills/mutagent-evaluator/scripts/render-build-cards.ts +37 -0
  29. package/.claude/skills/mutagent-evaluator/scripts/render-eval-report.ts +9 -91
  30. package/.claude/skills/mutagent-evaluator/scripts/report-fragments.ts +173 -0
  31. package/.claude/skills/mutagent-evaluator/scripts/route-failures.ts +14 -4
  32. package/.claude/skills/mutagent-evaluator/scripts/run-deterministic.ts +114 -18
  33. package/.claude/skills/mutagent-evaluator/scripts/run-pipeline.ts +39 -4
  34. package/.claude/skills/mutagent-evaluator/scripts/subject-profile.ts +82 -0
  35. package/.claude/skills/mutagent-evaluator/scripts/sync-eval-criteria.ts +244 -0
  36. package/.claude/skills/mutagent-evaluator/scripts/unitf-to-evaltrace.ts +64 -21
  37. package/bin/mutagent-cli.mjs +659 -8
  38. package/package.json +2 -2
@@ -5045,8 +5045,12 @@ var SpanSchema = Type.Object({
5045
5045
  role: Type.Optional(RoleSchema),
5046
5046
  toolName: Type.Optional(Type.String()),
5047
5047
  toolCallId: Type.Optional(Type.String()),
5048
- input: Type.Optional(Type.Unknown()),
5049
- output: Type.Optional(Type.Unknown()),
5048
+ input: Type.Optional(Type.Unknown({
5049
+ description: "Message text for the INPUT-side layers (role user\xB7system\xB7tool = user-input / system-input / tool-call arguments), or a chat-messages array on a multi-layer LLM span. See the field-per-layer rule; enforced by checkFieldConformance()."
5050
+ })),
5051
+ output: Type.Optional(Type.Unknown({
5052
+ description: "Message text for the OUTPUT-side layers (role assistant\xB7toolResult = assistant-output / tool-result). See the field-per-layer rule; enforced by checkFieldConformance()."
5053
+ })),
5050
5054
  tokens: Type.Optional(TokensSchema),
5051
5055
  costUsd: Type.Optional(Type.Number()),
5052
5056
  attributes: Type.Optional(Type.Record(Type.String(), Type.Unknown()))
@@ -6527,7 +6531,7 @@ function normalizeRecords(records, malformedIn) {
6527
6531
  if (ev.timestamp)
6528
6532
  span.startTime = ev.timestamp;
6529
6533
  if (text)
6530
- span.output = text;
6534
+ span.input = text;
6531
6535
  spans.push(span);
6532
6536
  spanIdx++;
6533
6537
  }
@@ -6542,7 +6546,7 @@ function normalizeRecords(records, malformedIn) {
6542
6546
  if (ev.timestamp)
6543
6547
  span.startTime = ev.timestamp;
6544
6548
  if (typeof content === "string" && content)
6545
- span.output = content;
6549
+ span.input = content;
6546
6550
  spans.push(span);
6547
6551
  spanIdx++;
6548
6552
  }
@@ -6797,7 +6801,7 @@ function normalizeRecords2(records, malformedIn) {
6797
6801
  span.startTime = ts;
6798
6802
  const msg = str2(p, "message");
6799
6803
  if (msg)
6800
- span.output = msg;
6804
+ span.input = msg;
6801
6805
  spans.push(span);
6802
6806
  spanIdx++;
6803
6807
  } else if (pType === "agent_message") {
@@ -9255,8 +9259,645 @@ function runSelect(flags, io) {
9255
9259
  return 0;
9256
9260
  }
9257
9261
 
9258
- // ../mutagent-tools/src/cli/index.ts
9262
+ // ../mutagent-tools/src/cli/apply.ts
9263
+ import { spawnSync } from "child_process";
9264
+ import { createHash as createHash3, randomUUID } from "crypto";
9265
+ import {
9266
+ existsSync as existsSync4,
9267
+ mkdirSync as mkdirSync2,
9268
+ readFileSync as readFileSync4,
9269
+ rmSync,
9270
+ writeFileSync as writeFileSync2
9271
+ } from "fs";
9272
+ import { dirname as dirname2 } from "path";
9273
+
9274
+ // ../mutagent-tools/src/apply/registry.ts
9275
+ var REGISTRY2 = new Map;
9276
+ function registerTargetAdapter(adapter) {
9277
+ REGISTRY2.set(adapter.id, adapter);
9278
+ }
9279
+ function getTargetAdapter(id) {
9280
+ const a = REGISTRY2.get(id);
9281
+ if (!a) {
9282
+ const known = [...REGISTRY2.keys()].sort().join(", ") || "(none registered)";
9283
+ throw new Error(`no target adapter registered for id '${id}'. Registered: ${known}.`);
9284
+ }
9285
+ return a;
9286
+ }
9287
+ function clearTargetAdapters() {
9288
+ REGISTRY2.clear();
9289
+ }
9290
+
9291
+ // ../mutagent-tools/src/apply/types.ts
9292
+ var AdapterId = {
9293
+ WorktreePr: "worktree-pr",
9294
+ Rest: "rest",
9295
+ VendorCli: "vendor-cli",
9296
+ ReportOnly: "report-only"
9297
+ };
9298
+ var ApplyKind = {
9299
+ CodePr: "code-pr",
9300
+ Markdown: "markdown",
9301
+ CloudDeploy: "cloud-deploy",
9302
+ ReportOnly: "report-only"
9303
+ };
9304
+
9305
+ // ../mutagent-tools/src/apply/binding.ts
9306
+ function adapterIdForKind(kind, opts) {
9307
+ switch (kind) {
9308
+ case ApplyKind.CodePr:
9309
+ case ApplyKind.Markdown:
9310
+ return AdapterId.WorktreePr;
9311
+ case ApplyKind.CloudDeploy:
9312
+ return opts?.platformTransport === "vendor-cli" ? AdapterId.VendorCli : AdapterId.Rest;
9313
+ case ApplyKind.ReportOnly:
9314
+ return AdapterId.ReportOnly;
9315
+ default: {
9316
+ const _never = kind;
9317
+ throw new Error(`no adapter bound for apply.kind '${String(_never)}'`);
9318
+ }
9319
+ }
9320
+ }
9321
+ function kindRunsBuild(kind) {
9322
+ return kind === ApplyKind.CodePr;
9323
+ }
9324
+
9325
+ // ../mutagent-tools/src/apply/audit.ts
9326
+ function auditPath(projectRoot, applyId) {
9327
+ return `${projectRoot}/.mutagent/apply/${applyId}/apply-audit.json`;
9328
+ }
9329
+ var REQUIRED_FIELDS = [
9330
+ "applyId",
9331
+ "transport",
9332
+ "kind",
9333
+ "mode",
9334
+ "fromRev",
9335
+ "toRev",
9336
+ "staleHash",
9337
+ "revertToken",
9338
+ "producedAt"
9339
+ ];
9340
+ function missingAuditFields(record) {
9341
+ return REQUIRED_FIELDS.filter((f) => {
9342
+ const v = record[f];
9343
+ return v === undefined || v === null || v === "";
9344
+ });
9345
+ }
9346
+ function writeAudit(record, ctx) {
9347
+ const missing = missingAuditFields(record);
9348
+ if (missing.length > 0) {
9349
+ throw new Error(`apply-audit is incomplete \u2014 missing: ${missing.join(", ")} (0-blind-writes contract, V-O6)`);
9350
+ }
9351
+ const path = auditPath(ctx.projectRoot, record.applyId);
9352
+ ctx.io.mkdirp(`${ctx.projectRoot}/.mutagent/apply/${record.applyId}`);
9353
+ ctx.io.writeFile(path, JSON.stringify(record, null, 2) + `
9354
+ `);
9355
+ }
9356
+
9357
+ // ../mutagent-tools/src/apply/diff.ts
9358
+ function edorderKey(edits) {
9359
+ return [...edits].sort((a, b) => a.path.localeCompare(b.path));
9360
+ }
9361
+ function concatCurrent(edits, read) {
9362
+ return edorderKey(edits).map((e) => `=== ${e.path} ===
9363
+ ${read(e.path) ?? ""}`).join(`
9364
+ `);
9365
+ }
9366
+ function concatProposed(edits) {
9367
+ return edorderKey(edits).map((e) => `=== ${e.path} ===
9368
+ ${e.contents ?? "<deleted>"}`).join(`
9369
+ `);
9370
+ }
9371
+ function lineDiff(current, proposed) {
9372
+ const a = current.split(`
9373
+ `);
9374
+ const b = proposed.split(`
9375
+ `);
9376
+ const out = [];
9377
+ const max = Math.max(a.length, b.length);
9378
+ for (let i = 0;i < max; i++) {
9379
+ const cur = a[i];
9380
+ const prop = b[i];
9381
+ if (cur === prop)
9382
+ continue;
9383
+ if (cur !== undefined)
9384
+ out.push(`- ${cur}`);
9385
+ if (prop !== undefined)
9386
+ out.push(`+ ${prop}`);
9387
+ }
9388
+ return out.join(`
9389
+ `);
9390
+ }
9391
+ function fileChangeDiff(change, read) {
9392
+ const edits = change.edits ?? [];
9393
+ const current = concatCurrent(edits, read);
9394
+ const proposed = concatProposed(edits);
9395
+ return { current, proposed, diff: lineDiff(current, proposed) };
9396
+ }
9397
+
9398
+ // ../mutagent-tools/src/apply/adapters/report-only.ts
9399
+ var reportOnlyAdapter = {
9400
+ id: AdapterId.ReportOnly,
9401
+ live: true,
9402
+ async read(change, ctx) {
9403
+ const content = (change.edits ?? []).map((e) => ctx.io.readFile(`${change.targetRoot}/${e.path}`) ?? "").join(`
9404
+ `);
9405
+ return { rev: "report-only", content, hash: ctx.io.hash(content), exists: true };
9406
+ },
9407
+ async dryRun(change, current, ctx) {
9408
+ const { diff } = fileChangeDiff(change, (p) => ctx.io.readFile(`${change.targetRoot}/${p}`));
9409
+ return {
9410
+ diff,
9411
+ staleHash: current.hash,
9412
+ wouldWrite: false,
9413
+ notes: ["report-only: no write will be performed"]
9414
+ };
9415
+ },
9416
+ async apply(change, current, ctx) {
9417
+ const applyId = ctx.io.uid();
9418
+ return { applyId, newRev: current.rev, notes: ["report-only: no write performed"] };
9419
+ },
9420
+ async rollback(audit) {
9421
+ return {
9422
+ applyId: audit.applyId,
9423
+ restoredRev: audit.fromRev,
9424
+ notes: ["report-only: nothing to roll back"]
9425
+ };
9426
+ },
9427
+ emitAudit(record, ctx) {
9428
+ writeAudit(record, ctx);
9429
+ }
9430
+ };
9431
+
9432
+ // ../mutagent-tools/src/apply/adapters/worktree-pr.ts
9433
+ function sh(ctx, cmd, args, cwd, input) {
9434
+ const r = ctx.io.shell(cmd, args, { cwd, input });
9435
+ if (r.code !== 0) {
9436
+ throw new Error(`\`${cmd} ${args.join(" ")}\` failed (exit ${r.code}): ${r.stderr.trim() || r.stdout.trim()}`);
9437
+ }
9438
+ return r;
9439
+ }
9440
+ function headRev(ctx, cwd) {
9441
+ const r = ctx.io.shell("git", ["rev-parse", "HEAD"], { cwd });
9442
+ return r.code === 0 ? r.stdout.trim() : "unknown";
9443
+ }
9444
+ function parsePrUrl(stdout) {
9445
+ const m = stdout.match(/https?:\/\/\S+/);
9446
+ return m ? m[0] : undefined;
9447
+ }
9448
+ var worktreePrAdapter = {
9449
+ id: AdapterId.WorktreePr,
9450
+ live: true,
9451
+ async read(change, ctx) {
9452
+ const edits = change.edits ?? [];
9453
+ const content = concatCurrent(edits, (p) => ctx.io.readFile(`${change.targetRoot}/${p}`));
9454
+ const exists = edits.some((e) => ctx.io.exists(`${change.targetRoot}/${e.path}`));
9455
+ return { rev: headRev(ctx, change.targetRoot), content, hash: ctx.io.hash(content), exists };
9456
+ },
9457
+ async dryRun(change, current, ctx) {
9458
+ const { diff } = fileChangeDiff(change, (p) => ctx.io.readFile(`${change.targetRoot}/${p}`));
9459
+ const proposed = concatProposed(change.edits ?? []);
9460
+ return {
9461
+ diff,
9462
+ staleHash: current.hash,
9463
+ wouldWrite: proposed !== current.content,
9464
+ notes: kindRunsBuild(change.kind) ? ["code-pr: lint + typecheck will run in the worktree"] : ["markdown: no build step"]
9465
+ };
9466
+ },
9467
+ async apply(change, current, ctx) {
9468
+ const edits = change.edits ?? [];
9469
+ if (edits.length === 0)
9470
+ throw new Error("worktree-PR apply: no edits provided");
9471
+ const now = concatCurrent(edits, (p) => ctx.io.readFile(`${change.targetRoot}/${p}`));
9472
+ if (ctx.io.hash(now) !== current.hash) {
9473
+ throw new Error("worktree-PR apply: target changed since read (stale-hash mismatch) \u2014 re-run dryRun");
9474
+ }
9475
+ const applyId = ctx.io.uid();
9476
+ const branch = `apply/${applyId}`;
9477
+ const wtdir = `${ctx.projectRoot}/.mutagent/apply/${applyId}/worktree`;
9478
+ ctx.io.mkdirp(`${ctx.projectRoot}/.mutagent/apply/${applyId}`);
9479
+ sh(ctx, "git", ["worktree", "add", "-b", branch, wtdir, "HEAD"], change.targetRoot);
9480
+ try {
9481
+ for (const e of edits) {
9482
+ const target = `${wtdir}/${e.path}`;
9483
+ if (e.contents === null)
9484
+ ctx.io.deleteFile(target);
9485
+ else {
9486
+ ctx.io.mkdirp(dirOf(target));
9487
+ ctx.io.writeFile(target, e.contents);
9488
+ }
9489
+ }
9490
+ if (kindRunsBuild(change.kind)) {
9491
+ sh(ctx, "bun", ["run", "lint"], wtdir);
9492
+ sh(ctx, "bun", ["run", "typecheck"], wtdir);
9493
+ }
9494
+ sh(ctx, "git", ["add", "-A"], wtdir);
9495
+ sh(ctx, "git", ["commit", "-m", change.title], wtdir);
9496
+ const newRev = headRev(ctx, wtdir);
9497
+ sh(ctx, "git", ["push", "-u", "origin", branch], wtdir);
9498
+ const pr = sh(ctx, "gh", ["pr", "create", "--base", "main", "--title", change.title, "--body", change.body ?? change.title], wtdir);
9499
+ return { applyId, newRev, ref: parsePrUrl(pr.stdout), notes: [`branch ${branch}`] };
9500
+ } finally {
9501
+ ctx.io.shell("git", ["worktree", "remove", "--force", wtdir], { cwd: change.targetRoot });
9502
+ }
9503
+ },
9504
+ async rollback(audit, ctx) {
9505
+ const branch = `rollback/${audit.applyId}`;
9506
+ const repoRoot = ctx.projectRoot;
9507
+ const wtdir = `${ctx.projectRoot}/.mutagent/apply/${audit.applyId}/rollback-worktree`;
9508
+ ctx.io.mkdirp(`${ctx.projectRoot}/.mutagent/apply/${audit.applyId}`);
9509
+ sh(ctx, "git", ["worktree", "add", "-b", branch, wtdir, "HEAD"], repoRoot);
9510
+ try {
9511
+ sh(ctx, "git", ["revert", "--no-edit", audit.revertToken], wtdir);
9512
+ const restoredRev = headRev(ctx, wtdir);
9513
+ sh(ctx, "git", ["push", "-u", "origin", branch], wtdir);
9514
+ const pr = sh(ctx, "gh", ["pr", "create", "--base", "main", "--title", `Revert ${audit.applyId}`, "--body", `Reverts ${audit.toRev}`], wtdir);
9515
+ return { applyId: audit.applyId, restoredRev, ref: parsePrUrl(pr.stdout), notes: [`revert of ${audit.toRev}`] };
9516
+ } finally {
9517
+ ctx.io.shell("git", ["worktree", "remove", "--force", wtdir], { cwd: repoRoot });
9518
+ }
9519
+ },
9520
+ emitAudit(record, ctx) {
9521
+ writeAudit(record, ctx);
9522
+ }
9523
+ };
9524
+ function dirOf(path) {
9525
+ const i = path.lastIndexOf("/");
9526
+ return i <= 0 ? path : path.slice(0, i);
9527
+ }
9528
+
9529
+ // ../mutagent-tools/src/apply/adapters/rest.ts
9530
+ function authHeaders(change) {
9531
+ const h = { "content-type": "application/json" };
9532
+ if (change.credentialToken)
9533
+ h["authorization"] = `Bearer ${change.credentialToken}`;
9534
+ return h;
9535
+ }
9536
+ function assertOk(r, what) {
9537
+ if (r.status < 200 || r.status >= 300) {
9538
+ throw new Error(`REST ${what} failed (HTTP ${r.status}): ${r.body.slice(0, 200)}`);
9539
+ }
9540
+ return r;
9541
+ }
9542
+ function extractRev(body) {
9543
+ try {
9544
+ const j = JSON.parse(body);
9545
+ for (const k of ["activeRev", "rev", "revision", "version", "id"]) {
9546
+ const v = j[k];
9547
+ if (typeof v === "string" && v)
9548
+ return v;
9549
+ if (typeof v === "number")
9550
+ return String(v);
9551
+ }
9552
+ } catch {}
9553
+ return "";
9554
+ }
9555
+ var restAdapter = {
9556
+ id: AdapterId.Rest,
9557
+ live: true,
9558
+ async read(change, ctx) {
9559
+ const r = assertOk(await ctx.io.http("GET", change.targetRoot, { headers: authHeaders(change) }), "GET");
9560
+ const rev = extractRev(r.body) || "unknown";
9561
+ return { rev, content: r.body, hash: ctx.io.hash(r.body), exists: r.status !== 404 };
9562
+ },
9563
+ async dryRun(change, current, ctx) {
9564
+ const proposed = JSON.stringify(change.config ?? {}, null, 2);
9565
+ return {
9566
+ diff: lineDiff(current.content, proposed),
9567
+ staleHash: current.hash,
9568
+ wouldWrite: ctx.io.hash(proposed) !== current.hash,
9569
+ notes: ["cloud-rest: non-destructive create-rev + activate"]
9570
+ };
9571
+ },
9572
+ async apply(change, current, ctx) {
9573
+ const applyId = ctx.io.uid();
9574
+ const created = assertOk(await ctx.io.http("POST", `${change.targetRoot}/revisions`, {
9575
+ headers: authHeaders(change),
9576
+ body: JSON.stringify(change.config ?? {})
9577
+ }), "POST create-rev");
9578
+ const newRev = extractRev(created.body);
9579
+ if (!newRev)
9580
+ throw new Error("REST apply: create-rev returned no rev id");
9581
+ assertOk(await ctx.io.http("PATCH", change.targetRoot, {
9582
+ headers: authHeaders(change),
9583
+ body: JSON.stringify({ activeRev: newRev })
9584
+ }), "PATCH activate");
9585
+ return { applyId, newRev, ref: newRev, notes: [`activated ${newRev} (prior ${current.rev} retained)`] };
9586
+ },
9587
+ async rollback(audit, ctx) {
9588
+ if (!audit.target)
9589
+ throw new Error("REST rollback: audit has no target url");
9590
+ assertOk(await ctx.io.http("PATCH", audit.target, {
9591
+ body: JSON.stringify({ activeRev: audit.revertToken }),
9592
+ headers: { "content-type": "application/json" }
9593
+ }), "PATCH re-activate");
9594
+ return {
9595
+ applyId: audit.applyId,
9596
+ restoredRev: audit.revertToken,
9597
+ ref: audit.revertToken,
9598
+ notes: [`re-activated ${audit.revertToken}; ${audit.toRev} retained`]
9599
+ };
9600
+ },
9601
+ emitAudit(record, ctx) {
9602
+ writeAudit(record, ctx);
9603
+ }
9604
+ };
9605
+
9606
+ // ../mutagent-tools/src/apply/adapters/vendor-cli.ts
9607
+ function sh2(ctx, cmd, args) {
9608
+ const r = ctx.io.shell(cmd, args);
9609
+ if (r.code !== 0) {
9610
+ throw new Error(`\`${cmd} ${args.join(" ")}\` failed (exit ${r.code}): ${r.stderr.trim() || r.stdout.trim()}`);
9611
+ }
9612
+ return r;
9613
+ }
9614
+ function parseHandle(stdout) {
9615
+ const m = stdout.match(/(?:version|rev|revision|handle)[:=\s]+(\S+)/i) ?? stdout.match(/\bv\d+\b/) ?? stdout.trim().split(`
9616
+ `).pop()?.match(/(\S+)$/);
9617
+ return (Array.isArray(m) ? m[1] ?? m[0] : "") || "";
9618
+ }
9619
+ function vendorBin(change) {
9620
+ if (!change.vendorBin)
9621
+ throw new Error("vendor-CLI: change.vendorBin is required (the deploy binary name)");
9622
+ return change.vendorBin;
9623
+ }
9624
+ var vendorCliAdapter = {
9625
+ id: AdapterId.VendorCli,
9626
+ live: true,
9627
+ async read(change, ctx) {
9628
+ const bin = vendorBin(change);
9629
+ const r = ctx.io.shell(bin, ["get", change.targetRoot]);
9630
+ const content = r.stdout;
9631
+ return { rev: parseHandle(content) || "unknown", content, hash: ctx.io.hash(content), exists: r.code === 0 };
9632
+ },
9633
+ async dryRun(change, current, ctx) {
9634
+ const proposed = JSON.stringify(change.config ?? {}, null, 2);
9635
+ return {
9636
+ diff: lineDiff(current.content, proposed),
9637
+ staleHash: current.hash,
9638
+ wouldWrite: ctx.io.hash(proposed) !== current.hash,
9639
+ notes: [`vendor-CLI (${change.vendorBin ?? "?"}): shell deploy \u2192 new version handle`]
9640
+ };
9641
+ },
9642
+ async apply(change, current, ctx) {
9643
+ const applyId = ctx.io.uid();
9644
+ const bin = vendorBin(change);
9645
+ const cfgPath = `${ctx.projectRoot}/.mutagent/apply/${applyId}/deploy-config.json`;
9646
+ ctx.io.mkdirp(`${ctx.projectRoot}/.mutagent/apply/${applyId}`);
9647
+ ctx.io.writeFile(cfgPath, JSON.stringify(change.config ?? {}, null, 2));
9648
+ const r = sh2(ctx, bin, ["deploy", change.targetRoot, "--config", cfgPath]);
9649
+ const newRev = parseHandle(r.stdout);
9650
+ if (!newRev)
9651
+ throw new Error("vendor-CLI apply: deploy returned no version handle");
9652
+ return { applyId, newRev, ref: newRev, notes: [`deployed ${newRev} (prior ${current.rev} retained)`] };
9653
+ },
9654
+ async rollback(audit, ctx) {
9655
+ const bin = (audit.note ?? "").match(/vendor-CLI \(([^)]+)\)/)?.[1];
9656
+ const [binName, target] = (audit.target ?? "").split("|");
9657
+ const resolvedBin = bin && bin !== "?" ? bin : binName;
9658
+ if (!resolvedBin || !target)
9659
+ throw new Error("vendor-CLI rollback: audit.target must be 'bin|url'");
9660
+ sh2(ctx, resolvedBin, ["rollback", target, "--to", audit.revertToken]);
9661
+ return {
9662
+ applyId: audit.applyId,
9663
+ restoredRev: audit.revertToken,
9664
+ ref: audit.revertToken,
9665
+ notes: [`vendor rollback to ${audit.revertToken}; ${audit.toRev} retained`]
9666
+ };
9667
+ },
9668
+ emitAudit(record, ctx) {
9669
+ writeAudit(record, ctx);
9670
+ }
9671
+ };
9672
+ // ../mutagent-tools/src/apply/target-select.ts
9673
+ var TargetSelectionOutcome = {
9674
+ Resolved: "resolved",
9675
+ NeedsDisambiguation: "needs-disambiguation",
9676
+ NoTarget: "no-target"
9677
+ };
9678
+ function selectApplyTarget(candidates, pick) {
9679
+ const uniq = [...new Set(candidates.filter((c) => c.trim().length > 0))];
9680
+ if (uniq.length === 0) {
9681
+ return { outcome: TargetSelectionOutcome.NoTarget };
9682
+ }
9683
+ if (uniq.length === 1) {
9684
+ return { outcome: TargetSelectionOutcome.Resolved, target: uniq[0] };
9685
+ }
9686
+ if (pick !== undefined && uniq.includes(pick)) {
9687
+ return { outcome: TargetSelectionOutcome.Resolved, target: pick };
9688
+ }
9689
+ return {
9690
+ outcome: TargetSelectionOutcome.NeedsDisambiguation,
9691
+ candidates: uniq,
9692
+ reason: pick !== undefined ? `--pick '${pick}' is not one of the ${uniq.length} candidate targets` : `${uniq.length} candidate targets present and no explicit --pick \u2014 refusing to default`
9693
+ };
9694
+ }
9695
+
9696
+ // ../mutagent-tools/src/apply/index.ts
9697
+ function registerBuiltinAdapters2() {
9698
+ clearTargetAdapters();
9699
+ registerTargetAdapter(reportOnlyAdapter);
9700
+ registerTargetAdapter(worktreePrAdapter);
9701
+ registerTargetAdapter(restAdapter);
9702
+ registerTargetAdapter(vendorCliAdapter);
9703
+ }
9704
+ async function runApplyFlow(change, ctx, mode) {
9705
+ const adapterId = adapterIdForKind(change.kind, { platformTransport: change.platformTransport });
9706
+ const adapter = getTargetAdapter(adapterId);
9707
+ const read = await adapter.read(change, ctx);
9708
+ const dryRun = await adapter.dryRun(change, read, ctx);
9709
+ if (mode === "dry-run") {
9710
+ return { adapterId, read, dryRun };
9711
+ }
9712
+ const applyResult = await adapter.apply(change, read, ctx);
9713
+ const audit = {
9714
+ applyId: applyResult.applyId,
9715
+ transport: adapterId,
9716
+ kind: change.kind,
9717
+ subject: change.subject,
9718
+ target: change.targetRoot,
9719
+ mode: change.kind === ApplyKind.ReportOnly ? "report-only" : "apply",
9720
+ fromRev: read.rev,
9721
+ toRev: applyResult.newRev,
9722
+ staleHash: dryRun.staleHash,
9723
+ revertToken: read.rev,
9724
+ ref: applyResult.ref,
9725
+ producedAt: ctx.io.producedAt,
9726
+ note: (applyResult.notes ?? []).join("; ") || undefined
9727
+ };
9728
+ adapter.emitAudit(audit, ctx);
9729
+ return { adapterId, read, dryRun, applyResult, audit };
9730
+ }
9731
+ async function runRollbackFlow(audit, ctx) {
9732
+ const adapter = getTargetAdapter(audit.transport);
9733
+ const rollback = await adapter.rollback(audit, ctx);
9734
+ const record = {
9735
+ applyId: `${audit.applyId}-rollback`,
9736
+ transport: audit.transport,
9737
+ kind: audit.kind,
9738
+ subject: audit.subject,
9739
+ target: audit.target,
9740
+ mode: "rollback",
9741
+ fromRev: audit.toRev,
9742
+ toRev: rollback.restoredRev,
9743
+ staleHash: audit.staleHash,
9744
+ revertToken: audit.toRev,
9745
+ ref: rollback.ref,
9746
+ producedAt: ctx.io.producedAt,
9747
+ note: (rollback.notes ?? []).join("; ") || undefined
9748
+ };
9749
+ adapter.emitAudit(record, ctx);
9750
+ return { rollback, audit: record };
9751
+ }
9752
+
9753
+ // ../mutagent-tools/src/cli/apply.ts
9259
9754
  function usage(err) {
9755
+ err("mutagent-cli apply \u2014 shared write transport (worktree-PR \xB7 REST \xB7 vendor-CLI)");
9756
+ err("");
9757
+ err("usage: mutagent-cli apply --kind <code-pr|markdown|cloud-deploy|report-only> --target <root>");
9758
+ err(" (--edit <relpath>=<srcfile>)... | --edits-json <file> # file transports");
9759
+ err(" [--config-json <file>] # platform transports");
9760
+ err(" --title <t> [--body <b>] [--subject <name>]");
9761
+ err(" [--platform-transport rest|vendor-cli] [--vendor-bin <bin>] [--token-ref <ENV>]");
9762
+ err(" [--dry-run | --commit] # default: --dry-run");
9763
+ err(" mutagent-cli apply --rollback <apply-audit.json>");
9764
+ err("");
9765
+ err(" read() + dryRun() are always safe. apply() writes ONLY on --commit, AFTER the caller's gate.");
9766
+ }
9767
+ function buildApplyIo(base) {
9768
+ return {
9769
+ now: base.now,
9770
+ producedAt: base.producedAt,
9771
+ out: base.out,
9772
+ err: base.err,
9773
+ readFile: (p) => existsSync4(p) ? readFileSync4(p, "utf8") : undefined,
9774
+ writeFile: (p, c) => {
9775
+ mkdirSync2(dirname2(p), { recursive: true });
9776
+ writeFileSync2(p, c);
9777
+ },
9778
+ deleteFile: (p) => {
9779
+ if (existsSync4(p))
9780
+ rmSync(p, { force: true });
9781
+ },
9782
+ exists: (p) => existsSync4(p),
9783
+ mkdirp: (p) => mkdirSync2(p, { recursive: true }),
9784
+ shell: (cmd, args, opts) => {
9785
+ const r = spawnSync(cmd, args, {
9786
+ cwd: opts?.cwd,
9787
+ input: opts?.input,
9788
+ encoding: "utf8"
9789
+ });
9790
+ return {
9791
+ stdout: r.stdout ?? "",
9792
+ stderr: r.stderr ?? (r.error ? r.error.message : ""),
9793
+ code: r.status ?? (r.error ? 1 : 0)
9794
+ };
9795
+ },
9796
+ http: async (method, url, opts) => {
9797
+ const res = await fetch(url, { method, headers: opts?.headers, body: opts?.body });
9798
+ const body = await res.text();
9799
+ return { status: res.status, body };
9800
+ },
9801
+ hash: (s) => createHash3("sha256").update(s).digest("hex").slice(0, 16),
9802
+ uid: () => randomUUID().slice(0, 8)
9803
+ };
9804
+ }
9805
+ function buildEdits(flags, cwd) {
9806
+ const editsJson = getStr(flags, "edits-json");
9807
+ if (editsJson) {
9808
+ const raw = readFileSync4(resolveRel(editsJson, cwd), "utf8");
9809
+ const parsed = JSON.parse(raw);
9810
+ if (!Array.isArray(parsed))
9811
+ throw new Error("--edits-json must be a JSON array of {path, contents}");
9812
+ return parsed;
9813
+ }
9814
+ const specs = getList(flags, "edit") ?? [];
9815
+ return specs.map((spec) => {
9816
+ const eq = spec.indexOf("=");
9817
+ if (eq < 0)
9818
+ throw new Error(`--edit expects '<relpath>=<srcfile>' (got '${spec}')`);
9819
+ const path = spec.slice(0, eq);
9820
+ const src = spec.slice(eq + 1);
9821
+ return { path, contents: readFileSync4(resolveRel(src, cwd), "utf8") };
9822
+ });
9823
+ }
9824
+ function resolveRel(p, cwd) {
9825
+ return p.startsWith("/") ? p : `${cwd}/${p}`;
9826
+ }
9827
+ var VALID_KINDS = new Set(Object.values(ApplyKind));
9828
+ async function runApply(argv, base, cwd) {
9829
+ const { flags } = parseArgs(argv);
9830
+ if (getBool(flags, "help") || argv.length === 0) {
9831
+ usage(base.err);
9832
+ return argv.length === 0 ? 1 : 0;
9833
+ }
9834
+ registerBuiltinAdapters2();
9835
+ const io = buildApplyIo(base);
9836
+ const ctx = { io, projectRoot: cwd };
9837
+ const rollbackPath = getStr(flags, "rollback");
9838
+ if (rollbackPath) {
9839
+ const auditRaw = readFileSync4(resolveRel(rollbackPath, cwd), "utf8");
9840
+ const audit = JSON.parse(auditRaw);
9841
+ const { rollback, audit: record } = await runRollbackFlow(audit, ctx);
9842
+ base.out(`\u2705 rollback: restored ${rollback.restoredRev}${rollback.ref ? ` (${rollback.ref})` : ""}`);
9843
+ base.out(` audit: .mutagent/apply/${record.applyId}/apply-audit.json`);
9844
+ return 0;
9845
+ }
9846
+ const kind = getStr(flags, "kind");
9847
+ if (!kind || !VALID_KINDS.has(kind)) {
9848
+ base.err(`apply: --kind must be one of ${[...VALID_KINDS].join(" | ")}`);
9849
+ return 1;
9850
+ }
9851
+ const candidates = getList(flags, "target") ?? [];
9852
+ const selection = selectApplyTarget(candidates, getStr(flags, "pick"));
9853
+ if (selection.outcome === TargetSelectionOutcome.NoTarget) {
9854
+ base.err("apply: --target is required");
9855
+ return 1;
9856
+ }
9857
+ if (selection.outcome === TargetSelectionOutcome.NeedsDisambiguation) {
9858
+ base.err(`apply: ambiguous target \u2014 ${selection.reason}`);
9859
+ base.err(` candidates: ${selection.candidates.join(", ")}`);
9860
+ base.err(" re-run with an explicit --pick <target> (the session must confirm which target to bind)");
9861
+ return 2;
9862
+ }
9863
+ const target = selection.target;
9864
+ const title = getStr(flags, "title") ?? "mutagent apply";
9865
+ const tokenRef = getStr(flags, "token-ref");
9866
+ const change = {
9867
+ kind,
9868
+ targetRoot: target,
9869
+ title,
9870
+ body: getStr(flags, "body"),
9871
+ subject: getStr(flags, "subject"),
9872
+ platformTransport: getStr(flags, "platform-transport"),
9873
+ vendorBin: getStr(flags, "vendor-bin"),
9874
+ credentialToken: tokenRef ? process.env[tokenRef] : undefined
9875
+ };
9876
+ if (kind === ApplyKind.CodePr || kind === ApplyKind.Markdown || kind === ApplyKind.ReportOnly) {
9877
+ change.edits = buildEdits(flags, cwd);
9878
+ }
9879
+ const configJson = getStr(flags, "config-json");
9880
+ if (configJson)
9881
+ change.config = JSON.parse(readFileSync4(resolveRel(configJson, cwd), "utf8"));
9882
+ const mode = getBool(flags, "commit") ? "commit" : "dry-run";
9883
+ const result = await runApplyFlow(change, ctx, mode);
9884
+ base.out(`transport: ${result.adapterId} \xB7 kind: ${kind} \xB7 from-rev: ${result.read.rev}`);
9885
+ base.out(`dry-run: ${result.dryRun.wouldWrite ? "WOULD write" : "no change"} \xB7 stale-hash: ${result.dryRun.staleHash}`);
9886
+ if (result.dryRun.diff)
9887
+ base.out(`--- diff ---
9888
+ ${result.dryRun.diff}`);
9889
+ if (mode === "dry-run") {
9890
+ base.out("(dry-run \u2014 no write; re-run with --commit after the caller's gate)");
9891
+ return 0;
9892
+ }
9893
+ const a = result.audit;
9894
+ base.out(`\u2705 applied: ${a?.fromRev} \u2192 ${a?.toRev}${result.applyResult?.ref ? ` (${result.applyResult.ref})` : ""}`);
9895
+ base.out(` audit: .mutagent/apply/${a?.applyId}/apply-audit.json (revert-token: ${a?.revertToken})`);
9896
+ return 0;
9897
+ }
9898
+
9899
+ // ../mutagent-tools/src/cli/index.ts
9900
+ function usage2(err) {
9260
9901
  err("mutagent-cli \u2014 MutagenT infra CLI");
9261
9902
  err("");
9262
9903
  err("usage: mutagent-cli <command> [args]");
@@ -9273,6 +9914,13 @@ function usage(err) {
9273
9914
  err(" trace select <--in <unitf.jsonl>|-> [--select <preset>] [--signal <flags>]");
9274
9915
  err(" [--signal-mode and|or] [--min-worthiness <0..1>] [--export <path.jsonl>]");
9275
9916
  err("");
9917
+ err(" apply --kind <code-pr|markdown|cloud-deploy|report-only> --target <root>");
9918
+ err(" (--edit <relpath>=<src>)... | --edits-json <f> | --config-json <f>");
9919
+ err(" --title <t> [--body <b>] [--subject <n>] [--platform-transport rest|vendor-cli]");
9920
+ err(" [--vendor-bin <bin>] [--token-ref <ENV>] [--dry-run | --commit]");
9921
+ err(" apply --rollback <apply-audit.json>");
9922
+ err(" the SHARED write transport \u2014 the caller gates BEFORE --commit (no approval logic here).");
9923
+ err("");
9276
9924
  err(" see references/TRACES-CLI.md for the full contract + UniTF format.");
9277
9925
  }
9278
9926
  async function main() {
@@ -9290,14 +9938,17 @@ async function main() {
9290
9938
  err
9291
9939
  };
9292
9940
  if (command === undefined || command === "--help" || command === "-h" || command === "help") {
9293
- usage(err);
9941
+ usage2(err);
9294
9942
  return command === undefined ? 1 : 0;
9295
9943
  }
9296
9944
  if (command === "trace") {
9297
9945
  return runTrace(rest, io, process.cwd());
9298
9946
  }
9947
+ if (command === "apply") {
9948
+ return runApply(rest, io, process.cwd());
9949
+ }
9299
9950
  err(`mutagent-cli: unknown command '${command}'`);
9300
- usage(err);
9951
+ usage2(err);
9301
9952
  return 1;
9302
9953
  }
9303
9954
  main().then((code) => process.exit(code)).catch((e) => {