@adhamalkhaja/seyola-runtime 0.11.4
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/LICENSE +56 -0
- package/README.md +87 -0
- package/dist/adapters/claude-code-local/index.d.ts +60 -0
- package/dist/adapters/claude-code-local/index.d.ts.map +1 -0
- package/dist/adapters/claude-code-local/index.js +270 -0
- package/dist/adapters/claude-code-local/index.js.map +1 -0
- package/dist/bundle/index.d.ts +60 -0
- package/dist/bundle/index.d.ts.map +1 -0
- package/dist/bundle/index.js +989 -0
- package/dist/bundle/index.js.map +1 -0
- package/dist/cli/index.d.ts +12 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +207 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/compile-plan/budgets.d.ts +23 -0
- package/dist/compile-plan/budgets.d.ts.map +1 -0
- package/dist/compile-plan/budgets.js +55 -0
- package/dist/compile-plan/budgets.js.map +1 -0
- package/dist/compile-plan/graph.d.ts +40 -0
- package/dist/compile-plan/graph.d.ts.map +1 -0
- package/dist/compile-plan/graph.js +114 -0
- package/dist/compile-plan/graph.js.map +1 -0
- package/dist/compile-plan/index.d.ts +28 -0
- package/dist/compile-plan/index.d.ts.map +1 -0
- package/dist/compile-plan/index.js +423 -0
- package/dist/compile-plan/index.js.map +1 -0
- package/dist/compile-plan/registries.d.ts +35 -0
- package/dist/compile-plan/registries.d.ts.map +1 -0
- package/dist/compile-plan/registries.js +81 -0
- package/dist/compile-plan/registries.js.map +1 -0
- package/dist/compile-plan/resolver.d.ts +24 -0
- package/dist/compile-plan/resolver.d.ts.map +1 -0
- package/dist/compile-plan/resolver.js +57 -0
- package/dist/compile-plan/resolver.js.map +1 -0
- package/dist/compile-plan/types.d.ts +192 -0
- package/dist/compile-plan/types.d.ts.map +1 -0
- package/dist/compile-plan/types.js +7 -0
- package/dist/compile-plan/types.js.map +1 -0
- package/dist/doctor/index.d.ts +43 -0
- package/dist/doctor/index.d.ts.map +1 -0
- package/dist/doctor/index.js +224 -0
- package/dist/doctor/index.js.map +1 -0
- package/dist/init/index.d.ts +53 -0
- package/dist/init/index.d.ts.map +1 -0
- package/dist/init/index.js +414 -0
- package/dist/init/index.js.map +1 -0
- package/dist/lib/ajvSetup.d.ts +30 -0
- package/dist/lib/ajvSetup.d.ts.map +1 -0
- package/dist/lib/ajvSetup.js +44 -0
- package/dist/lib/ajvSetup.js.map +1 -0
- package/dist/lib/loadJson.d.ts +21 -0
- package/dist/lib/loadJson.d.ts.map +1 -0
- package/dist/lib/loadJson.js +43 -0
- package/dist/lib/loadJson.js.map +1 -0
- package/dist/lib/loadYaml.d.ts +18 -0
- package/dist/lib/loadYaml.d.ts.map +1 -0
- package/dist/lib/loadYaml.js +41 -0
- package/dist/lib/loadYaml.js.map +1 -0
- package/dist/lib/paths.d.ts +22 -0
- package/dist/lib/paths.d.ts.map +1 -0
- package/dist/lib/paths.js +61 -0
- package/dist/lib/paths.js.map +1 -0
- package/dist/run-plan/index.d.ts +17 -0
- package/dist/run-plan/index.d.ts.map +1 -0
- package/dist/run-plan/index.js +235 -0
- package/dist/run-plan/index.js.map +1 -0
- package/dist/run-plan/types.d.ts +53 -0
- package/dist/run-plan/types.d.ts.map +1 -0
- package/dist/run-plan/types.js +6 -0
- package/dist/run-plan/types.js.map +1 -0
- package/dist/run-step/contextPacket.d.ts +39 -0
- package/dist/run-step/contextPacket.d.ts.map +1 -0
- package/dist/run-step/contextPacket.js +213 -0
- package/dist/run-step/contextPacket.js.map +1 -0
- package/dist/run-step/derivation.d.ts +41 -0
- package/dist/run-step/derivation.d.ts.map +1 -0
- package/dist/run-step/derivation.js +61 -0
- package/dist/run-step/derivation.js.map +1 -0
- package/dist/run-step/effectExecutor.d.ts +42 -0
- package/dist/run-step/effectExecutor.d.ts.map +1 -0
- package/dist/run-step/effectExecutor.js +297 -0
- package/dist/run-step/effectExecutor.js.map +1 -0
- package/dist/run-step/formatExecutor.d.ts +34 -0
- package/dist/run-step/formatExecutor.d.ts.map +1 -0
- package/dist/run-step/formatExecutor.js +329 -0
- package/dist/run-step/formatExecutor.js.map +1 -0
- package/dist/run-step/index.d.ts +23 -0
- package/dist/run-step/index.d.ts.map +1 -0
- package/dist/run-step/index.js +627 -0
- package/dist/run-step/index.js.map +1 -0
- package/dist/run-step/inputResolver.d.ts +48 -0
- package/dist/run-step/inputResolver.d.ts.map +1 -0
- package/dist/run-step/inputResolver.js +268 -0
- package/dist/run-step/inputResolver.js.map +1 -0
- package/dist/run-step/types.d.ts +123 -0
- package/dist/run-step/types.d.ts.map +1 -0
- package/dist/run-step/types.js +6 -0
- package/dist/run-step/types.js.map +1 -0
- package/dist/validate-pack/checks/capabilitiesRegistry.d.ts +3 -0
- package/dist/validate-pack/checks/capabilitiesRegistry.d.ts.map +1 -0
- package/dist/validate-pack/checks/capabilitiesRegistry.js +83 -0
- package/dist/validate-pack/checks/capabilitiesRegistry.js.map +1 -0
- package/dist/validate-pack/checks/contextPolicies.d.ts +3 -0
- package/dist/validate-pack/checks/contextPolicies.d.ts.map +1 -0
- package/dist/validate-pack/checks/contextPolicies.js +40 -0
- package/dist/validate-pack/checks/contextPolicies.js.map +1 -0
- package/dist/validate-pack/checks/mvkExamples.d.ts +10 -0
- package/dist/validate-pack/checks/mvkExamples.d.ts.map +1 -0
- package/dist/validate-pack/checks/mvkExamples.js +77 -0
- package/dist/validate-pack/checks/mvkExamples.js.map +1 -0
- package/dist/validate-pack/checks/requiredFiles.d.ts +3 -0
- package/dist/validate-pack/checks/requiredFiles.d.ts.map +1 -0
- package/dist/validate-pack/checks/requiredFiles.js +35 -0
- package/dist/validate-pack/checks/requiredFiles.js.map +1 -0
- package/dist/validate-pack/checks/resolutionPolicy.d.ts +3 -0
- package/dist/validate-pack/checks/resolutionPolicy.d.ts.map +1 -0
- package/dist/validate-pack/checks/resolutionPolicy.js +88 -0
- package/dist/validate-pack/checks/resolutionPolicy.js.map +1 -0
- package/dist/validate-pack/checks/schemas.d.ts +16 -0
- package/dist/validate-pack/checks/schemas.d.ts.map +1 -0
- package/dist/validate-pack/checks/schemas.js +70 -0
- package/dist/validate-pack/checks/schemas.js.map +1 -0
- package/dist/validate-pack/index.d.ts +25 -0
- package/dist/validate-pack/index.d.ts.map +1 -0
- package/dist/validate-pack/index.js +95 -0
- package/dist/validate-pack/index.js.map +1 -0
- package/dist/validate-pack/reporting.d.ts +6 -0
- package/dist/validate-pack/reporting.d.ts.map +1 -0
- package/dist/validate-pack/reporting.js +40 -0
- package/dist/validate-pack/reporting.js.map +1 -0
- package/dist/validate-pack/types.d.ts +34 -0
- package/dist/validate-pack/types.d.ts.map +1 -0
- package/dist/validate-pack/types.js +8 -0
- package/dist/validate-pack/types.js.map +1 -0
- package/package.json +54 -0
|
@@ -0,0 +1,989 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* seyola-runtime bundle — generate a Client Alpha distribution folder.
|
|
3
|
+
*
|
|
4
|
+
* The bundle is meant to be zipped/copied to a friendly tester. The tester
|
|
5
|
+
* unzips, runs setup.ps1 (or setup.sh), and gets a working workspace.
|
|
6
|
+
*
|
|
7
|
+
* What bundle does:
|
|
8
|
+
* 1. Validate the pack (refuse to bundle from a broken pack).
|
|
9
|
+
* 2. Create the output directory if missing.
|
|
10
|
+
* 3. Create empty member-owned folders if missing (preserve if exist).
|
|
11
|
+
* 4. Copy the pack into <out>/.seyola/pack/.
|
|
12
|
+
* 5. Drop a runtime-info note at <out>/.seyola/runtime/.
|
|
13
|
+
* 6. Write <out>/.seyola/version.json with bundle metadata.
|
|
14
|
+
* 7. Write setup.ps1 (Windows) and setup.sh (mac/linux).
|
|
15
|
+
* 8. Write README.md and GETTING-STARTED.md only if missing.
|
|
16
|
+
*
|
|
17
|
+
* What bundle does NOT do:
|
|
18
|
+
* - Run init. Init runs on the tester's machine, via setup script.
|
|
19
|
+
* - Touch .claude/ — init creates .claude/skills + .claude/settings.json
|
|
20
|
+
* using the TESTER's workspace path.
|
|
21
|
+
* - Bundle Node.js or seyola-runtime itself. Setup script checks them
|
|
22
|
+
* and fails loudly with install instructions if missing.
|
|
23
|
+
*
|
|
24
|
+
* Exposed skills (set when init runs on the tester's side):
|
|
25
|
+
* linkedin-writer, brand-voice, audience-profile-builder, story-bank-builder.
|
|
26
|
+
*/
|
|
27
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync, statSync, readdirSync, copyFileSync, rmSync } from "node:fs";
|
|
28
|
+
import { join, resolve, dirname } from "node:path";
|
|
29
|
+
import { validatePack } from "../validate-pack/index.js";
|
|
30
|
+
const APPROVED_SKILLS = ["linkedin-writer", "brand-voice", "audience-profile-builder", "story-bank-builder"];
|
|
31
|
+
const MEMBER_OWNED_DIRS = ["_foundations", "market", "market/audience-profiles", "market/language-bank", "clients", "runs", "output"];
|
|
32
|
+
export async function runBundle(args) {
|
|
33
|
+
const start = Date.now();
|
|
34
|
+
const out = resolve(args.outPath);
|
|
35
|
+
const pack = resolve(args.packPath);
|
|
36
|
+
const bundleName = args.bundleName ?? "seyola-client-alpha";
|
|
37
|
+
const result = {
|
|
38
|
+
exitCode: 0,
|
|
39
|
+
status: "success",
|
|
40
|
+
out_path: out,
|
|
41
|
+
pack_path: pack,
|
|
42
|
+
pack_version: "",
|
|
43
|
+
runtime_version: readRuntimeVersion(),
|
|
44
|
+
bundle_name: bundleName,
|
|
45
|
+
files_written: [],
|
|
46
|
+
runtime_bundled: false,
|
|
47
|
+
warnings: [],
|
|
48
|
+
errors: [],
|
|
49
|
+
duration_ms: 0,
|
|
50
|
+
};
|
|
51
|
+
// 1. Pack must exist.
|
|
52
|
+
if (!existsSync(pack) || !statSync(pack).isDirectory()) {
|
|
53
|
+
result.exitCode = 1;
|
|
54
|
+
result.status = "failed";
|
|
55
|
+
result.errors.push({ code: "pack_not_found", message: `--pack '${pack}' does not exist or is not a directory.` });
|
|
56
|
+
return finalize(args, result, start);
|
|
57
|
+
}
|
|
58
|
+
// 2. Read pack version.
|
|
59
|
+
const manifestPath = join(pack, "manifests", "universal.pack.json");
|
|
60
|
+
if (!existsSync(manifestPath)) {
|
|
61
|
+
result.exitCode = 1;
|
|
62
|
+
result.status = "failed";
|
|
63
|
+
result.errors.push({ code: "pack_manifest_missing", message: `Pack manifest not found at ${manifestPath}.` });
|
|
64
|
+
return finalize(args, result, start);
|
|
65
|
+
}
|
|
66
|
+
try {
|
|
67
|
+
const manifest = JSON.parse(readFileSync(manifestPath, "utf8"));
|
|
68
|
+
result.pack_version = manifest.version ?? "unknown";
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
result.warnings.push({ code: "pack_manifest_read_warning", message: "Could not read pack version." });
|
|
72
|
+
}
|
|
73
|
+
// 3. Pack must validate clean.
|
|
74
|
+
const validation = await validatePack({ packPath: pack, json: true });
|
|
75
|
+
if (validation.exitCode !== 0) {
|
|
76
|
+
result.exitCode = 1;
|
|
77
|
+
result.status = "failed";
|
|
78
|
+
result.errors.push({ code: "pack_validation_failed", message: `Pack at '${pack}' fails validate-pack. Refusing to bundle from a broken pack.` });
|
|
79
|
+
return finalize(args, result, start);
|
|
80
|
+
}
|
|
81
|
+
// 4. Output dir.
|
|
82
|
+
if (!existsSync(out)) {
|
|
83
|
+
mkdirSync(out, { recursive: true });
|
|
84
|
+
}
|
|
85
|
+
// 5. Member-owned dirs (preserve on rerun). Each gets a .gitkeep so the
|
|
86
|
+
// directory is tracked by git even when empty.
|
|
87
|
+
for (const d of MEMBER_OWNED_DIRS) {
|
|
88
|
+
const abs = join(out, d);
|
|
89
|
+
if (!existsSync(abs))
|
|
90
|
+
mkdirSync(abs, { recursive: true });
|
|
91
|
+
const keep = join(abs, ".gitkeep");
|
|
92
|
+
if (!existsSync(keep)) {
|
|
93
|
+
writeFileSync(keep, "", "utf8");
|
|
94
|
+
result.files_written.push(`${d}/.gitkeep`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
// 5a. Write .gitignore that protects member-owned folders. The bundle is
|
|
98
|
+
// distributed via git (or zip), so we must not track member-generated
|
|
99
|
+
// content. Always rewritten — system-owned.
|
|
100
|
+
const gitignore = bundleGitignore();
|
|
101
|
+
writeFileSync(join(out, ".gitignore"), gitignore, "utf8");
|
|
102
|
+
result.files_written.push(".gitignore");
|
|
103
|
+
// 6. .seyola/ tree (system-owned, replaced on every bundle).
|
|
104
|
+
const seyolaDir = join(out, ".seyola");
|
|
105
|
+
const seyolaPackDest = join(seyolaDir, "pack");
|
|
106
|
+
const seyolaRuntimeDest = join(seyolaDir, "runtime");
|
|
107
|
+
if (existsSync(seyolaPackDest))
|
|
108
|
+
rmSync(seyolaPackDest, { recursive: true, force: true });
|
|
109
|
+
mkdirSync(seyolaPackDest, { recursive: true });
|
|
110
|
+
copyAlphaPack(pack, seyolaPackDest);
|
|
111
|
+
result.files_written.push(".seyola/pack/ (allowlist copy from " + pack + ")");
|
|
112
|
+
if (existsSync(seyolaRuntimeDest))
|
|
113
|
+
rmSync(seyolaRuntimeDest, { recursive: true, force: true });
|
|
114
|
+
mkdirSync(seyolaRuntimeDest, { recursive: true });
|
|
115
|
+
// If --runtime <path> is supplied, copy a usable runtime artifact in.
|
|
116
|
+
// Otherwise leave a README that says runtime install is manual.
|
|
117
|
+
if (args.runtimePath) {
|
|
118
|
+
const rt = resolve(args.runtimePath);
|
|
119
|
+
const distSrc = join(rt, "dist");
|
|
120
|
+
const pkgSrc = join(rt, "package.json");
|
|
121
|
+
const lockSrc = join(rt, "package-lock.json");
|
|
122
|
+
if (!existsSync(distSrc)) {
|
|
123
|
+
result.warnings.push({
|
|
124
|
+
code: "runtime_dist_missing",
|
|
125
|
+
message: `--runtime '${rt}' has no dist/ directory. Run 'npm run build' in the runtime first. Bundle continuing without an embedded runtime.`,
|
|
126
|
+
});
|
|
127
|
+
writeFileSync(join(seyolaRuntimeDest, "README.md"), runtimeNote(result.runtime_version), "utf8");
|
|
128
|
+
result.files_written.push(".seyola/runtime/README.md");
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
copyDir(distSrc, join(seyolaRuntimeDest, "dist"), {});
|
|
132
|
+
result.files_written.push(".seyola/runtime/dist/");
|
|
133
|
+
if (existsSync(pkgSrc)) {
|
|
134
|
+
copyFileSync(pkgSrc, join(seyolaRuntimeDest, "package.json"));
|
|
135
|
+
result.files_written.push(".seyola/runtime/package.json");
|
|
136
|
+
}
|
|
137
|
+
if (existsSync(lockSrc)) {
|
|
138
|
+
copyFileSync(lockSrc, join(seyolaRuntimeDest, "package-lock.json"));
|
|
139
|
+
result.files_written.push(".seyola/runtime/package-lock.json");
|
|
140
|
+
}
|
|
141
|
+
writeFileSync(join(seyolaRuntimeDest, "README.md"), runtimeBundledNote(result.runtime_version), "utf8");
|
|
142
|
+
result.files_written.push(".seyola/runtime/README.md");
|
|
143
|
+
result.runtime_bundled = true;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
writeFileSync(join(seyolaRuntimeDest, "README.md"), runtimeNote(result.runtime_version), "utf8");
|
|
148
|
+
result.files_written.push(".seyola/runtime/README.md");
|
|
149
|
+
}
|
|
150
|
+
const versionFile = {
|
|
151
|
+
bundle_name: bundleName,
|
|
152
|
+
workspace_version: "0.1.0",
|
|
153
|
+
pack_version: result.pack_version,
|
|
154
|
+
runtime_version: result.runtime_version,
|
|
155
|
+
runtime_bundled: result.runtime_bundled,
|
|
156
|
+
generated_at: new Date().toISOString(),
|
|
157
|
+
bundle_created_at: new Date().toISOString().slice(0, 10),
|
|
158
|
+
update_channel: "alpha",
|
|
159
|
+
exposed_skills: [...APPROVED_SKILLS],
|
|
160
|
+
note: "Run setup.ps1 (Windows) or setup.sh (mac/linux) to finalize this workspace on your machine.",
|
|
161
|
+
};
|
|
162
|
+
writeFileSync(join(seyolaDir, "version.json"), JSON.stringify(versionFile, null, 2) + "\n", "utf8");
|
|
163
|
+
result.files_written.push(".seyola/version.json");
|
|
164
|
+
// 7. Setup scripts.
|
|
165
|
+
writeFileSync(join(out, "setup.ps1"), setupPowershell(), "utf8");
|
|
166
|
+
result.files_written.push("setup.ps1");
|
|
167
|
+
writeFileSync(join(out, "setup.sh"), setupBash(), { encoding: "utf8", mode: 0o755 });
|
|
168
|
+
result.files_written.push("setup.sh");
|
|
169
|
+
// Windows convenience wrapper that bypasses PowerShell execution-policy
|
|
170
|
+
// restrictions on downloaded files. cmd files don't trigger the restriction.
|
|
171
|
+
writeFileSync(join(out, "setup.cmd"), setupCmd(), "utf8");
|
|
172
|
+
result.files_written.push("setup.cmd");
|
|
173
|
+
// 8. README + GETTING-STARTED — only write if missing.
|
|
174
|
+
const readmePath = join(out, "README.md");
|
|
175
|
+
if (!existsSync(readmePath)) {
|
|
176
|
+
writeFileSync(readmePath, bundleReadme(result.pack_version, result.runtime_version, bundleName), "utf8");
|
|
177
|
+
result.files_written.push("README.md");
|
|
178
|
+
}
|
|
179
|
+
const gsPath = join(out, "GETTING-STARTED.md");
|
|
180
|
+
if (!existsSync(gsPath)) {
|
|
181
|
+
writeFileSync(gsPath, bundleGettingStarted(), "utf8");
|
|
182
|
+
result.files_written.push("GETTING-STARTED.md");
|
|
183
|
+
}
|
|
184
|
+
return finalize(args, result, start);
|
|
185
|
+
}
|
|
186
|
+
// =====================================================================
|
|
187
|
+
// Allowlist-based pack copy for Client Alpha.
|
|
188
|
+
//
|
|
189
|
+
// The bundle ships ONLY what the four approved workflows need:
|
|
190
|
+
// /linkedin-writer, /brand-voice, /audience-profile-builder, /story-bank-builder
|
|
191
|
+
// Plus their runtime dependencies (atom.draft + linkedin-post methodology,
|
|
192
|
+
// the registries, schemas, configs they reference).
|
|
193
|
+
//
|
|
194
|
+
// Strips internal docs, legacy library, unapproved skills, ARCHITECTURE,
|
|
195
|
+
// CHANGELOG, MAP, audits, internal strategy docs.
|
|
196
|
+
// =====================================================================
|
|
197
|
+
const ALPHA_ALLOWED_CAPABILITIES = new Set([
|
|
198
|
+
"atom.draft",
|
|
199
|
+
"artifact.create.social.linkedin-post",
|
|
200
|
+
"artifact.create.analytical.voice-profile",
|
|
201
|
+
"artifact.create.analytical.audience-profile",
|
|
202
|
+
"artifact.create.analytical.story-bank",
|
|
203
|
+
]);
|
|
204
|
+
const ALPHA_ALLOWED_CONFIG_NAMESPACES = new Set([
|
|
205
|
+
"universal:voice:plain-business@v1",
|
|
206
|
+
"universal:structure:hell-heaven-bridge@v1",
|
|
207
|
+
"universal:structure:default@v1",
|
|
208
|
+
"universal:quality:default@v1",
|
|
209
|
+
"universal:quality:humanizer@v1",
|
|
210
|
+
"universal:quality:cringe-test@v1",
|
|
211
|
+
"universal:quality:would-i-send-this@v1",
|
|
212
|
+
]);
|
|
213
|
+
function copyAlphaPack(src, dest) {
|
|
214
|
+
// 1. Whole directories that are runtime-essential and contain no IP leak.
|
|
215
|
+
const wholeDirs = [
|
|
216
|
+
".types",
|
|
217
|
+
".tags",
|
|
218
|
+
".substrate",
|
|
219
|
+
".composer",
|
|
220
|
+
".context",
|
|
221
|
+
".harness/schemas",
|
|
222
|
+
];
|
|
223
|
+
for (const d of wholeDirs) {
|
|
224
|
+
const srcDir = join(src, d);
|
|
225
|
+
if (existsSync(srcDir)) {
|
|
226
|
+
copyDir(srcDir, join(dest, d), {});
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
// 2. Selected harness files (policies + linkedin-only examples).
|
|
230
|
+
const harnessFiles = [
|
|
231
|
+
".harness/routing-policy.json",
|
|
232
|
+
".harness/planning-policy.json",
|
|
233
|
+
".harness/README.md",
|
|
234
|
+
".harness/examples/linkedin-post.task.yaml",
|
|
235
|
+
".harness/examples/linkedin-post-inline.task.yaml",
|
|
236
|
+
];
|
|
237
|
+
for (const f of harnessFiles) {
|
|
238
|
+
const srcFile = join(src, f);
|
|
239
|
+
if (existsSync(srcFile)) {
|
|
240
|
+
copyFileWithMkdir(srcFile, join(dest, f));
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
// 3. Trimmed registries (capabilities, artifacts, configs).
|
|
244
|
+
trimAndCopyCapabilitiesRegistry(src, dest);
|
|
245
|
+
const allowedArtifactTypes = trimAndCopyArtifactsRegistry(src, dest);
|
|
246
|
+
trimAndCopyConfigsRegistry(src, dest);
|
|
247
|
+
trimAndCopyShapes(src, dest, allowedArtifactTypes);
|
|
248
|
+
// 4. Format adapters and effects.
|
|
249
|
+
// For alpha, /linkedin-writer uses output_format=plain_text (no adapter)
|
|
250
|
+
// and file.export effect. So:
|
|
251
|
+
// - Include effects/ implementation dir (file.export is real and used).
|
|
252
|
+
// - Include .effects/registry.json (whole, file.export is the only entry).
|
|
253
|
+
// - Include .formats/registry.json with no adapters (the gov-proposal
|
|
254
|
+
// adapter is stripped because its owning capability was stripped).
|
|
255
|
+
// - Skip formats/ implementation dir entirely (no adapters in alpha).
|
|
256
|
+
const effectsSrc = join(src, "effects");
|
|
257
|
+
if (existsSync(effectsSrc))
|
|
258
|
+
copyDir(effectsSrc, join(dest, "effects"), {});
|
|
259
|
+
const effectsRegSrc = join(src, ".effects/registry.json");
|
|
260
|
+
if (existsSync(effectsRegSrc)) {
|
|
261
|
+
copyFileWithMkdir(effectsRegSrc, join(dest, ".effects/registry.json"));
|
|
262
|
+
}
|
|
263
|
+
// Trimmed formats registry — empty adapters for alpha.
|
|
264
|
+
trimAndCopyFormatsRegistry(src, dest);
|
|
265
|
+
// 5. Approved skill folders (whole subtrees).
|
|
266
|
+
const approvedSkillFolders = [
|
|
267
|
+
"skills/layer-1/brand-voice",
|
|
268
|
+
"skills/layer-1/audience-profile-builder",
|
|
269
|
+
"skills/layer-1/story-bank-builder",
|
|
270
|
+
"skills/layer-3/linkedin-writer",
|
|
271
|
+
];
|
|
272
|
+
for (const sk of approvedSkillFolders) {
|
|
273
|
+
const srcSk = join(src, sk);
|
|
274
|
+
if (existsSync(srcSk)) {
|
|
275
|
+
copyDir(srcSk, join(dest, sk), {});
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
// 6. Skill dependencies — only the parts of /draft that linkedin-post needs.
|
|
279
|
+
const draftDeps = [
|
|
280
|
+
"skills/layer-2/draft/SKILL.md",
|
|
281
|
+
"skills/layer-2/draft/methodologies/linkedin-post.md",
|
|
282
|
+
];
|
|
283
|
+
for (const f of draftDeps) {
|
|
284
|
+
const srcFile = join(src, f);
|
|
285
|
+
if (existsSync(srcFile)) {
|
|
286
|
+
copyFileWithMkdir(srcFile, join(dest, f));
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
// If draft has a references/ dir, copy it (the SKILL.md references files there).
|
|
290
|
+
const draftRefs = join(src, "skills/layer-2/draft/references");
|
|
291
|
+
if (existsSync(draftRefs)) {
|
|
292
|
+
copyDir(draftRefs, join(dest, "skills/layer-2/draft/references"), {});
|
|
293
|
+
}
|
|
294
|
+
// 7. Manifest (universal pack only).
|
|
295
|
+
const manifestSrc = join(src, "manifests/universal.pack.json");
|
|
296
|
+
if (existsSync(manifestSrc)) {
|
|
297
|
+
copyFileWithMkdir(manifestSrc, join(dest, "manifests/universal.pack.json"));
|
|
298
|
+
}
|
|
299
|
+
// 8. VERSION (small, identifies pack).
|
|
300
|
+
const versionSrc = join(src, "VERSION");
|
|
301
|
+
if (existsSync(versionSrc)) {
|
|
302
|
+
copyFileSync(versionSrc, join(dest, "VERSION"));
|
|
303
|
+
}
|
|
304
|
+
// What is intentionally NOT copied (per docs/SHARING-AND-IP-MODEL.md):
|
|
305
|
+
// docs/ internal strategy + IP docs
|
|
306
|
+
// skills/foundations/ legacy library
|
|
307
|
+
// skills/layer-0/ untyped tour guide
|
|
308
|
+
// skills/layer-1/{capacity,expertise,vault,...} unapproved L1 builders
|
|
309
|
+
// skills/layer-2/{review,frame,research,mine,capture}/ unused atoms
|
|
310
|
+
// skills/layer-2/draft/methodologies/* (except linkedin-post.md)
|
|
311
|
+
// skills/layer-3/* (except linkedin-writer)
|
|
312
|
+
// ARCHITECTURE*.md, MAP.md, CHANGELOG.md, README.md (source-pack one)
|
|
313
|
+
// .skills/inventory.json the audit
|
|
314
|
+
// bin/, knowledge/, seyola-paid-upgrade/, seyola-foundations/
|
|
315
|
+
// formats/ no format adapters used in alpha
|
|
316
|
+
}
|
|
317
|
+
function trimAndCopyCapabilitiesRegistry(src, dest) {
|
|
318
|
+
const srcFile = join(src, ".capabilities/registry.json");
|
|
319
|
+
if (!existsSync(srcFile))
|
|
320
|
+
return;
|
|
321
|
+
const reg = JSON.parse(readFileSync(srcFile, "utf8"));
|
|
322
|
+
const trimmed = {};
|
|
323
|
+
for (const [id, body] of Object.entries(reg.capabilities ?? {})) {
|
|
324
|
+
if (ALPHA_ALLOWED_CAPABILITIES.has(id)) {
|
|
325
|
+
trimmed[id] = body;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
reg.capabilities = trimmed;
|
|
329
|
+
// Strip the verbose _log too — it names internal versions.
|
|
330
|
+
delete reg._log;
|
|
331
|
+
copyFileWithMkdir(srcFile, join(dest, ".capabilities/registry.json"));
|
|
332
|
+
writeFileSync(join(dest, ".capabilities/registry.json"), JSON.stringify(reg, null, 2) + "\n", "utf8");
|
|
333
|
+
}
|
|
334
|
+
function trimAndCopyArtifactsRegistry(src, dest) {
|
|
335
|
+
// Compute artifact types referenced by allowed capabilities.
|
|
336
|
+
const allowed = new Set(["derivation-record"]); // always include
|
|
337
|
+
const capsFile = join(src, ".capabilities/registry.json");
|
|
338
|
+
if (existsSync(capsFile)) {
|
|
339
|
+
const reg = JSON.parse(readFileSync(capsFile, "utf8"));
|
|
340
|
+
for (const [id, cap] of Object.entries(reg.capabilities ?? {})) {
|
|
341
|
+
if (!ALPHA_ALLOWED_CAPABILITIES.has(id))
|
|
342
|
+
continue;
|
|
343
|
+
if (cap.output_artifact_type)
|
|
344
|
+
allowed.add(cap.output_artifact_type);
|
|
345
|
+
for (const t of cap.input_artifact_types ?? [])
|
|
346
|
+
allowed.add(t);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
const srcFile = join(src, ".artifacts/registry.json");
|
|
350
|
+
if (!existsSync(srcFile))
|
|
351
|
+
return allowed;
|
|
352
|
+
const reg = JSON.parse(readFileSync(srcFile, "utf8"));
|
|
353
|
+
const trimmed = {};
|
|
354
|
+
for (const [name, body] of Object.entries(reg.artifact_types ?? {})) {
|
|
355
|
+
if (allowed.has(name))
|
|
356
|
+
trimmed[name] = body;
|
|
357
|
+
}
|
|
358
|
+
reg.artifact_types = trimmed;
|
|
359
|
+
delete reg._log;
|
|
360
|
+
copyFileWithMkdir(srcFile, join(dest, ".artifacts/registry.json"));
|
|
361
|
+
writeFileSync(join(dest, ".artifacts/registry.json"), JSON.stringify(reg, null, 2) + "\n", "utf8");
|
|
362
|
+
return allowed;
|
|
363
|
+
}
|
|
364
|
+
function trimAndCopyConfigsRegistry(src, dest) {
|
|
365
|
+
const srcFile = join(src, ".configs/registry.json");
|
|
366
|
+
if (!existsSync(srcFile))
|
|
367
|
+
return;
|
|
368
|
+
const reg = JSON.parse(readFileSync(srcFile, "utf8"));
|
|
369
|
+
const trimmed = {};
|
|
370
|
+
for (const [ns, body] of Object.entries(reg.configs ?? {})) {
|
|
371
|
+
if (ALPHA_ALLOWED_CONFIG_NAMESPACES.has(ns)) {
|
|
372
|
+
trimmed[ns] = body;
|
|
373
|
+
// Copy the source file too.
|
|
374
|
+
if (body.source_file) {
|
|
375
|
+
const cfgSrc = join(src, body.source_file);
|
|
376
|
+
if (existsSync(cfgSrc)) {
|
|
377
|
+
copyFileWithMkdir(cfgSrc, join(dest, body.source_file));
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
reg.configs = trimmed;
|
|
383
|
+
delete reg._log;
|
|
384
|
+
copyFileWithMkdir(srcFile, join(dest, ".configs/registry.json"));
|
|
385
|
+
writeFileSync(join(dest, ".configs/registry.json"), JSON.stringify(reg, null, 2) + "\n", "utf8");
|
|
386
|
+
}
|
|
387
|
+
function trimAndCopyFormatsRegistry(src, dest) {
|
|
388
|
+
const srcFile = join(src, ".formats/registry.json");
|
|
389
|
+
if (!existsSync(srcFile))
|
|
390
|
+
return;
|
|
391
|
+
const reg = JSON.parse(readFileSync(srcFile, "utf8"));
|
|
392
|
+
// Alpha ships with no format adapters — clear the adapters dict but keep
|
|
393
|
+
// the registry shape so the runtime's required-files check passes.
|
|
394
|
+
reg.adapters = {};
|
|
395
|
+
delete reg._log;
|
|
396
|
+
copyFileWithMkdir(srcFile, join(dest, ".formats/registry.json"));
|
|
397
|
+
writeFileSync(join(dest, ".formats/registry.json"), JSON.stringify(reg, null, 2) + "\n", "utf8");
|
|
398
|
+
}
|
|
399
|
+
function trimAndCopyShapes(src, dest, allowedArtifactTypes) {
|
|
400
|
+
// Shapes are needed for: every artifact type used + the type-family shapes
|
|
401
|
+
// referenced by the type registry (voice, structure, audience, quality, format).
|
|
402
|
+
const familyShapes = ["voice", "structure", "audience", "quality", "format"];
|
|
403
|
+
const shapeFiles = new Set(familyShapes);
|
|
404
|
+
for (const t of allowedArtifactTypes)
|
|
405
|
+
shapeFiles.add(t);
|
|
406
|
+
const srcDir = join(src, ".shapes");
|
|
407
|
+
if (!existsSync(srcDir))
|
|
408
|
+
return;
|
|
409
|
+
for (const entry of readdirSync(srcDir, { withFileTypes: true })) {
|
|
410
|
+
if (!entry.isFile() || !entry.name.endsWith(".json"))
|
|
411
|
+
continue;
|
|
412
|
+
const baseName = entry.name.replace(/\.json$/, "");
|
|
413
|
+
if (shapeFiles.has(baseName)) {
|
|
414
|
+
copyFileWithMkdir(join(srcDir, entry.name), join(dest, ".shapes", entry.name));
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
function copyFileWithMkdir(src, dest) {
|
|
419
|
+
mkdirSync(dirname(dest), { recursive: true });
|
|
420
|
+
copyFileSync(src, dest);
|
|
421
|
+
}
|
|
422
|
+
function copyDir(src, dest, opts) {
|
|
423
|
+
const exclude = new Set(opts.excludeRel ?? []);
|
|
424
|
+
if (!existsSync(dest))
|
|
425
|
+
mkdirSync(dest, { recursive: true });
|
|
426
|
+
const entries = readdirSync(src, { withFileTypes: true });
|
|
427
|
+
for (const entry of entries) {
|
|
428
|
+
if (exclude.has(entry.name))
|
|
429
|
+
continue;
|
|
430
|
+
const s = join(src, entry.name);
|
|
431
|
+
const d = join(dest, entry.name);
|
|
432
|
+
if (entry.isDirectory()) {
|
|
433
|
+
copyDir(s, d, opts);
|
|
434
|
+
}
|
|
435
|
+
else if (entry.isFile()) {
|
|
436
|
+
mkdirSync(dirname(d), { recursive: true });
|
|
437
|
+
copyFileSync(s, d);
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
function readRuntimeVersion() {
|
|
442
|
+
try {
|
|
443
|
+
const here = dirname(new URL(import.meta.url).pathname.replace(/^\/([A-Za-z]:)/, "$1"));
|
|
444
|
+
for (const candidate of [resolve(here, "../../package.json"), resolve(here, "../../../package.json")]) {
|
|
445
|
+
try {
|
|
446
|
+
const pkg = JSON.parse(readFileSync(candidate, "utf8"));
|
|
447
|
+
if (pkg.version)
|
|
448
|
+
return pkg.version;
|
|
449
|
+
}
|
|
450
|
+
catch {
|
|
451
|
+
// try next
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
catch {
|
|
456
|
+
// fall through
|
|
457
|
+
}
|
|
458
|
+
return "unknown";
|
|
459
|
+
}
|
|
460
|
+
function setupPowershell() {
|
|
461
|
+
return `# Seyola Client Alpha — setup script (Windows / PowerShell)
|
|
462
|
+
# Run from inside the bundle folder: ./setup.ps1
|
|
463
|
+
|
|
464
|
+
$ErrorActionPreference = "Stop"
|
|
465
|
+
|
|
466
|
+
Write-Host ""
|
|
467
|
+
Write-Host "Seyola Client Alpha setup" -ForegroundColor Cyan
|
|
468
|
+
Write-Host "============================="
|
|
469
|
+
Write-Host ""
|
|
470
|
+
|
|
471
|
+
# 1. Node check
|
|
472
|
+
Write-Host "Checking Node.js..." -NoNewline
|
|
473
|
+
try {
|
|
474
|
+
$nodeVersion = node --version 2>&1
|
|
475
|
+
if ($nodeVersion -match "v(\\d+)") {
|
|
476
|
+
$major = [int]$Matches[1]
|
|
477
|
+
if ($major -lt 20) {
|
|
478
|
+
Write-Host " FAIL" -ForegroundColor Red
|
|
479
|
+
Write-Host " Node.js 20 or higher is required. Found: $nodeVersion"
|
|
480
|
+
Write-Host " Install Node 20+ from https://nodejs.org/, then rerun this script."
|
|
481
|
+
exit 1
|
|
482
|
+
}
|
|
483
|
+
Write-Host " ok ($nodeVersion)" -ForegroundColor Green
|
|
484
|
+
} else {
|
|
485
|
+
throw "Could not parse Node version"
|
|
486
|
+
}
|
|
487
|
+
} catch {
|
|
488
|
+
Write-Host " FAIL" -ForegroundColor Red
|
|
489
|
+
Write-Host " Node.js is not installed or not on PATH."
|
|
490
|
+
Write-Host " Install Node 20+ from https://nodejs.org/, then rerun this script."
|
|
491
|
+
exit 1
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
# 2. Claude Code check
|
|
495
|
+
Write-Host "Checking Claude Code..." -NoNewline
|
|
496
|
+
try {
|
|
497
|
+
$claudeVersion = claude --version 2>&1
|
|
498
|
+
Write-Host " ok ($claudeVersion)" -ForegroundColor Green
|
|
499
|
+
} catch {
|
|
500
|
+
Write-Host " WARN" -ForegroundColor Yellow
|
|
501
|
+
Write-Host " Claude Code is not detected on PATH."
|
|
502
|
+
Write-Host " Install from https://claude.com/claude-code, authenticate (run claude once), then rerun this script."
|
|
503
|
+
Write-Host " Continuing anyway so you can finish setup; the workspace will work once Claude Code is installed."
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
# 3. seyola-runtime check
|
|
507
|
+
Write-Host "Checking seyola-runtime..." -NoNewline
|
|
508
|
+
$rtFound = $false
|
|
509
|
+
try {
|
|
510
|
+
$rtVersion = seyola-runtime --version 2>&1
|
|
511
|
+
Write-Host " ok ($rtVersion, global)" -ForegroundColor Green
|
|
512
|
+
$rtFound = $true
|
|
513
|
+
} catch {
|
|
514
|
+
Write-Host " not on PATH; trying bundled runtime..." -ForegroundColor Yellow
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
if (-not $rtFound) {
|
|
518
|
+
$bundledRuntime = Join-Path (Get-Location).Path ".seyola/runtime"
|
|
519
|
+
$bundledDist = Join-Path $bundledRuntime "dist/cli/index.js"
|
|
520
|
+
if (Test-Path $bundledDist) {
|
|
521
|
+
Write-Host "Installing bundled runtime from .seyola/runtime/ ..." -ForegroundColor Cyan
|
|
522
|
+
Push-Location $bundledRuntime
|
|
523
|
+
npm install --omit=dev --silent
|
|
524
|
+
if ($LASTEXITCODE -ne 0) {
|
|
525
|
+
Write-Host "npm install failed in .seyola/runtime/." -ForegroundColor Red
|
|
526
|
+
Pop-Location
|
|
527
|
+
exit 1
|
|
528
|
+
}
|
|
529
|
+
npm link --silent 2>&1 | Out-Null
|
|
530
|
+
if ($LASTEXITCODE -ne 0) {
|
|
531
|
+
Write-Host "npm link failed. You may need elevated permissions." -ForegroundColor Red
|
|
532
|
+
Pop-Location
|
|
533
|
+
exit 1
|
|
534
|
+
}
|
|
535
|
+
Pop-Location
|
|
536
|
+
try {
|
|
537
|
+
$rtVersion = seyola-runtime --version 2>&1
|
|
538
|
+
Write-Host " ok ($rtVersion, bundled)" -ForegroundColor Green
|
|
539
|
+
$rtFound = $true
|
|
540
|
+
} catch {
|
|
541
|
+
Write-Host " installed but seyola-runtime still not on PATH. Open a new terminal and rerun." -ForegroundColor Red
|
|
542
|
+
exit 1
|
|
543
|
+
}
|
|
544
|
+
} else {
|
|
545
|
+
Write-Host " no bundled runtime found at .seyola/runtime/dist/." -ForegroundColor Red
|
|
546
|
+
Write-Host " Reach out to the Seyola maintainer for an updated bundle that includes the runtime."
|
|
547
|
+
exit 1
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
# 4. Run init against this folder.
|
|
552
|
+
$workspace = (Get-Location).Path
|
|
553
|
+
$pack = Join-Path $workspace ".seyola/pack"
|
|
554
|
+
Write-Host ""
|
|
555
|
+
Write-Host "Initializing workspace at $workspace" -ForegroundColor Cyan
|
|
556
|
+
seyola-runtime init --workspace "$workspace" --pack "$pack"
|
|
557
|
+
if ($LASTEXITCODE -ne 0) {
|
|
558
|
+
Write-Host "init failed. See output above." -ForegroundColor Red
|
|
559
|
+
exit 1
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
# 5. Run doctor.
|
|
563
|
+
Write-Host ""
|
|
564
|
+
Write-Host "Running health check..." -ForegroundColor Cyan
|
|
565
|
+
seyola-runtime doctor --workspace "$workspace"
|
|
566
|
+
|
|
567
|
+
Write-Host ""
|
|
568
|
+
Write-Host "Setup complete." -ForegroundColor Green
|
|
569
|
+
Write-Host ""
|
|
570
|
+
Write-Host "Next steps:"
|
|
571
|
+
Write-Host " 1. Run: claude"
|
|
572
|
+
Write-Host " 2. Type: /linkedin-writer"
|
|
573
|
+
Write-Host ""
|
|
574
|
+
Write-Host "See GETTING-STARTED.md for the full first-run flow."
|
|
575
|
+
Write-Host ""
|
|
576
|
+
`;
|
|
577
|
+
}
|
|
578
|
+
function setupCmd() {
|
|
579
|
+
return `@echo off
|
|
580
|
+
REM Seyola Client Alpha - Windows convenience wrapper.
|
|
581
|
+
REM
|
|
582
|
+
REM Why this file exists:
|
|
583
|
+
REM When you download Seyola as a zip from GitHub, Windows marks every
|
|
584
|
+
REM file inside as "potentially unsafe" (zone marker). PowerShell's
|
|
585
|
+
REM default execution policy then refuses to run setup.ps1 directly.
|
|
586
|
+
REM This wrapper invokes PowerShell with -ExecutionPolicy Bypass so the
|
|
587
|
+
REM setup runs without changing your global PowerShell settings.
|
|
588
|
+
REM
|
|
589
|
+
REM How to run:
|
|
590
|
+
REM - Double-click setup.cmd, OR
|
|
591
|
+
REM - From cmd / PowerShell, type: setup.cmd
|
|
592
|
+
REM
|
|
593
|
+
REM Either way, it calls setup.ps1 with the right policy flag.
|
|
594
|
+
|
|
595
|
+
powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0setup.ps1" %*
|
|
596
|
+
exit /B %ERRORLEVEL%
|
|
597
|
+
`;
|
|
598
|
+
}
|
|
599
|
+
function setupBash() {
|
|
600
|
+
return `#!/usr/bin/env bash
|
|
601
|
+
# Seyola Client Alpha — setup script (mac/linux)
|
|
602
|
+
# Run from inside the bundle folder: ./setup.sh
|
|
603
|
+
|
|
604
|
+
set -e
|
|
605
|
+
|
|
606
|
+
echo ""
|
|
607
|
+
echo "Seyola Client Alpha setup"
|
|
608
|
+
echo "========================="
|
|
609
|
+
echo ""
|
|
610
|
+
|
|
611
|
+
# 1. Node check
|
|
612
|
+
printf "Checking Node.js... "
|
|
613
|
+
if ! command -v node >/dev/null 2>&1; then
|
|
614
|
+
echo "FAIL"
|
|
615
|
+
echo " Node.js is not installed or not on PATH."
|
|
616
|
+
echo " Install Node 20+ from https://nodejs.org/, then rerun this script."
|
|
617
|
+
exit 1
|
|
618
|
+
fi
|
|
619
|
+
NODE_VERSION=$(node --version)
|
|
620
|
+
NODE_MAJOR=$(echo "$NODE_VERSION" | sed 's/v\\([0-9]*\\).*/\\1/')
|
|
621
|
+
if [ "$NODE_MAJOR" -lt 20 ]; then
|
|
622
|
+
echo "FAIL"
|
|
623
|
+
echo " Node.js 20 or higher is required. Found: $NODE_VERSION"
|
|
624
|
+
echo " Install Node 20+ from https://nodejs.org/, then rerun this script."
|
|
625
|
+
exit 1
|
|
626
|
+
fi
|
|
627
|
+
echo "ok ($NODE_VERSION)"
|
|
628
|
+
|
|
629
|
+
# 2. Claude Code check
|
|
630
|
+
printf "Checking Claude Code... "
|
|
631
|
+
if command -v claude >/dev/null 2>&1; then
|
|
632
|
+
CLAUDE_VERSION=$(claude --version 2>&1 || echo "unknown")
|
|
633
|
+
echo "ok ($CLAUDE_VERSION)"
|
|
634
|
+
else
|
|
635
|
+
echo "WARN"
|
|
636
|
+
echo " Claude Code is not detected on PATH."
|
|
637
|
+
echo " Install from https://claude.com/claude-code, authenticate (run claude once), then rerun this script."
|
|
638
|
+
echo " Continuing anyway."
|
|
639
|
+
fi
|
|
640
|
+
|
|
641
|
+
# 3. seyola-runtime check (global first, bundled fallback)
|
|
642
|
+
printf "Checking seyola-runtime... "
|
|
643
|
+
if command -v seyola-runtime >/dev/null 2>&1; then
|
|
644
|
+
RT_VERSION=$(seyola-runtime --version 2>&1)
|
|
645
|
+
echo "ok ($RT_VERSION, global)"
|
|
646
|
+
else
|
|
647
|
+
echo "not on PATH; trying bundled runtime..."
|
|
648
|
+
BUNDLED_RUNTIME="$(pwd)/.seyola/runtime"
|
|
649
|
+
if [ -f "$BUNDLED_RUNTIME/dist/cli/index.js" ]; then
|
|
650
|
+
echo "Installing bundled runtime from .seyola/runtime/ ..."
|
|
651
|
+
(cd "$BUNDLED_RUNTIME" && npm install --omit=dev --silent && npm link --silent)
|
|
652
|
+
if ! command -v seyola-runtime >/dev/null 2>&1; then
|
|
653
|
+
echo " installed but seyola-runtime still not on PATH. Open a new terminal and rerun."
|
|
654
|
+
exit 1
|
|
655
|
+
fi
|
|
656
|
+
RT_VERSION=$(seyola-runtime --version 2>&1)
|
|
657
|
+
echo " ok ($RT_VERSION, bundled)"
|
|
658
|
+
else
|
|
659
|
+
echo " no bundled runtime found at .seyola/runtime/dist/."
|
|
660
|
+
echo " Reach out to the Seyola maintainer for an updated bundle that includes the runtime."
|
|
661
|
+
exit 1
|
|
662
|
+
fi
|
|
663
|
+
fi
|
|
664
|
+
|
|
665
|
+
# 4. Run init.
|
|
666
|
+
WORKSPACE=$(pwd)
|
|
667
|
+
PACK="$WORKSPACE/.seyola/pack"
|
|
668
|
+
echo ""
|
|
669
|
+
echo "Initializing workspace at $WORKSPACE"
|
|
670
|
+
seyola-runtime init --workspace "$WORKSPACE" --pack "$PACK"
|
|
671
|
+
|
|
672
|
+
# 5. Run doctor.
|
|
673
|
+
echo ""
|
|
674
|
+
echo "Running health check..."
|
|
675
|
+
seyola-runtime doctor --workspace "$WORKSPACE"
|
|
676
|
+
|
|
677
|
+
echo ""
|
|
678
|
+
echo "Setup complete."
|
|
679
|
+
echo ""
|
|
680
|
+
echo "Next steps:"
|
|
681
|
+
echo " 1. Run: claude"
|
|
682
|
+
echo " 2. Type: /linkedin-writer"
|
|
683
|
+
echo ""
|
|
684
|
+
echo "See GETTING-STARTED.md for the full first-run flow."
|
|
685
|
+
echo ""
|
|
686
|
+
`;
|
|
687
|
+
}
|
|
688
|
+
function bundleGitignore() {
|
|
689
|
+
return `# Member-owned folders. The repo ships empty placeholders (.gitkeep).
|
|
690
|
+
# Member-generated content stays local — never committed, never overwritten.
|
|
691
|
+
_foundations/*
|
|
692
|
+
!_foundations/.gitkeep
|
|
693
|
+
|
|
694
|
+
market/*
|
|
695
|
+
!market/.gitkeep
|
|
696
|
+
|
|
697
|
+
clients/*
|
|
698
|
+
!clients/.gitkeep
|
|
699
|
+
|
|
700
|
+
runs/*
|
|
701
|
+
!runs/.gitkeep
|
|
702
|
+
|
|
703
|
+
output/*
|
|
704
|
+
!output/.gitkeep
|
|
705
|
+
|
|
706
|
+
# OS / editor noise
|
|
707
|
+
.DS_Store
|
|
708
|
+
Thumbs.db
|
|
709
|
+
*.swp
|
|
710
|
+
*.swo
|
|
711
|
+
*~
|
|
712
|
+
.idea/
|
|
713
|
+
.vscode/
|
|
714
|
+
|
|
715
|
+
# Node
|
|
716
|
+
node_modules/
|
|
717
|
+
|
|
718
|
+
# Misc
|
|
719
|
+
.env
|
|
720
|
+
.env.local
|
|
721
|
+
`;
|
|
722
|
+
}
|
|
723
|
+
function runtimeNote(version) {
|
|
724
|
+
return `# Runtime placeholder
|
|
725
|
+
|
|
726
|
+
This bundle does NOT include a built runtime. The setup script assumes \`seyola-runtime\` (v${version} or compatible) is installed globally on the tester's machine via \`npm link\` or a published package.
|
|
727
|
+
|
|
728
|
+
Setup scripts will fail with a clear instruction if the runtime is missing.
|
|
729
|
+
`;
|
|
730
|
+
}
|
|
731
|
+
function runtimeBundledNote(version) {
|
|
732
|
+
return `# Bundled runtime (v${version})
|
|
733
|
+
|
|
734
|
+
This bundle ships a built copy of \`seyola-runtime\` so the tester does not need to clone the runtime source repo separately.
|
|
735
|
+
|
|
736
|
+
The setup script will:
|
|
737
|
+
1. Check whether \`seyola-runtime\` is already installed globally. If yes, use that.
|
|
738
|
+
2. Otherwise, run \`npm install --omit=dev\` inside this directory and link it.
|
|
739
|
+
|
|
740
|
+
Contents:
|
|
741
|
+
- \`dist/\` — compiled CLI (TypeScript output).
|
|
742
|
+
- \`package.json\` — declares the dependencies.
|
|
743
|
+
- \`package-lock.json\` — pinned dependency versions.
|
|
744
|
+
|
|
745
|
+
If you want to update or rebuild the runtime, replace the contents of this directory with a new build artifact and rerun setup.
|
|
746
|
+
`;
|
|
747
|
+
}
|
|
748
|
+
function bundleReadme(packVersion, runtimeVersion, bundleName) {
|
|
749
|
+
return `# ${bundleName}
|
|
750
|
+
|
|
751
|
+
Seyola Client Alpha bundle.
|
|
752
|
+
|
|
753
|
+
- Pack version: ${packVersion}
|
|
754
|
+
- Runtime version: ${runtimeVersion}
|
|
755
|
+
- Channel: alpha
|
|
756
|
+
|
|
757
|
+
## To use this bundle
|
|
758
|
+
|
|
759
|
+
The most reliable path on Windows is to clone via git (avoids "downloaded from the internet" zone markers that Smart App Control and PowerShell's execution policy use to block scripts) OR to run the runtime commands directly without a script.
|
|
760
|
+
|
|
761
|
+
### Path A — Manual commands (most reliable on Windows)
|
|
762
|
+
|
|
763
|
+
This works around Smart App Control + execution policy entirely. From inside the unzipped folder, in any terminal:
|
|
764
|
+
|
|
765
|
+
\`\`\`
|
|
766
|
+
seyola-runtime init --workspace . --pack ./.seyola/pack
|
|
767
|
+
seyola-runtime doctor --workspace .
|
|
768
|
+
claude
|
|
769
|
+
\`\`\`
|
|
770
|
+
|
|
771
|
+
Three commands. \`seyola-runtime\` is an installed Node binary, so Windows Smart App Control doesn't block it.
|
|
772
|
+
|
|
773
|
+
### Path B — Git clone instead of zip download
|
|
774
|
+
|
|
775
|
+
\`\`\`
|
|
776
|
+
git clone https://github.com/seyola-community/seyola-paid.git my-seyola
|
|
777
|
+
cd my-seyola
|
|
778
|
+
\`\`\`
|
|
779
|
+
|
|
780
|
+
Then run Path A's commands, or try the scripts below.
|
|
781
|
+
|
|
782
|
+
### Path C — Setup scripts (try if A or B felt fiddly)
|
|
783
|
+
|
|
784
|
+
Scripts inside zips downloaded from GitHub are often blocked by Windows. If they work for you, great:
|
|
785
|
+
|
|
786
|
+
- **Windows:** double-click \`setup.cmd\`, or run \`setup.cmd\` from any terminal. If blocked: right-click → Properties → Unblock → OK.
|
|
787
|
+
- **PowerShell direct:** \`powershell -ExecutionPolicy Bypass -File .\\setup.ps1\` — bypasses execution policy but NOT Smart App Control.
|
|
788
|
+
- **mac / linux:** \`bash setup.sh\` (no Windows blocking issues).
|
|
789
|
+
|
|
790
|
+
If Smart App Control or execution policy is still blocking on Windows, fall back to Path A.
|
|
791
|
+
|
|
792
|
+
The setup script:
|
|
793
|
+
|
|
794
|
+
1. Checks that Node 20+, Claude Code, and \`seyola-runtime\` are on PATH.
|
|
795
|
+
2. Runs \`seyola-runtime init\` against this folder.
|
|
796
|
+
3. Runs \`seyola-runtime doctor\` to verify health.
|
|
797
|
+
4. Tells you what to do next.
|
|
798
|
+
|
|
799
|
+
After setup completes successfully:
|
|
800
|
+
|
|
801
|
+
\`\`\`
|
|
802
|
+
claude
|
|
803
|
+
\`\`\`
|
|
804
|
+
|
|
805
|
+
Then type \`/linkedin-writer\` in the chat.
|
|
806
|
+
|
|
807
|
+
For detailed steps and what each folder is for, see \`GETTING-STARTED.md\`.
|
|
808
|
+
|
|
809
|
+
## What's in this folder
|
|
810
|
+
|
|
811
|
+
- \`.seyola/\` — system parts. Replaced on update. Do not edit.
|
|
812
|
+
- \`.claude/\` — slash commands and settings. Generated by setup. Do not edit.
|
|
813
|
+
- \`_foundations/\`, \`market/\`, \`clients/\`, \`runs/\`, \`output/\` — yours. Never overwritten on update.
|
|
814
|
+
|
|
815
|
+
## Health check
|
|
816
|
+
|
|
817
|
+
Any time:
|
|
818
|
+
|
|
819
|
+
\`\`\`
|
|
820
|
+
seyola-runtime doctor --workspace .
|
|
821
|
+
\`\`\`
|
|
822
|
+
`;
|
|
823
|
+
}
|
|
824
|
+
function bundleGettingStarted() {
|
|
825
|
+
return `# Getting Started with Seyola — Client Alpha
|
|
826
|
+
|
|
827
|
+
This is your Seyola workspace. Everything you create stays here.
|
|
828
|
+
|
|
829
|
+
## Step 1: Setup
|
|
830
|
+
|
|
831
|
+
Open this folder in a terminal. The reliable Windows path is to run the runtime commands directly (the setup scripts can be blocked by Windows security on downloaded files — see notes below).
|
|
832
|
+
|
|
833
|
+
### macOS / Linux
|
|
834
|
+
|
|
835
|
+
\`\`\`
|
|
836
|
+
bash setup.sh
|
|
837
|
+
\`\`\`
|
|
838
|
+
|
|
839
|
+
### Windows — recommended path (manual commands, most reliable)
|
|
840
|
+
|
|
841
|
+
\`\`\`
|
|
842
|
+
seyola-runtime init --workspace . --pack ./.seyola/pack
|
|
843
|
+
seyola-runtime doctor --workspace .
|
|
844
|
+
\`\`\`
|
|
845
|
+
|
|
846
|
+
Then continue to Step 2.
|
|
847
|
+
|
|
848
|
+
### Windows — try the script (sometimes works)
|
|
849
|
+
|
|
850
|
+
Double-click \`setup.cmd\`, or from a terminal:
|
|
851
|
+
|
|
852
|
+
\`\`\`
|
|
853
|
+
setup.cmd
|
|
854
|
+
\`\`\`
|
|
855
|
+
|
|
856
|
+
If Windows says "this app was blocked" or "not digitally signed":
|
|
857
|
+
- Right-click \`setup.cmd\` → **Properties** → check **Unblock** → **OK** → double-click again.
|
|
858
|
+
- Or fall back to the manual commands above.
|
|
859
|
+
|
|
860
|
+
> **Why scripts get blocked on Windows.** Files downloaded inside a zip get tagged as "potentially unsafe" by Windows. PowerShell's execution policy blocks unsigned scripts with that tag, and Windows 11's Smart App Control blocks dangerous file extensions (\`.cmd\`, \`.ps1\`, \`.bat\`) entirely. The manual command path sidesteps all of that — \`seyola-runtime\` is a regular installed Node binary that Windows trusts.
|
|
861
|
+
|
|
862
|
+
The setup script checks prerequisites and finalizes the workspace. If anything is missing (Node, Claude Code, or the runtime), it tells you exactly what to install.
|
|
863
|
+
|
|
864
|
+
## Step 2: Open Claude Code
|
|
865
|
+
|
|
866
|
+
\`\`\`
|
|
867
|
+
claude
|
|
868
|
+
\`\`\`
|
|
869
|
+
|
|
870
|
+
Claude Code launches in this folder and picks up the slash commands.
|
|
871
|
+
|
|
872
|
+
## Step 3: Build your voice profile (~30 minutes)
|
|
873
|
+
|
|
874
|
+
\`\`\`
|
|
875
|
+
/brand-voice
|
|
876
|
+
\`\`\`
|
|
877
|
+
|
|
878
|
+
Interactive interview. Output: \`_foundations/voice-profile.md\`. Every drafting workflow reads this. Run it first.
|
|
879
|
+
|
|
880
|
+
## Step 4: Build your audience profile (~20 minutes)
|
|
881
|
+
|
|
882
|
+
\`\`\`
|
|
883
|
+
/audience-profile-builder
|
|
884
|
+
\`\`\`
|
|
885
|
+
|
|
886
|
+
Interactive. One profile per session. Output: \`market/audience-profiles/[name].md\`.
|
|
887
|
+
|
|
888
|
+
## Step 5: Build your story bank (~30-45 minutes)
|
|
889
|
+
|
|
890
|
+
\`\`\`
|
|
891
|
+
/story-bank-builder
|
|
892
|
+
\`\`\`
|
|
893
|
+
|
|
894
|
+
Interactive. Surfaces 5-15 signature stories. Output: \`_foundations/story-bank.md\`.
|
|
895
|
+
|
|
896
|
+
## Step 6: Write a LinkedIn post
|
|
897
|
+
|
|
898
|
+
\`\`\`
|
|
899
|
+
/linkedin-writer
|
|
900
|
+
\`\`\`
|
|
901
|
+
|
|
902
|
+
Paste an idea or topic. The runtime drafts ONE post in your voice using the Hell-Heaven-Bridge structure. Outputs land in \`runs/run-YYYY-MM-DD-li<NN>/\` and \`output/socials/\`.
|
|
903
|
+
|
|
904
|
+
## What's mine vs what's the system
|
|
905
|
+
|
|
906
|
+
| Folder | Owner | Notes |
|
|
907
|
+
|---|---|---|
|
|
908
|
+
| \`_foundations/\` | yours | Voice profile, story bank. |
|
|
909
|
+
| \`market/\` | yours | Audience profiles, language bank. |
|
|
910
|
+
| \`clients/\` | yours | Per-client engagement folders. |
|
|
911
|
+
| \`runs/\` | yours | Each workflow run gets a folder. |
|
|
912
|
+
| \`output/\` | yours | Final exports. |
|
|
913
|
+
| \`.seyola/\` | system | Replaced on update. Do not edit. |
|
|
914
|
+
| \`.claude/\` | system | Regenerated on update. Do not edit. |
|
|
915
|
+
|
|
916
|
+
## Health check anytime
|
|
917
|
+
|
|
918
|
+
\`\`\`
|
|
919
|
+
seyola-runtime doctor --workspace .
|
|
920
|
+
\`\`\`
|
|
921
|
+
|
|
922
|
+
If anything's wrong, this command tells you what.
|
|
923
|
+
|
|
924
|
+
## Slash commands available in this Alpha
|
|
925
|
+
|
|
926
|
+
- \`/brand-voice\`
|
|
927
|
+
- \`/audience-profile-builder\`
|
|
928
|
+
- \`/story-bank-builder\`
|
|
929
|
+
- \`/linkedin-writer\`
|
|
930
|
+
|
|
931
|
+
That's the complete loop for the Alpha. More workflows ship in later releases.
|
|
932
|
+
|
|
933
|
+
## When something feels wrong
|
|
934
|
+
|
|
935
|
+
If a post doesn't sound like you, the most common cause is: the voice profile hasn't been built yet, or the audience profile is missing. Run \`/brand-voice\` and \`/audience-profile-builder\` first; the difference is usually large.
|
|
936
|
+
|
|
937
|
+
If the wrapper hangs at preflight, check that \`seyola-runtime --version\` works from this folder. If not, rerun the setup script.
|
|
938
|
+
|
|
939
|
+
## Reporting issues during Alpha
|
|
940
|
+
|
|
941
|
+
This is a friendly-tester release. If something breaks:
|
|
942
|
+
|
|
943
|
+
1. Run \`seyola-runtime doctor --workspace .\` and capture the output.
|
|
944
|
+
2. Note which slash command was invoked and what idea/input you provided.
|
|
945
|
+
3. Share both with the developer.
|
|
946
|
+
`;
|
|
947
|
+
}
|
|
948
|
+
function finalize(args, result, start) {
|
|
949
|
+
result.duration_ms = Date.now() - start;
|
|
950
|
+
if (args.json) {
|
|
951
|
+
process.stdout.write(JSON.stringify(result, null, 2) + "\n");
|
|
952
|
+
}
|
|
953
|
+
else {
|
|
954
|
+
process.stderr.write(formatHuman(result));
|
|
955
|
+
}
|
|
956
|
+
return result;
|
|
957
|
+
}
|
|
958
|
+
function formatHuman(result) {
|
|
959
|
+
const lines = [];
|
|
960
|
+
lines.push("");
|
|
961
|
+
lines.push(`seyola-runtime bundle: status=${result.status}, exitCode=${result.exitCode}`);
|
|
962
|
+
lines.push(` bundle_name: ${result.bundle_name}`);
|
|
963
|
+
lines.push(` out: ${result.out_path}`);
|
|
964
|
+
lines.push(` pack: ${result.pack_path}`);
|
|
965
|
+
lines.push(` pack_version: ${result.pack_version}`);
|
|
966
|
+
lines.push(` runtime_version: ${result.runtime_version}`);
|
|
967
|
+
lines.push(` duration: ${result.duration_ms}ms`);
|
|
968
|
+
if (result.files_written.length > 0) {
|
|
969
|
+
lines.push("");
|
|
970
|
+
lines.push(" written:");
|
|
971
|
+
for (const f of result.files_written)
|
|
972
|
+
lines.push(` + ${f}`);
|
|
973
|
+
}
|
|
974
|
+
if (result.warnings.length > 0) {
|
|
975
|
+
lines.push("");
|
|
976
|
+
lines.push(" warnings:");
|
|
977
|
+
for (const w of result.warnings)
|
|
978
|
+
lines.push(` [${w.code}] ${w.message}`);
|
|
979
|
+
}
|
|
980
|
+
if (result.errors.length > 0) {
|
|
981
|
+
lines.push("");
|
|
982
|
+
lines.push(" errors:");
|
|
983
|
+
for (const e of result.errors)
|
|
984
|
+
lines.push(` [${e.code}] ${e.message}`);
|
|
985
|
+
}
|
|
986
|
+
lines.push("");
|
|
987
|
+
return lines.join("\n");
|
|
988
|
+
}
|
|
989
|
+
//# sourceMappingURL=index.js.map
|