@easysociable/cli 0.1.1 → 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.
Files changed (116) hide show
  1. package/README.md +29 -18
  2. package/content-engine/fixtures/golden-run-en/draft.md +13 -0
  3. package/content-engine/fixtures/golden-run-en/idea.md +26 -0
  4. package/content-engine/fixtures/golden-run-en/pack-tiktok.json +11 -0
  5. package/content-engine/scripts/contracts.mjs +183 -0
  6. package/content-engine/scripts/export-contracts.mjs +76 -0
  7. package/content-engine/scripts/fixture.test.mjs +24 -0
  8. package/content-engine/scripts/starter.mjs +55 -0
  9. package/content-engine/scripts/validate-export.mjs +42 -0
  10. package/content-engine/scripts/validate-pack.mjs +48 -0
  11. package/content-engine/scripts/validate-run.mjs +100 -0
  12. package/content-engine/scripts/validate.test.mjs +101 -0
  13. package/content-engine/scripts/workbench.mjs +135 -0
  14. package/content-engine/starter/CLAUDE.md +756 -0
  15. package/content-engine/starter/accounts/README.md +9 -0
  16. package/content-engine/starter/accounts/_index.md +31 -0
  17. package/content-engine/starter/accounts/overrides/README.md +3 -0
  18. package/content-engine/starter/discovery/README.md +18 -0
  19. package/content-engine/starter/discovery/_index.md +27 -0
  20. package/content-engine/starter/discovery/_template.md +41 -0
  21. package/content-engine/starter/engine.json +19 -0
  22. package/content-engine/starter/exports/README.md +28 -0
  23. package/content-engine/starter/inbox/README.md +9 -0
  24. package/content-engine/starter/inbox/_index.md +24 -0
  25. package/content-engine/starter/inbox/entries/README.md +3 -0
  26. package/content-engine/starter/platforms/README.md +19 -0
  27. package/content-engine/starter/platforms/ig.md +69 -0
  28. package/content-engine/starter/platforms/instagram.md +5 -0
  29. package/content-engine/starter/platforms/linkedin.md +72 -0
  30. package/content-engine/starter/platforms/threads.md +69 -0
  31. package/content-engine/starter/platforms/tiktok.md +71 -0
  32. package/content-engine/starter/platforms/x.md +78 -0
  33. package/content-engine/starter/platforms/xiaohongshu.md +19 -0
  34. package/content-engine/starter/profiles/_index.md +13 -0
  35. package/content-engine/starter/profiles/default/README.md +11 -0
  36. package/content-engine/starter/profiles/default/audience.md +38 -0
  37. package/content-engine/starter/profiles/default/pillars.md +33 -0
  38. package/content-engine/starter/profiles/default/voice.md +24 -0
  39. package/content-engine/starter/published/_index.md +20 -0
  40. package/content-engine/starter/raw/README.md +4 -0
  41. package/content-engine/starter/raw/_index.md +12 -0
  42. package/content-engine/starter/runs/README.md +22 -0
  43. package/content-engine/starter/runs/_index.md +41 -0
  44. package/content-engine/starter/runs/_template/brief.md +66 -0
  45. package/content-engine/starter/runs/_template/draft.md +11 -0
  46. package/content-engine/starter/runs/_template/editor.md +18 -0
  47. package/content-engine/starter/runs/_template/feedback.md +15 -0
  48. package/content-engine/starter/runs/_template/idea.md +27 -0
  49. package/content-engine/starter/runs/_template/pack.md +47 -0
  50. package/content-engine/starter/runs/_template/packet.md +25 -0
  51. package/content-engine/starter/runs/_template/rubric.md +19 -0
  52. package/content-engine/starter/swipe/_index.md +27 -0
  53. package/content-engine/starter/swipe/_template.md +34 -0
  54. package/content-engine/starter/topics/README.md +25 -0
  55. package/content-engine/starter/topics/WATCHLIST.md +65 -0
  56. package/content-engine/starter/topics/_index.md +44 -0
  57. package/content-engine/starter/topics/_template-item.md +90 -0
  58. package/content-engine/starter/topics/items/README.md +4 -0
  59. package/content-engine/starter/topics/scoring.md +201 -0
  60. package/content-engine/starter/topics/watchlists/README.md +10 -0
  61. package/content-engine/starter/topics/watchlists/_shared.md +14 -0
  62. package/content-engine/starter/topics/watchlists/default.md +6 -0
  63. package/content-engine/starter/wiki/README.md +13 -0
  64. package/content-engine/starter/wiki/atoms/_index.md +23 -0
  65. package/content-engine/starter/wiki/atoms/_template.md +31 -0
  66. package/content-engine/starter/wiki/claims/_index.md +26 -0
  67. package/content-engine/starter/wiki/claims/_template.md +38 -0
  68. package/content-engine/starter/workbench/README.md +17 -0
  69. package/content-engine/workbench/build.py +1074 -0
  70. package/content-engine/workbench/serve.py +53 -0
  71. package/content-engine/workbench/template.html +2119 -0
  72. package/dist/easysociable.js +804 -2903
  73. package/dist/easysociable.js.map +1 -1
  74. package/package.json +19 -13
  75. package/skills/ONBOARDING.md +20 -14
  76. package/skills/easysociable-content/SKILL.md +38 -102
  77. package/skills/easysociable-slideshow/SKILL.md +24 -16
  78. package/skills/easysociable-slideshow/references/VAULT.md +13 -11
  79. package/skills/easysociable-template/SKILL.md +26 -24
  80. package/skills/easysociable-template/references/CATALOG_METADATA.md +2 -2
  81. package/skills/easysociable-template/references/DESIGN_V2_1.md +53 -2
  82. package/skills/easysociable-template/references/GATEWAY.md +7 -2
  83. package/skills/easysociable-template/references/PLATFORM_SPECS.md +11 -3
  84. package/skills/easysociable-template/references/THEME_TOKENS.md +1 -1
  85. package/skills/easysociable-template/references/fixtures/image-reference.design-plan.json +74 -0
  86. package/skills/easysociable-template/references/fixtures/image-reference.visual-qa.json +28 -0
  87. package/skills/easysociable-template/scripts/generate-theme-from-hex.mjs +0 -0
  88. package/skills/easysociable-template/scripts/validate-design-v2.mjs +97 -0
  89. package/skills/easysociable-template/workflows/ASSET_PLAN.md +2 -1
  90. package/skills/easysociable-template/workflows/AUTHORING_FROM_BRIEF.md +4 -4
  91. package/skills/easysociable-template/workflows/AUTHORING_FROM_IMAGE.md +66 -16
  92. package/skills/easysociable-template/workflows/AUTHORING_STUDIO.md +28 -15
  93. package/skills/easysociable-template/workflows/DESIGN_BRIEF.md +4 -3
  94. package/skills/easysociable-template/workflows/MULTI_PLATFORM_FAMILY.md +81 -0
  95. package/skills/easysociable-template/workflows/PUBLISH_FROM_DRAFT.md +1 -1
  96. package/skills/easysociable-template/workflows/VISUAL_QA.md +10 -0
  97. package/skills/easysociable-content/platforms/claude.md +0 -5
  98. package/skills/easysociable-content/platforms/codex.md +0 -5
  99. package/skills/easysociable-content/platforms/hermes.md +0 -5
  100. package/skills/easysociable-content/platforms/openclaw.md +0 -5
  101. package/skills/easysociable-content/references/CLI.md +0 -27
  102. package/skills/easysociable-content/references/MCP.md +0 -20
  103. package/skills/easysociable-content/references/PAGE_PACK.md +0 -6
  104. package/skills/easysociable-content/references/TROUBLESHOOTING.md +0 -11
  105. package/skills/easysociable-content/references/VAULT.md +0 -20
  106. package/skills/easysociable-content/scripts/setup.mjs +0 -156
  107. package/skills/easysociable-content/scripts/setup.test.mjs +0 -71
  108. package/skills/easysociable-content/scripts/starter.mjs +0 -53
  109. package/skills/easysociable-content/workflows/ANALYSIS_AND_COVERAGE.md +0 -8
  110. package/skills/easysociable-content/workflows/CONTENT_EXPORT.md +0 -19
  111. package/skills/easysociable-content/workflows/DEVICE_TASKS.md +0 -5
  112. package/skills/easysociable-content/workflows/EXPERIMENTS.md +0 -6
  113. package/skills/easysociable-content/workflows/MCP_FIRST.md +0 -10
  114. package/skills/easysociable-content/workflows/PUBLICATION.md +0 -5
  115. package/skills/easysociable-content/workflows/TOPIC_DRAFT.md +0 -5
  116. package/skills/easysociable-content/workflows/WORKING_DRAFT.md +0 -10
@@ -0,0 +1,101 @@
1
+ import assert from 'node:assert/strict'
2
+ import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'
3
+ import { tmpdir } from 'node:os'
4
+ import { join } from 'node:path'
5
+ import { spawnSync } from 'node:child_process'
6
+ import { validateContentPack } from './contracts.mjs'
7
+ import { validateExportDirectory } from './export-contracts.mjs'
8
+
9
+ const root = new URL('.', import.meta.url).pathname
10
+ const validatePack = join(root, 'validate-pack.mjs')
11
+ const validateRun = join(root, 'validate-run.mjs')
12
+ const fixture = mkdtempSync(join(tmpdir(), 'easysociable-validate-'))
13
+
14
+ function run(script, args) {
15
+ return spawnSync(process.execPath, [script, ...args, '--json'], { encoding: 'utf8' })
16
+ }
17
+
18
+ function ideaMd({ platform = 'tiktok', status = 'pack-ready' } = {}) {
19
+ return `---
20
+ run_id: RUN-demo
21
+ status: ${status}
22
+ created: 2026-08-28
23
+ ---
24
+
25
+ # Idea
26
+
27
+ - **one_liner:** Demo
28
+ - **primary_platform:** ${platform}
29
+ - **platforms:** ${platform}
30
+ - **swipe_id:** none
31
+ - **atoms:** none
32
+ - **claim_id:** none
33
+ `
34
+ }
35
+
36
+ const tiktokPack = {
37
+ schemaVersion: 'content-pack.v1',
38
+ platform: 'tiktok',
39
+ title: 'Demo',
40
+ pages: [
41
+ { role: 'hook', pageTemplateId: 'tpl_hook', slots: { text_0: 'a' } },
42
+ { role: 'value', pageTemplateId: 'tpl_value', slots: { text_0: 'b' } },
43
+ { role: 'value', pageTemplateId: 'tpl_value', slots: { text_0: 'c' } },
44
+ { role: 'cta', pageTemplateId: 'tpl_cta', slots: { text_0: 'd' } },
45
+ ],
46
+ }
47
+
48
+ try {
49
+ const ok = validateContentPack(tiktokPack)
50
+ assert.equal(ok.valid, true)
51
+
52
+ const short = validateContentPack({ ...tiktokPack, pages: tiktokPack.pages.slice(0, 3) })
53
+ assert.equal(short.valid, false)
54
+
55
+ const forbidden = validateContentPack({ ...tiktokPack, deck_template_id: 'x' })
56
+ assert.equal(forbidden.valid, false)
57
+
58
+ const badSlot = validateContentPack({ ...tiktokPack, pages: [{ ...tiktokPack.pages[0], slots: { text_0: [] } }, ...tiktokPack.pages.slice(1)] })
59
+ assert.equal(badSlot.valid, false)
60
+
61
+ const missingTemplate = validateContentPack({ ...tiktokPack, pages: [{ role: 'hook', slots: {} }, ...tiktokPack.pages.slice(1)] })
62
+ assert.equal(missingTemplate.valid, false)
63
+
64
+ const packFile = join(fixture, 'pack-tiktok.json')
65
+ writeFileSync(packFile, JSON.stringify(tiktokPack), 'utf8')
66
+ const packCli = run(validatePack, ['--file', packFile, '--platform', 'tiktok'])
67
+ assert.equal(packCli.status, 0)
68
+
69
+ const runDir = join(fixture, 'demo-run')
70
+ mkdirSync(runDir)
71
+ writeFileSync(join(runDir, 'idea.md'), ideaMd(), 'utf8')
72
+ writeFileSync(join(runDir, 'draft.md'), '# Draft\n', 'utf8')
73
+ writeFileSync(join(runDir, 'pack-tiktok.json'), JSON.stringify(tiktokPack), 'utf8')
74
+
75
+ const runCli = run(validateRun, ['--run', runDir, '--require-pack'])
76
+ assert.equal(runCli.status, 0)
77
+ const runReport = JSON.parse(runCli.stdout)
78
+ assert.equal(runReport.valid, true)
79
+
80
+ rmSync(join(runDir, 'pack-tiktok.json'))
81
+ const missingPack = run(validateRun, ['--run', runDir, '--require-pack'])
82
+ assert.equal(missingPack.status, 2)
83
+
84
+ const exportDir = join(fixture, 'exported')
85
+ mkdirSync(exportDir)
86
+ writeFileSync(join(exportDir, '01.png'), Buffer.from('89504e470d0a1a0a', 'hex'))
87
+ writeFileSync(join(exportDir, 'manifest.json'), JSON.stringify({ schema: 'template-export.v1', artifacts: [{ pageIndex: 0 }] }))
88
+ assert.equal(validateExportDirectory(exportDir, { expectedPages: 1 }).valid, true)
89
+
90
+ const exportedRun = join(fixture, 'exported-run')
91
+ mkdirSync(exportedRun)
92
+ writeFileSync(join(exportedRun, 'idea.md'), ideaMd({ platform: 'x', status: 'exported' }), 'utf8')
93
+ writeFileSync(join(exportedRun, 'draft.md'), '# Draft\n', 'utf8')
94
+ const exportedCli = run(validateRun, ['--run', exportedRun, '--export', exportDir, '--require-export'])
95
+ assert.equal(exportedCli.status, 0, exportedCli.stdout)
96
+
97
+ console.log('Content Skill validate smoke passed.')
98
+ }
99
+ finally {
100
+ rmSync(fixture, { recursive: true, force: true })
101
+ }
@@ -0,0 +1,135 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { spawn, spawnSync } from 'node:child_process'
4
+ import { existsSync, mkdirSync, copyFileSync } from 'node:fs'
5
+ import net from 'node:net'
6
+ import { pathToFileURL } from 'node:url'
7
+ import { join, resolve } from 'node:path'
8
+ import { WORKBENCH_FILES, WORKBENCH_SRC } from './starter.mjs'
9
+
10
+ export const WORKBENCH_PORT = 8765
11
+ export const WORKBENCH_URL = `http://127.0.0.1:${WORKBENCH_PORT}/workbench/`
12
+
13
+ function output(value, json) {
14
+ process.stdout.write(json ? `${JSON.stringify(value, null, 2)}\n` : `${value.message ?? value}\n`)
15
+ }
16
+
17
+ export function python3Bin() {
18
+ const result = spawnSync('python3', ['--version'], { encoding: 'utf8' })
19
+ if (result.status === 0) return 'python3'
20
+ return ''
21
+ }
22
+
23
+ export function portInUse(port = WORKBENCH_PORT) {
24
+ return new Promise((resolvePort) => {
25
+ const socket = net.connect({ port, host: '127.0.0.1' }, () => {
26
+ socket.end()
27
+ resolvePort(true)
28
+ })
29
+ socket.on('error', () => resolvePort(false))
30
+ })
31
+ }
32
+
33
+ export function ensureWorkbench(engine) {
34
+ const destDir = join(engine, 'workbench')
35
+ mkdirSync(destDir, { recursive: true })
36
+ const copied = []
37
+ const skipped = []
38
+ for (const name of WORKBENCH_FILES) {
39
+ const dest = join(destDir, name)
40
+ if (existsSync(dest)) {
41
+ skipped.push(name)
42
+ continue
43
+ }
44
+ copyFileSync(join(WORKBENCH_SRC, name), dest)
45
+ copied.push(name)
46
+ }
47
+ return { destDir, copied, skipped }
48
+ }
49
+
50
+ export function buildWorkbench(engine) {
51
+ const python = python3Bin()
52
+ if (!python) {
53
+ return { ok: false, code: 'PYTHON3_REQUIRED', message: 'python3 is required for the workbench. Install Python 3 and retry.' }
54
+ }
55
+ const script = join(engine, 'workbench', 'build.py')
56
+ if (!existsSync(script)) {
57
+ return { ok: false, code: 'WORKBENCH_MISSING', message: 'workbench/build.py is missing. Re-run setup --apply.' }
58
+ }
59
+ const result = spawnSync(python, [script], { encoding: 'utf8', cwd: engine })
60
+ if (result.status !== 0) {
61
+ return { ok: false, code: 'WORKBENCH_BUILD_FAILED', message: result.stderr.trim() || result.stdout.trim() || 'workbench build failed' }
62
+ }
63
+ return { ok: true, message: result.stdout.trim() }
64
+ }
65
+
66
+ export async function startWorkbench(engine, { noOpen = false } = {}) {
67
+ const python = python3Bin()
68
+ if (!python) {
69
+ return { ok: false, code: 'PYTHON3_REQUIRED', message: 'python3 is required for the workbench. Install Python 3 and retry.' }
70
+ }
71
+ ensureWorkbench(engine)
72
+ const built = buildWorkbench(engine)
73
+ if (!built.ok) return built
74
+ const url = WORKBENCH_URL
75
+ if (await portInUse()) {
76
+ if (!noOpen && process.env.EASYSOCIABLE_NO_OPEN !== 'true') openUrl(url)
77
+ return { ok: true, alreadyRunning: true, url, build: built.message }
78
+ }
79
+ const args = [join(engine, 'workbench', 'serve.py')]
80
+ if (noOpen) args.push('--no-open')
81
+ const child = spawn(python, args, { cwd: engine, detached: true, stdio: 'ignore' })
82
+ child.unref()
83
+ return { ok: true, alreadyRunning: false, url, pid: child.pid, build: built.message }
84
+ }
85
+
86
+ function openUrl(url) {
87
+ const command = process.platform === 'darwin' ? 'open' : process.platform === 'win32' ? 'cmd' : 'xdg-open'
88
+ const args = process.platform === 'win32' ? ['/c', 'start', url] : [url]
89
+ spawn(command, args, { stdio: 'ignore', detached: true }).unref()
90
+ }
91
+
92
+ function parseArgs(argv) {
93
+ const options = { engine: '', command: '', noOpen: false, json: false }
94
+ for (let i = 0; i < argv.length; i += 1) {
95
+ const arg = argv[i]
96
+ if (arg === '--engine') options.engine = argv[++i] ?? ''
97
+ else if (arg === '--no-open') options.noOpen = true
98
+ else if (arg === '--json') options.json = true
99
+ else if (arg === '--help' || arg === '-h') options.help = true
100
+ else if (!arg.startsWith('--') && !options.command) options.command = arg
101
+ else throw new Error(`Unknown argument: ${arg}`)
102
+ }
103
+ return options
104
+ }
105
+
106
+ async function main() {
107
+ const options = parseArgs(process.argv.slice(2))
108
+ if (options.help) {
109
+ output({ usage: 'node workbench.mjs build|start --engine <Content Engine> [--no-open] [--json]' }, false)
110
+ return
111
+ }
112
+ if (!options.engine) throw new Error('--engine is required')
113
+ const engine = resolve(options.engine)
114
+ if (options.command === 'build') {
115
+ ensureWorkbench(engine)
116
+ const built = buildWorkbench(engine)
117
+ output(built, options.json)
118
+ if (!built.ok) process.exitCode = 2
119
+ return
120
+ }
121
+ if (options.command === 'start' || options.command === '') {
122
+ const started = await startWorkbench(engine, { noOpen: options.noOpen })
123
+ output(started, options.json)
124
+ if (!started.ok) process.exitCode = 2
125
+ return
126
+ }
127
+ throw new Error('Command must be build or start')
128
+ }
129
+
130
+ if (process.argv[1] && import.meta.url === pathToFileURL(resolve(process.argv[1])).href) {
131
+ main().catch((error) => {
132
+ output({ code: 'WORKBENCH_FAILED', message: error instanceof Error ? error.message : String(error) }, true)
133
+ process.exitCode = 1
134
+ })
135
+ }