@easysociable/cli 0.1.8 → 0.1.10
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 +30 -9
- package/dist/easysociable.js +234 -98
- package/dist/easysociable.js.map +1 -1
- package/package.json +2 -2
- package/scripts/engine/migrate-split-vault.py +75 -0
- package/skills/ONBOARDING.md +19 -4
- package/skills/easysociable-content/SKILL.md +25 -9
- package/content-engine/scripts/fixture.test.mjs +0 -24
- package/content-engine/scripts/starter.mjs +0 -58
- package/content-engine/scripts/validate.test.mjs +0 -101
- package/content-engine/scripts/workbench.mjs +0 -140
- package/content-engine/starter/CLAUDE.md +0 -908
- package/content-engine/starter/accounts/README.md +0 -9
- package/content-engine/starter/accounts/_index.md +0 -31
- package/content-engine/starter/accounts/overrides/README.md +0 -3
- package/content-engine/starter/engine.json +0 -23
- package/content-engine/starter/exports/README.md +0 -28
- package/content-engine/starter/hits/README.md +0 -5
- package/content-engine/starter/hits/_index.md +0 -30
- package/content-engine/starter/hits/_template.md +0 -46
- package/content-engine/starter/hits/entries/README.md +0 -3
- package/content-engine/starter/inbox/README.md +0 -9
- package/content-engine/starter/inbox/_index.md +0 -24
- package/content-engine/starter/inbox/entries/README.md +0 -3
- package/content-engine/starter/needs/README.md +0 -38
- package/content-engine/starter/needs/_index.md +0 -31
- package/content-engine/starter/needs/_template.md +0 -24
- package/content-engine/starter/needs/entries/README.md +0 -5
- package/content-engine/starter/platforms/README.md +0 -19
- package/content-engine/starter/platforms/ig.md +0 -69
- package/content-engine/starter/platforms/instagram.md +0 -5
- package/content-engine/starter/platforms/linkedin.md +0 -72
- package/content-engine/starter/platforms/threads.md +0 -69
- package/content-engine/starter/platforms/tiktok.md +0 -71
- package/content-engine/starter/platforms/x.md +0 -78
- package/content-engine/starter/platforms/xiaohongshu.md +0 -19
- package/content-engine/starter/products/README.md +0 -38
- package/content-engine/starter/products/_index.md +0 -25
- package/content-engine/starter/products/_template.md +0 -56
- package/content-engine/starter/products/entries/P-001-easysociable.md +0 -150
- package/content-engine/starter/products/entries/P-002-content-engine.md +0 -136
- package/content-engine/starter/profiles/_index.md +0 -16
- package/content-engine/starter/profiles/default/README.md +0 -17
- package/content-engine/starter/profiles/default/audience.md +0 -38
- package/content-engine/starter/profiles/default/boundaries.md +0 -14
- package/content-engine/starter/profiles/default/expression.md +0 -18
- package/content-engine/starter/profiles/default/handles.md +0 -8
- package/content-engine/starter/profiles/default/keywords.md +0 -31
- package/content-engine/starter/profiles/default/lanes.md +0 -12
- package/content-engine/starter/profiles/default/pillars.md +0 -33
- package/content-engine/starter/profiles/default/stories.md +0 -15
- package/content-engine/starter/profiles/default/voice.md +0 -38
- package/content-engine/starter/published/_index.md +0 -20
- package/content-engine/starter/raw/README.md +0 -4
- package/content-engine/starter/raw/_index.md +0 -12
- package/content-engine/starter/recommendations/README.md +0 -21
- package/content-engine/starter/recommendations/_index.md +0 -21
- package/content-engine/starter/recommendations/_template.md +0 -44
- package/content-engine/starter/runs/README.md +0 -22
- package/content-engine/starter/runs/_index.md +0 -41
- package/content-engine/starter/runs/_template/brief.md +0 -76
- package/content-engine/starter/runs/_template/draft.md +0 -11
- package/content-engine/starter/runs/_template/editor.md +0 -18
- package/content-engine/starter/runs/_template/feedback.md +0 -15
- package/content-engine/starter/runs/_template/idea.md +0 -27
- package/content-engine/starter/runs/_template/pack.md +0 -55
- package/content-engine/starter/runs/_template/packet.md +0 -31
- package/content-engine/starter/runs/_template/rubric.md +0 -19
- package/content-engine/starter/swipe/_index.md +0 -27
- package/content-engine/starter/swipe/_template.md +0 -36
- package/content-engine/starter/topics/README.md +0 -21
- package/content-engine/starter/topics/_index.md +0 -51
- package/content-engine/starter/topics/_template-item.md +0 -106
- package/content-engine/starter/topics/items/README.md +0 -4
- package/content-engine/starter/topics/scoring.md +0 -255
- package/content-engine/starter/wiki/README.md +0 -13
- package/content-engine/starter/wiki/atoms/_index.md +0 -23
- package/content-engine/starter/wiki/atoms/_template.md +0 -31
- package/content-engine/starter/wiki/claims/_index.md +0 -26
- package/content-engine/starter/wiki/claims/_template.md +0 -38
- package/content-engine/starter/workbench/README.md +0 -17
- package/content-engine/workbench/__pycache__/build.cpython-310.pyc +0 -0
- package/content-engine/workbench/build.py +0 -1467
- package/content-engine/workbench/serve.py +0 -53
- package/content-engine/workbench/template.html +0 -2683
- /package/{content-engine/scripts → scripts/engine}/contracts.mjs +0 -0
- /package/{content-engine/scripts → scripts/engine}/export-contracts.mjs +0 -0
- /package/{content-engine → scripts/engine}/fixtures/golden-run-en/draft.md +0 -0
- /package/{content-engine → scripts/engine}/fixtures/golden-run-en/idea.md +0 -0
- /package/{content-engine → scripts/engine}/fixtures/golden-run-en/pack-tiktok.json +0 -0
- /package/{content-engine/scripts → scripts/engine}/validate-export.mjs +0 -0
- /package/{content-engine/scripts → scripts/engine}/validate-pack.mjs +0 -0
- /package/{content-engine/scripts → scripts/engine}/validate-run.mjs +0 -0
package/README.md
CHANGED
|
@@ -21,27 +21,48 @@ 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
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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]` |
|
|
39
|
-
| `easysociable engine
|
|
40
|
-
| `easysociable engine
|
|
55
|
+
| `easysociable engine init [--root] [--yes] [--serve]` | Clone public core into `Content Engine/` (no overwrite) |
|
|
56
|
+
| `easysociable engine update [--engine] [--yes]` | Sync core rules from the public template; preview first, apply with `--yes`. Never touches vault data |
|
|
57
|
+
| `easysociable engine serve [--engine] [--no-open] [--sync-workbench]` | Rebuild + start on `:8765` |
|
|
58
|
+
| `easysociable engine build [--engine] [--sync-workbench]` | Rebuild `workbench/index.html` |
|
|
41
59
|
| `easysociable engine detect` | Find engines under search roots |
|
|
42
|
-
| `easysociable engine validate --root …` |
|
|
60
|
+
| `easysociable engine validate --root …` | Core marker completeness |
|
|
43
61
|
| `easysociable engine validate --run …` | Run / pack checks before render |
|
|
44
62
|
|
|
63
|
+
Optional `engine.json` field `vault_root` (default `"."`) points data at a
|
|
64
|
+
separate vault; see the public LAYOUT.md.
|
|
65
|
+
|
|
45
66
|
The Workbench exposes Products and Recommendations as separate read-only
|
|
46
67
|
indexes. Topics can cite either one without forcing a product connection.
|
|
47
68
|
|
package/dist/easysociable.js
CHANGED
|
@@ -32639,10 +32639,12 @@ 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,
|
|
32647
|
+
rmSync,
|
|
32646
32648
|
statSync,
|
|
32647
32649
|
writeFileSync as writeFileSync2
|
|
32648
32650
|
} from "fs";
|
|
@@ -32652,29 +32654,61 @@ import { basename, dirname as dirname3, join as join3, resolve } from "path";
|
|
|
32652
32654
|
var ENGINE_ROOT_NAME = "Content Engine";
|
|
32653
32655
|
var WORKBENCH_PORT = 8765;
|
|
32654
32656
|
var WORKBENCH_URL = `http://127.0.0.1:${WORKBENCH_PORT}/workbench/`;
|
|
32655
|
-
var
|
|
32656
|
-
|
|
32657
|
-
|
|
32658
|
-
|
|
32659
|
-
|
|
32660
|
-
|
|
32661
|
-
|
|
32662
|
-
|
|
32663
|
-
|
|
32664
|
-
|
|
32665
|
-
|
|
32666
|
-
|
|
32667
|
-
|
|
32668
|
-
|
|
32669
|
-
|
|
32670
|
-
return join3(contentEnginePackageRoot(moduleUrl), "workbench");
|
|
32671
|
-
}
|
|
32672
|
-
function scriptsDir(moduleUrl) {
|
|
32673
|
-
return join3(contentEnginePackageRoot(moduleUrl), "scripts");
|
|
32674
|
-
}
|
|
32657
|
+
var CONTENT_ENGINE_REPO = "https://github.com/murphywuwu/content-engine.git";
|
|
32658
|
+
var WORKBENCH_FILES = ["template.html", "build.py", "serve.py", "roots.py"];
|
|
32659
|
+
var REQUIRED_RELATIVE = [
|
|
32660
|
+
"CLAUDE.md",
|
|
32661
|
+
"engine.json",
|
|
32662
|
+
"LAYOUT.md",
|
|
32663
|
+
"workbench/build.py",
|
|
32664
|
+
"workbench/serve.py",
|
|
32665
|
+
"workbench/template.html",
|
|
32666
|
+
"workbench/roots.py",
|
|
32667
|
+
"needs/_index.md",
|
|
32668
|
+
"topics/_index.md",
|
|
32669
|
+
"runs/_index.md",
|
|
32670
|
+
"profiles/_index.md"
|
|
32671
|
+
];
|
|
32675
32672
|
function isDir(path) {
|
|
32676
32673
|
return existsSync3(path) && statSync(path).isDirectory();
|
|
32677
32674
|
}
|
|
32675
|
+
function scriptsDir(moduleUrl) {
|
|
32676
|
+
return join3(cliPackageRoot(moduleUrl), "scripts", "engine");
|
|
32677
|
+
}
|
|
32678
|
+
function contentEngineRepoUrl() {
|
|
32679
|
+
return (process.env.CONTENT_ENGINE_REPO || CONTENT_ENGINE_REPO).trim() || CONTENT_ENGINE_REPO;
|
|
32680
|
+
}
|
|
32681
|
+
function ensureCoreCheckout({ update = false } = {}) {
|
|
32682
|
+
const local = (process.env.CONTENT_ENGINE_CORE || "").trim();
|
|
32683
|
+
if (local) {
|
|
32684
|
+
const abs = resolve(local);
|
|
32685
|
+
if (!existsSync3(join3(abs, "CLAUDE.md")))
|
|
32686
|
+
throw new Error(`CONTENT_ENGINE_CORE=${abs} is missing CLAUDE.md`);
|
|
32687
|
+
return abs;
|
|
32688
|
+
}
|
|
32689
|
+
const cache = join3(homedir2(), ".cache", "easysociable", "content-engine");
|
|
32690
|
+
const marker = join3(cache, "CLAUDE.md");
|
|
32691
|
+
if (existsSync3(marker) && !update)
|
|
32692
|
+
return cache;
|
|
32693
|
+
mkdirSync2(dirname3(cache), { recursive: true });
|
|
32694
|
+
if (existsSync3(marker) && update) {
|
|
32695
|
+
const pull = spawnSync("git", ["-C", cache, "pull", "--ff-only"], { encoding: "utf8" });
|
|
32696
|
+
if (pull.status !== 0) {
|
|
32697
|
+
rmSync(cache, { recursive: true, force: true });
|
|
32698
|
+
} else {
|
|
32699
|
+
return cache;
|
|
32700
|
+
}
|
|
32701
|
+
}
|
|
32702
|
+
if (existsSync3(cache))
|
|
32703
|
+
rmSync(cache, { recursive: true, force: true });
|
|
32704
|
+
const clone3 = spawnSync("git", ["clone", "--depth", "1", contentEngineRepoUrl(), cache], { encoding: "utf8" });
|
|
32705
|
+
if (clone3.status !== 0 || !existsSync3(marker)) {
|
|
32706
|
+
throw new Error(
|
|
32707
|
+
`Failed to clone Content Engine core from ${contentEngineRepoUrl()}. ${clone3.stderr || clone3.stdout || ""}`.trim()
|
|
32708
|
+
);
|
|
32709
|
+
}
|
|
32710
|
+
return cache;
|
|
32711
|
+
}
|
|
32678
32712
|
function isEngine(path) {
|
|
32679
32713
|
return isDir(path) && existsSync3(join3(path, "CLAUDE.md")) && isDir(join3(path, "runs"));
|
|
32680
32714
|
}
|
|
@@ -32684,62 +32718,10 @@ function resolveEngine(rootOrEngine) {
|
|
|
32684
32718
|
return absolute;
|
|
32685
32719
|
return join3(absolute, ENGINE_ROOT_NAME);
|
|
32686
32720
|
}
|
|
32687
|
-
function
|
|
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);
|
|
32721
|
+
function inventory(engine, _moduleUrl) {
|
|
32740
32722
|
const missing = [];
|
|
32741
32723
|
const existing = [];
|
|
32742
|
-
for (const relative of
|
|
32724
|
+
for (const relative of REQUIRED_RELATIVE) {
|
|
32743
32725
|
const path = join3(engine, relative);
|
|
32744
32726
|
(existsSync3(path) ? existing : missing).push(relative);
|
|
32745
32727
|
}
|
|
@@ -32747,6 +32729,7 @@ function inventory(engine, moduleUrl) {
|
|
|
32747
32729
|
engine,
|
|
32748
32730
|
engineExists: isDir(engine),
|
|
32749
32731
|
rootName: ENGINE_ROOT_NAME,
|
|
32732
|
+
repo: contentEngineRepoUrl(),
|
|
32750
32733
|
missing,
|
|
32751
32734
|
existing,
|
|
32752
32735
|
starterComplete: missing.length === 0
|
|
@@ -32855,7 +32838,7 @@ function validateEngine(rootOrEngine, moduleUrl, { strict = false, requireKnowle
|
|
|
32855
32838
|
if (!result.engineExists)
|
|
32856
32839
|
errors.push("Content Engine/ folder does not exist");
|
|
32857
32840
|
else if (result.missing.length > 0)
|
|
32858
|
-
errors.push(`
|
|
32841
|
+
errors.push(`Engine incomplete: ${result.missing.length} file(s) missing (clone ${contentEngineRepoUrl()} or run engine init --yes)`);
|
|
32859
32842
|
}
|
|
32860
32843
|
const knowledge = requireKnowledge ? checkKnowledge(engine) : void 0;
|
|
32861
32844
|
if (knowledge && !knowledge.valid)
|
|
@@ -32866,20 +32849,31 @@ function python3Bin() {
|
|
|
32866
32849
|
const result = spawnSync("python3", ["--version"], { encoding: "utf8" });
|
|
32867
32850
|
return result.status === 0 ? "python3" : "";
|
|
32868
32851
|
}
|
|
32869
|
-
function
|
|
32852
|
+
function wantWorkbenchSync(explicit) {
|
|
32853
|
+
if (explicit === true)
|
|
32854
|
+
return true;
|
|
32855
|
+
if (explicit === false)
|
|
32856
|
+
return false;
|
|
32857
|
+
const env = (process.env.EASYSOCIABLE_SYNC_WORKBENCH || "").trim().toLowerCase();
|
|
32858
|
+
return env === "1" || env === "true" || env === "yes";
|
|
32859
|
+
}
|
|
32860
|
+
function ensureWorkbench(engine, _moduleUrl, { update = false } = {}) {
|
|
32870
32861
|
const destDir = join3(engine, "workbench");
|
|
32871
32862
|
mkdirSync2(destDir, { recursive: true });
|
|
32872
32863
|
const copied = [];
|
|
32873
32864
|
const updated = [];
|
|
32874
32865
|
const skipped = [];
|
|
32875
|
-
const
|
|
32866
|
+
const needFetch = update || WORKBENCH_FILES.some((name) => !existsSync3(join3(destDir, name)));
|
|
32867
|
+
const srcRoot = needFetch ? join3(ensureCoreCheckout({ update }), "workbench") : "";
|
|
32876
32868
|
for (const name of WORKBENCH_FILES) {
|
|
32877
32869
|
const dest = join3(destDir, name);
|
|
32878
|
-
const from = join3(src, name);
|
|
32879
32870
|
if (existsSync3(dest) && !update) {
|
|
32880
32871
|
skipped.push(name);
|
|
32881
32872
|
continue;
|
|
32882
32873
|
}
|
|
32874
|
+
const from = join3(srcRoot, name);
|
|
32875
|
+
if (!existsSync3(from))
|
|
32876
|
+
throw new Error(`Missing ${name} in Content Engine core checkout`);
|
|
32883
32877
|
const existed = existsSync3(dest);
|
|
32884
32878
|
copyFileSync(from, dest);
|
|
32885
32879
|
if (existed)
|
|
@@ -32895,7 +32889,7 @@ function buildWorkbench(engine) {
|
|
|
32895
32889
|
return { ok: false, code: "PYTHON3_REQUIRED", message: "python3 is required for the workbench. Install Python 3 and retry." };
|
|
32896
32890
|
const script = join3(engine, "workbench", "build.py");
|
|
32897
32891
|
if (!existsSync3(script))
|
|
32898
|
-
return { ok: false, code: "WORKBENCH_MISSING", message:
|
|
32892
|
+
return { ok: false, code: "WORKBENCH_MISSING", message: `workbench/build.py is missing. Clone ${contentEngineRepoUrl()} or run engine init --yes.` };
|
|
32899
32893
|
const result = spawnSync(python, [script], { encoding: "utf8", cwd: engine });
|
|
32900
32894
|
if (result.status !== 0)
|
|
32901
32895
|
return { ok: false, code: "WORKBENCH_BUILD_FAILED", message: result.stderr.trim() || result.stdout.trim() || "workbench build failed" };
|
|
@@ -32915,11 +32909,15 @@ function openUrl(url2) {
|
|
|
32915
32909
|
const args = process.platform === "win32" ? ["/c", "start", url2] : [url2];
|
|
32916
32910
|
spawn(command, args, { stdio: "ignore", detached: true }).unref();
|
|
32917
32911
|
}
|
|
32918
|
-
async function startWorkbench(engine, moduleUrl, {
|
|
32912
|
+
async function startWorkbench(engine, moduleUrl, {
|
|
32913
|
+
noOpen = false,
|
|
32914
|
+
syncWorkbench = false
|
|
32915
|
+
} = {}) {
|
|
32919
32916
|
const python = python3Bin();
|
|
32920
32917
|
if (!python)
|
|
32921
32918
|
return { ok: false, code: "PYTHON3_REQUIRED", message: "python3 is required for the workbench. Install Python 3 and retry." };
|
|
32922
|
-
|
|
32919
|
+
const sync = wantWorkbenchSync(syncWorkbench || void 0);
|
|
32920
|
+
ensureWorkbench(engine, moduleUrl, { update: sync });
|
|
32923
32921
|
const built = buildWorkbench(engine);
|
|
32924
32922
|
if (!built.ok)
|
|
32925
32923
|
return built;
|
|
@@ -32947,7 +32945,7 @@ async function initEngine(rootOrEngine, moduleUrl, {
|
|
|
32947
32945
|
return {
|
|
32948
32946
|
...preview,
|
|
32949
32947
|
action: "preview",
|
|
32950
|
-
next: "
|
|
32948
|
+
next: isEngine(engine) ? "Engine already present; nothing to do" : `Re-run with --yes to git clone ${contentEngineRepoUrl()} into this path (or clone manually)`
|
|
32951
32949
|
};
|
|
32952
32950
|
}
|
|
32953
32951
|
const parent = resolve(engine, "..");
|
|
@@ -32961,15 +32959,24 @@ async function initEngine(rootOrEngine, moduleUrl, {
|
|
|
32961
32959
|
created: []
|
|
32962
32960
|
};
|
|
32963
32961
|
}
|
|
32964
|
-
mkdirSync2(engine, { recursive: true });
|
|
32965
|
-
const report = inventory(engine, moduleUrl);
|
|
32966
32962
|
const created = [];
|
|
32967
|
-
|
|
32968
|
-
|
|
32969
|
-
|
|
32970
|
-
|
|
32971
|
-
|
|
32972
|
-
|
|
32963
|
+
if (isEngine(engine)) {
|
|
32964
|
+
} else if (existsSync3(engine) && readdirSync(engine).filter((n) => n !== ".DS_Store").length > 0) {
|
|
32965
|
+
return {
|
|
32966
|
+
...validateEngine(engine, moduleUrl),
|
|
32967
|
+
created,
|
|
32968
|
+
action: "applied",
|
|
32969
|
+
valid: false,
|
|
32970
|
+
code: "ENGINE_DIR_DIRTY",
|
|
32971
|
+
errors: [`${engine} exists but is not a Content Engine. Remove it or clone ${contentEngineRepoUrl()} into an empty path.`]
|
|
32972
|
+
};
|
|
32973
|
+
} else {
|
|
32974
|
+
const core = ensureCoreCheckout();
|
|
32975
|
+
if (existsSync3(engine))
|
|
32976
|
+
rmSync(engine, { recursive: true, force: true });
|
|
32977
|
+
cpSync(core, engine, { recursive: true });
|
|
32978
|
+
rmSync(join3(engine, ".git"), { recursive: true, force: true });
|
|
32979
|
+
created.push("CLAUDE.md", "engine.json", "workbench/", "(cloned public core)");
|
|
32973
32980
|
}
|
|
32974
32981
|
const workbench = ensureWorkbench(engine, moduleUrl);
|
|
32975
32982
|
const python = python3Bin();
|
|
@@ -32981,7 +32988,7 @@ async function initEngine(rootOrEngine, moduleUrl, {
|
|
|
32981
32988
|
action: "applied",
|
|
32982
32989
|
valid: false,
|
|
32983
32990
|
code: "PYTHON3_REQUIRED",
|
|
32984
|
-
errors: ["python3 is required for the workbench. Install Python 3 and retry.
|
|
32991
|
+
errors: ["python3 is required for the workbench. Install Python 3 and retry."]
|
|
32985
32992
|
};
|
|
32986
32993
|
}
|
|
32987
32994
|
const built = buildWorkbench(engine);
|
|
@@ -32992,7 +32999,123 @@ async function initEngine(rootOrEngine, moduleUrl, {
|
|
|
32992
32999
|
workbench,
|
|
32993
33000
|
workbenchBuild: built,
|
|
32994
33001
|
workbenchStart: started,
|
|
32995
|
-
action: "applied"
|
|
33002
|
+
action: "applied",
|
|
33003
|
+
source: contentEngineRepoUrl()
|
|
33004
|
+
};
|
|
33005
|
+
}
|
|
33006
|
+
var CORE_REFRESH_FILES = /* @__PURE__ */ new Set(["CLAUDE.md", "LAYOUT.md", "topics/scoring.md"]);
|
|
33007
|
+
var CORE_ADD_ONLY_FILES = /* @__PURE__ */ new Set(["media/_index.md", "media/README.md", "profiles/default/brand.md"]);
|
|
33008
|
+
function classifyCoreFile(rel) {
|
|
33009
|
+
const parts = rel.split("/");
|
|
33010
|
+
if (parts[0] === ".git")
|
|
33011
|
+
return "skip";
|
|
33012
|
+
if (CORE_ADD_ONLY_FILES.has(rel))
|
|
33013
|
+
return "add";
|
|
33014
|
+
if (CORE_REFRESH_FILES.has(rel))
|
|
33015
|
+
return "refresh";
|
|
33016
|
+
if (parts[0] === "workbench")
|
|
33017
|
+
return "refresh";
|
|
33018
|
+
if (parts[0] === "platforms" && rel.endsWith(".md"))
|
|
33019
|
+
return "refresh";
|
|
33020
|
+
if (basename(rel) === "_template.md" || parts.includes("_template"))
|
|
33021
|
+
return "refresh";
|
|
33022
|
+
return "skip";
|
|
33023
|
+
}
|
|
33024
|
+
function walkCoreFiles(core, relDir = "") {
|
|
33025
|
+
const out = [];
|
|
33026
|
+
const absDir = join3(core, relDir);
|
|
33027
|
+
if (!isDir(absDir))
|
|
33028
|
+
return out;
|
|
33029
|
+
for (const name of readdirSync(absDir)) {
|
|
33030
|
+
if (name === ".git" || name === ".DS_Store")
|
|
33031
|
+
continue;
|
|
33032
|
+
const rel = relDir ? `${relDir}/${name}` : name;
|
|
33033
|
+
if (isDir(join3(core, rel)))
|
|
33034
|
+
out.push(...walkCoreFiles(core, rel));
|
|
33035
|
+
else
|
|
33036
|
+
out.push(rel);
|
|
33037
|
+
}
|
|
33038
|
+
return out;
|
|
33039
|
+
}
|
|
33040
|
+
function mergeEngineJson(engine, core) {
|
|
33041
|
+
const engPath = join3(engine, "engine.json");
|
|
33042
|
+
const corePath = join3(core, "engine.json");
|
|
33043
|
+
if (!existsSync3(engPath) || !existsSync3(corePath))
|
|
33044
|
+
return [];
|
|
33045
|
+
let eng;
|
|
33046
|
+
let cor;
|
|
33047
|
+
try {
|
|
33048
|
+
eng = JSON.parse(readFileSync2(engPath, "utf8"));
|
|
33049
|
+
cor = JSON.parse(readFileSync2(corePath, "utf8"));
|
|
33050
|
+
} catch {
|
|
33051
|
+
return [];
|
|
33052
|
+
}
|
|
33053
|
+
const engPaths = eng.paths ?? (eng.paths = {});
|
|
33054
|
+
const corPaths = cor.paths ?? {};
|
|
33055
|
+
const added = [];
|
|
33056
|
+
for (const [key, value] of Object.entries(corPaths)) {
|
|
33057
|
+
if (!(key in engPaths)) {
|
|
33058
|
+
engPaths[key] = value;
|
|
33059
|
+
added.push(`paths.${key}`);
|
|
33060
|
+
}
|
|
33061
|
+
}
|
|
33062
|
+
if (added.length)
|
|
33063
|
+
writeFileSync2(engPath, `${JSON.stringify(eng, null, 2)}
|
|
33064
|
+
`);
|
|
33065
|
+
return added;
|
|
33066
|
+
}
|
|
33067
|
+
function updateEngine(rootOrEngine, moduleUrl, { yes = false } = {}) {
|
|
33068
|
+
const engine = resolveEngine(rootOrEngine);
|
|
33069
|
+
if (!isEngine(engine)) {
|
|
33070
|
+
return {
|
|
33071
|
+
engine,
|
|
33072
|
+
action: "preview",
|
|
33073
|
+
valid: false,
|
|
33074
|
+
code: "NOT_AN_ENGINE",
|
|
33075
|
+
errors: [`${engine} is not a Content Engine. Run engine init first, or pass --engine <path>.`]
|
|
33076
|
+
};
|
|
33077
|
+
}
|
|
33078
|
+
const core = ensureCoreCheckout({ update: true });
|
|
33079
|
+
const refresh = [];
|
|
33080
|
+
const add = [];
|
|
33081
|
+
for (const rel of walkCoreFiles(core)) {
|
|
33082
|
+
const cls = classifyCoreFile(rel);
|
|
33083
|
+
if (cls === "refresh")
|
|
33084
|
+
refresh.push(rel);
|
|
33085
|
+
else if (cls === "add" && !existsSync3(join3(engine, rel)))
|
|
33086
|
+
add.push(rel);
|
|
33087
|
+
}
|
|
33088
|
+
if (!yes) {
|
|
33089
|
+
return {
|
|
33090
|
+
engine,
|
|
33091
|
+
action: "preview",
|
|
33092
|
+
source: contentEngineRepoUrl(),
|
|
33093
|
+
willRefresh: refresh,
|
|
33094
|
+
willAdd: add,
|
|
33095
|
+
next: "Re-run with --yes to apply. Vault data (profiles, needs, runs, media files) is never touched."
|
|
33096
|
+
};
|
|
33097
|
+
}
|
|
33098
|
+
const copy = (list) => {
|
|
33099
|
+
for (const rel of list) {
|
|
33100
|
+
const dest = join3(engine, rel);
|
|
33101
|
+
mkdirSync2(dirname3(dest), { recursive: true });
|
|
33102
|
+
copyFileSync(join3(core, rel), dest);
|
|
33103
|
+
}
|
|
33104
|
+
return list;
|
|
33105
|
+
};
|
|
33106
|
+
const refreshed = copy(refresh);
|
|
33107
|
+
const added = copy(add);
|
|
33108
|
+
const enginePaths = mergeEngineJson(engine, core);
|
|
33109
|
+
const workbenchBuild = buildWorkbench(engine);
|
|
33110
|
+
return {
|
|
33111
|
+
...validateEngine(engine, moduleUrl),
|
|
33112
|
+
engine,
|
|
33113
|
+
action: "applied",
|
|
33114
|
+
source: contentEngineRepoUrl(),
|
|
33115
|
+
refreshed,
|
|
33116
|
+
added,
|
|
33117
|
+
enginePaths,
|
|
33118
|
+
workbenchBuild
|
|
32996
33119
|
};
|
|
32997
33120
|
}
|
|
32998
33121
|
function detectEngines() {
|
|
@@ -33230,7 +33353,7 @@ function buildV1Cli() {
|
|
|
33230
33353
|
const command = args.at(-1);
|
|
33231
33354
|
print({ action: "detect", engines: detectEngines() }, commandWantsJson(command));
|
|
33232
33355
|
});
|
|
33233
|
-
jsonOption(engine.command("init").description("
|
|
33356
|
+
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
33357
|
const command = args.at(-1);
|
|
33235
33358
|
const opts = command.opts();
|
|
33236
33359
|
const result = await initEngine(String(opts.root), CLI_MODULE_URL, {
|
|
@@ -33280,25 +33403,38 @@ function buildV1Cli() {
|
|
|
33280
33403
|
if (!report.valid)
|
|
33281
33404
|
throw new Error(report.errors[0] ?? "engine validate failed");
|
|
33282
33405
|
});
|
|
33283
|
-
jsonOption(engine.command("serve").description("
|
|
33406
|
+
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
33407
|
const command = args.at(-1);
|
|
33285
33408
|
const opts = command.opts();
|
|
33286
33409
|
const enginePath = resolveEngine(String(opts.engine));
|
|
33287
|
-
const started = await startWorkbench(enginePath, CLI_MODULE_URL, {
|
|
33410
|
+
const started = await startWorkbench(enginePath, CLI_MODULE_URL, {
|
|
33411
|
+
noOpen: opts.noOpen === true,
|
|
33412
|
+
syncWorkbench: opts.syncWorkbench === true
|
|
33413
|
+
});
|
|
33288
33414
|
print({ action: "serve", engine: enginePath, ...started }, commandWantsJson(command));
|
|
33289
33415
|
if (!started.ok)
|
|
33290
33416
|
throw new Error("message" in started ? String(started.message) : "engine serve failed");
|
|
33291
33417
|
});
|
|
33292
|
-
jsonOption(engine.command("build").description("
|
|
33418
|
+
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
33419
|
const command = args.at(-1);
|
|
33294
33420
|
const opts = command.opts();
|
|
33295
33421
|
const enginePath = resolveEngine(String(opts.engine));
|
|
33296
|
-
const
|
|
33422
|
+
const sync = wantWorkbenchSync(opts.syncWorkbench === true ? true : void 0);
|
|
33423
|
+
const workbench = ensureWorkbench(enginePath, CLI_MODULE_URL, { update: sync });
|
|
33297
33424
|
const built = buildWorkbench(enginePath);
|
|
33298
|
-
print({ action: "build", engine: enginePath, workbench, ...built }, commandWantsJson(command));
|
|
33425
|
+
print({ action: "build", engine: enginePath, workbench, syncWorkbench: sync, ...built }, commandWantsJson(command));
|
|
33299
33426
|
if (!built.ok)
|
|
33300
33427
|
throw new Error(built.message);
|
|
33301
33428
|
});
|
|
33429
|
+
jsonOption(engine.command("update").description("Sync core rules from the template (CLAUDE.md, workbench, platforms, templates) and add new scaffolds; never touches your vault data. Preview first; apply with --yes.").option("--engine <path>", "Content Engine path or parent", defaultEngineRoot()).option("--yes", "Apply the update (default is a dry-run preview)")).action((...args) => {
|
|
33430
|
+
const command = args.at(-1);
|
|
33431
|
+
const opts = command.opts();
|
|
33432
|
+
const result = updateEngine(String(opts.engine), CLI_MODULE_URL, { yes: opts.yes === true });
|
|
33433
|
+
print(result, commandWantsJson(command));
|
|
33434
|
+
const code = result.code;
|
|
33435
|
+
if (code)
|
|
33436
|
+
throw new Error(result.errors?.[0] ?? code);
|
|
33437
|
+
});
|
|
33302
33438
|
const auth = program.command("auth").description("Manage the shared EasySociable credential");
|
|
33303
33439
|
jsonOption(auth.command("status").description("Show authentication status")).action(async (...args) => {
|
|
33304
33440
|
const command = args.at(-1);
|