editorbrain 0.31.0 → 0.34.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,9 +1,5 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- // packages/cli/src/sfx.ts
4
- import { readdir as readdir2, lstat as lstat3 } from "node:fs/promises";
5
- import { join as join2, extname as extname2, basename } from "node:path";
6
-
7
3
  // packages/core/src/creative-direction.ts
8
4
  var creativePreferenceSections = [
9
5
  { id: "brand", title: "Marca y tipograf\xEDa", question: "\xBFConservamos tus colores, fuentes e idioma, o quieres ajustarlos?" },
@@ -57125,13 +57121,6 @@ function canonicalFiles(files) {
57125
57121
  return result2;
57126
57122
  }
57127
57123
 
57128
- // packages/cli/src/media-runtime.ts
57129
- import { spawn } from "node:child_process";
57130
- import { createHash } from "node:crypto";
57131
- import { constants } from "node:fs";
57132
- import { lstat, open, realpath } from "node:fs/promises";
57133
- import { dirname, extname, parse, resolve } from "node:path";
57134
-
57135
57124
  // packages/cli/src/errors.ts
57136
57125
  var CliError = class extends Error {
57137
57126
  constructor(code, message, hint, retryable = false, details = {}) {
@@ -57154,7 +57143,346 @@ function errorOutput(error) {
57154
57143
  return { error: safe, code: "COMMAND_FAILED", retryable: false, hint: "Read the command help and run editorbrain doctor. Do not retry writes blindly." };
57155
57144
  }
57156
57145
 
57146
+ // packages/cli/src/production-output.ts
57147
+ import { constants as constants2 } from "node:fs";
57148
+ import { mkdir as mkdir2, open as open2 } from "node:fs/promises";
57149
+ import { dirname as dirname2 } from "node:path";
57150
+
57151
+ // packages/cli/src/io.ts
57152
+ import { constants } from "node:fs";
57153
+ import { lstat, mkdir, open, readdir, realpath, rename, unlink, link } from "node:fs/promises";
57154
+ import { dirname, join, relative, resolve, sep } from "node:path";
57155
+ import { randomUUID } from "node:crypto";
57156
+ async function ensureRoot(directory, create = false) {
57157
+ const root = resolve(directory);
57158
+ if (create) await mkdir(root, { recursive: true });
57159
+ const info = await lstat(root);
57160
+ if (info.isSymbolicLink() || !info.isDirectory()) throw new Error("Workspace root must be a real directory, not a symlink");
57161
+ return realpath(root);
57162
+ }
57163
+ async function checkedPath(root, path, document2 = true) {
57164
+ if (document2) safeDocumentPath(path);
57165
+ const target = resolve(root, path);
57166
+ const rel = relative(root, target);
57167
+ if (!rel || rel.startsWith(`..${sep}`) || rel === ".." || resolve(path) === path) throw new Error("Path escapes workspace");
57168
+ let cursor = root;
57169
+ for (const segment of rel.split(sep)) {
57170
+ cursor = join(cursor, segment);
57171
+ try {
57172
+ const info = await lstat(cursor);
57173
+ if (info.isSymbolicLink()) throw new Error(`Symlinks are not allowed: ${path}`);
57174
+ } catch (error) {
57175
+ if (error.code !== "ENOENT") throw error;
57176
+ }
57177
+ }
57178
+ return target;
57179
+ }
57180
+ async function readText(root, path, document2 = true) {
57181
+ const target = await checkedPath(root, path, document2);
57182
+ const handle = await open(target, constants.O_RDONLY | constants.O_NOFOLLOW);
57183
+ try {
57184
+ const info = await handle.stat();
57185
+ if (!info.isFile()) throw new Error(`Not a regular file: ${path}`);
57186
+ if (document2 && info.size > MAX_DOCUMENT_BYTES) throw new Error(`Document exceeds 128 KiB: ${path}`);
57187
+ return new TextDecoder("utf-8", { fatal: true }).decode(await handle.readFile());
57188
+ } finally {
57189
+ await handle.close();
57190
+ }
57191
+ }
57192
+ async function atomicWrite(root, path, content, options = {}) {
57193
+ const target = await checkedPath(root, path, options.document ?? true);
57194
+ await mkdir(dirname(target), { recursive: true });
57195
+ await checkedPath(root, path, options.document ?? true);
57196
+ const temporary = join(dirname(target), `.eh-${randomUUID()}.tmp`);
57197
+ const handle = await open(temporary, "wx", options.mode ?? 420);
57198
+ try {
57199
+ await handle.writeFile(content, "utf8");
57200
+ await handle.close();
57201
+ await checkedPath(root, path, options.document ?? true);
57202
+ if (options.overwrite === false) {
57203
+ await link(temporary, target);
57204
+ await unlink(temporary);
57205
+ } else await rename(temporary, target);
57206
+ } catch (error) {
57207
+ await handle.close().catch(() => {
57208
+ });
57209
+ await unlink(temporary).catch(() => {
57210
+ });
57211
+ throw error;
57212
+ }
57213
+ }
57214
+ async function readWorkspace(root) {
57215
+ const files = /* @__PURE__ */ Object.create(null);
57216
+ async function walk(path) {
57217
+ const directory = await checkedPath(root, path, false);
57218
+ let entries;
57219
+ try {
57220
+ entries = await readdir(directory, { withFileTypes: true });
57221
+ } catch (error) {
57222
+ if (error.code === "ENOENT") return;
57223
+ throw error;
57224
+ }
57225
+ for (const entry of entries) {
57226
+ const child = `${path}/${entry.name}`;
57227
+ if (entry.isSymbolicLink()) throw new Error(`Symlinks are not allowed: ${child}`);
57228
+ if (entry.name.startsWith(".")) continue;
57229
+ if (entry.isDirectory()) {
57230
+ if (path.startsWith("videos/") && ["assets", "renders"].includes(entry.name)) continue;
57231
+ await walk(child);
57232
+ } else if (entry.isFile() && /\.(json|md)$/.test(entry.name)) {
57233
+ safeDocumentPath(child);
57234
+ files[child] = await readText(root, child);
57235
+ }
57236
+ }
57237
+ }
57238
+ await walk(".editorharness");
57239
+ await walk(".editorbrain");
57240
+ await walk("videos");
57241
+ return canonicalFiles(files);
57242
+ }
57243
+ async function readProjectWorkspace(root, projectId) {
57244
+ assertIdentifier(projectId);
57245
+ const projectPath = `videos/${projectId}/project.json`;
57246
+ const projectContent = await readText(root, projectPath);
57247
+ const checked = validateDocument(projectPath, projectContent);
57248
+ if (!checked.valid) throw new ValidationError(checked.issues);
57249
+ const project = JSON.parse(projectContent);
57250
+ const files = { [projectPath]: projectContent };
57251
+ for (const path of /* @__PURE__ */ new Set([...projectSources(project), ...optionalProjectSources(project)])) {
57252
+ if (path === projectPath) continue;
57253
+ const aliases2 = path.startsWith(".editorbrain/") ? [path, path.replace(/^\.editorbrain\//, ".editorharness/")] : [path];
57254
+ for (const alias of aliases2) {
57255
+ try {
57256
+ files[alias] = await readText(root, alias);
57257
+ } catch (error) {
57258
+ if (error.code !== "ENOENT") throw error;
57259
+ }
57260
+ }
57261
+ }
57262
+ if (await readText(root, projectPath) !== projectContent) throw new Error("The selected project changed while reading its context. Retry before editing.");
57263
+ return canonicalFiles(files);
57264
+ }
57265
+
57266
+ // packages/cli/src/production-output.ts
57267
+ async function productionOutputPath(root, relativePath) {
57268
+ if (!relativePath.startsWith(".editorbrain-local/") || relativePath.split(/[\\/]/).some((p) => !p || p === ".." || p === ".") || /[\\\x00-\x1f\x7f]/.test(relativePath)) {
57269
+ throw new CliError("LOCAL_OUTPUT_REQUIRED", "Production output must be inside .editorbrain-local/.", "Use --out .editorbrain-local/<project>/inventory.json, or omit --out for JSON stdout. These reports are not synced.");
57270
+ }
57271
+ return checkedPath(root, relativePath, false);
57272
+ }
57273
+ async function prepareSampleDirectory(root, path) {
57274
+ const target = await productionOutputPath(root, path);
57275
+ await mkdir2(dirname2(target), { recursive: true });
57276
+ await productionOutputPath(root, path);
57277
+ return target;
57278
+ }
57279
+ async function readProductionJson(root, path) {
57280
+ const target = await checkedPath(root, path, false);
57281
+ const handle = await open2(target, constants2.O_RDONLY | constants2.O_NOFOLLOW | constants2.O_NONBLOCK);
57282
+ try {
57283
+ const stat = await handle.stat();
57284
+ if (!stat.isFile() || stat.size > 16 * 1024 * 1024) throw new CliError("INVALID_REPORT", "Expected a regular JSON file of at most 16 MiB.", "Select a local inventory, plan or check profile.");
57285
+ return JSON.parse(await handle.readFile("utf8"));
57286
+ } finally {
57287
+ await handle.close();
57288
+ }
57289
+ }
57290
+ async function saveProductionReport(root, path, report) {
57291
+ const target = await productionOutputPath(root, path);
57292
+ if (!path.endsWith(".json")) throw new CliError("INVALID_ARGUMENT", "Report output needs a .json extension.", "Use a new path under .editorbrain-local/.");
57293
+ await atomicWrite(root, path, stableJson(report), { document: false, overwrite: false, mode: 384 });
57294
+ return target;
57295
+ }
57296
+ function summarizeProductionReport(value) {
57297
+ const r = value;
57298
+ const base = { schemaVersion: r.schemaVersion, kind: r.kind, summary: true, mediaUploaded: false, quality: "not_assessed" };
57299
+ if (r.kind === "editorbrain.media-inventory") return {
57300
+ ...base,
57301
+ complete: r.complete,
57302
+ contentReviewed: false,
57303
+ counts: { assets: r.assets.length, failures: r.failures.length, skipped: r.skipped.length },
57304
+ assets: r.assets.slice(0, 8),
57305
+ failures: r.failures.slice(0, 5),
57306
+ omitted: { assets: Math.max(0, r.assets.length - 8), failures: Math.max(0, r.failures.length - 5), skipped: r.skipped.length },
57307
+ next: "Inspect selected source content. Use media sample for specific frames. Read the saved report or rerun with --details for omitted metadata; no content was reviewed automatically."
57308
+ };
57309
+ if (r.kind === "editorbrain.media-samples") return { ...base, sourceSha256: r.source.sha256, frames: r.frames, contentReviewed: false, next: "View these frames to inspect composition. They do not prove motion or sound." };
57310
+ if (r.kind === "editorbrain.production-plan-check") return {
57311
+ ...base,
57312
+ valid: r.valid,
57313
+ inventoryComplete: r.inventoryComplete,
57314
+ observations: r.observations,
57315
+ sourceFilesReverified: false,
57316
+ counts: { sources: r.coverage.length, findings: r.findings.length, repeatedPairs: r.repeatedPairs ?? 0 },
57317
+ findings: [...r.findings].sort((a, b) => ({ error: 0, warning: 1, info: 2 })[a.severity] - { error: 0, warning: 1, info: 2 }[b.severity]).slice(0, 8),
57318
+ omittedFindings: Math.max(0, r.findings.length - 8),
57319
+ next: "Review source reuse and inspection gaps in the saved report. Observations are agent-declared, not independently certified."
57320
+ };
57321
+ if (r.kind === "editorbrain.render-check") return {
57322
+ ...base,
57323
+ complete: r.complete,
57324
+ technicalStatus: r.technicalStatus,
57325
+ file: r.file,
57326
+ checks: r.checks,
57327
+ findings: r.findings.slice(0, 8),
57328
+ findingsSummary: r.findingsSummary,
57329
+ omittedFindings: Math.max(0, r.findings.length - 8),
57330
+ next: "Reconcile technical candidates with the brief; inspect the actual video/audio. Technical checks do not assess narrative, aesthetics or SFX audibility."
57331
+ };
57332
+ if (r.kind === "editorbrain.media-source-search") return {
57333
+ ...base,
57334
+ complete: r.complete,
57335
+ results: r.results.slice(0, 5).map((item) => ({ provider: item.provider, id: item.id.slice(0, 180), title: item.title.slice(0, 180), sourceUrl: item.sourceUrl.length <= 1500 ? item.sourceUrl : null, author: item.author?.slice(0, 120) ?? null, license: { id: item.license.id, reason: item.license.reason, url: item.license.url && item.license.url.length <= 500 ? item.license.url : null }, contentReviewed: false, rightsVerified: false })),
57336
+ metadataAbbreviated: true,
57337
+ failures: r.failures.slice(0, 5),
57338
+ counts: r.counts,
57339
+ omitted: { results: Math.max(0, r.results.length - 5), failures: Math.max(0, r.failures.length - 5) },
57340
+ searchExhaustive: false,
57341
+ rightsVerified: false,
57342
+ next: "Inspect relevant source pages and media. These archives supplement web image/video search; empty results do not prove absence on the internet."
57343
+ };
57344
+ if (r.kind === "editorbrain.evidence-check") return {
57345
+ ...base,
57346
+ valid: r.valid,
57347
+ inventoryComplete: r.inventoryComplete,
57348
+ observations: r.observations,
57349
+ sourceFilesReverified: false,
57350
+ factualTruth: "not_assessed",
57351
+ reuseRights: "not_assessed",
57352
+ findings: r.findings.slice(0, 8),
57353
+ omitted: { findings: Math.max(0, r.findings.length - 8) }
57354
+ };
57355
+ if (r.kind === "editorbrain.audio-check") return {
57356
+ ...base,
57357
+ complete: r.complete,
57358
+ technicalStatus: r.technicalStatus,
57359
+ file: r.file,
57360
+ fileStable: r.fileStable,
57361
+ coverage: r.coverage,
57362
+ measurements: r.measurements.slice(0, 8),
57363
+ errorCode: r.errorCode,
57364
+ creativeQuality: r.creativeQuality,
57365
+ omitted: { measurements: Math.max(0, r.measurements.length - 8) },
57366
+ next: "Measurements describe this exact file. Listen for intelligibility, pronunciation, SFX and sync when the modality is available."
57367
+ };
57368
+ if (r.kind === "editorbrain.motion-review") return {
57369
+ ...base,
57370
+ samples: r.samples ? { frameCount: r.samples.frameCount, fps: r.samples.fps, radiusFrames: r.samples.radiusFrames, samples: r.samples.samples.slice(0, 12), omittedSamples: Math.max(0, r.samples.samples.length - 12) } : null,
57371
+ geometryReviews: r.geometryReviews.slice(0, 3).map((g) => ({ scope: g.scope, status: g.status, candidates: g.candidates.slice(0, 8), omittedCandidates: Math.max(0, g.candidates.length - 8), checks: g.checks })),
57372
+ omittedGeometryReviews: Math.max(0, r.geometryReviews.length - 3),
57373
+ next: "This plans samples and checks declared boxes only. Render/view those frames; read saved details for omitted candidates and intentional overlaps."
57374
+ };
57375
+ throw new CliError("INVALID_REPORT", "Unknown production report kind.", "Use media inventory/sample or production check/plan-check.");
57376
+ }
57377
+
57378
+ // packages/cli/src/editing-batches.ts
57379
+ async function batchCommand(client, root, command, argument, flags) {
57380
+ const fail2 = () => {
57381
+ throw new CliError(
57382
+ "INVALID_ARGUMENT",
57383
+ "Use batches list/get/create/pause/resume/cancel/retry/detach/claim/renew/release/attention.",
57384
+ "Create with --file batch.json; updates need --expected-revision; leases need --project and --lease-token UUID."
57385
+ );
57386
+ };
57387
+ const lease = ["claim", "renew", "release", "attention"].includes(command), update = ["pause", "resume", "cancel", "retry", "detach"].includes(command);
57388
+ if (!["list", "get", "create"].includes(command) && !lease && !update) fail2();
57389
+ const allowed = [
57390
+ "dir",
57391
+ "url",
57392
+ ...command === "create" ? ["file"] : command === "list" ? ["after"] : lease ? ["project", "lease-token"] : update ? [
57393
+ "expected-revision",
57394
+ ...["retry", "detach"].includes(command) ? ["project"] : []
57395
+ ] : []
57396
+ ];
57397
+ for (const key of Object.keys(flags)) if (!allowed.includes(key)) fail2();
57398
+ if (command === "list") {
57399
+ if (argument) fail2();
57400
+ return client.request(
57401
+ "/v1/batches" + (typeof flags.after === "string" ? "?after=" + encodeURIComponent(flags.after) : "")
57402
+ );
57403
+ }
57404
+ if (command === "create") {
57405
+ if (argument || typeof flags.file !== "string") fail2();
57406
+ const body = await readProductionJson(root, flags.file);
57407
+ return client.request("/v1/batches", { method: "POST", body });
57408
+ }
57409
+ if (!argument || !/^[a-f0-9-]{36}$/i.test(argument)) fail2();
57410
+ const path = "/v1/batches/" + argument;
57411
+ if (command === "get") return client.request(path);
57412
+ if (update) {
57413
+ const revision = Number(flags["expected-revision"]);
57414
+ if (!Number.isSafeInteger(revision) || revision < 1 || ["retry", "detach"].includes(command) && typeof flags.project !== "string")
57415
+ fail2();
57416
+ return client.request(path, {
57417
+ method: "POST",
57418
+ body: {
57419
+ action: command,
57420
+ expectedRevision: revision,
57421
+ ...["retry", "detach"].includes(command) ? { projectId: flags.project } : {}
57422
+ }
57423
+ });
57424
+ }
57425
+ if (typeof flags.project !== "string" || !/^[a-z0-9][a-z0-9_-]{0,79}$/.test(flags.project) || typeof flags["lease-token"] !== "string" || !/^[a-f0-9-]{36}$/i.test(flags["lease-token"]))
57426
+ fail2();
57427
+ return client.request(path + "/lease", {
57428
+ method: "POST",
57429
+ body: {
57430
+ action: command,
57431
+ projectId: flags.project,
57432
+ leaseToken: flags["lease-token"]
57433
+ }
57434
+ });
57435
+ }
57436
+
57437
+ // packages/cli/src/project-progress-client.ts
57438
+ async function projectProgressCommand(client, command, flags) {
57439
+ const fail2 = (message) => {
57440
+ throw new CliError(
57441
+ "INVALID_ARGUMENT",
57442
+ message,
57443
+ "Use projects progress/start/queue/complete --project ID. Read progress first; pass --expected-version HASH for changes."
57444
+ );
57445
+ };
57446
+ if (!["progress", "start", "queue", "complete"].includes(command))
57447
+ fail2("Unknown project command.");
57448
+ const allowed = [
57449
+ "project",
57450
+ "url",
57451
+ "dir",
57452
+ ...command === "progress" ? [] : ["expected-version"],
57453
+ ...command === "complete" ? ["revision", "user-confirmed"] : []
57454
+ ];
57455
+ for (const name of Object.keys(flags))
57456
+ if (!allowed.includes(name)) fail2(`Unsupported flag --${name}.`);
57457
+ if (typeof flags.project !== "string") fail2("--project is required.");
57458
+ const project = assertIdentifier(flags.project), path = `/v1/projects/${encodeURIComponent(project)}/progress`;
57459
+ if (command === "progress") return client.request(path);
57460
+ if (typeof flags["expected-version"] !== "string" || !/^[a-f0-9]{64}$/.test(flags["expected-version"]))
57461
+ fail2("--expected-version must be the current progress version.");
57462
+ if (command === "complete" && (typeof flags.revision !== "string" || !/^[a-f0-9-]{36}$/.test(flags.revision) || flags["user-confirmed"] !== true))
57463
+ fail2(
57464
+ "Completion requires --revision UUID and --user-confirmed after explicit final-video approval."
57465
+ );
57466
+ return client.request(path, {
57467
+ method: "POST",
57468
+ body: {
57469
+ action: command,
57470
+ expectedVersion: flags["expected-version"],
57471
+ ...command === "complete" ? { revisionId: flags.revision, userConfirmed: true } : {}
57472
+ }
57473
+ });
57474
+ }
57475
+
57476
+ // packages/cli/src/sfx.ts
57477
+ import { readdir as readdir2, lstat as lstat3 } from "node:fs/promises";
57478
+ import { join as join2, extname as extname2, basename } from "node:path";
57479
+
57157
57480
  // packages/cli/src/media-runtime.ts
57481
+ import { spawn } from "node:child_process";
57482
+ import { createHash } from "node:crypto";
57483
+ import { constants as constants3 } from "node:fs";
57484
+ import { lstat as lstat2, open as open3, realpath as realpath2 } from "node:fs/promises";
57485
+ import { dirname as dirname3, extname, parse, resolve as resolve2 } from "node:path";
57158
57486
  var MediaToolError = class extends CliError {
57159
57487
  constructor(code, message) {
57160
57488
  super(code, message, code === "MEDIA_TOOL_NOT_FOUND" ? "Install FFmpeg/FFprobe separately and make them available on PATH; no account or model is needed." : "Review the local input and reported limit. An incomplete check does not validate the media.");
@@ -57206,24 +57534,24 @@ function resolveLocalMediaPath(value) {
57206
57534
  if (!value || value.includes("\0") || /[\r\n]/.test(value) || /^[a-z][a-z\d+.-]*:/i.test(value) && !(process.platform === "win32" && /^[a-z]:[\\/]/i.test(value)) || value.startsWith("\\\\")) {
57207
57535
  throw new MediaToolError("MEDIA_LOCAL_PATH_REQUIRED", "Provide a local filesystem path, not a URL, protocol or network share.");
57208
57536
  }
57209
- return resolve(value);
57537
+ return resolve2(value);
57210
57538
  }
57211
57539
  async function assertNoMediaSymlinks(value) {
57212
57540
  const path = resolveLocalMediaPath(value);
57213
57541
  let cursor = path;
57214
57542
  while (cursor !== parse(cursor).root) {
57215
- const info = await lstat(cursor);
57543
+ const info = await lstat2(cursor);
57216
57544
  if (info.isSymbolicLink()) {
57217
- const systemAlias = process.platform === "darwin" && (cursor === "/tmp" || cursor === "/var") && await realpath(cursor) === `/private${cursor}`;
57545
+ const systemAlias = process.platform === "darwin" && (cursor === "/tmp" || cursor === "/var") && await realpath2(cursor) === `/private${cursor}`;
57218
57546
  if (!systemAlias) throw new MediaToolError("MEDIA_SYMLINK", "Symlink media paths are not followed. Use the original local path.");
57219
57547
  }
57220
- cursor = dirname(cursor);
57548
+ cursor = dirname3(cursor);
57221
57549
  }
57222
57550
  return path;
57223
57551
  }
57224
57552
  async function assertLocalMediaFile(value) {
57225
57553
  const path = await assertNoMediaSymlinks(value);
57226
- const info = await lstat(path);
57554
+ const info = await lstat2(path);
57227
57555
  if (!info.isFile()) throw new MediaToolError("MEDIA_NOT_REGULAR", "The media input must be a regular local file.");
57228
57556
  if (!SUPPORTED_MEDIA_EXTENSIONS.has(extname(path).toLowerCase())) throw new MediaToolError("MEDIA_FORMAT_UNSUPPORTED", "This file extension is not supported for local media inspection.");
57229
57557
  return path;
@@ -57308,7 +57636,7 @@ var rate2 = (value) => {
57308
57636
  var sameFile = (a, b) => a.dev === b.dev && a.ino === b.ino && a.size === b.size && a.mtimeMs === b.mtimeMs && a.ctimeMs === b.ctimeMs;
57309
57637
  async function probeLocalMedia(value) {
57310
57638
  const path = await assertLocalMediaFile(value);
57311
- const handle = await open(path, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0));
57639
+ const handle = await open3(path, constants3.O_RDONLY | (constants3.O_NOFOLLOW ?? 0));
57312
57640
  try {
57313
57641
  const before = await handle.stat();
57314
57642
  if (!before.isFile()) throw new MediaToolError("MEDIA_NOT_REGULAR", "The media input must be a regular local file.");
@@ -57318,7 +57646,7 @@ async function probeLocalMedia(value) {
57318
57646
  for await (const chunk of handle.createReadStream({ autoClose: false })) hash6.update(chunk);
57319
57647
  const after = await handle.stat();
57320
57648
  await assertNoMediaSymlinks(path);
57321
- const current2 = await lstat(path);
57649
+ const current2 = await lstat2(path);
57322
57650
  if (!sameFile(before, after) || !sameFile(after, current2)) throw new MediaToolError("MEDIA_FILE_CHANGED", "The media file changed while it was being inspected. Retry with a stable source.");
57323
57651
  let raw;
57324
57652
  try {
@@ -57346,121 +57674,6 @@ async function probeLocalMedia(value) {
57346
57674
  }
57347
57675
  }
57348
57676
 
57349
- // packages/cli/src/io.ts
57350
- import { constants as constants2 } from "node:fs";
57351
- import { lstat as lstat2, mkdir, open as open2, readdir, realpath as realpath2, rename, unlink, link } from "node:fs/promises";
57352
- import { dirname as dirname2, join, relative, resolve as resolve2, sep } from "node:path";
57353
- import { randomUUID } from "node:crypto";
57354
- async function ensureRoot(directory, create = false) {
57355
- const root = resolve2(directory);
57356
- if (create) await mkdir(root, { recursive: true });
57357
- const info = await lstat2(root);
57358
- if (info.isSymbolicLink() || !info.isDirectory()) throw new Error("Workspace root must be a real directory, not a symlink");
57359
- return realpath2(root);
57360
- }
57361
- async function checkedPath(root, path, document2 = true) {
57362
- if (document2) safeDocumentPath(path);
57363
- const target = resolve2(root, path);
57364
- const rel = relative(root, target);
57365
- if (!rel || rel.startsWith(`..${sep}`) || rel === ".." || resolve2(path) === path) throw new Error("Path escapes workspace");
57366
- let cursor = root;
57367
- for (const segment of rel.split(sep)) {
57368
- cursor = join(cursor, segment);
57369
- try {
57370
- const info = await lstat2(cursor);
57371
- if (info.isSymbolicLink()) throw new Error(`Symlinks are not allowed: ${path}`);
57372
- } catch (error) {
57373
- if (error.code !== "ENOENT") throw error;
57374
- }
57375
- }
57376
- return target;
57377
- }
57378
- async function readText(root, path, document2 = true) {
57379
- const target = await checkedPath(root, path, document2);
57380
- const handle = await open2(target, constants2.O_RDONLY | constants2.O_NOFOLLOW);
57381
- try {
57382
- const info = await handle.stat();
57383
- if (!info.isFile()) throw new Error(`Not a regular file: ${path}`);
57384
- if (document2 && info.size > MAX_DOCUMENT_BYTES) throw new Error(`Document exceeds 128 KiB: ${path}`);
57385
- return new TextDecoder("utf-8", { fatal: true }).decode(await handle.readFile());
57386
- } finally {
57387
- await handle.close();
57388
- }
57389
- }
57390
- async function atomicWrite(root, path, content, options = {}) {
57391
- const target = await checkedPath(root, path, options.document ?? true);
57392
- await mkdir(dirname2(target), { recursive: true });
57393
- await checkedPath(root, path, options.document ?? true);
57394
- const temporary = join(dirname2(target), `.eh-${randomUUID()}.tmp`);
57395
- const handle = await open2(temporary, "wx", options.mode ?? 420);
57396
- try {
57397
- await handle.writeFile(content, "utf8");
57398
- await handle.close();
57399
- await checkedPath(root, path, options.document ?? true);
57400
- if (options.overwrite === false) {
57401
- await link(temporary, target);
57402
- await unlink(temporary);
57403
- } else await rename(temporary, target);
57404
- } catch (error) {
57405
- await handle.close().catch(() => {
57406
- });
57407
- await unlink(temporary).catch(() => {
57408
- });
57409
- throw error;
57410
- }
57411
- }
57412
- async function readWorkspace(root) {
57413
- const files = /* @__PURE__ */ Object.create(null);
57414
- async function walk(path) {
57415
- const directory = await checkedPath(root, path, false);
57416
- let entries;
57417
- try {
57418
- entries = await readdir(directory, { withFileTypes: true });
57419
- } catch (error) {
57420
- if (error.code === "ENOENT") return;
57421
- throw error;
57422
- }
57423
- for (const entry of entries) {
57424
- const child = `${path}/${entry.name}`;
57425
- if (entry.isSymbolicLink()) throw new Error(`Symlinks are not allowed: ${child}`);
57426
- if (entry.name.startsWith(".")) continue;
57427
- if (entry.isDirectory()) {
57428
- if (path.startsWith("videos/") && ["assets", "renders"].includes(entry.name)) continue;
57429
- await walk(child);
57430
- } else if (entry.isFile() && /\.(json|md)$/.test(entry.name)) {
57431
- safeDocumentPath(child);
57432
- files[child] = await readText(root, child);
57433
- }
57434
- }
57435
- }
57436
- await walk(".editorharness");
57437
- await walk(".editorbrain");
57438
- await walk("videos");
57439
- return canonicalFiles(files);
57440
- }
57441
- async function readProjectWorkspace(root, projectId) {
57442
- assertIdentifier(projectId);
57443
- const projectPath = `videos/${projectId}/project.json`;
57444
- const projectContent = await readText(root, projectPath);
57445
- const checked = validateDocument(projectPath, projectContent);
57446
- if (!checked.valid) throw new ValidationError(checked.issues);
57447
- const project = JSON.parse(projectContent);
57448
- const files = { [projectPath]: projectContent };
57449
- for (const path of /* @__PURE__ */ new Set([...projectSources(project), ...optionalProjectSources(project)])) {
57450
- if (path === projectPath) continue;
57451
- const aliases2 = path.startsWith(".editorbrain/") ? [path, path.replace(/^\.editorbrain\//, ".editorharness/")] : [path];
57452
- for (const alias of aliases2) {
57453
- try {
57454
- files[alias] = await readText(root, alias);
57455
- } catch (error) {
57456
- if (error.code !== "ENOENT") throw error;
57457
- }
57458
- }
57459
- }
57460
- if (await readText(root, projectPath) !== projectContent) throw new Error("The selected project changed while reading its context. Retry before editing.");
57461
- return canonicalFiles(files);
57462
- }
57463
-
57464
57677
  // packages/cli/src/production-context.ts
57465
57678
  async function prepareProductionContext(root, projectId, client) {
57466
57679
  const resolved = await resolveProject(await readProjectWorkspace(root, projectId), projectId);
@@ -57561,123 +57774,6 @@ async function remoteSfx(client, subcommand, params) {
57561
57774
  // packages/cli/src/media-preview.ts
57562
57775
  import { chmod, link as link2, mkdtemp, rm } from "node:fs/promises";
57563
57776
  import { dirname as dirname4, join as join3 } from "node:path";
57564
-
57565
- // packages/cli/src/production-output.ts
57566
- import { constants as constants3 } from "node:fs";
57567
- import { mkdir as mkdir2, open as open3 } from "node:fs/promises";
57568
- import { dirname as dirname3 } from "node:path";
57569
- async function productionOutputPath(root, relativePath) {
57570
- if (!relativePath.startsWith(".editorbrain-local/") || relativePath.split(/[\\/]/).some((p) => !p || p === ".." || p === ".") || /[\\\x00-\x1f\x7f]/.test(relativePath)) {
57571
- throw new CliError("LOCAL_OUTPUT_REQUIRED", "Production output must be inside .editorbrain-local/.", "Use --out .editorbrain-local/<project>/inventory.json, or omit --out for JSON stdout. These reports are not synced.");
57572
- }
57573
- return checkedPath(root, relativePath, false);
57574
- }
57575
- async function prepareSampleDirectory(root, path) {
57576
- const target = await productionOutputPath(root, path);
57577
- await mkdir2(dirname3(target), { recursive: true });
57578
- await productionOutputPath(root, path);
57579
- return target;
57580
- }
57581
- async function readProductionJson(root, path) {
57582
- const target = await checkedPath(root, path, false);
57583
- const handle = await open3(target, constants3.O_RDONLY | constants3.O_NOFOLLOW | constants3.O_NONBLOCK);
57584
- try {
57585
- const stat = await handle.stat();
57586
- if (!stat.isFile() || stat.size > 16 * 1024 * 1024) throw new CliError("INVALID_REPORT", "Expected a regular JSON file of at most 16 MiB.", "Select a local inventory, plan or check profile.");
57587
- return JSON.parse(await handle.readFile("utf8"));
57588
- } finally {
57589
- await handle.close();
57590
- }
57591
- }
57592
- async function saveProductionReport(root, path, report) {
57593
- const target = await productionOutputPath(root, path);
57594
- if (!path.endsWith(".json")) throw new CliError("INVALID_ARGUMENT", "Report output needs a .json extension.", "Use a new path under .editorbrain-local/.");
57595
- await atomicWrite(root, path, stableJson(report), { document: false, overwrite: false, mode: 384 });
57596
- return target;
57597
- }
57598
- function summarizeProductionReport(value) {
57599
- const r = value;
57600
- const base = { schemaVersion: r.schemaVersion, kind: r.kind, summary: true, mediaUploaded: false, quality: "not_assessed" };
57601
- if (r.kind === "editorbrain.media-inventory") return {
57602
- ...base,
57603
- complete: r.complete,
57604
- contentReviewed: false,
57605
- counts: { assets: r.assets.length, failures: r.failures.length, skipped: r.skipped.length },
57606
- assets: r.assets.slice(0, 8),
57607
- failures: r.failures.slice(0, 5),
57608
- omitted: { assets: Math.max(0, r.assets.length - 8), failures: Math.max(0, r.failures.length - 5), skipped: r.skipped.length },
57609
- next: "Inspect selected source content. Use media sample for specific frames. Read the saved report or rerun with --details for omitted metadata; no content was reviewed automatically."
57610
- };
57611
- if (r.kind === "editorbrain.media-samples") return { ...base, sourceSha256: r.source.sha256, frames: r.frames, contentReviewed: false, next: "View these frames to inspect composition. They do not prove motion or sound." };
57612
- if (r.kind === "editorbrain.production-plan-check") return {
57613
- ...base,
57614
- valid: r.valid,
57615
- inventoryComplete: r.inventoryComplete,
57616
- observations: r.observations,
57617
- sourceFilesReverified: false,
57618
- counts: { sources: r.coverage.length, findings: r.findings.length, repeatedPairs: r.repeatedPairs ?? 0 },
57619
- findings: [...r.findings].sort((a, b) => ({ error: 0, warning: 1, info: 2 })[a.severity] - { error: 0, warning: 1, info: 2 }[b.severity]).slice(0, 8),
57620
- omittedFindings: Math.max(0, r.findings.length - 8),
57621
- next: "Review source reuse and inspection gaps in the saved report. Observations are agent-declared, not independently certified."
57622
- };
57623
- if (r.kind === "editorbrain.render-check") return {
57624
- ...base,
57625
- complete: r.complete,
57626
- technicalStatus: r.technicalStatus,
57627
- file: r.file,
57628
- checks: r.checks,
57629
- findings: r.findings.slice(0, 8),
57630
- findingsSummary: r.findingsSummary,
57631
- omittedFindings: Math.max(0, r.findings.length - 8),
57632
- next: "Reconcile technical candidates with the brief; inspect the actual video/audio. Technical checks do not assess narrative, aesthetics or SFX audibility."
57633
- };
57634
- if (r.kind === "editorbrain.media-source-search") return {
57635
- ...base,
57636
- complete: r.complete,
57637
- results: r.results.slice(0, 5).map((item) => ({ provider: item.provider, id: item.id.slice(0, 180), title: item.title.slice(0, 180), sourceUrl: item.sourceUrl.length <= 1500 ? item.sourceUrl : null, author: item.author?.slice(0, 120) ?? null, license: { id: item.license.id, reason: item.license.reason, url: item.license.url && item.license.url.length <= 500 ? item.license.url : null }, contentReviewed: false, rightsVerified: false })),
57638
- metadataAbbreviated: true,
57639
- failures: r.failures.slice(0, 5),
57640
- counts: r.counts,
57641
- omitted: { results: Math.max(0, r.results.length - 5), failures: Math.max(0, r.failures.length - 5) },
57642
- searchExhaustive: false,
57643
- rightsVerified: false,
57644
- next: "Inspect relevant source pages and media. These archives supplement web image/video search; empty results do not prove absence on the internet."
57645
- };
57646
- if (r.kind === "editorbrain.evidence-check") return {
57647
- ...base,
57648
- valid: r.valid,
57649
- inventoryComplete: r.inventoryComplete,
57650
- observations: r.observations,
57651
- sourceFilesReverified: false,
57652
- factualTruth: "not_assessed",
57653
- reuseRights: "not_assessed",
57654
- findings: r.findings.slice(0, 8),
57655
- omitted: { findings: Math.max(0, r.findings.length - 8) }
57656
- };
57657
- if (r.kind === "editorbrain.audio-check") return {
57658
- ...base,
57659
- complete: r.complete,
57660
- technicalStatus: r.technicalStatus,
57661
- file: r.file,
57662
- fileStable: r.fileStable,
57663
- coverage: r.coverage,
57664
- measurements: r.measurements.slice(0, 8),
57665
- errorCode: r.errorCode,
57666
- creativeQuality: r.creativeQuality,
57667
- omitted: { measurements: Math.max(0, r.measurements.length - 8) },
57668
- next: "Measurements describe this exact file. Listen for intelligibility, pronunciation, SFX and sync when the modality is available."
57669
- };
57670
- if (r.kind === "editorbrain.motion-review") return {
57671
- ...base,
57672
- samples: r.samples ? { frameCount: r.samples.frameCount, fps: r.samples.fps, radiusFrames: r.samples.radiusFrames, samples: r.samples.samples.slice(0, 12), omittedSamples: Math.max(0, r.samples.samples.length - 12) } : null,
57673
- geometryReviews: r.geometryReviews.slice(0, 3).map((g) => ({ scope: g.scope, status: g.status, candidates: g.candidates.slice(0, 8), omittedCandidates: Math.max(0, g.candidates.length - 8), checks: g.checks })),
57674
- omittedGeometryReviews: Math.max(0, r.geometryReviews.length - 3),
57675
- next: "This plans samples and checks declared boxes only. Render/view those frames; read saved details for omitted candidates and intentional overlaps."
57676
- };
57677
- throw new CliError("INVALID_REPORT", "Unknown production report kind.", "Use media inventory/sample or production check/plan-check.");
57678
- }
57679
-
57680
- // packages/cli/src/media-preview.ts
57681
57777
  async function prepareMediaPreview(root, file, out, quality = 720, audioStream) {
57682
57778
  if (![720, 1080].includes(quality) || !out.endsWith(".mp4")) throw new CliError("INVALID_ARGUMENT", "Use --quality 720 or 1080 and a new .mp4 output.", "Previews belong under .editorbrain-local/.");
57683
57779
  const source = await probeLocalMedia(await assertLocalMediaFile(file));
@@ -57736,6 +57832,16 @@ async function prepareStudioAudio(root, file, out, streamIndex) {
57736
57832
  // packages/cli/src/client.ts
57737
57833
  import { setTimeout as delay } from "node:timers/promises";
57738
57834
  var searchErrors = {
57835
+ batch_changed: { status: 409, code: "BATCH_CHANGED", hint: "Read batches get before retrying; preserve new changes." },
57836
+ batch_claim_blocked: { status: 409, code: "BATCH_CLAIM_BLOCKED", hint: "Read the batch. Wait for an available slot or reconcile an expired turn; do not start work without a claim." },
57837
+ batch_budget_exceeded: { status: 409, code: "BATCH_BUDGET_EXCEEDED", hint: "Stop generation. The batch or video budget was reached. Do not increase the budget yourself." },
57838
+ batch_not_running: { status: 409, code: "BATCH_NOT_RUNNING", hint: "The batch is paused/cancelled or the turn expired. Read the batch before continuing." },
57839
+ batch_retry_blocked: { status: 409, code: "BATCH_RETRY_BLOCKED", hint: "Inspect existing jobs and outputs before retrying. Do not duplicate a paid request." },
57840
+ batch_project_busy: { status: 409, code: "BATCH_PROJECT_BUSY", hint: "Wait for existing Studio jobs before grouping these projects." },
57841
+ batch_project_taken: { status: 409, code: "BATCH_PROJECT_TAKEN", hint: "Use the project existing batch or create a separate video project." },
57842
+ studio_context_or_lease_changed: { status: 409, code: "STUDIO_CONTEXT_OR_LEASE_CHANGED", hint: "Refresh the workflow and batch lease before generating." },
57843
+ progress_changed: { status: 409, code: "PROGRESS_CHANGED", hint: "Read projects progress --project ID again. Preserve the latest state; never force an old progress version." },
57844
+ final_not_approved: { status: 409, code: "FINAL_NOT_APPROVED", hint: "Review and approve the latest complete video, resolve pending changes, then record explicit final-delivery acceptance." },
57739
57845
  invalid_search_brief: { status: 400, code: "INVALID_SEARCH_BRIEF", hint: "Check references/search-brief.md and the service contract. Older services may reject eventQueries objects with channels. Do not silently remove constraints or retry a changed query as if filtering succeeded." },
57740
57846
  source_inspection_required: { status: 422, code: "SOURCE_INSPECTION_REQUIRED", hint: "Inspect the supplied source and declare only modalities actually reviewed. A script requires script inspection; video requires frames, motion or audio." },
57741
57847
  search_index_not_ready: { status: 503, code: "SEARCH_INDEX_NOT_READY", hint: "No current authorized evidence is indexed. Report the gap; literal library lookup is available but is not semantic retrieval or proof of fit." }
@@ -57835,7 +57941,7 @@ var Client = class {
57835
57941
  const retryAfterMs = retryDelay(response.headers.get("Retry-After"), this.runtime.now());
57836
57942
  if (!response.ok) {
57837
57943
  let errorBody;
57838
- if (path === "/v1/search/brief" && [400, 422, 503].includes(response.status)) {
57944
+ if ((path.startsWith("/v1/batches") || path === "/v1/studio/jobs") && response.status === 409 || path === "/v1/search/brief" && [400, 422, 503].includes(response.status) || /^\/v1\/projects\/[^/]+\/progress$/.test(path) && response.status === 409) {
57839
57945
  try {
57840
57946
  const diagnostic = searchDiagnostic(response.status, await boundedJson(response, 16 * 1024));
57841
57947
  errorBody = diagnostic ? { error: { code: diagnostic.serviceCode } } : void 0;
@@ -58017,7 +58123,7 @@ ${conflicts.join("\n")}`, "Run pull --dry-run and reconcile the reported paths."
58017
58123
 
58018
58124
  // packages/cli/src/studio.ts
58019
58125
  async function studioCommand(client, root, command, argument, flags) {
58020
- const allowed = /* @__PURE__ */ new Set(["dir", "url", ...command === "quote" ? ["file"] : command === "run" ? ["max-credits", "user-confirmed"] : []]);
58126
+ const allowed = /* @__PURE__ */ new Set(["dir", "url", ...command === "quote" ? ["file"] : command === "run" ? ["max-credits", "user-confirmed", "batch-lease-token"] : []]);
58021
58127
  for (const flag of Object.keys(flags)) if (!allowed.has(flag)) throw new CliError("INVALID_ARGUMENT", `--${flag} is not supported for studio ${command}.`, "Read editorbrain help.");
58022
58128
  try {
58023
58129
  if (command === "catalog" || command === "jobs") {
@@ -58035,7 +58141,7 @@ async function studioCommand(client, root, command, argument, flags) {
58035
58141
  if (command === "run") {
58036
58142
  const maxCredits = Number(flags["max-credits"]);
58037
58143
  if (flags["user-confirmed"] !== true || !Number.isFinite(maxCredits) || maxCredits <= 0 || maxCredits > 1e4) throw new CliError("SPENDING_APPROVAL_REQUIRED", "Supply --user-confirmed and --max-credits only after the user authorized this quote or budget.", "Preferences and direct editing mode do not authorize spending.");
58038
- return await client.request("/v1/studio/jobs", { method: "POST", body: { quoteId: argument, maxCredits, userConfirmed: true } });
58144
+ return await client.request("/v1/studio/jobs", { method: "POST", body: { quoteId: argument, maxCredits, userConfirmed: true, ...flags["batch-lease-token"] ? { batchLeaseToken: flags["batch-lease-token"] } : {} } });
58039
58145
  }
58040
58146
  throw new CliError("INVALID_ARGUMENT", "Use studio catalog, quote, run, get or jobs.", "Read editorbrain help.");
58041
58147
  } catch (error) {
@@ -58348,6 +58454,74 @@ var VERIFIED_GUIDANCE_RELEASES = [
58348
58454
  "editorbrain/references/style-creator.md": "1b1bc289a5bc308cbbf7f4d699de2933b8934733a2b69b3e98fb1b7420d986f2",
58349
58455
  "editorbrain/references/workspace-files.md": "cd3006a7cc4f04ff5dfeb1bd901a21c53455017aa7d19fcde9a6e26f1fbbd5b6"
58350
58456
  }
58457
+ },
58458
+ {
58459
+ "version": "0.31.0",
58460
+ "archiveSha256": "34a074361f35b099d673e4f310b566d359bc18ec6f326b055c4e0ce811f07c97",
58461
+ "files": {
58462
+ "editorbrain/AGENTS.md": "2361aaa3202d3f4ea9e941684fbda1a84d9711389cca89f7f6cd3ae03b7ab6d0",
58463
+ "editorbrain/references/audio-production.md": "038504d738db98939aeed14407b15561e4f82c650aace7eb545e77cdffd8dadb",
58464
+ "editorbrain-motion/references/authored-scenes.md": "11109dcd35ab7138fdc18e54605885bb26d9e948f12221e18dea4038987dede2",
58465
+ "editorbrain/references/cli-mcp.md": "3a83731d73dd09b55ee5a250bebd950b5a0daedc2b1c3af23bfc6c36f748d793",
58466
+ "editorbrain-motion/references/code-motion.md": "50382a797adc1e1596086e2408e81ee75ceb6730ca409be9c884646db643a7cd",
58467
+ "editorbrain-motion/references/component-library.md": "e4593622ebe371bba1b1c62f698c6709c36a8d01ddce4a07d40534fca44b797e",
58468
+ "editorbrain/references/editorial-evidence.md": "caf72365b3e7492a62d96b39cd17031fa7c961671ce32bc4649420d8a353a026",
58469
+ "editorbrain/references/first-edit.md": "d71271ab32c6432cce01316917b4504f15411ea59511566a1f6169ac6d09723c",
58470
+ "editorbrain-motion/references/generated-motion.md": "a8d214b53c15f742658abd84f03b768fa2821f8cde94f50aab82a3759a30b36b",
58471
+ "editorbrain/references/integrations.md": "a3500dd79c71ab3ee80a865bb5bf8c94287efb933e07d577738891e276a819a0",
58472
+ "editorbrain/references/local-production.md": "2d170bb217854eda7581f64d8342fffe3b56234bbe56b2715fa93eb3210e6354",
58473
+ "editorbrain/references/media-preferences.md": "d1b92b181fbc226c8d3fda762726dd413944175768651fbfb8200eae44fb2ec8",
58474
+ "editorbrain/references/memory-and-review.md": "2a89d6fc4737b750a380c69630d6fd4f4132aed27656b92e8f0c5748887b70ae",
58475
+ "editorbrain-motion/references/motion-craft.md": "adba1b0cbb3c6327a7665857b0a6d144f97dc843464ccf154d3f76ee3ada905d",
58476
+ "editorbrain/references/motion-review.md": "0fb09a2d61ecd13114ced5cca5daa4580a922cf7d7b3f8698077ceb0e96da6f0",
58477
+ "editorbrain/references/onboarding.md": "875d1b9049c4663780f993c2843528c531a51d6b51380b08ed77d82f618185fb",
58478
+ "editorbrain/references/production-and-review.md": "50f10a98a4f3709a5a585f58e4154f33d6d883b218cd25f3abd72eccc43b15a7",
58479
+ "editorbrain/references/production-continuity.md": "c4b346c9634f3d1ef63b24a97d178e0253c2e45d53f6934076a5417e66e34d9b",
58480
+ "editorbrain/references/real-media.md": "106bee5a34641d53f021ead5d41630d473c2bfc98e4f7ee5500dae837e2e45bb",
58481
+ "editorbrain/references/search-brief.md": "ea2a9c2d5a5cf685c1e5918366901ef2589c4d79aaa78a0dd31e97d6fed42b7b",
58482
+ "editorbrain/references/sfx-library.md": "fa0b02c8765988257e46e16298ecca9cdb0e338a863915e29f831162e237eac4",
58483
+ "editorbrain-motion/SKILL.md": "86617d39341d7325d613b877838258f40f05b4286301dacf8e1ec85ccd502aab",
58484
+ "editorbrain/SKILL.md": "d53070052608fc2535af7cb51bcac2c10097b289450958ca7372cab3fb8364a9",
58485
+ "editorbrain/references/storage-and-previews.md": "8a3057f1c1a2a1e1cd32028059dc897a8461da38c36e812fbd24943d3740b4d5",
58486
+ "editorbrain/references/studio.md": "7a84e43bd72f8590d1a91c7d1bc5f6b60678c1c8eba6523b51b99243e22a1322",
58487
+ "editorbrain/references/style-creator.md": "1b1bc289a5bc308cbbf7f4d699de2933b8934733a2b69b3e98fb1b7420d986f2",
58488
+ "editorbrain/references/workspace-files.md": "18462e7b66ba6204770c654e4a2f7e505c343319671a0447aa55c59f0b9096ec"
58489
+ }
58490
+ },
58491
+ {
58492
+ "version": "0.32.0",
58493
+ "archiveSha256": "30c6c6d97da7de16bb620c0975c2d3f2a4b7a6ea4a56f2eb3c5b0f5f53914f45",
58494
+ "files": {
58495
+ "editorbrain-formats/SKILL.md": "b8b03266a9b7cde8337846144d3032b63b6d9cfbfc4530ae1b06f3dd43074ff2",
58496
+ "editorbrain-formats/references/numbered-tutorial.md": "95754ae7dbfaa678e11ddf1a2b84710dba83a16e2fa4055051bd4f5f4f868a5f",
58497
+ "editorbrain-motion/SKILL.md": "86617d39341d7325d613b877838258f40f05b4286301dacf8e1ec85ccd502aab",
58498
+ "editorbrain-motion/references/authored-scenes.md": "11109dcd35ab7138fdc18e54605885bb26d9e948f12221e18dea4038987dede2",
58499
+ "editorbrain-motion/references/code-motion.md": "50382a797adc1e1596086e2408e81ee75ceb6730ca409be9c884646db643a7cd",
58500
+ "editorbrain-motion/references/component-library.md": "e4593622ebe371bba1b1c62f698c6709c36a8d01ddce4a07d40534fca44b797e",
58501
+ "editorbrain-motion/references/generated-motion.md": "a8d214b53c15f742658abd84f03b768fa2821f8cde94f50aab82a3759a30b36b",
58502
+ "editorbrain-motion/references/motion-craft.md": "adba1b0cbb3c6327a7665857b0a6d144f97dc843464ccf154d3f76ee3ada905d",
58503
+ "editorbrain/AGENTS.md": "2361aaa3202d3f4ea9e941684fbda1a84d9711389cca89f7f6cd3ae03b7ab6d0",
58504
+ "editorbrain/SKILL.md": "b15eb4e2438c0a3e6ec5c3c3170f11c973676d5c6a850cf5d2e9cb3bc7c44e42",
58505
+ "editorbrain/references/audio-production.md": "038504d738db98939aeed14407b15561e4f82c650aace7eb545e77cdffd8dadb",
58506
+ "editorbrain/references/cli-mcp.md": "9febfcac6c1dc763b79ab26ffb07e53bce703cbe4c2332ddcd250e10f2fa75dd",
58507
+ "editorbrain/references/editorial-evidence.md": "caf72365b3e7492a62d96b39cd17031fa7c961671ce32bc4649420d8a353a026",
58508
+ "editorbrain/references/first-edit.md": "d71271ab32c6432cce01316917b4504f15411ea59511566a1f6169ac6d09723c",
58509
+ "editorbrain/references/integrations.md": "a3500dd79c71ab3ee80a865bb5bf8c94287efb933e07d577738891e276a819a0",
58510
+ "editorbrain/references/local-production.md": "2d170bb217854eda7581f64d8342fffe3b56234bbe56b2715fa93eb3210e6354",
58511
+ "editorbrain/references/media-preferences.md": "d1b92b181fbc226c8d3fda762726dd413944175768651fbfb8200eae44fb2ec8",
58512
+ "editorbrain/references/memory-and-review.md": "2a89d6fc4737b750a380c69630d6fd4f4132aed27656b92e8f0c5748887b70ae",
58513
+ "editorbrain/references/motion-review.md": "0fb09a2d61ecd13114ced5cca5daa4580a922cf7d7b3f8698077ceb0e96da6f0",
58514
+ "editorbrain/references/onboarding.md": "875d1b9049c4663780f993c2843528c531a51d6b51380b08ed77d82f618185fb",
58515
+ "editorbrain/references/production-and-review.md": "50f10a98a4f3709a5a585f58e4154f33d6d883b218cd25f3abd72eccc43b15a7",
58516
+ "editorbrain/references/production-continuity.md": "c4b346c9634f3d1ef63b24a97d178e0253c2e45d53f6934076a5417e66e34d9b",
58517
+ "editorbrain/references/real-media.md": "106bee5a34641d53f021ead5d41630d473c2bfc98e4f7ee5500dae837e2e45bb",
58518
+ "editorbrain/references/search-brief.md": "ea2a9c2d5a5cf685c1e5918366901ef2589c4d79aaa78a0dd31e97d6fed42b7b",
58519
+ "editorbrain/references/sfx-library.md": "fa0b02c8765988257e46e16298ecca9cdb0e338a863915e29f831162e237eac4",
58520
+ "editorbrain/references/storage-and-previews.md": "8a3057f1c1a2a1e1cd32028059dc897a8461da38c36e812fbd24943d3740b4d5",
58521
+ "editorbrain/references/studio.md": "7a84e43bd72f8590d1a91c7d1bc5f6b60678c1c8eba6523b51b99243e22a1322",
58522
+ "editorbrain/references/style-creator.md": "1b1bc289a5bc308cbbf7f4d699de2933b8934733a2b69b3e98fb1b7420d986f2",
58523
+ "editorbrain/references/workspace-files.md": "c16fe08b209c65baf8f57054f7cc01f514292fc073b7072298b86c0054da3e19"
58524
+ }
58351
58525
  }
58352
58526
  ];
58353
58527
  function isVerifiedHistoricalGuidance(skillName, relativePath, sha2562) {
@@ -58356,7 +58530,7 @@ function isVerifiedHistoricalGuidance(skillName, relativePath, sha2562) {
58356
58530
  }
58357
58531
 
58358
58532
  // packages/cli/src/agent-guidance.ts
58359
- var BUNDLED_SKILLS = ["editorbrain", "editorbrain-motion"];
58533
+ var BUNDLED_SKILLS = ["editorbrain", "editorbrain-motion", "editorbrain-formats"];
58360
58534
  var previousSkillHashes = { "SKILL.md": ["9df2c89deeec30ddf556c359027efe1d90e6b4e9c08717cd8827e210b38a187f", "67b157cb45cae3621e6f2c43235c1d69907ad6abd45ab6b52a3c65f236732db9", "1e2fff65a1b303b18b2d65156bef6eb2439a19d77ba2a25b37bcb1819ff009e3", "dacce1c4127f4281df6f7e1681e243444bb6f8a8e74b829e52ee0108c6672348", "09740ab4b275eada65b5040ae6000104ff39094f534a2784e67feb2afc2a59b3", "f18269ad5504fff8a4fc522d463483261f5041e56cb7f1081f9614bc67e0d96f", "5779a28879313070b0d819771ac967d4a133cff1c3dbda2ec44b4dd2abcb17d1", "30f1e64622e70552a1a6129cacc646f56b09b3d7a1fd3f5bf22299bc6bfd0d1e", "6975d917226f75ad55c67543bf7f1c5ef18f4cfe43426c69400c56c1ed497db3", "0029d5da60efc14759a66c4e3299d5da09c4ab8e3f255fe5da9990699c42f3e4", "09d5441cba061349b43b36ab683d966f772d0cec94698823e32c35f2475d287e"], "AGENTS.md": ["4f3ef860ade3a31aa7c6a6aab5cf6ec2a700850bf19ea2320324c12c2e0fbf5b", "1b07de01698a0040b47ea0eddb1b017f70a69e81acd0bf9710647ca5e02f5125", "b5ee3439ff974c668755816149fab76899d7ad9266f63978b30fbc9b7d2cd640", "bacedb7bdedab5abe75c9601717cfdb05a96208fa2e2a42af878e2dfd2ae4251", "ea3b473179857ccc07e9e4f21a6ede9e0906a09e495f4a051069d15aee1556cd", "faa2bba99ae3023be7f588d02177a4abf583d3a8a65f617b8cff7a3ceed09dfc", "83c75394bc7eaab6662a76d89339361e836cc1a988237720eeeacdd6b62e726f", "b685cb7ff21d5bfe19d96ce0139c17b7eb014b7ecda8ec5321558b0004bfdc71", "9da0b84e166ad3799dd0c644adaa3ce8727f383e5571b0a212c3d34f5c05ab40", "12f5dce738eb974ddd96f4bd00d8de5be236a54dbb20720f6f4ab455bcc319d9"], "references/cli-mcp.md": ["5d3a79343fbd76cdae5bdbe12fb9c944d5dff4f489ac6534fd96e0cb8630b988", "8e011c31368cef2117a28ee972b39aab9535eeb0f601a4a0f9d6fc019b22c1c2", "666082653efc000f8a7cc5f1836c80bbdda5f0f57cdbbdcb393f2b84aeb93a01", "78b737796a8685b1135e0acb91b620c51845b62e31dba04eb51dd9dd38b41008", "8b2e01bfd937237919f5ade794eed01dc6e32b556ede9de3a0ccc28cbdd988d1", "ca88b2816a795a5ebe834074b178469367634b80ad837e3697182ea585af1c05", "490daf154b428b536d0a6f590030c854b2fc8ca6adb995c6ecd5a162f85e471e", "9f98affecd69752c17f52fb51ae74b26a1cadea5919b8b74b7865a20661c4418", "19fc85e92cef203f4162e2bfb2dd2df8ef90472bc8f81dfb568b65a714335ff9", "f697c525a32d8ed6c0752d833cb98cca8b9aec9eadf456e178dfbc9fc246c58c", "d2a3dd980fb712110861ac9802288bb025ac5ea0055f1e56d70d649412a4d733", "955d60d1be71d0eb134ae9fc35a771d41303fae8fe63daa99bfce514a6522e14"], "references/production-and-review.md": ["055c6df9e0f167be148b0a5c0bba5b43e108eb1f58f478d88b3536c6589ac7b3", "4a4035b87a6c024b419847ab8592bddfeb9b2d9c184f65880c47d661f0bb250e", "c3669af0717a5fafbf7cc8b01bd66f74d4c82028715323a870a1298d25250d5e"], "references/workspace-files.md": ["e72af2871f42cd776be1bfff8009ed5562780e7f8a65b12c27887f3880631f81", "00780c64bd0244c6afb18d786b7512b43e315c376e8f88c67d6ba53d91275ccf", "97053b092e0ff36e3cfb764095fcb4a4fca94e722567e774b53bc118279f40e5", "70fc660e7ed3d70c16c33c62c8e0399b99c3251808f093ef3d4ea5e8d39e4c8a", "28c62c1e28842142607f5168ee79b18fd797900dd66dd9ac5eb3cfcf8a09f781", "b86eae85cf86dbf4409b10f4d8df1f1649fc408ce1a240f7cf26d5bb69aabfd1", "fc65f343e50b91327653bdc5d94f35af9c86c097bb46810b4b2f7c9a1c200166", "8a650485bc4f26bbebf6d6a66957bdea87b156632e52a27e9becc194b003a9fc"], "references/first-edit.md": ["cb1e9d3272b2b22ae095aed02e33067c646da48e7b64cf71ff81197f6e67b673", "5a02253d9907f5e6705304565da7b2d1787e84ae17616414b295a8526ec3e4a5", "586b1f18bab01ad6b2bb0efd95cfdc8f8002ecfd022ae91021779ab1d5e432c9"], "references/memory-and-review.md": ["51a274e522270f1fc295d0c0b9a1c23672ce01abea097c2499c9a8db56c0c289"], "references/search-brief.md": ["137203383a7873593136428520f5f9cd6ce1ab6cfeab365c277175786eba3ab3", "df93154562bb89a9f22d31260c17406565f5f308c407643c85d15e528ef2c086", "a472eeb61a2a60172d5cdb71f80be3d18f59e248028ab107b6cd1b68535ff588"], "references/style-creator.md": ["1b1bc289a5bc308cbbf7f4d699de2933b8934733a2b69b3e98fb1b7420d986f2"], "references/local-production.md": ["0a13c65cd60e40ba703095f5e33a1ea66433323b38345c2034fb73c602c438ee"], "references/integrations.md": ["a3500dd79c71ab3ee80a865bb5bf8c94287efb933e07d577738891e276a819a0"], "references/media-preferences.md": ["970b97fbd55b8e9e05fd7e51b4a953cd1b96b9bee7830382aa7514f1b240b3aa", "481c643a5e6516413a6cae970b5170573c981d0effd7d4e146c5cd07c0ee3b1f", "8f11e1956ed06ab73ba58d8c880f0d2292eb76829b07d2de2eaaed1965885158", "e94bc13c05ffd57eceecc4ceaa42df2b83904c12f8c552e161c98f13e6427e5b", "00e50c6231a917639c8d9eb39bff61b0b90e3a1886a4d4684ef6d85fa754e87c"] };
58361
58535
  var destinations = {
58362
58536
  codex: { directory: ".agents/skills/editorbrain", entryPoint: "AGENTS.md" },
@@ -58364,6 +58538,7 @@ var destinations = {
58364
58538
  };
58365
58539
  var previousHashesBySkill = {
58366
58540
  editorbrain: previousSkillHashes,
58541
+ "editorbrain-formats": {},
58367
58542
  "editorbrain-motion": { "references/code-motion.md": ["9c9bd4c7c6ef8c607e4de6198408e4a49e6d6c4deda681dd7aadada1e46c1a80", "dc7446d5349fd107d6c722fcf62ad6ad6dfc7b9a1d4829c0adfbc00e41a9c8a5", "08fd07e904424a98ec4dd68732d55e15fa9f6edffe14e4ee0241f0483acb82be"], "references/generated-motion.md": ["a8d214b53c15f742658abd84f03b768fa2821f8cde94f50aab82a3759a30b36b"], "SKILL.md": ["f732ba6c896e01a844df92a353a77d18da1be7f09ce557bf7a543e3066a667d2", "0920c79979dc029fae3526a565e5eab75d5b7f1f51c369fb7ef0d652b95876eb", "0b6f6e6990528579fdff4275a2bcf0b0fe6f0f2de59ae84d0f57b81a07185cec"], "references/component-library.md": ["ad7142f9bbd52903b61f401104dfdbb430535f358946e58788f9f740d8f44b56", "7ff9e079116c410be0dc80fb470e79226c380b6fcc7d6eb72cb94443dd8915a4", "c2ab96ee61dc1e9480cb6f7fd160c016b35ff946e3d8ffe31267d127718482c8", "fea1fb710d23830c3ed14c3e4bed7bc53540ae2e2e662b74fe6eb6307b010d28"] }
58368
58543
  };
58369
58544
  function installedSkillDirectory(agent, skillName = "editorbrain") {
@@ -58421,11 +58596,11 @@ async function prepareAgentGuidance(root, agent = "both", sourceDirectory, compa
58421
58596
  root = await ensureRoot(root);
58422
58597
  parseAgentTarget(agent);
58423
58598
  const agents = agent === "both" ? ["codex", "claude"] : [agent];
58424
- const companions = companionSources ?? (sourceDirectory === void 0 ? {
58425
- "editorbrain-motion": bundledSkillDirectory(void 0, "editorbrain-motion")
58426
- } : {});
58599
+ const companions = companionSources ?? (sourceDirectory === void 0 ? Object.fromEntries(
58600
+ BUNDLED_SKILLS.filter((name) => name !== "editorbrain").map((name) => [name, bundledSkillDirectory(void 0, name)])
58601
+ ) : {});
58427
58602
  for (const name of Object.keys(companions)) {
58428
- if (name !== "editorbrain-motion") throw new Error(`Unknown companion skill: ${name}`);
58603
+ if (name === "editorbrain" || !BUNDLED_SKILLS.includes(name)) throw new Error(`Unknown companion skill: ${name}`);
58429
58604
  }
58430
58605
  const sources = /* @__PURE__ */ new Map([
58431
58606
  ["editorbrain", await loadSkill(sourceDirectory ?? bundledSkillDirectory())]
@@ -63497,7 +63672,16 @@ editorbrain register --url https://server --name workspace --invite-stdin
63497
63672
  editorbrain status
63498
63673
  editorbrain pull [--project id | --all] [--dir folder] [--dry-run]
63499
63674
  editorbrain push [--dir folder] [--dry-run]
63675
+ editorbrain batches list [--after UUID]
63676
+ editorbrain batches create --file batch.json
63677
+ editorbrain batches get BATCH_ID
63678
+ editorbrain batches pause|resume|cancel BATCH_ID --expected-revision N
63679
+ editorbrain batches retry|detach BATCH_ID --project ID --expected-revision N
63680
+ editorbrain batches claim|renew|release|attention BATCH_ID --project ID --lease-token UUID
63500
63681
  editorbrain projects list
63682
+ editorbrain projects progress --project ID
63683
+ editorbrain projects start|queue --project ID --expected-version HASH
63684
+ editorbrain projects complete --project ID --expected-version HASH --revision UUID --user-confirmed
63501
63685
  editorbrain documents list [--prefix path]
63502
63686
  editorbrain documents get <path> [--revision N]
63503
63687
  editorbrain documents history <path> [--before N]
@@ -63516,7 +63700,7 @@ editorbrain studio prepare --file source.mp4 --out .editorbrain-local/studio/aud
63516
63700
  editorbrain studio catalog
63517
63701
  editorbrain billing status
63518
63702
  editorbrain studio quote --file request.json [--dir folder]
63519
- editorbrain studio run QUOTE_ID --max-credits N --user-confirmed
63703
+ editorbrain studio run QUOTE_ID --max-credits N --user-confirmed [--batch-lease-token UUID]
63520
63704
  editorbrain studio get JOB_ID
63521
63705
  editorbrain studio jobs
63522
63706
  editorbrain integrations catalog
@@ -63602,6 +63786,9 @@ function parseArgs(argv) {
63602
63786
  known.add("preview-kind");
63603
63787
  known.add("preset");
63604
63788
  known.add("context-version");
63789
+ known.add("expected-version");
63790
+ known.add("lease-token");
63791
+ known.add("batch-lease-token");
63605
63792
  for (let i = 0; i < argv.length; i++) {
63606
63793
  const arg = argv[i];
63607
63794
  if (!arg.startsWith("--")) {
@@ -64187,6 +64374,14 @@ async function main() {
64187
64374
  }
64188
64375
  throw new Error("Use keys list, keys create --label name, or keys revoke <key-id>");
64189
64376
  }
64377
+ if (command === "batches") {
64378
+ output(await batchCommand(new Client(await config(args)), await ensureRoot(str(args, "dir") ?? process.cwd()), subcommand, argument, args.flags));
64379
+ return;
64380
+ }
64381
+ if (command === "projects" && ["progress", "start", "queue", "complete"].includes(subcommand)) {
64382
+ output(await projectProgressCommand(new Client(await config(args)), subcommand, args.flags));
64383
+ return;
64384
+ }
64190
64385
  if (command === "projects" && subcommand === "list") {
64191
64386
  output({ projects: await listProjects(new Client(await config(args))) });
64192
64387
  return;