@easysociable/cli 0.1.8 → 0.1.9

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 (93) hide show
  1. package/README.md +29 -9
  2. package/dist/easysociable.js +111 -100
  3. package/dist/easysociable.js.map +1 -1
  4. package/package.json +2 -2
  5. package/scripts/engine/migrate-split-vault.py +75 -0
  6. package/skills/ONBOARDING.md +19 -4
  7. package/skills/easysociable-content/SKILL.md +25 -9
  8. package/content-engine/scripts/fixture.test.mjs +0 -24
  9. package/content-engine/scripts/starter.mjs +0 -58
  10. package/content-engine/scripts/validate.test.mjs +0 -101
  11. package/content-engine/scripts/workbench.mjs +0 -140
  12. package/content-engine/starter/CLAUDE.md +0 -908
  13. package/content-engine/starter/accounts/README.md +0 -9
  14. package/content-engine/starter/accounts/_index.md +0 -31
  15. package/content-engine/starter/accounts/overrides/README.md +0 -3
  16. package/content-engine/starter/engine.json +0 -23
  17. package/content-engine/starter/exports/README.md +0 -28
  18. package/content-engine/starter/hits/README.md +0 -5
  19. package/content-engine/starter/hits/_index.md +0 -30
  20. package/content-engine/starter/hits/_template.md +0 -46
  21. package/content-engine/starter/hits/entries/README.md +0 -3
  22. package/content-engine/starter/inbox/README.md +0 -9
  23. package/content-engine/starter/inbox/_index.md +0 -24
  24. package/content-engine/starter/inbox/entries/README.md +0 -3
  25. package/content-engine/starter/needs/README.md +0 -38
  26. package/content-engine/starter/needs/_index.md +0 -31
  27. package/content-engine/starter/needs/_template.md +0 -24
  28. package/content-engine/starter/needs/entries/README.md +0 -5
  29. package/content-engine/starter/platforms/README.md +0 -19
  30. package/content-engine/starter/platforms/ig.md +0 -69
  31. package/content-engine/starter/platforms/instagram.md +0 -5
  32. package/content-engine/starter/platforms/linkedin.md +0 -72
  33. package/content-engine/starter/platforms/threads.md +0 -69
  34. package/content-engine/starter/platforms/tiktok.md +0 -71
  35. package/content-engine/starter/platforms/x.md +0 -78
  36. package/content-engine/starter/platforms/xiaohongshu.md +0 -19
  37. package/content-engine/starter/products/README.md +0 -38
  38. package/content-engine/starter/products/_index.md +0 -25
  39. package/content-engine/starter/products/_template.md +0 -56
  40. package/content-engine/starter/products/entries/P-001-easysociable.md +0 -150
  41. package/content-engine/starter/products/entries/P-002-content-engine.md +0 -136
  42. package/content-engine/starter/profiles/_index.md +0 -16
  43. package/content-engine/starter/profiles/default/README.md +0 -17
  44. package/content-engine/starter/profiles/default/audience.md +0 -38
  45. package/content-engine/starter/profiles/default/boundaries.md +0 -14
  46. package/content-engine/starter/profiles/default/expression.md +0 -18
  47. package/content-engine/starter/profiles/default/handles.md +0 -8
  48. package/content-engine/starter/profiles/default/keywords.md +0 -31
  49. package/content-engine/starter/profiles/default/lanes.md +0 -12
  50. package/content-engine/starter/profiles/default/pillars.md +0 -33
  51. package/content-engine/starter/profiles/default/stories.md +0 -15
  52. package/content-engine/starter/profiles/default/voice.md +0 -38
  53. package/content-engine/starter/published/_index.md +0 -20
  54. package/content-engine/starter/raw/README.md +0 -4
  55. package/content-engine/starter/raw/_index.md +0 -12
  56. package/content-engine/starter/recommendations/README.md +0 -21
  57. package/content-engine/starter/recommendations/_index.md +0 -21
  58. package/content-engine/starter/recommendations/_template.md +0 -44
  59. package/content-engine/starter/runs/README.md +0 -22
  60. package/content-engine/starter/runs/_index.md +0 -41
  61. package/content-engine/starter/runs/_template/brief.md +0 -76
  62. package/content-engine/starter/runs/_template/draft.md +0 -11
  63. package/content-engine/starter/runs/_template/editor.md +0 -18
  64. package/content-engine/starter/runs/_template/feedback.md +0 -15
  65. package/content-engine/starter/runs/_template/idea.md +0 -27
  66. package/content-engine/starter/runs/_template/pack.md +0 -55
  67. package/content-engine/starter/runs/_template/packet.md +0 -31
  68. package/content-engine/starter/runs/_template/rubric.md +0 -19
  69. package/content-engine/starter/swipe/_index.md +0 -27
  70. package/content-engine/starter/swipe/_template.md +0 -36
  71. package/content-engine/starter/topics/README.md +0 -21
  72. package/content-engine/starter/topics/_index.md +0 -51
  73. package/content-engine/starter/topics/_template-item.md +0 -106
  74. package/content-engine/starter/topics/items/README.md +0 -4
  75. package/content-engine/starter/topics/scoring.md +0 -255
  76. package/content-engine/starter/wiki/README.md +0 -13
  77. package/content-engine/starter/wiki/atoms/_index.md +0 -23
  78. package/content-engine/starter/wiki/atoms/_template.md +0 -31
  79. package/content-engine/starter/wiki/claims/_index.md +0 -26
  80. package/content-engine/starter/wiki/claims/_template.md +0 -38
  81. package/content-engine/starter/workbench/README.md +0 -17
  82. package/content-engine/workbench/__pycache__/build.cpython-310.pyc +0 -0
  83. package/content-engine/workbench/build.py +0 -1467
  84. package/content-engine/workbench/serve.py +0 -53
  85. package/content-engine/workbench/template.html +0 -2683
  86. /package/{content-engine/scripts → scripts/engine}/contracts.mjs +0 -0
  87. /package/{content-engine/scripts → scripts/engine}/export-contracts.mjs +0 -0
  88. /package/{content-engine → scripts/engine}/fixtures/golden-run-en/draft.md +0 -0
  89. /package/{content-engine → scripts/engine}/fixtures/golden-run-en/idea.md +0 -0
  90. /package/{content-engine → scripts/engine}/fixtures/golden-run-en/pack-tiktok.json +0 -0
  91. /package/{content-engine/scripts → scripts/engine}/validate-export.mjs +0 -0
  92. /package/{content-engine/scripts → scripts/engine}/validate-pack.mjs +0 -0
  93. /package/{content-engine/scripts → scripts/engine}/validate-run.mjs +0 -0
package/README.md CHANGED
@@ -21,27 +21,47 @@ Default install does **not** include `easysociable-content`. Add it when you wan
21
21
 
22
22
  ```bash
23
23
  easysociable install --skills content,template,slideshow --yes
24
- easysociable engine init --root /workspace --yes
25
- easysociable engine serve --engine /workspace/Content Engine --no-open
26
24
  ```
27
25
 
28
- Then tell the Agent to follow `Content Engine/CLAUDE.md`. The starter includes
29
- separate `products/` (owned offers) and `recommendations/` (third-party review
30
- subjects) libraries; neither is a substitute for `needs/`.
26
+ **Preferred:** clone the public core:
27
+
28
+ ```bash
29
+ git clone https://github.com/murphywuwu/content-engine.git
30
+ cd content-engine
31
+ python3 workbench/build.py
32
+ python3 workbench/serve.py --no-open
33
+ ```
34
+
35
+ See https://github.com/murphywuwu/content-engine (LAYOUT.md, migrate-split-vault.py).
36
+
37
+ **Optional:** `easysociable engine init --root … --yes` clones that repo into
38
+ `Content Engine/` (never overwrites an existing engine).
39
+
40
+ Then tell the Agent to follow `CLAUDE.md`. The core includes separate
41
+ `products/` (owned offers) and `recommendations/` (third-party review subjects);
42
+ neither is a substitute for `needs/`.
31
43
 
32
44
  Lazy-auth guidance lives in the package at `skills/ONBOARDING.md`.
33
45
 
34
46
  ## Content Engine
35
47
 
48
+ Core methodology lives in the **public GitHub repo**, not inside this CLI
49
+ tarball. `engine serve` / `build` do **not** overwrite local workbench files
50
+ unless you pass `--sync-workbench` (pulls from the public core checkout /
51
+ `CONTENT_ENGINE_CORE`).
52
+
36
53
  | Command | Purpose |
37
54
  |---------|---------|
38
- | `easysociable engine init [--root] [--yes] [--serve]` | Scaffold starter (never overwrites) |
39
- | `easysociable engine serve [--engine] [--no-open]` | Sync workbench runtime from CLI, rebuild, start on `:8765` |
40
- | `easysociable engine build [--engine]` | Sync workbench runtime from CLI, then rebuild `workbench/index.html` |
55
+ | `easysociable engine init [--root] [--yes] [--serve]` | Clone public core into `Content Engine/` (no overwrite) |
56
+ | `easysociable engine serve [--engine] [--no-open] [--sync-workbench]` | Rebuild + start on `:8765` |
57
+ | `easysociable engine build [--engine] [--sync-workbench]` | Rebuild `workbench/index.html` |
41
58
  | `easysociable engine detect` | Find engines under search roots |
42
- | `easysociable engine validate --root …` | Starter completeness |
59
+ | `easysociable engine validate --root …` | Core marker completeness |
43
60
  | `easysociable engine validate --run …` | Run / pack checks before render |
44
61
 
62
+ Optional `engine.json` field `vault_root` (default `"."`) points data at a
63
+ separate vault; see the public LAYOUT.md.
64
+
45
65
  The Workbench exposes Products and Recommendations as separate read-only
46
66
  indexes. Topics can cite either one without forcing a product connection.
47
67
 
@@ -32639,12 +32639,13 @@ function resolveDefaultSkillsSource(moduleUrl) {
32639
32639
  import { spawn, spawnSync } from "child_process";
32640
32640
  import {
32641
32641
  copyFileSync,
32642
+ cpSync,
32642
32643
  existsSync as existsSync3,
32643
32644
  mkdirSync as mkdirSync2,
32644
32645
  readdirSync,
32645
32646
  readFileSync as readFileSync2,
32646
- statSync,
32647
- writeFileSync as writeFileSync2
32647
+ rmSync,
32648
+ statSync
32648
32649
  } from "fs";
32649
32650
  import net from "net";
32650
32651
  import { homedir as homedir2 } from "os";
@@ -32652,29 +32653,61 @@ import { basename, dirname as dirname3, join as join3, resolve } from "path";
32652
32653
  var ENGINE_ROOT_NAME = "Content Engine";
32653
32654
  var WORKBENCH_PORT = 8765;
32654
32655
  var WORKBENCH_URL = `http://127.0.0.1:${WORKBENCH_PORT}/workbench/`;
32655
- var WORKBENCH_FILES = ["template.html", "build.py", "serve.py"];
32656
- function contentEnginePackageRoot(moduleUrl) {
32657
- const pkg = cliPackageRoot(moduleUrl);
32658
- const bundled = join3(pkg, "content-engine");
32659
- if (existsSync3(join3(bundled, "starter")))
32660
- return bundled;
32661
- const sibling = join3(pkg, "content-engine");
32662
- if (existsSync3(join3(sibling, "starter")))
32663
- return sibling;
32664
- return bundled;
32665
- }
32666
- function starterDir(moduleUrl) {
32667
- return join3(contentEnginePackageRoot(moduleUrl), "starter");
32668
- }
32669
- function workbenchSrc(moduleUrl) {
32670
- return join3(contentEnginePackageRoot(moduleUrl), "workbench");
32671
- }
32672
- function scriptsDir(moduleUrl) {
32673
- return join3(contentEnginePackageRoot(moduleUrl), "scripts");
32674
- }
32656
+ var CONTENT_ENGINE_REPO = "https://github.com/murphywuwu/content-engine.git";
32657
+ var WORKBENCH_FILES = ["template.html", "build.py", "serve.py", "roots.py"];
32658
+ var REQUIRED_RELATIVE = [
32659
+ "CLAUDE.md",
32660
+ "engine.json",
32661
+ "LAYOUT.md",
32662
+ "workbench/build.py",
32663
+ "workbench/serve.py",
32664
+ "workbench/template.html",
32665
+ "workbench/roots.py",
32666
+ "needs/_index.md",
32667
+ "topics/_index.md",
32668
+ "runs/_index.md",
32669
+ "profiles/_index.md"
32670
+ ];
32675
32671
  function isDir(path) {
32676
32672
  return existsSync3(path) && statSync(path).isDirectory();
32677
32673
  }
32674
+ function scriptsDir(moduleUrl) {
32675
+ return join3(cliPackageRoot(moduleUrl), "scripts", "engine");
32676
+ }
32677
+ function contentEngineRepoUrl() {
32678
+ return (process.env.CONTENT_ENGINE_REPO || CONTENT_ENGINE_REPO).trim() || CONTENT_ENGINE_REPO;
32679
+ }
32680
+ function ensureCoreCheckout({ update = false } = {}) {
32681
+ const local = (process.env.CONTENT_ENGINE_CORE || "").trim();
32682
+ if (local) {
32683
+ const abs = resolve(local);
32684
+ if (!existsSync3(join3(abs, "CLAUDE.md")))
32685
+ throw new Error(`CONTENT_ENGINE_CORE=${abs} is missing CLAUDE.md`);
32686
+ return abs;
32687
+ }
32688
+ const cache = join3(homedir2(), ".cache", "easysociable", "content-engine");
32689
+ const marker = join3(cache, "CLAUDE.md");
32690
+ if (existsSync3(marker) && !update)
32691
+ return cache;
32692
+ mkdirSync2(dirname3(cache), { recursive: true });
32693
+ if (existsSync3(marker) && update) {
32694
+ const pull = spawnSync("git", ["-C", cache, "pull", "--ff-only"], { encoding: "utf8" });
32695
+ if (pull.status !== 0) {
32696
+ rmSync(cache, { recursive: true, force: true });
32697
+ } else {
32698
+ return cache;
32699
+ }
32700
+ }
32701
+ if (existsSync3(cache))
32702
+ rmSync(cache, { recursive: true, force: true });
32703
+ const clone3 = spawnSync("git", ["clone", "--depth", "1", contentEngineRepoUrl(), cache], { encoding: "utf8" });
32704
+ if (clone3.status !== 0 || !existsSync3(marker)) {
32705
+ throw new Error(
32706
+ `Failed to clone Content Engine core from ${contentEngineRepoUrl()}. ${clone3.stderr || clone3.stdout || ""}`.trim()
32707
+ );
32708
+ }
32709
+ return cache;
32710
+ }
32678
32711
  function isEngine(path) {
32679
32712
  return isDir(path) && existsSync3(join3(path, "CLAUDE.md")) && isDir(join3(path, "runs"));
32680
32713
  }
@@ -32684,62 +32717,10 @@ function resolveEngine(rootOrEngine) {
32684
32717
  return absolute;
32685
32718
  return join3(absolute, ENGINE_ROOT_NAME);
32686
32719
  }
32687
- function walkFiles(dir) {
32688
- const out = [];
32689
- if (!existsSync3(dir))
32690
- return out;
32691
- for (const name of readdirSync(dir)) {
32692
- if (name === ".DS_Store")
32693
- continue;
32694
- const path = join3(dir, name);
32695
- if (statSync(path).isDirectory())
32696
- out.push(...walkFiles(path));
32697
- else
32698
- out.push(path);
32699
- }
32700
- return out;
32701
- }
32702
- function engineJson() {
32703
- return `${JSON.stringify({
32704
- schemaVersion: "content-engine.v1",
32705
- locale: "en",
32706
- root: ENGINE_ROOT_NAME,
32707
- paths: {
32708
- runs: "runs",
32709
- exports: "exports",
32710
- topics: "topics",
32711
- needs: "needs",
32712
- hits: "hits",
32713
- products: "products",
32714
- recommendations: "recommendations",
32715
- swipe: "swipe",
32716
- profiles: "profiles",
32717
- inbox: "inbox",
32718
- accounts: "accounts",
32719
- raw: "raw",
32720
- published: "published",
32721
- wiki: "wiki",
32722
- platforms: "platforms",
32723
- workbench: "workbench"
32724
- }
32725
- }, null, 2)}
32726
- `;
32727
- }
32728
- function starterFiles(moduleUrl) {
32729
- const root = starterDir(moduleUrl);
32730
- const files = {};
32731
- for (const path of walkFiles(root)) {
32732
- const relativePath = path.slice(root.length + 1).split("\\").join("/");
32733
- files[relativePath] = readFileSync2(path, "utf8");
32734
- }
32735
- files["engine.json"] = engineJson();
32736
- return files;
32737
- }
32738
- function inventory(engine, moduleUrl) {
32739
- const files = starterFiles(moduleUrl);
32720
+ function inventory(engine, _moduleUrl) {
32740
32721
  const missing = [];
32741
32722
  const existing = [];
32742
- for (const relative of Object.keys(files)) {
32723
+ for (const relative of REQUIRED_RELATIVE) {
32743
32724
  const path = join3(engine, relative);
32744
32725
  (existsSync3(path) ? existing : missing).push(relative);
32745
32726
  }
@@ -32747,6 +32728,7 @@ function inventory(engine, moduleUrl) {
32747
32728
  engine,
32748
32729
  engineExists: isDir(engine),
32749
32730
  rootName: ENGINE_ROOT_NAME,
32731
+ repo: contentEngineRepoUrl(),
32750
32732
  missing,
32751
32733
  existing,
32752
32734
  starterComplete: missing.length === 0
@@ -32855,7 +32837,7 @@ function validateEngine(rootOrEngine, moduleUrl, { strict = false, requireKnowle
32855
32837
  if (!result.engineExists)
32856
32838
  errors.push("Content Engine/ folder does not exist");
32857
32839
  else if (result.missing.length > 0)
32858
- errors.push(`Starter incomplete: ${result.missing.length} file(s) missing (use engine init --yes to add only missing files)`);
32840
+ errors.push(`Engine incomplete: ${result.missing.length} file(s) missing (clone ${contentEngineRepoUrl()} or run engine init --yes)`);
32859
32841
  }
32860
32842
  const knowledge = requireKnowledge ? checkKnowledge(engine) : void 0;
32861
32843
  if (knowledge && !knowledge.valid)
@@ -32866,20 +32848,31 @@ function python3Bin() {
32866
32848
  const result = spawnSync("python3", ["--version"], { encoding: "utf8" });
32867
32849
  return result.status === 0 ? "python3" : "";
32868
32850
  }
32869
- function ensureWorkbench(engine, moduleUrl, { update = false } = {}) {
32851
+ function wantWorkbenchSync(explicit) {
32852
+ if (explicit === true)
32853
+ return true;
32854
+ if (explicit === false)
32855
+ return false;
32856
+ const env = (process.env.EASYSOCIABLE_SYNC_WORKBENCH || "").trim().toLowerCase();
32857
+ return env === "1" || env === "true" || env === "yes";
32858
+ }
32859
+ function ensureWorkbench(engine, _moduleUrl, { update = false } = {}) {
32870
32860
  const destDir = join3(engine, "workbench");
32871
32861
  mkdirSync2(destDir, { recursive: true });
32872
32862
  const copied = [];
32873
32863
  const updated = [];
32874
32864
  const skipped = [];
32875
- const src = workbenchSrc(moduleUrl);
32865
+ const needFetch = update || WORKBENCH_FILES.some((name) => !existsSync3(join3(destDir, name)));
32866
+ const srcRoot = needFetch ? join3(ensureCoreCheckout({ update }), "workbench") : "";
32876
32867
  for (const name of WORKBENCH_FILES) {
32877
32868
  const dest = join3(destDir, name);
32878
- const from = join3(src, name);
32879
32869
  if (existsSync3(dest) && !update) {
32880
32870
  skipped.push(name);
32881
32871
  continue;
32882
32872
  }
32873
+ const from = join3(srcRoot, name);
32874
+ if (!existsSync3(from))
32875
+ throw new Error(`Missing ${name} in Content Engine core checkout`);
32883
32876
  const existed = existsSync3(dest);
32884
32877
  copyFileSync(from, dest);
32885
32878
  if (existed)
@@ -32895,7 +32888,7 @@ function buildWorkbench(engine) {
32895
32888
  return { ok: false, code: "PYTHON3_REQUIRED", message: "python3 is required for the workbench. Install Python 3 and retry." };
32896
32889
  const script = join3(engine, "workbench", "build.py");
32897
32890
  if (!existsSync3(script))
32898
- return { ok: false, code: "WORKBENCH_MISSING", message: "workbench/build.py is missing. Re-run easysociable engine init --yes." };
32891
+ return { ok: false, code: "WORKBENCH_MISSING", message: `workbench/build.py is missing. Clone ${contentEngineRepoUrl()} or run engine init --yes.` };
32899
32892
  const result = spawnSync(python, [script], { encoding: "utf8", cwd: engine });
32900
32893
  if (result.status !== 0)
32901
32894
  return { ok: false, code: "WORKBENCH_BUILD_FAILED", message: result.stderr.trim() || result.stdout.trim() || "workbench build failed" };
@@ -32915,11 +32908,15 @@ function openUrl(url2) {
32915
32908
  const args = process.platform === "win32" ? ["/c", "start", url2] : [url2];
32916
32909
  spawn(command, args, { stdio: "ignore", detached: true }).unref();
32917
32910
  }
32918
- async function startWorkbench(engine, moduleUrl, { noOpen = false } = {}) {
32911
+ async function startWorkbench(engine, moduleUrl, {
32912
+ noOpen = false,
32913
+ syncWorkbench = false
32914
+ } = {}) {
32919
32915
  const python = python3Bin();
32920
32916
  if (!python)
32921
32917
  return { ok: false, code: "PYTHON3_REQUIRED", message: "python3 is required for the workbench. Install Python 3 and retry." };
32922
- ensureWorkbench(engine, moduleUrl, { update: true });
32918
+ const sync = wantWorkbenchSync(syncWorkbench || void 0);
32919
+ ensureWorkbench(engine, moduleUrl, { update: sync });
32923
32920
  const built = buildWorkbench(engine);
32924
32921
  if (!built.ok)
32925
32922
  return built;
@@ -32947,7 +32944,7 @@ async function initEngine(rootOrEngine, moduleUrl, {
32947
32944
  return {
32948
32945
  ...preview,
32949
32946
  action: "preview",
32950
- next: "Review missing files and re-run with --yes"
32947
+ next: isEngine(engine) ? "Engine already present; nothing to do" : `Re-run with --yes to git clone ${contentEngineRepoUrl()} into this path (or clone manually)`
32951
32948
  };
32952
32949
  }
32953
32950
  const parent = resolve(engine, "..");
@@ -32961,15 +32958,24 @@ async function initEngine(rootOrEngine, moduleUrl, {
32961
32958
  created: []
32962
32959
  };
32963
32960
  }
32964
- mkdirSync2(engine, { recursive: true });
32965
- const report = inventory(engine, moduleUrl);
32966
32961
  const created = [];
32967
- const files = starterFiles(moduleUrl);
32968
- for (const relative of report.missing) {
32969
- const path = join3(engine, relative);
32970
- mkdirSync2(dirname3(path), { recursive: true });
32971
- writeFileSync2(path, files[relative], { encoding: "utf8", flag: "wx" });
32972
- created.push(relative);
32962
+ if (isEngine(engine)) {
32963
+ } else if (existsSync3(engine) && readdirSync(engine).filter((n) => n !== ".DS_Store").length > 0) {
32964
+ return {
32965
+ ...validateEngine(engine, moduleUrl),
32966
+ created,
32967
+ action: "applied",
32968
+ valid: false,
32969
+ code: "ENGINE_DIR_DIRTY",
32970
+ errors: [`${engine} exists but is not a Content Engine. Remove it or clone ${contentEngineRepoUrl()} into an empty path.`]
32971
+ };
32972
+ } else {
32973
+ const core = ensureCoreCheckout();
32974
+ if (existsSync3(engine))
32975
+ rmSync(engine, { recursive: true, force: true });
32976
+ cpSync(core, engine, { recursive: true });
32977
+ rmSync(join3(engine, ".git"), { recursive: true, force: true });
32978
+ created.push("CLAUDE.md", "engine.json", "workbench/", "(cloned public core)");
32973
32979
  }
32974
32980
  const workbench = ensureWorkbench(engine, moduleUrl);
32975
32981
  const python = python3Bin();
@@ -32981,7 +32987,7 @@ async function initEngine(rootOrEngine, moduleUrl, {
32981
32987
  action: "applied",
32982
32988
  valid: false,
32983
32989
  code: "PYTHON3_REQUIRED",
32984
- errors: ["python3 is required for the workbench. Install Python 3 and retry. Starter files were written; workbench was not started."]
32990
+ errors: ["python3 is required for the workbench. Install Python 3 and retry."]
32985
32991
  };
32986
32992
  }
32987
32993
  const built = buildWorkbench(engine);
@@ -32992,7 +32998,8 @@ async function initEngine(rootOrEngine, moduleUrl, {
32992
32998
  workbench,
32993
32999
  workbenchBuild: built,
32994
33000
  workbenchStart: started,
32995
- action: "applied"
33001
+ action: "applied",
33002
+ source: contentEngineRepoUrl()
32996
33003
  };
32997
33004
  }
32998
33005
  function detectEngines() {
@@ -33230,7 +33237,7 @@ function buildV1Cli() {
33230
33237
  const command = args.at(-1);
33231
33238
  print({ action: "detect", engines: detectEngines() }, commandWantsJson(command));
33232
33239
  });
33233
- jsonOption(engine.command("init").description("Scaffold Content Engine starter files (never overwrites existing)").option("--root <path>", "Parent directory or Content Engine path", defaultEngineRoot()).option("--yes", "Write missing starter files").option("--serve", "Also start the workbench server").option("--no-open", "Do not open a browser when starting the workbench")).action(async (...args) => {
33240
+ jsonOption(engine.command("init").description("Clone public Content Engine core into Content Engine/ (never overwrites an existing engine). Prefer: git clone https://github.com/murphywuwu/content-engine.git").option("--root <path>", "Parent directory or Content Engine path", defaultEngineRoot()).option("--yes", "Clone core into place").option("--serve", "Also start the workbench server").option("--no-open", "Do not open a browser when starting the workbench")).action(async (...args) => {
33234
33241
  const command = args.at(-1);
33235
33242
  const opts = command.opts();
33236
33243
  const result = await initEngine(String(opts.root), CLI_MODULE_URL, {
@@ -33280,22 +33287,26 @@ function buildV1Cli() {
33280
33287
  if (!report.valid)
33281
33288
  throw new Error(report.errors[0] ?? "engine validate failed");
33282
33289
  });
33283
- jsonOption(engine.command("serve").description("Sync workbench runtime from CLI, rebuild, start on :8765").option("--engine <path>", "Content Engine path or parent", defaultEngineRoot()).option("--no-open", "Do not open a browser")).action(async (...args) => {
33290
+ jsonOption(engine.command("serve").description("Rebuild workbench and start on :8765 (does not overwrite local workbench unless --sync-workbench)").option("--engine <path>", "Content Engine path or parent", defaultEngineRoot()).option("--no-open", "Do not open a browser").option("--sync-workbench", "Refresh workbench runtime files from the CLI package")).action(async (...args) => {
33284
33291
  const command = args.at(-1);
33285
33292
  const opts = command.opts();
33286
33293
  const enginePath = resolveEngine(String(opts.engine));
33287
- const started = await startWorkbench(enginePath, CLI_MODULE_URL, { noOpen: opts.noOpen === true });
33294
+ const started = await startWorkbench(enginePath, CLI_MODULE_URL, {
33295
+ noOpen: opts.noOpen === true,
33296
+ syncWorkbench: opts.syncWorkbench === true
33297
+ });
33288
33298
  print({ action: "serve", engine: enginePath, ...started }, commandWantsJson(command));
33289
33299
  if (!started.ok)
33290
33300
  throw new Error("message" in started ? String(started.message) : "engine serve failed");
33291
33301
  });
33292
- jsonOption(engine.command("build").description("Sync workbench runtime from CLI, then rebuild workbench/index.html").option("--engine <path>", "Content Engine path or parent", defaultEngineRoot())).action((...args) => {
33302
+ jsonOption(engine.command("build").description("Rebuild workbench/index.html (does not overwrite local workbench unless --sync-workbench)").option("--engine <path>", "Content Engine path or parent", defaultEngineRoot()).option("--sync-workbench", "Refresh workbench runtime files from the CLI package")).action((...args) => {
33293
33303
  const command = args.at(-1);
33294
33304
  const opts = command.opts();
33295
33305
  const enginePath = resolveEngine(String(opts.engine));
33296
- const workbench = ensureWorkbench(enginePath, CLI_MODULE_URL, { update: true });
33306
+ const sync = wantWorkbenchSync(opts.syncWorkbench === true ? true : void 0);
33307
+ const workbench = ensureWorkbench(enginePath, CLI_MODULE_URL, { update: sync });
33297
33308
  const built = buildWorkbench(enginePath);
33298
- print({ action: "build", engine: enginePath, workbench, ...built }, commandWantsJson(command));
33309
+ print({ action: "build", engine: enginePath, workbench, syncWorkbench: sync, ...built }, commandWantsJson(command));
33299
33310
  if (!built.ok)
33300
33311
  throw new Error(built.message);
33301
33312
  });