@easysociable/cli 0.1.2 → 0.1.3
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 +29 -21
- package/dist/easysociable.js +481 -52
- package/dist/easysociable.js.map +1 -1
- package/package.json +3 -2
- package/skills/ONBOARDING.md +20 -16
- package/skills/easysociable-content/SKILL.md +29 -104
- package/skills/easysociable-slideshow/SKILL.md +4 -3
- package/skills/easysociable-template/SKILL.md +26 -24
- package/skills/easysociable-template/references/CATALOG_METADATA.md +2 -2
- package/skills/easysociable-template/references/DESIGN_V2_1.md +53 -2
- package/skills/easysociable-template/references/GATEWAY.md +7 -2
- package/skills/easysociable-template/references/PLATFORM_SPECS.md +11 -3
- package/skills/easysociable-template/references/THEME_TOKENS.md +1 -1
- package/skills/easysociable-template/references/fixtures/image-reference.design-plan.json +74 -0
- package/skills/easysociable-template/references/fixtures/image-reference.visual-qa.json +28 -0
- package/skills/easysociable-template/scripts/validate-design-v2.mjs +97 -0
- package/skills/easysociable-template/workflows/ASSET_PLAN.md +2 -1
- package/skills/easysociable-template/workflows/AUTHORING_FROM_BRIEF.md +4 -4
- package/skills/easysociable-template/workflows/AUTHORING_FROM_IMAGE.md +66 -16
- package/skills/easysociable-template/workflows/AUTHORING_STUDIO.md +28 -15
- package/skills/easysociable-template/workflows/DESIGN_BRIEF.md +4 -3
- package/skills/easysociable-template/workflows/MULTI_PLATFORM_FAMILY.md +81 -0
- package/skills/easysociable-template/workflows/PUBLISH_FROM_DRAFT.md +1 -1
- package/skills/easysociable-template/workflows/VISUAL_QA.md +10 -0
- package/skills/easysociable-content/INSTALL.md +0 -60
- package/skills/easysociable-content/platforms/claude.md +0 -4
- package/skills/easysociable-content/platforms/codex.md +0 -4
- package/skills/easysociable-content/platforms/hermes.md +0 -4
- package/skills/easysociable-content/platforms/openclaw.md +0 -4
- package/skills/easysociable-content/references/CLI.md +0 -40
- package/skills/easysociable-content/references/CONTRACT_SYNC.md +0 -42
- package/skills/easysociable-content/references/MCP.md +0 -15
- package/skills/easysociable-content/references/PAGE_PACK.md +0 -129
- package/skills/easysociable-content/references/PLATFORM_GUIDE.md +0 -36
- package/skills/easysociable-content/references/RUN_LAYOUT.md +0 -73
- package/skills/easysociable-content/references/TROUBLESHOOTING.md +0 -13
- package/skills/easysociable-content/references/VAULT.md +0 -40
- package/skills/easysociable-content/references/VOICE.md +0 -16
- package/skills/easysociable-content/scripts/package-skill.mjs +0 -49
- package/skills/easysociable-content/scripts/setup.mjs +0 -200
- package/skills/easysociable-content/scripts/setup.test.mjs +0 -82
- package/skills/easysociable-content/workflows/ANALYSIS_AND_COVERAGE.md +0 -17
- package/skills/easysociable-content/workflows/CONTENT_EXPORT.md +0 -24
- package/skills/easysociable-content/workflows/GETTING_STARTED.md +0 -40
- package/skills/easysociable-content/workflows/MCP_FIRST.md +0 -5
- package/skills/easysociable-content/workflows/OUT_OF_SCOPE.md +0 -18
- package/skills/easysociable-content/workflows/PACK_AUTHORING.md +0 -42
- package/skills/easysociable-content/workflows/SWIPE_CAPTURE.md +0 -18
- package/skills/easysociable-content/workflows/TOPIC_DRAFT.md +0 -29
- package/skills/easysociable-content/workflows/WORKING_DRAFT.md +0 -19
- /package/{skills/easysociable-content → content-engine}/fixtures/golden-run-en/draft.md +0 -0
- /package/{skills/easysociable-content → content-engine}/fixtures/golden-run-en/idea.md +0 -0
- /package/{skills/easysociable-content → content-engine}/fixtures/golden-run-en/pack-tiktok.json +0 -0
- /package/{skills/easysociable-content → content-engine}/scripts/contracts.mjs +0 -0
- /package/{skills/easysociable-content → content-engine}/scripts/export-contracts.mjs +0 -0
- /package/{skills/easysociable-content → content-engine}/scripts/fixture.test.mjs +0 -0
- /package/{skills/easysociable-content → content-engine}/scripts/starter.mjs +0 -0
- /package/{skills/easysociable-content → content-engine}/scripts/validate-export.mjs +0 -0
- /package/{skills/easysociable-content → content-engine}/scripts/validate-pack.mjs +0 -0
- /package/{skills/easysociable-content → content-engine}/scripts/validate-run.mjs +0 -0
- /package/{skills/easysociable-content → content-engine}/scripts/validate.test.mjs +0 -0
- /package/{skills/easysociable-content → content-engine}/scripts/workbench.mjs +0 -0
- /package/{skills/easysociable-content → content-engine}/starter/CLAUDE.md +0 -0
- /package/{skills/easysociable-content → content-engine}/starter/accounts/README.md +0 -0
- /package/{skills/easysociable-content → content-engine}/starter/accounts/_index.md +0 -0
- /package/{skills/easysociable-content → content-engine}/starter/accounts/overrides/README.md +0 -0
- /package/{skills/easysociable-content → content-engine}/starter/discovery/README.md +0 -0
- /package/{skills/easysociable-content → content-engine}/starter/discovery/_index.md +0 -0
- /package/{skills/easysociable-content → content-engine}/starter/discovery/_template.md +0 -0
- /package/{skills/easysociable-content → content-engine}/starter/engine.json +0 -0
- /package/{skills/easysociable-content → content-engine}/starter/exports/README.md +0 -0
- /package/{skills/easysociable-content → content-engine}/starter/inbox/README.md +0 -0
- /package/{skills/easysociable-content → content-engine}/starter/inbox/_index.md +0 -0
- /package/{skills/easysociable-content → content-engine}/starter/inbox/entries/README.md +0 -0
- /package/{skills/easysociable-content → content-engine}/starter/platforms/README.md +0 -0
- /package/{skills/easysociable-content → content-engine}/starter/platforms/ig.md +0 -0
- /package/{skills/easysociable-content → content-engine}/starter/platforms/instagram.md +0 -0
- /package/{skills/easysociable-content → content-engine}/starter/platforms/linkedin.md +0 -0
- /package/{skills/easysociable-content → content-engine}/starter/platforms/threads.md +0 -0
- /package/{skills/easysociable-content → content-engine}/starter/platforms/tiktok.md +0 -0
- /package/{skills/easysociable-content → content-engine}/starter/platforms/x.md +0 -0
- /package/{skills/easysociable-content → content-engine}/starter/platforms/xiaohongshu.md +0 -0
- /package/{skills/easysociable-content → content-engine}/starter/profiles/_index.md +0 -0
- /package/{skills/easysociable-content → content-engine}/starter/profiles/default/README.md +0 -0
- /package/{skills/easysociable-content → content-engine}/starter/profiles/default/audience.md +0 -0
- /package/{skills/easysociable-content → content-engine}/starter/profiles/default/pillars.md +0 -0
- /package/{skills/easysociable-content → content-engine}/starter/profiles/default/voice.md +0 -0
- /package/{skills/easysociable-content → content-engine}/starter/published/_index.md +0 -0
- /package/{skills/easysociable-content → content-engine}/starter/raw/README.md +0 -0
- /package/{skills/easysociable-content → content-engine}/starter/raw/_index.md +0 -0
- /package/{skills/easysociable-content → content-engine}/starter/runs/README.md +0 -0
- /package/{skills/easysociable-content → content-engine}/starter/runs/_index.md +0 -0
- /package/{skills/easysociable-content → content-engine}/starter/runs/_template/brief.md +0 -0
- /package/{skills/easysociable-content → content-engine}/starter/runs/_template/draft.md +0 -0
- /package/{skills/easysociable-content → content-engine}/starter/runs/_template/editor.md +0 -0
- /package/{skills/easysociable-content → content-engine}/starter/runs/_template/feedback.md +0 -0
- /package/{skills/easysociable-content → content-engine}/starter/runs/_template/idea.md +0 -0
- /package/{skills/easysociable-content → content-engine}/starter/runs/_template/pack.md +0 -0
- /package/{skills/easysociable-content → content-engine}/starter/runs/_template/packet.md +0 -0
- /package/{skills/easysociable-content → content-engine}/starter/runs/_template/rubric.md +0 -0
- /package/{skills/easysociable-content → content-engine}/starter/swipe/_index.md +0 -0
- /package/{skills/easysociable-content → content-engine}/starter/swipe/_template.md +0 -0
- /package/{skills/easysociable-content → content-engine}/starter/topics/README.md +0 -0
- /package/{skills/easysociable-content → content-engine}/starter/topics/WATCHLIST.md +0 -0
- /package/{skills/easysociable-content → content-engine}/starter/topics/_index.md +0 -0
- /package/{skills/easysociable-content → content-engine}/starter/topics/_template-item.md +0 -0
- /package/{skills/easysociable-content → content-engine}/starter/topics/items/README.md +0 -0
- /package/{skills/easysociable-content → content-engine}/starter/topics/scoring.md +0 -0
- /package/{skills/easysociable-content → content-engine}/starter/topics/watchlists/README.md +0 -0
- /package/{skills/easysociable-content → content-engine}/starter/topics/watchlists/_shared.md +0 -0
- /package/{skills/easysociable-content → content-engine}/starter/topics/watchlists/default.md +0 -0
- /package/{skills/easysociable-content → content-engine}/starter/wiki/README.md +0 -0
- /package/{skills/easysociable-content → content-engine}/starter/wiki/atoms/_index.md +0 -0
- /package/{skills/easysociable-content → content-engine}/starter/wiki/atoms/_template.md +0 -0
- /package/{skills/easysociable-content → content-engine}/starter/wiki/claims/_index.md +0 -0
- /package/{skills/easysociable-content → content-engine}/starter/wiki/claims/_template.md +0 -0
- /package/{skills/easysociable-content → content-engine}/starter/workbench/README.md +0 -0
- /package/{skills/easysociable-content → content-engine}/workbench/build.py +0 -0
- /package/{skills/easysociable-content → content-engine}/workbench/serve.py +0 -0
- /package/{skills/easysociable-content → content-engine}/workbench/template.html +0 -0
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
# Content Skill filesystem reference
|
|
2
|
-
|
|
3
|
-
The Content Skill does not own a Content CLI. It writes `Content Engine/` on disk and uses the unified `easysociable` command only for hosted Template/Slideshow handoff.
|
|
4
|
-
|
|
5
|
-
## Local workflow
|
|
6
|
-
|
|
7
|
-
Ask for a parent directory (or existing engine path). Do not ask language. Do not require Obsidian.
|
|
8
|
-
|
|
9
|
-
Do not invent `easysociable-content init/start/doctor`.
|
|
10
|
-
|
|
11
|
-
## Setup
|
|
12
|
-
|
|
13
|
-
```bash
|
|
14
|
-
node scripts/setup.mjs --detect --json
|
|
15
|
-
node scripts/setup.mjs --root <parent-or-engine> --preview --json
|
|
16
|
-
node scripts/setup.mjs --root <parent-or-engine> --apply --json
|
|
17
|
-
node scripts/setup.mjs --root <parent-or-engine> --validate --json
|
|
18
|
-
node scripts/workbench.mjs start --engine <Content Engine> --json
|
|
19
|
-
node scripts/workbench.mjs build --engine <Content Engine> --json
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
`--no-open` skips the browser and the workbench server.
|
|
23
|
-
|
|
24
|
-
## Hosted handoff
|
|
25
|
-
|
|
26
|
-
```bash
|
|
27
|
-
easysociable templates list --platforms tiktok --json
|
|
28
|
-
easysociable slideshows create --file ./runs/<slug>/pack-tiktok.json --json
|
|
29
|
-
easysociable slideshows render --id run_xxx --idempotency-key run_xxx-final-1 --json
|
|
30
|
-
easysociable slideshows export --id run_xxx --output ./exports/<slug> --json
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
Validate before slideshow create:
|
|
34
|
-
|
|
35
|
-
```bash
|
|
36
|
-
node scripts/validate-run.mjs --run ./runs/<slug> --require-pack --json
|
|
37
|
-
node scripts/validate-pack.mjs --file ./runs/<slug>/pack-tiktok.json --json
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
Use unified MCP first when the host exposes it. CLI is the fallback.
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
# Contract sync
|
|
2
|
-
|
|
3
|
-
Pack validation in this skill mirrors the hosted public contract. When those
|
|
4
|
-
contracts change, update this skill in the same PR.
|
|
5
|
-
|
|
6
|
-
## Source of truth
|
|
7
|
-
|
|
8
|
-
| Contract | Repository path |
|
|
9
|
-
|---|---|
|
|
10
|
-
| `content-pack.v1` schema | `packages/template-public-contracts/src/index.ts` → `contentPackSchema` |
|
|
11
|
-
| Platform page caps | `slideshowPlatformCaps` in the same file |
|
|
12
|
-
| Page roles | `pageRoleSchema` in the same file |
|
|
13
|
-
|
|
14
|
-
## Mirrored in skill
|
|
15
|
-
|
|
16
|
-
| Skill file | What to sync |
|
|
17
|
-
|---|---|
|
|
18
|
-
| `scripts/contracts.mjs` | caps, roles, forbidden keys, validate logic |
|
|
19
|
-
| `references/PAGE_PACK.md` | Agent-facing summary + examples |
|
|
20
|
-
| `references/PLATFORM_GUIDE.md` | slideshow vs template routing |
|
|
21
|
-
|
|
22
|
-
## Maintainer checklist
|
|
23
|
-
|
|
24
|
-
Before merging contract changes:
|
|
25
|
-
|
|
26
|
-
1. Diff `contentPackSchema` / `slideshowPlatformCaps` / `pageRoleSchema`.
|
|
27
|
-
2. Update `contracts.mjs` and `PAGE_PACK.md`.
|
|
28
|
-
3. Run:
|
|
29
|
-
|
|
30
|
-
```bash
|
|
31
|
-
node skills/easysociable-content/scripts/validate.test.mjs
|
|
32
|
-
node skills/easysociable-content/scripts/fixture.test.mjs
|
|
33
|
-
node skills/easysociable-content/scripts/setup.test.mjs
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
4. If caps or roles changed, refresh `fixtures/golden-run-en/pack-tiktok.json` when needed.
|
|
37
|
-
|
|
38
|
-
## Optional hard sync (future)
|
|
39
|
-
|
|
40
|
-
Import `contentPackSchema` from `@easysociable/template-public-contracts` in
|
|
41
|
-
`validate-pack.mjs` once skill scripts run through the monorepo toolchain. Until
|
|
42
|
-
then, `contracts.mjs` is the portable mirror (marked with `ponytail:`).
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
# Unified MCP reference
|
|
2
|
-
|
|
3
|
-
V1 has one MCP server from the unified EasySociable installer. It owns hosted Template, Image, Profile, Theme, Slideshow and Render tools. This Content Skill does not require a Content MCP.
|
|
4
|
-
|
|
5
|
-
Content work stays on disk under `Content Engine/`. Hosted calls use the same Gateway client as the CLI.
|
|
6
|
-
|
|
7
|
-
## Handoff sequence
|
|
8
|
-
|
|
9
|
-
1. After SHIP, write `pack.md` (slideshow) or slot values (single-page).
|
|
10
|
-
2. Slideshow skill writes `content-pack.v1` into the run folder, then `create_slideshow_run`.
|
|
11
|
-
3. Open Studio URL; wait for Save.
|
|
12
|
-
4. After confirmation call `render_slideshow_run`.
|
|
13
|
-
5. Export PNGs into `exports/<run-slug>/`.
|
|
14
|
-
|
|
15
|
-
The MCP server must never expose private AST, Binding Maps, RenderPlans, R2 credentials, Directus credentials, Stripe credentials or Local Runtime tokens.
|
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
# PagePack handoff (`content-pack.v1`)
|
|
2
|
-
|
|
3
|
-
The Pack is the boundary between semantic content (Content Skill) and visual
|
|
4
|
-
composition (Template / Slideshow Skills). Content writes slots and roles;
|
|
5
|
-
downstream skills choose Page Templates and render.
|
|
6
|
-
|
|
7
|
-
**Source of truth for caps and roles:** `packages/template-public-contracts`
|
|
8
|
-
(`contentPackSchema`). This file is the Agent-facing summary.
|
|
9
|
-
|
|
10
|
-
## Schema
|
|
11
|
-
|
|
12
|
-
```json
|
|
13
|
-
{
|
|
14
|
-
"schemaVersion": "content-pack.v1",
|
|
15
|
-
"platform": "tiktok",
|
|
16
|
-
"title": "Optional title",
|
|
17
|
-
"pages": [
|
|
18
|
-
{
|
|
19
|
-
"role": "hook",
|
|
20
|
-
"slots": { "text_0": "Hook line", "text_1": "Support" },
|
|
21
|
-
"pageTemplateId": "tpl_selected_at_handoff"
|
|
22
|
-
}
|
|
23
|
-
]
|
|
24
|
-
}
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
| Field | Required | Notes |
|
|
28
|
-
|---|---|---|
|
|
29
|
-
| `schemaVersion` | yes | Must be `content-pack.v1` |
|
|
30
|
-
| `platform` | yes | See platform table below |
|
|
31
|
-
| `title` | no | Display title for Studio |
|
|
32
|
-
| `pages` | yes | Ordered array, length within platform caps |
|
|
33
|
-
| `pages[].role` | yes | Closed `pageRoles` enum |
|
|
34
|
-
| `pages[].slots` | yes | Public slot key → string/number/boolean/null |
|
|
35
|
-
| `pages[].pageTemplateId` | yes | Published Page Template selected by Slideshow before `create` |
|
|
36
|
-
|
|
37
|
-
## Forbidden in Pack
|
|
38
|
-
|
|
39
|
-
Never include:
|
|
40
|
-
|
|
41
|
-
- `deck_template_id` / Deck references
|
|
42
|
-
- Template AST, Binding Maps, RenderPlans
|
|
43
|
-
- `ContentDocument` / `contentDocumentHash`
|
|
44
|
-
- Private gateway credentials or image URLs as slot values (use `imageId` after import)
|
|
45
|
-
|
|
46
|
-
## Platform page caps
|
|
47
|
-
|
|
48
|
-
| Platform | Min pages | Max pages | V1 Slideshow |
|
|
49
|
-
|---|---:|---:|---|
|
|
50
|
-
| `tiktok` | 4 | 35 | yes |
|
|
51
|
-
| `instagram` | 2 | 20 | yes |
|
|
52
|
-
| `linkedin` | 2 | 300 | yes |
|
|
53
|
-
| `threads` | 2 | 20 | yes |
|
|
54
|
-
| `xiaohongshu` | — | — | not a Content Pack route in V1; use Template single-page |
|
|
55
|
-
|
|
56
|
-
`x` is not a Pack platform. Use Template fill-slots for single-page X graphics.
|
|
57
|
-
|
|
58
|
-
## Page roles (closed set)
|
|
59
|
-
|
|
60
|
-
`cover`, `hook`, `setup`, `context`, `value`, `point`, `checklist`, `proof`,
|
|
61
|
-
`evidence`, `compare`, `screenshot`, `metric`, `quote`, `steps`, `cta`, `follow`, `recap`
|
|
62
|
-
|
|
63
|
-
Pick roles that match the narrative arc. Slideshow maps each page to a published
|
|
64
|
-
Page Template with compatible `supportedRoles`.
|
|
65
|
-
|
|
66
|
-
## File naming
|
|
67
|
-
|
|
68
|
-
During the run, write vault-native pack markdown:
|
|
69
|
-
|
|
70
|
-
```text
|
|
71
|
-
pack.md
|
|
72
|
-
pack-<platform>.md
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
`content-pack.v1` JSON is created **at Slideshow/Template handoff**, not during draft:
|
|
76
|
-
|
|
77
|
-
```text
|
|
78
|
-
pack-<platform>.json
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
Example: `pack-tiktok.json`. Pure X text has no pack.
|
|
82
|
-
|
|
83
|
-
## Validation
|
|
84
|
-
|
|
85
|
-
```bash
|
|
86
|
-
node scripts/validate-pack.mjs --file ./pack-tiktok.json --platform tiktok --json
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
Before Slideshow `create`, also run:
|
|
90
|
-
|
|
91
|
-
```bash
|
|
92
|
-
node scripts/validate-pack.mjs --file ./pack-tiktok.json --require-page-template-id --json
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
## Examples
|
|
96
|
-
|
|
97
|
-
### English — TikTok (4 pages, content stage)
|
|
98
|
-
|
|
99
|
-
```json
|
|
100
|
-
{
|
|
101
|
-
"schemaVersion": "content-pack.v1",
|
|
102
|
-
"platform": "tiktok",
|
|
103
|
-
"title": "Morning routine tips",
|
|
104
|
-
"pages": [
|
|
105
|
-
{ "role": "hook", "pageTemplateId": "tpl_hook_001", "slots": { "text_0": "Stop scrolling — this changed my mornings" } },
|
|
106
|
-
{ "role": "value", "pageTemplateId": "tpl_value_001", "slots": { "text_0": "Wake at the same time", "text_1": "No phone for 20 minutes" } },
|
|
107
|
-
{ "role": "value", "pageTemplateId": "tpl_value_001", "slots": { "text_0": "Move before caffeine", "text_1": "Even 5 minutes counts" } },
|
|
108
|
-
{ "role": "cta", "pageTemplateId": "tpl_cta_001", "slots": { "text_0": "Save this for tomorrow" } }
|
|
109
|
-
]
|
|
110
|
-
}
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
### 中文 — TikTok(4 页,内容阶段)
|
|
114
|
-
|
|
115
|
-
```json
|
|
116
|
-
{
|
|
117
|
-
"schemaVersion": "content-pack.v1",
|
|
118
|
-
"platform": "tiktok",
|
|
119
|
-
"title": "晨间习惯三条",
|
|
120
|
-
"pages": [
|
|
121
|
-
{ "role": "hook", "pageTemplateId": "tpl_hook_001", "slots": { "text_0": "别再瞎忙了——我靠这三条稳住早晨" } },
|
|
122
|
-
{ "role": "value", "pageTemplateId": "tpl_value_001", "slots": { "text_0": "固定起床时间", "text_1": "醒来先别碰手机" } },
|
|
123
|
-
{ "role": "value", "pageTemplateId": "tpl_value_001", "slots": { "text_0": "喝咖啡前先动一动", "text_1": "五分钟也算数" } },
|
|
124
|
-
{ "role": "cta", "pageTemplateId": "tpl_cta_001", "slots": { "text_0": "先收藏,明早试" } }
|
|
125
|
-
]
|
|
126
|
-
}
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
JSON field names and `role` values stay English even when slot copy is 中文.
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
# Platform guide
|
|
2
|
-
|
|
3
|
-
Use this matrix before opening a run. **Ask** if the user did not name a platform. Multi-select → **N runs**.
|
|
4
|
-
|
|
5
|
-
| Platform | Slideshow (V1) | Pack platform key | Page cap (slideshow) | Default path |
|
|
6
|
-
|---|---|---|---:|---|
|
|
7
|
-
| TikTok | yes | `tiktok` | 4–35 | Slideshow |
|
|
8
|
-
| Instagram | yes | `instagram` (`ig`) | 2–20 | Slideshow |
|
|
9
|
-
| LinkedIn | yes | `linkedin` | 2–300 | Slideshow |
|
|
10
|
-
| Threads | yes | `threads` | 2–20 | Slideshow |
|
|
11
|
-
| X | **no** | — (no Pack) | — | Template single-page or text-only |
|
|
12
|
-
| Xiaohongshu | **no** (V1) | — | — | Template single-page |
|
|
13
|
-
|
|
14
|
-
Craft notes live under `Content Engine/platforms/`. Map: `instagram` → `ig.md`. Read the file before brief/draft/editor.
|
|
15
|
-
|
|
16
|
-
## Routing
|
|
17
|
-
|
|
18
|
-
```text
|
|
19
|
-
1 page → easysociable-template
|
|
20
|
-
2+ pages + slideshow platform → easysociable-slideshow
|
|
21
|
-
x → easysociable-template only (if images)
|
|
22
|
-
xiaohongshu → easysociable-template until slideshow ships
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
## Canvas hints (for downstream skills)
|
|
26
|
-
|
|
27
|
-
| Platform | Typical canvas | Notes |
|
|
28
|
-
|---|---|---|
|
|
29
|
-
| tiktok | 9:16 @ 1080×1920 | Photo Mode carousel |
|
|
30
|
-
| instagram / ig | 4:5 or 1:1 @ 1080 | Feed carousel |
|
|
31
|
-
| linkedin | 4:5 or 1:1 | Document-style decks |
|
|
32
|
-
| threads | 4:5 or 1:1 | Conversational copy |
|
|
33
|
-
| x | 16:9 or 1:1 | Single image post |
|
|
34
|
-
| xiaohongshu | 3:4 @ 1080×1440 | See Template `PLATFORM_SPECS` |
|
|
35
|
-
|
|
36
|
-
Content Skill does not pick Page Templates — only platform, roles and slot copy. Pack JSON is written at Slideshow/Template handoff, not during draft.
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
# Run layout
|
|
2
|
-
|
|
3
|
-
Copy `runs/_template/` to `runs/<YYYY-MM-DD-short-slug[-platform]>/`.
|
|
4
|
-
|
|
5
|
-
**One folder = one platform.** Same Topic on another network = another folder sharing `topic_id`.
|
|
6
|
-
|
|
7
|
-
```text
|
|
8
|
-
Content Engine/runs/<slug>/
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
## Core files
|
|
12
|
-
|
|
13
|
-
| File | Purpose |
|
|
14
|
-
|---|---|
|
|
15
|
-
| `idea.md` | Run id, status, **this** platform, selection (`swipe_id` / `atoms` / `claim_id`) |
|
|
16
|
-
| `brief.md` | Angle, constraints, selection why |
|
|
17
|
-
| `packet.md` | Writer context (pillar + voice + audience + craft notes) |
|
|
18
|
-
| `draft.md` | Copy for **this platform only** |
|
|
19
|
-
| `editor.md` | REVISE / APPROVE |
|
|
20
|
-
| `rubric.md` | 6 × 0–2; SHIP ≥ 8 |
|
|
21
|
-
| `pack.md` | Image deck slots after SHIP (slideshow platforms) |
|
|
22
|
-
| `feedback.md` | URL + `win`/`flat`/`loss` after publish |
|
|
23
|
-
| `pack-<platform>.json` | Complete `content-pack.v1` — **only at render handoff** |
|
|
24
|
-
|
|
25
|
-
## `idea.md` frontmatter
|
|
26
|
-
|
|
27
|
-
```yaml
|
|
28
|
-
---
|
|
29
|
-
run_id: RUN-YYYYMMDD-slug
|
|
30
|
-
status: RUN_OPEN
|
|
31
|
-
created: YYYY-MM-DD
|
|
32
|
-
---
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
Body must include `primary_platform` equal to `platforms` (single value) and the three selection fields. `none` is allowed with why.
|
|
36
|
-
|
|
37
|
-
### File status vs index status
|
|
38
|
-
|
|
39
|
-
| `idea.md` | `runs/_index.md` |
|
|
40
|
-
|---|---|
|
|
41
|
-
| `RUN_OPEN` | `open` |
|
|
42
|
-
| `BRIEF_WAIT` | `brief` |
|
|
43
|
-
| `DRAFTED` / `EDIT_WAIT` | `draft` |
|
|
44
|
-
| `SCORED` | `scored` |
|
|
45
|
-
| `READY_TO_POST` | `ready` |
|
|
46
|
-
| `PUBLISHED` | `published` |
|
|
47
|
-
| `HOLD` / `KILLED` / `SUPERSEDED` | `hold` / `killed` / `superseded` |
|
|
48
|
-
|
|
49
|
-
Do not use index status `partial` for new runs.
|
|
50
|
-
|
|
51
|
-
## Validation
|
|
52
|
-
|
|
53
|
-
Semantic run (no Pack JSON yet):
|
|
54
|
-
|
|
55
|
-
```bash
|
|
56
|
-
node scripts/validate-run.mjs --run <run-dir> --json
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
Slideshow handoff (Pack JSON required):
|
|
60
|
-
|
|
61
|
-
```bash
|
|
62
|
-
node scripts/validate-run.mjs --run <run-dir> --require-pack --json
|
|
63
|
-
node scripts/validate-pack.mjs --file <run-dir>/pack-<platform>.json --platform <platform> --json
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
## Export path
|
|
67
|
-
|
|
68
|
-
Final PNGs at the **engine root**, not inside the run folder:
|
|
69
|
-
|
|
70
|
-
```text
|
|
71
|
-
Content Engine/exports/<run-slug>/01.png
|
|
72
|
-
Content Engine/exports/<run-slug>/<platform>/01.png # optional subfolder
|
|
73
|
-
```
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
# Troubleshooting
|
|
2
|
-
|
|
3
|
-
No Content CLI, Local API, SQLite, or Obsidian requirement.
|
|
4
|
-
|
|
5
|
-
If setup fails, stop before overwriting files. `python3` missing → install Python 3; starter files may already be written.
|
|
6
|
-
|
|
7
|
-
If port 8765 is in use, reuse `http://127.0.0.1:8765/workbench/` — do not start a second server. Rebuild with `python3 workbench/build.py`.
|
|
8
|
-
|
|
9
|
-
Never overwrite an existing `workbench/template.html`. `--apply` only adds missing files.
|
|
10
|
-
|
|
11
|
-
If a run is incomplete, keep existing markdown and report the missing file. `validate-run.mjs` / `validate-pack.mjs` list concrete gaps. Do not auto-migrate an existing engine.
|
|
12
|
-
|
|
13
|
-
Deferred features: `workflows/OUT_OF_SCOPE.md`.
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
# Engine layout
|
|
2
|
-
|
|
3
|
-
The skill creates one English root:
|
|
4
|
-
|
|
5
|
-
```text
|
|
6
|
-
<parent>/Content Engine/
|
|
7
|
-
```
|
|
8
|
-
|
|
9
|
-
`--root` is the **parent** directory, or the `Content Engine/` path itself.
|
|
10
|
-
|
|
11
|
-
```json
|
|
12
|
-
{
|
|
13
|
-
"schemaVersion": "content-engine.v1",
|
|
14
|
-
"locale": "en",
|
|
15
|
-
"root": "Content Engine",
|
|
16
|
-
"paths": { "runs": "runs", "exports": "exports", "workbench": "workbench" }
|
|
17
|
-
}
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
Folders: `accounts`, `inbox`, `swipe`, `topics`, `discovery`, `raw`, `profiles`, `published`, `wiki`, `platforms`, `runs`, `exports`, `workbench`.
|
|
21
|
-
|
|
22
|
-
Platform craft files: `x.md`, `ig.md` (`instagram` → `ig.md`), `tiktok.md`, `linkedin.md`, `threads.md`, `xiaohongshu.md`.
|
|
23
|
-
|
|
24
|
-
## Workbench
|
|
25
|
-
|
|
26
|
-
Copied from the skill package **only when missing**. Never overwrite an existing `workbench/template.html`.
|
|
27
|
-
|
|
28
|
-
```text
|
|
29
|
-
http://127.0.0.1:8765/workbench/
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
Rebuild with `python3 workbench/build.py` after markdown writes.
|
|
33
|
-
|
|
34
|
-
## Export path
|
|
35
|
-
|
|
36
|
-
```text
|
|
37
|
-
Content Engine/exports/<run-slug>/01.png
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
Exports are **not** nested under `runs/<slug>/`. See `references/RUN_LAYOUT.md`.
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
# Local voice vs hosted Profile
|
|
2
|
-
|
|
3
|
-
| Concept | Where | When | Purpose |
|
|
4
|
-
|---|---|---|---|
|
|
5
|
-
| **Local voice** | `profiles/<id>/voice.md` | First draft (optional) | Identity + tone for the Agent |
|
|
6
|
-
| **Local profile** | `profiles/<id>/` | Multi-brand | Pillars + audience + voice |
|
|
7
|
-
| **Hosted Brand Profile** | Gateway (`easysociable profiles …`) | First render | Visual identity: Theme, logo, quotas |
|
|
8
|
-
|
|
9
|
-
## Rules
|
|
10
|
-
|
|
11
|
-
1. **Setup does not create a hosted Profile.**
|
|
12
|
-
2. **Local voice is optional.** Ask once; user may skip.
|
|
13
|
-
3. **Hosted Profile is required for render**, owned by Template/Slideshow skills.
|
|
14
|
-
4. Never store API keys, passwords or Stripe tokens in `profiles/` or `accounts/`.
|
|
15
|
-
|
|
16
|
-
When writing `draft.md` or pack slots, read the resolved voice file and audience **core + this platform**.
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import { cpSync, existsSync, mkdirSync, rmSync } from 'node:fs'
|
|
4
|
-
import { join, relative, resolve, sep } from 'node:path'
|
|
5
|
-
|
|
6
|
-
const skillRoot = resolve(new URL('..', import.meta.url).pathname)
|
|
7
|
-
const include = ['SKILL.md', 'INSTALL.md', 'scripts', 'references', 'workflows', 'platforms', 'fixtures', 'starter', 'workbench']
|
|
8
|
-
|
|
9
|
-
function parseArgs(argv) {
|
|
10
|
-
let output = ''
|
|
11
|
-
let force = false
|
|
12
|
-
for (let i = 0; i < argv.length; i += 1) {
|
|
13
|
-
if (argv[i] === '--output') output = argv[++i] ?? ''
|
|
14
|
-
else if (argv[i] === '--force') force = true
|
|
15
|
-
else if (argv[i] === '--help' || argv[i] === '-h') return { help: true }
|
|
16
|
-
else throw new Error(`Unknown argument: ${argv[i]}`)
|
|
17
|
-
}
|
|
18
|
-
return { output, force }
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
try {
|
|
22
|
-
const options = parseArgs(process.argv.slice(2))
|
|
23
|
-
if (options.help) {
|
|
24
|
-
process.stdout.write('Usage: node package-skill.mjs --output <destination-dir> [--force]\n')
|
|
25
|
-
process.exit(0)
|
|
26
|
-
}
|
|
27
|
-
if (!options.output) throw new Error('--output is required')
|
|
28
|
-
|
|
29
|
-
const dest = resolve(options.output)
|
|
30
|
-
const sourceToDest = relative(skillRoot, dest)
|
|
31
|
-
const destToSource = relative(dest, skillRoot)
|
|
32
|
-
const touchesSource = dest === skillRoot
|
|
33
|
-
|| (!sourceToDest.startsWith(`..${sep}`) && sourceToDest !== '..')
|
|
34
|
-
|| (!destToSource.startsWith(`..${sep}`) && destToSource !== '..')
|
|
35
|
-
if (touchesSource) throw new Error('--output must be outside the skill source directory')
|
|
36
|
-
if (existsSync(dest) && !options.force) throw new Error('Output already exists; pass --force to replace it')
|
|
37
|
-
if (existsSync(dest)) rmSync(dest, { recursive: true, force: true })
|
|
38
|
-
mkdirSync(dest, { recursive: true })
|
|
39
|
-
|
|
40
|
-
for (const name of include) {
|
|
41
|
-
cpSync(join(skillRoot, name), join(dest, name), { recursive: true })
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
process.stdout.write(`Packaged skill → ${dest}\n`)
|
|
45
|
-
}
|
|
46
|
-
catch (error) {
|
|
47
|
-
console.error(error instanceof Error ? error.message : String(error))
|
|
48
|
-
process.exitCode = 1
|
|
49
|
-
}
|
|
@@ -1,200 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import { existsSync, mkdirSync, readdirSync, statSync, writeFileSync } from 'node:fs'
|
|
4
|
-
import { homedir } from 'node:os'
|
|
5
|
-
import { basename, join, resolve } from 'node:path'
|
|
6
|
-
import { ENGINE_ROOT, filesFor } from './starter.mjs'
|
|
7
|
-
import { buildWorkbench, ensureWorkbench, python3Bin, startWorkbench } from './workbench.mjs'
|
|
8
|
-
|
|
9
|
-
function parseArgs(argv) {
|
|
10
|
-
const options = {
|
|
11
|
-
root: '', engine: '', apply: false, validate: false, preview: false,
|
|
12
|
-
detect: false, noOpen: false, json: false, workbench: false,
|
|
13
|
-
}
|
|
14
|
-
for (let i = 0; i < argv.length; i += 1) {
|
|
15
|
-
const arg = argv[i]
|
|
16
|
-
if (arg === '--root') options.root = argv[++i] ?? ''
|
|
17
|
-
else if (arg === '--engine') options.engine = argv[++i] ?? ''
|
|
18
|
-
else if (arg === '--apply') options.apply = true
|
|
19
|
-
else if (arg === '--validate') options.validate = true
|
|
20
|
-
else if (arg === '--preview') options.preview = true
|
|
21
|
-
else if (arg === '--detect') options.detect = true
|
|
22
|
-
else if (arg === '--no-open') options.noOpen = true
|
|
23
|
-
else if (arg === '--workbench') options.workbench = true
|
|
24
|
-
else if (arg === '--json') options.json = true
|
|
25
|
-
else if (arg === '--help' || arg === '-h') options.help = true
|
|
26
|
-
else throw new Error(`Unknown argument: ${arg}`)
|
|
27
|
-
}
|
|
28
|
-
return options
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function output(value, json) {
|
|
32
|
-
process.stdout.write(json ? `${JSON.stringify(value, null, 2)}\n` : `${value.message ?? value}\n`)
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function isDir(path) {
|
|
36
|
-
return existsSync(path) && statSync(path).isDirectory()
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function isEngine(path) {
|
|
40
|
-
return isDir(path) && existsSync(join(path, 'CLAUDE.md')) && isDir(join(path, 'runs'))
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function resolveEngine(rootOrEngine) {
|
|
44
|
-
const absolute = resolve(rootOrEngine)
|
|
45
|
-
if (isEngine(absolute) || basename(absolute) === ENGINE_ROOT) return absolute
|
|
46
|
-
return join(absolute, ENGINE_ROOT)
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function searchRoots() {
|
|
50
|
-
const extra = (process.env.EASYSOCIABLE_ENGINE_SEARCH_ROOTS || '')
|
|
51
|
-
.split(process.platform === 'win32' ? ';' : ':')
|
|
52
|
-
.map((item) => item.trim())
|
|
53
|
-
.filter(Boolean)
|
|
54
|
-
const home = homedir()
|
|
55
|
-
return [...extra, join(home, 'Documents'), home]
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
function detectEngines() {
|
|
59
|
-
const found = []
|
|
60
|
-
for (const root of searchRoots()) {
|
|
61
|
-
if (!isDir(root)) continue
|
|
62
|
-
const direct = join(root, ENGINE_ROOT)
|
|
63
|
-
if (isEngine(direct) || isDir(direct)) found.push(direct)
|
|
64
|
-
try {
|
|
65
|
-
for (const name of readdirSync(root)) {
|
|
66
|
-
const child = join(root, name, ENGINE_ROOT)
|
|
67
|
-
if (isDir(child)) found.push(child)
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
catch {
|
|
71
|
-
continue
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
return [...new Set(found)]
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function inventory(engine) {
|
|
78
|
-
const files = filesFor()
|
|
79
|
-
const missing = []
|
|
80
|
-
const existing = []
|
|
81
|
-
for (const relative of Object.keys(files)) {
|
|
82
|
-
const path = join(engine, relative)
|
|
83
|
-
;(existsSync(path) ? existing : missing).push(relative)
|
|
84
|
-
}
|
|
85
|
-
return {
|
|
86
|
-
engine,
|
|
87
|
-
engineExists: isDir(engine),
|
|
88
|
-
rootName: ENGINE_ROOT,
|
|
89
|
-
missing,
|
|
90
|
-
existing,
|
|
91
|
-
starterComplete: missing.length === 0,
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
function validateRoot(rootOrEngine, { strict = false } = {}) {
|
|
96
|
-
const engine = resolveEngine(rootOrEngine)
|
|
97
|
-
const parent = resolve(engine, '..')
|
|
98
|
-
const errors = []
|
|
99
|
-
if (!existsSync(parent) || !statSync(parent).isDirectory()) {
|
|
100
|
-
errors.push('Parent directory does not exist')
|
|
101
|
-
return { engine, valid: false, errors, created: [] }
|
|
102
|
-
}
|
|
103
|
-
const result = inventory(engine)
|
|
104
|
-
if (existsSync(engine) && !isDir(engine)) errors.push('Content Engine exists but is not a directory')
|
|
105
|
-
if (strict) {
|
|
106
|
-
if (!result.engineExists) errors.push('Content Engine/ folder does not exist')
|
|
107
|
-
else if (result.missing.length > 0) {
|
|
108
|
-
errors.push(`Starter incomplete: ${result.missing.length} file(s) missing (use --apply to add only missing files)`)
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
return { ...result, valid: errors.length === 0, errors, created: [] }
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
async function applyStarter(rootOrEngine, noOpen) {
|
|
115
|
-
const engine = resolveEngine(rootOrEngine)
|
|
116
|
-
const checked = validateRoot(engine)
|
|
117
|
-
if (checked.code) return { ...checked, created: [] }
|
|
118
|
-
mkdirSync(engine, { recursive: true })
|
|
119
|
-
const report = inventory(engine)
|
|
120
|
-
const created = []
|
|
121
|
-
const files = filesFor()
|
|
122
|
-
for (const relative of report.missing) {
|
|
123
|
-
const path = join(engine, relative)
|
|
124
|
-
mkdirSync(join(path, '..'), { recursive: true })
|
|
125
|
-
writeFileSync(path, files[relative], { encoding: 'utf8', flag: 'wx' })
|
|
126
|
-
created.push(relative)
|
|
127
|
-
}
|
|
128
|
-
const workbench = ensureWorkbench(engine)
|
|
129
|
-
const python = python3Bin()
|
|
130
|
-
if (!python) {
|
|
131
|
-
return {
|
|
132
|
-
...validateRoot(engine),
|
|
133
|
-
created,
|
|
134
|
-
workbench,
|
|
135
|
-
action: 'applied',
|
|
136
|
-
valid: false,
|
|
137
|
-
code: 'PYTHON3_REQUIRED',
|
|
138
|
-
errors: ['python3 is required for the workbench. Install Python 3 and retry. Starter files were written; workbench was not started.'],
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
const built = buildWorkbench(engine)
|
|
142
|
-
let started = { skipped: true, build: built }
|
|
143
|
-
if (!noOpen && process.env.EASYSOCIABLE_NO_OPEN !== 'true') {
|
|
144
|
-
started = await startWorkbench(engine, { noOpen: false })
|
|
145
|
-
}
|
|
146
|
-
return { ...validateRoot(engine), created, workbench, workbenchBuild: built, workbenchStart: started, action: 'applied' }
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
function printHelp() {
|
|
150
|
-
process.stdout.write(`Usage:
|
|
151
|
-
node setup.mjs --detect [--json]
|
|
152
|
-
node setup.mjs --root <parent-or-engine> [--preview|--apply|--validate] [--workbench] [--no-open] [--json]
|
|
153
|
-
|
|
154
|
-
English Content Engine/ only. Existing files are never overwritten.
|
|
155
|
-
`)
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
try {
|
|
159
|
-
const options = parseArgs(process.argv.slice(2))
|
|
160
|
-
if (options.help) {
|
|
161
|
-
printHelp()
|
|
162
|
-
process.exit(0)
|
|
163
|
-
}
|
|
164
|
-
if (options.detect) {
|
|
165
|
-
output({ action: 'detect', engines: detectEngines() }, options.json)
|
|
166
|
-
process.exit(0)
|
|
167
|
-
}
|
|
168
|
-
const target = options.engine || options.root
|
|
169
|
-
if (!target) {
|
|
170
|
-
output({ code: 'ROOT_REQUIRED', message: 'Choose a parent directory or Content Engine path with --root.', created: false }, options.json)
|
|
171
|
-
process.exitCode = 2
|
|
172
|
-
}
|
|
173
|
-
else if (options.apply) {
|
|
174
|
-
const applied = await applyStarter(target, options.noOpen)
|
|
175
|
-
output(applied, options.json)
|
|
176
|
-
if (applied.code) process.exitCode = 2
|
|
177
|
-
}
|
|
178
|
-
else if (options.workbench) {
|
|
179
|
-
const engine = resolveEngine(target)
|
|
180
|
-
if (!isEngine(engine) && !isDir(engine)) {
|
|
181
|
-
output({ code: 'ENGINE_MISSING', message: 'Content Engine/ not found. Run --apply first.', created: false }, options.json)
|
|
182
|
-
process.exitCode = 2
|
|
183
|
-
}
|
|
184
|
-
else {
|
|
185
|
-
const started = await startWorkbench(engine, { noOpen: options.noOpen })
|
|
186
|
-
output({ action: 'workbench', ...started, engine }, options.json)
|
|
187
|
-
if (!started.ok) process.exitCode = 2
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
else {
|
|
191
|
-
const report = validateRoot(target, { strict: options.validate })
|
|
192
|
-
const action = options.validate ? 'validated' : 'preview'
|
|
193
|
-
output({ ...report, action, created: [] }, options.json)
|
|
194
|
-
if (report.code || (options.validate && !report.valid)) process.exitCode = 2
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
catch (error) {
|
|
198
|
-
output({ code: 'CONTENT_SETUP_FAILED', message: error instanceof Error ? error.message : String(error), created: false }, true)
|
|
199
|
-
process.exitCode = 1
|
|
200
|
-
}
|