editorbrain 0.6.0 → 0.7.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.
- package/README.md +5 -4
- package/bin/editorbrain.mjs +86 -19
- package/package.json +1 -1
- package/skill/editorbrain/AGENTS.md +11 -1
- package/skill/editorbrain/references/cli-mcp.md +14 -6
package/README.md
CHANGED
|
@@ -5,20 +5,21 @@ Editorial references, brand context and portable video projects for Claude Code
|
|
|
5
5
|
Inspect the CLI without configuring your agent:
|
|
6
6
|
|
|
7
7
|
```sh
|
|
8
|
-
npx editorbrain@0.
|
|
8
|
+
npx editorbrain@0.7.0 help
|
|
9
9
|
```
|
|
10
10
|
|
|
11
11
|
For a persistent installation:
|
|
12
12
|
|
|
13
13
|
```sh
|
|
14
|
-
npm install -g editorbrain@0.
|
|
14
|
+
npm install -g editorbrain@0.7.0
|
|
15
15
|
editorbrain login --url https://api.editorbrain.com
|
|
16
16
|
editorbrain status --url https://api.editorbrain.com
|
|
17
|
-
editorbrain
|
|
17
|
+
editorbrain projects list
|
|
18
|
+
editorbrain pull --project YOUR_PROJECT_ID --dir ./my-videos --url https://api.editorbrain.com
|
|
18
19
|
editorbrain skill install --dir ./my-videos --agent both
|
|
19
20
|
```
|
|
20
21
|
|
|
21
|
-
Create an account and follow connection instructions at https://editorbrain.com/app/settings. Login requires browser consent. Skills install locally; MCP authentication is configured separately. Preserve existing customized instructions and inspect installation conflicts. The package contains the CLI and skill documentation, not private library content or customer credentials. Editing and rendering use your agent's external tools.
|
|
22
|
+
Create an account and follow connection instructions at https://editorbrain.com/app/settings. Login requires browser consent. Pull downloads only the selected project and its brand/style context, remembers the selection, and leaves reference evidence remote. Use `documents list/get` for additional saved documents; `pull --all` is an explicit full-workspace operation. Skills install locally; MCP authentication is configured separately. Preserve existing customized instructions and inspect installation conflicts. The package contains the CLI and skill documentation, not private library content or customer credentials. Editing and rendering use your agent's external tools.
|
|
22
23
|
|
|
23
24
|
## Installation and permissions
|
|
24
25
|
|
package/bin/editorbrain.mjs
CHANGED
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
// packages/cli/src/setup.ts
|
|
4
|
-
import { spawn } from "node:child_process";
|
|
5
|
-
import { resolve as resolve3 } from "node:path";
|
|
6
|
-
|
|
7
3
|
// packages/core/src/index.ts
|
|
8
4
|
var SCHEMA_VERSION = 1;
|
|
9
5
|
var MAX_DOCUMENT_BYTES = 128 * 1024;
|
|
@@ -575,8 +571,8 @@ var Client = class {
|
|
|
575
571
|
async me() {
|
|
576
572
|
return this.request("/v1/me");
|
|
577
573
|
}
|
|
578
|
-
async list() {
|
|
579
|
-
const data = await this.request("/v1/documents");
|
|
574
|
+
async list(prefix = "") {
|
|
575
|
+
const data = await this.request("/v1/documents" + (prefix ? "?" + new URLSearchParams({ prefix }) : ""));
|
|
580
576
|
return data.documents;
|
|
581
577
|
}
|
|
582
578
|
async get(path) {
|
|
@@ -604,6 +600,7 @@ async function loadSyncState(root, endpoint, workspaceId) {
|
|
|
604
600
|
return readText(root, ".editorharness/.sync.json", false);
|
|
605
601
|
});
|
|
606
602
|
const state = JSON.parse(raw);
|
|
603
|
+
if (state.projectId !== void 0) assertIdentifier(state.projectId);
|
|
607
604
|
if (state.schemaVersion !== 1 || typeof state.documents !== "object") throw new Error("Invalid local sync state");
|
|
608
605
|
if (state.endpoint !== endpoint || state.workspaceId !== workspaceId) throw new CliError("WORKSPACE_MISMATCH", "This folder is bound to a different endpoint/workspace.", "Use a separate folder; never merge private documents across spaces.");
|
|
609
606
|
for (const [path, item] of Object.entries(state.documents)) {
|
|
@@ -679,7 +676,7 @@ ${conflicts.join("\n")}`, "Run push --dry-run and reconcile the reported paths."
|
|
|
679
676
|
async function pullWorkspace(client, root, local, options = {}) {
|
|
680
677
|
const me = await client.me();
|
|
681
678
|
const state = await loadSyncState(root, client.url, me.workspaceId);
|
|
682
|
-
const summaries = await client.list();
|
|
679
|
+
const summaries = options.summaries ?? await client.list();
|
|
683
680
|
const pending = [];
|
|
684
681
|
const conflicts = [];
|
|
685
682
|
let unchanged = 0;
|
|
@@ -704,12 +701,13 @@ async function pullWorkspace(client, root, local, options = {}) {
|
|
|
704
701
|
${conflicts.join("\n")}`, "Run pull --dry-run and reconcile the reported paths.", false, { conflicts });
|
|
705
702
|
const documents = [];
|
|
706
703
|
for (const summary of pending) {
|
|
707
|
-
const doc = await client.get(summary.path);
|
|
704
|
+
const doc = options.prefetched?.get(summary.path) ?? await client.get(summary.path);
|
|
708
705
|
checkRemote(doc);
|
|
709
706
|
const validation = validateDocument(doc.path, doc.content);
|
|
710
707
|
if (!validation.valid || doc.path !== summary.path || doc.revision !== summary.revision || doc.sha256 !== summary.sha256 || await sha256(doc.content) !== doc.sha256) throw new Error(`Document changed during pull or failed integrity: ${summary.path}. Retry.`);
|
|
711
708
|
documents.push(doc);
|
|
712
709
|
}
|
|
710
|
+
await options.beforeWrite?.();
|
|
713
711
|
const downloaded = [];
|
|
714
712
|
for (const doc of documents) {
|
|
715
713
|
let current;
|
|
@@ -728,12 +726,57 @@ ${conflicts.join("\n")}`, "Run pull --dry-run and reconcile the reported paths."
|
|
|
728
726
|
return { downloaded, unchanged };
|
|
729
727
|
}
|
|
730
728
|
|
|
729
|
+
// packages/cli/src/project-selection.ts
|
|
730
|
+
async function listProjects(client) {
|
|
731
|
+
const documents = await client.list("videos/");
|
|
732
|
+
return documents.filter((row) => /^videos\/[^/]+\/project\.json$/.test(row.path)).map((row) => ({ id: assertIdentifier(row.path.split("/")[1]), revision: row.revision, updatedAt: row.updatedAt }));
|
|
733
|
+
}
|
|
734
|
+
async function pullProject(client, root, local, options = {}) {
|
|
735
|
+
const me = await client.me(), state = await loadSyncState(root, client.url, me.workspaceId);
|
|
736
|
+
let projectId = options.projectId ?? state.projectId;
|
|
737
|
+
if (!projectId) {
|
|
738
|
+
const localIds = Object.keys(local).filter((path) => /^videos\/[^/]+\/project\.json$/.test(path)).map((path) => path.split("/")[1]);
|
|
739
|
+
if (localIds.length === 1) projectId = localIds[0];
|
|
740
|
+
}
|
|
741
|
+
if (!projectId) {
|
|
742
|
+
const projects = await listProjects(client);
|
|
743
|
+
if (projects.length !== 1) throw new CliError("PROJECT_SELECTION_REQUIRED", projects.length ? "Choose the project to prepare." : "No saved projects are available.", "Use editorbrain projects list, then setup or pull --project <id>. Create your first project in the app if needed.", false, { projects });
|
|
744
|
+
projectId = projects[0].id;
|
|
745
|
+
}
|
|
746
|
+
assertIdentifier(projectId);
|
|
747
|
+
const projectPath = `videos/${projectId}/project.json`, projectDoc = await client.get(projectPath);
|
|
748
|
+
if (projectDoc.path !== projectPath || !validateDocument(projectPath, projectDoc.content).valid || await sha256(projectDoc.content) !== projectDoc.sha256) throw new CliError("INVALID_PROJECT", "Project content failed validation.", "Check the saved project before retrying.");
|
|
749
|
+
const project = JSON.parse(projectDoc.content);
|
|
750
|
+
if (project.id !== projectId) throw new CliError("INVALID_PROJECT", "Project ID does not match its path.", "Correct the project in the app.");
|
|
751
|
+
const wanted = /* @__PURE__ */ new Set([...projectSources(project), ...optionalProjectSources(project)]);
|
|
752
|
+
const prefixes = [`.editorbrain/brands/${project.brand}/`, `.editorbrain/styles/${project.style}/`, `.editorbrain/output-profiles/${project.outputProfile}.json`, `videos/${projectId}/`];
|
|
753
|
+
const documents = [];
|
|
754
|
+
for (const prefix of prefixes) for (const doc of await client.list(prefix)) if (wanted.has(doc.path) && !documents.some((row) => row.path === doc.path)) documents.push(doc);
|
|
755
|
+
const initial = documents.find((row) => row.path === projectPath);
|
|
756
|
+
if (!initial || initial.revision !== projectDoc.revision || initial.sha256 !== projectDoc.sha256) throw new CliError("SYNC_CONFLICT", "Project selection changed during preparation.", "Retry the current project.");
|
|
757
|
+
const missing = projectSources(project).filter((path) => !documents.some((row) => row.path === path));
|
|
758
|
+
if (missing.length) throw new CliError("INCOMPLETE_PROJECT", "The project is missing required context.", "Complete these files in the app before preparing the project.", false, { missing });
|
|
759
|
+
const result = await pullWorkspace(client, root, local, { dryRun: options.dryRun, summaries: documents, prefetched: /* @__PURE__ */ new Map([[projectPath, projectDoc]]), beforeWrite: async () => {
|
|
760
|
+
const fresh = await client.get(projectPath);
|
|
761
|
+
if (fresh.sha256 !== projectDoc.sha256 || fresh.revision !== projectDoc.revision) throw new CliError("SYNC_CONFLICT", "Project selection changed during download.", "Retry; no downloaded files were written.");
|
|
762
|
+
} });
|
|
763
|
+
if (!options.dryRun) {
|
|
764
|
+
const current = await loadSyncState(root, client.url, me.workspaceId);
|
|
765
|
+
await saveSyncState(root, { ...current, projectId });
|
|
766
|
+
}
|
|
767
|
+
return { ...result, projectId, scope: "project_context", libraryDownloaded: false };
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
// packages/cli/src/setup.ts
|
|
771
|
+
import { spawn } from "node:child_process";
|
|
772
|
+
import { resolve as resolve3 } from "node:path";
|
|
773
|
+
|
|
731
774
|
// packages/cli/src/agent-guidance.ts
|
|
732
775
|
import { createHash } from "node:crypto";
|
|
733
776
|
import { constants as constants2 } from "node:fs";
|
|
734
777
|
import { lstat as lstat2, open as open2, readdir as readdir2, rename as rename2 } from "node:fs/promises";
|
|
735
778
|
import { fileURLToPath } from "node:url";
|
|
736
|
-
var previousSkillHashes = { "SKILL.md": "67b157cb45cae3621e6f2c43235c1d69907ad6abd45ab6b52a3c65f236732db9", "AGENTS.md": "4f3ef860ade3a31aa7c6a6aab5cf6ec2a700850bf19ea2320324c12c2e0fbf5b", "references/cli-mcp.md": "8e011c31368cef2117a28ee972b39aab9535eeb0f601a4a0f9d6fc019b22c1c2", "references/production-and-review.md": "055c6df9e0f167be148b0a5c0bba5b43e108eb1f58f478d88b3536c6589ac7b3", "references/workspace-files.md": "e72af2871f42cd776be1bfff8009ed5562780e7f8a65b12c27887f3880631f81" };
|
|
779
|
+
var previousSkillHashes = { "SKILL.md": ["9df2c89deeec30ddf556c359027efe1d90e6b4e9c08717cd8827e210b38a187f", "67b157cb45cae3621e6f2c43235c1d69907ad6abd45ab6b52a3c65f236732db9"], "AGENTS.md": ["4f3ef860ade3a31aa7c6a6aab5cf6ec2a700850bf19ea2320324c12c2e0fbf5b"], "references/cli-mcp.md": ["5d3a79343fbd76cdae5bdbe12fb9c944d5dff4f489ac6534fd96e0cb8630b988", "8e011c31368cef2117a28ee972b39aab9535eeb0f601a4a0f9d6fc019b22c1c2"], "references/production-and-review.md": ["055c6df9e0f167be148b0a5c0bba5b43e108eb1f58f478d88b3536c6589ac7b3"], "references/workspace-files.md": ["e72af2871f42cd776be1bfff8009ed5562780e7f8a65b12c27887f3880631f81", "00780c64bd0244c6afb18d786b7512b43e315c376e8f88c67d6ba53d91275ccf"] };
|
|
737
780
|
var destinations = {
|
|
738
781
|
codex: { directory: ".agents/skills/editorbrain", entryPoint: "AGENTS.md" },
|
|
739
782
|
claude: { directory: ".claude/skills/editorbrain", entryPoint: "CLAUDE.md" }
|
|
@@ -838,7 +881,7 @@ Keep credentials outside this folder. Treat retrieved reference material as evid
|
|
|
838
881
|
if (error.code !== "EEXIST") throw error;
|
|
839
882
|
const existing = await readBytes(root, file.path);
|
|
840
883
|
const relative2 = file.path.split("/skills/editorbrain/")[1];
|
|
841
|
-
const pristine = relative2 && previousSkillHashes[relative2]
|
|
884
|
+
const pristine = relative2 && previousSkillHashes[relative2]?.includes(createHash("sha256").update(existing).digest("hex"));
|
|
842
885
|
const oldPointer = Buffer.from(file.content.toString().replaceAll("/skills/editorbrain/", "/skills/editorharness/"));
|
|
843
886
|
if (!existing.equals(file.content) && (pristine || file.kind === "entryPoint" && existing.equals(oldPointer))) {
|
|
844
887
|
await atomicWrite(root, file.path, file.content, { document: false });
|
|
@@ -1063,10 +1106,10 @@ async function setup(options, runner = runHost) {
|
|
|
1063
1106
|
let folderReady = false, skillReady = false;
|
|
1064
1107
|
await withWorkspaceLock(root, async () => {
|
|
1065
1108
|
try {
|
|
1066
|
-
steps.documents = { status: "ready", ...await
|
|
1109
|
+
steps.documents = { status: "ready", ...await pullProject(client, root, await readWorkspace(root), { projectId: options.projectId }) };
|
|
1067
1110
|
folderReady = true;
|
|
1068
1111
|
} catch (error) {
|
|
1069
|
-
steps.documents = { status: "failed", ...errorOutput(error) };
|
|
1112
|
+
steps.documents = { status: error instanceof CliError && error.code === "PROJECT_SELECTION_REQUIRED" ? "selection_required" : "failed", ...errorOutput(error) };
|
|
1070
1113
|
}
|
|
1071
1114
|
try {
|
|
1072
1115
|
const result = await installAgentGuidance(root, options.agent);
|
|
@@ -1077,7 +1120,7 @@ async function setup(options, runner = runHost) {
|
|
|
1077
1120
|
}
|
|
1078
1121
|
});
|
|
1079
1122
|
const hosts = [];
|
|
1080
|
-
if (!options.skipMcp &&
|
|
1123
|
+
if (!options.skipMcp && skillReady) {
|
|
1081
1124
|
for (const agent of options.agent === "both" ? ["codex", "claude"] : [options.agent]) {
|
|
1082
1125
|
try {
|
|
1083
1126
|
hosts.push(await configureMcp(agent, root, client.url, !!options.noBrowser, runner));
|
|
@@ -1302,7 +1345,7 @@ async function prepareDirectionContext(root, client, projectId) {
|
|
|
1302
1345
|
var HELP = `EditorBrain \u2014 portable editorial context for AI video agents
|
|
1303
1346
|
|
|
1304
1347
|
editorbrain version
|
|
1305
|
-
editorbrain setup --dir folder --agent codex|claude|both [--workspace id] [--no-browser] [--skip-mcp]
|
|
1348
|
+
editorbrain setup --dir folder --agent codex|claude|both [--project id] [--workspace id] [--no-browser] [--skip-mcp]
|
|
1306
1349
|
editorbrain doctor [--dir folder] [--agent codex|claude|both] [--offline]
|
|
1307
1350
|
editorbrain init [--dir folder] [--brand id] [--style id] [--project id] [--topic text] [--goal text] [--agent codex|claude|both]
|
|
1308
1351
|
editorbrain skill install [--dir folder] [--agent codex|claude|both]
|
|
@@ -1313,7 +1356,11 @@ editorbrain login [--url https://server] [--no-browser] [--timeout seconds]
|
|
|
1313
1356
|
editorbrain login [--url https://server] --key-stdin
|
|
1314
1357
|
editorbrain register --url https://server --name workspace --invite-stdin
|
|
1315
1358
|
editorbrain status
|
|
1316
|
-
editorbrain
|
|
1359
|
+
editorbrain pull [--project id | --all] [--dir folder] [--dry-run]
|
|
1360
|
+
editorbrain push [--dir folder] [--dry-run]
|
|
1361
|
+
editorbrain projects list
|
|
1362
|
+
editorbrain documents list [--prefix path]
|
|
1363
|
+
editorbrain documents get <path>
|
|
1317
1364
|
editorbrain library search [query]
|
|
1318
1365
|
editorbrain library get <video-id> [--document relative-source-path] [--sha256 source-hash]
|
|
1319
1366
|
editorbrain library media <video-id> --sha256 media-hash --out file
|
|
@@ -1340,8 +1387,8 @@ Output is JSON except help; login progress/code goes to stderr. --url overrides
|
|
|
1340
1387
|
function parseArgs(argv) {
|
|
1341
1388
|
const positional = [];
|
|
1342
1389
|
const flags = {};
|
|
1343
|
-
const booleans = /* @__PURE__ */ new Set(["help", "version", "offline", "dry-run", "key-stdin", "invite-stdin", "no-browser", "skip-mcp"]);
|
|
1344
|
-
const known = /* @__PURE__ */ new Set(["dir", "brand", "style", "project", "topic", "goal", "name", "adapter", "out", "url", "document", "mime", "label", "role", "limit", "cursor", "timeout", "assets", "capabilities", "duration", "agent", "workspace", "sha256", "kind", "channel", "epistemic-status", "event-offset", "relation-offset", ...booleans]);
|
|
1390
|
+
const booleans = /* @__PURE__ */ new Set(["help", "version", "offline", "dry-run", "key-stdin", "invite-stdin", "no-browser", "skip-mcp", "all"]);
|
|
1391
|
+
const known = /* @__PURE__ */ new Set(["dir", "brand", "style", "project", "topic", "goal", "name", "adapter", "out", "url", "document", "mime", "label", "role", "limit", "cursor", "timeout", "assets", "capabilities", "duration", "agent", "workspace", "prefix", "sha256", "kind", "channel", "epistemic-status", "event-offset", "relation-offset", ...booleans]);
|
|
1345
1392
|
for (let i = 0; i < argv.length; i++) {
|
|
1346
1393
|
const arg = argv[i];
|
|
1347
1394
|
if (!arg.startsWith("--")) {
|
|
@@ -1487,6 +1534,8 @@ Waiting for authorization. Press Ctrl+C to cancel.
|
|
|
1487
1534
|
async function main() {
|
|
1488
1535
|
const args = parseArgs(process.argv.slice(2));
|
|
1489
1536
|
const [command, subcommand, argument] = args.positional;
|
|
1537
|
+
if (args.flags.all && command !== "pull") throw new Error("--all is supported only by pull.");
|
|
1538
|
+
if (args.flags.all && str(args, "project")) throw new Error("Choose --project or --all, not both.");
|
|
1490
1539
|
if (command === "version" || args.flags.version) {
|
|
1491
1540
|
output({ name: "EditorBrain", version: await cliVersion() });
|
|
1492
1541
|
return;
|
|
@@ -1520,7 +1569,7 @@ async function main() {
|
|
|
1520
1569
|
return;
|
|
1521
1570
|
}
|
|
1522
1571
|
if (command === "setup") {
|
|
1523
|
-
const result = await setup({ directory: str(args, "dir") ?? "./mis-videos", agent: parseAgentTarget(str(args, "agent")), workspaceId: str(args, "workspace"), skipMcp: !!args.flags["skip-mcp"], noBrowser: !!args.flags["no-browser"], getConfig: () => config(args, false), login: () => login(args, true) });
|
|
1572
|
+
const result = await setup({ directory: str(args, "dir") ?? "./mis-videos", agent: parseAgentTarget(str(args, "agent")), workspaceId: str(args, "workspace"), projectId: str(args, "project"), skipMcp: !!args.flags["skip-mcp"], noBrowser: !!args.flags["no-browser"], getConfig: () => config(args, false), login: () => login(args, true) });
|
|
1524
1573
|
output(result);
|
|
1525
1574
|
if (result.status !== "ready") process.exitCode = 2;
|
|
1526
1575
|
return;
|
|
@@ -1603,6 +1652,24 @@ async function main() {
|
|
|
1603
1652
|
}
|
|
1604
1653
|
throw new Error("Use keys list, keys create --label name, or keys revoke <key-id>");
|
|
1605
1654
|
}
|
|
1655
|
+
if (command === "projects" && subcommand === "list") {
|
|
1656
|
+
output({ projects: await listProjects(new Client(await config(args))) });
|
|
1657
|
+
return;
|
|
1658
|
+
}
|
|
1659
|
+
if (command === "documents") {
|
|
1660
|
+
const client = new Client(await config(args));
|
|
1661
|
+
if (subcommand === "list") {
|
|
1662
|
+
output({ documents: await client.list(str(args, "prefix") ?? "") });
|
|
1663
|
+
return;
|
|
1664
|
+
}
|
|
1665
|
+
if (subcommand === "get" && argument) {
|
|
1666
|
+
const doc = await client.get(argument);
|
|
1667
|
+
if (await sha256(doc.content) !== doc.sha256) throw new Error("Document integrity failed");
|
|
1668
|
+
output(doc);
|
|
1669
|
+
return;
|
|
1670
|
+
}
|
|
1671
|
+
throw new Error("Use documents list --prefix path or documents get <path>.");
|
|
1672
|
+
}
|
|
1606
1673
|
if (command === "assets") {
|
|
1607
1674
|
const client = new Client(await config(args));
|
|
1608
1675
|
if (subcommand === "list") {
|
|
@@ -1717,7 +1784,7 @@ async function main() {
|
|
|
1717
1784
|
const run = async () => {
|
|
1718
1785
|
const files = await readWorkspace(root);
|
|
1719
1786
|
if (command === "pull") {
|
|
1720
|
-
output(await pullWorkspace(new Client(await config(args)), root, files, { dryRun: !!args.flags["dry-run"] }));
|
|
1787
|
+
output(await (args.flags.all ? pullWorkspace(new Client(await config(args)), root, files, { dryRun: !!args.flags["dry-run"] }) : pullProject(new Client(await config(args)), root, files, { projectId: str(args, "project"), dryRun: !!args.flags["dry-run"] })));
|
|
1721
1788
|
return;
|
|
1722
1789
|
}
|
|
1723
1790
|
if (Object.keys(files).length === 0) throw new Error("No portable documents found. Run init or pull first.");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Customer agent contract
|
|
2
2
|
|
|
3
|
-
Your responsibility is to turn
|
|
3
|
+
Your responsibility is to turn EditorBrain evidence into editorial decisions suitable for this project, execute the requested work, and check the result. Do not stop at matching a palette or exporting a plan when the user asked for an edited video.
|
|
4
4
|
|
|
5
5
|
## Understand the project
|
|
6
6
|
|
|
@@ -10,6 +10,16 @@ Your responsibility is to turn Harness evidence into editorial decisions suitabl
|
|
|
10
10
|
- Separate a faithful preset transfer, an adaptation of an editorial family, and inspiration from selected techniques. Infer the intended degree from the request and record it briefly. Preserve the distinctive features the user asked for; do not silently substitute a generic approximation.
|
|
11
11
|
- If evidence supports only part of a faithful transfer, name that part and the unverified details. A provisional plan can be useful, but missing media or final timing must remain explicit production dependencies rather than a claim that it is fully ready to execute.
|
|
12
12
|
|
|
13
|
+
## Selective access
|
|
14
|
+
|
|
15
|
+
Prepare only the active project's immediate context with `editorbrain pull --project <id>`.
|
|
16
|
+
Use `editorbrain projects list` to discover IDs; ask which project if the intent is ambiguous.
|
|
17
|
+
Default pull remembers the successful selection; never use `--all` during normal onboarding.
|
|
18
|
+
Reference-library documents/media remain remote and are retrieved to answer a concrete
|
|
19
|
+
editing question. Existing proposals or other project documents can be discovered through
|
|
20
|
+
`documents list --prefix videos/<id>/` and read with `documents get <path>` or MCP
|
|
21
|
+
`list_documents` / `read_document`. No need to enumerate or read the whole workspace.
|
|
22
|
+
|
|
13
23
|
## Read context and evidence
|
|
14
24
|
|
|
15
25
|
Read [the workspace file map](references/workspace-files.md) for the full structure, required/optional files and token precedence. Start with `videos/<project-id>/project.json` to discover the selected brand, style and output profile. Read their actual files plus the brief and direction; do not assume that all documents under the workspace are included automatically by `resolve`.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# CLI and MCP: read, retrieve, save
|
|
2
2
|
|
|
3
|
-
Use the connected MCP schema and `editorbrain help` to confirm the installed contract. Examples below describe the 0.
|
|
3
|
+
Use the connected MCP schema and `editorbrain help` to confirm the installed contract. Examples below describe the 0.7.0 customer workflow; older installations may lack the skill installer or reference-media tool. Do not invent commands from historical architecture documents.
|
|
4
4
|
|
|
5
5
|
API/MCP: `https://api.editorbrain.com/mcp`. The host must connect this server separately; installing a skill does not register or authorize MCP.
|
|
6
6
|
|
|
@@ -10,7 +10,8 @@ An existing account normally starts with:
|
|
|
10
10
|
|
|
11
11
|
```sh
|
|
12
12
|
editorbrain status
|
|
13
|
-
editorbrain
|
|
13
|
+
editorbrain projects list
|
|
14
|
+
editorbrain pull --project <id> --dir ./my-videos
|
|
14
15
|
editorbrain skill install --dir ./my-videos --agent both
|
|
15
16
|
```
|
|
16
17
|
|
|
@@ -20,13 +21,20 @@ Skill installation is offline and independent of login. It creates `.agents/skil
|
|
|
20
21
|
|
|
21
22
|
Existing root `AGENTS.md`/`CLAUDE.md` are preserved. Read the install result's instruction paths and conflict reports. A preserved root guide does not mean the native skill failed; a conflicting existing skill needs a deliberate merge, not an overwrite. Do not replace the user's custom instructions to finish setup.
|
|
22
23
|
|
|
23
|
-
For a separate local workspace, `editorbrain init --dir ./new-video --brand <id> --style <id> --project <id>` creates portable context and installs guidance. Do not initialize over a pulled workspace to reset it. `pull`
|
|
24
|
+
For a separate local workspace, `editorbrain init --dir ./new-video --brand <id> --style <id> --project <id>` creates portable context and installs guidance. Do not initialize over a pulled workspace to reset it. `pull` prepares selected project documents. `push` uploads changed documents already present in the local folder, not the entire remote workspace. Keep unrelated projects in separate local folders and inspect `push --dry-run` before uploading. Neither command synchronizes installed native skill folders or local render media.
|
|
25
|
+
|
|
26
|
+
`setup --project <id>` prepares only that project's immediate brand/style/output
|
|
27
|
+
context. With multiple projects and no remembered selection it reports
|
|
28
|
+
`PROJECT_SELECTION_REQUIRED`, without downloading their contents. Normal `pull`
|
|
29
|
+
remembers the selected project; full-space `pull --all` is an explicit advanced action.
|
|
30
|
+
Use `documents list --prefix videos/<id>/` and `documents get <path>` to consult
|
|
31
|
+
additional saved plans/reviews when needed. These reads do not materialize or sync files.
|
|
24
32
|
|
|
25
33
|
## Reading the project
|
|
26
34
|
|
|
27
35
|
MCP sequence:
|
|
28
36
|
|
|
29
|
-
1. `workspace_info({})`, `list_documents({})
|
|
37
|
+
1. `workspace_info({})`, then `list_documents({prefix:"videos/"})` to discover project IDs only when needed. Prefer a known project ID; use narrower brand/style/project prefixes for additional metadata. Never read every listed document by default.
|
|
30
38
|
2. `read_document({path})` for discovered project, brief, brand, style and optional media manifest.
|
|
31
39
|
3. `get_asset({assetId})` for production media IDs. Download the authorized asset for inspection; preserve its ID/hash, not its temporary URL.
|
|
32
40
|
4. `resolve_project({projectId})` to inspect effective tokens, exceptions, source hashes and pending requirements.
|
|
@@ -105,7 +113,7 @@ editorbrain push --dir ./my-videos
|
|
|
105
113
|
`push` is not a media upload. Use `assets upload <file>` for authorized production files within current limits and then record returned IDs/hashes. Keep large renders/evidence in the appropriate media store, with permanent identifiers in the project. Preserve old revisions and disclose unsynchronized local work if saving fails.
|
|
106
114
|
|
|
107
115
|
|
|
108
|
-
## Recovery and diagnostics (CLI 0.
|
|
116
|
+
## Recovery and diagnostics (CLI 0.7.0)
|
|
109
117
|
|
|
110
118
|
Use `editorbrain version` to discover the installed release. Run `editorbrain doctor --dir <folder> --agent codex|claude|both` before troubleshooting; `--offline` deliberately skips credentials and remote checks. Doctor is read-only. Its `mcpClient: not_verified` is not a claim that the host is connected. A customized skill is reported as a warning, not replaced automatically.
|
|
111
119
|
|
|
@@ -115,7 +123,7 @@ Errors include `code`, `hint`, `retryable` and the legacy text `error`. `AUTH_RE
|
|
|
115
123
|
|
|
116
124
|
GET requests use bounded retries and respect Retry-After. Writes are not retried automatically: after a network error, inspect remote state before repeating them. Do not treat `retryable` as permission for an unbounded loop. HTTP error bodies are not printed. The MCP now provides matching structuredContent and text; consume either without counting them as separate results. Writing a document is declared mutating and may update existing content even though revision history is preserved.
|
|
117
125
|
|
|
118
|
-
## Guided setup and OAuth (0.
|
|
126
|
+
## Guided setup and OAuth (0.7.0)
|
|
119
127
|
|
|
120
128
|
`editorbrain setup --dir ./mis-videos --agent codex --workspace <expected-id>` authorizes the CLI when necessary, pulls documents, installs the complete skill and registers an absent MCP connection. Use `claude` or `both` for other hosts. The expected ID is a check, never authorization. Setup preserves existing MCP entries and customized guidance. Exit 2 means action is still required; inspect each step instead of repeating blindly. `--no-browser` defers MCP registration; `--skip-mcp` prepares only the CLI/skill.
|
|
121
129
|
|