@slatesvideo/cli 0.1.0

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.
@@ -0,0 +1,6 @@
1
+ interface InstallSkillsOptions {
2
+ force: boolean;
3
+ }
4
+ export declare function runInstallSkills(opts: InstallSkillsOptions): void;
5
+ export {};
6
+ //# sourceMappingURL=install-skills.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"install-skills.d.ts","sourceRoot":"","sources":["../../src/commands/install-skills.ts"],"names":[],"mappings":"AAUA,UAAU,oBAAoB;IAC5B,KAAK,EAAE,OAAO,CAAA;CACf;AAOD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,oBAAoB,GAAG,IAAI,CA8BjE"}
@@ -0,0 +1,36 @@
1
+ import { join, dirname } from 'node:path';
2
+ import { fileURLToPath } from 'node:url';
3
+ import { existsSync, readdirSync, readFileSync, writeFileSync, mkdirSync, } from 'node:fs';
4
+ // Copy bundled skill markdown files into ./.claude/skills/ in the user's
5
+ // current working directory. Mirrors how Higgsfield ships its CLI: skills
6
+ // are markdown recipes that compose the operation surface. They auto-
7
+ // register with Claude Code skill discovery once they're on disk.
8
+ export function runInstallSkills(opts) {
9
+ const here = dirname(fileURLToPath(import.meta.url));
10
+ // dist/commands → walk up to package root, then look for /skills.
11
+ const pkgRoot = join(here, '..', '..');
12
+ const skillsRoot = join(pkgRoot, 'skills');
13
+ if (!existsSync(skillsRoot)) {
14
+ console.error(`Bundled skills not found at ${skillsRoot}`);
15
+ process.exit(1);
16
+ }
17
+ const target = join(process.cwd(), '.claude', 'skills');
18
+ if (!existsSync(target))
19
+ mkdirSync(target, { recursive: true });
20
+ const files = readdirSync(skillsRoot).filter((f) => f.endsWith('.md'));
21
+ let copied = 0;
22
+ let skipped = 0;
23
+ for (const file of files) {
24
+ const dest = join(target, file);
25
+ if (existsSync(dest) && !opts.force) {
26
+ console.log(`skip ${file} (already exists, use --force to overwrite)`);
27
+ skipped++;
28
+ continue;
29
+ }
30
+ writeFileSync(dest, readFileSync(join(skillsRoot, file)));
31
+ console.log(`write ${file}`);
32
+ copied++;
33
+ }
34
+ console.log(`\nInstalled ${copied} skill(s), skipped ${skipped}, into ${target}`);
35
+ }
36
+ //# sourceMappingURL=install-skills.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"install-skills.js","sourceRoot":"","sources":["../../src/commands/install-skills.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AACxC,OAAO,EACL,UAAU,EACV,WAAW,EACX,YAAY,EACZ,aAAa,EACb,SAAS,GACV,MAAM,SAAS,CAAA;AAMhB,yEAAyE;AACzE,0EAA0E;AAC1E,sEAAsE;AACtE,kEAAkE;AAElE,MAAM,UAAU,gBAAgB,CAAC,IAA0B;IACzD,MAAM,IAAI,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAA;IACpD,kEAAkE;IAClE,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAA;IACtC,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAA;IAE1C,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC5B,OAAO,CAAC,KAAK,CAAC,+BAA+B,UAAU,EAAE,CAAC,CAAA;QAC1D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAA;IACvD,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC;QAAE,SAAS,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IAE/D,MAAM,KAAK,GAAG,WAAW,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAA;IACtE,IAAI,MAAM,GAAG,CAAC,CAAA;IACd,IAAI,OAAO,GAAG,CAAC,CAAA;IACf,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;QAC/B,IAAI,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;YACpC,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,6CAA6C,CAAC,CAAA;YACxE,OAAO,EAAE,CAAA;YACT,SAAQ;QACV,CAAC;QACD,aAAa,CAAC,IAAI,EAAE,YAAY,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC,CAAC,CAAA;QACzD,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,EAAE,CAAC,CAAA;QAC7B,MAAM,EAAE,CAAA;IACV,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,eAAe,MAAM,sBAAsB,OAAO,UAAU,MAAM,EAAE,CAAC,CAAA;AACnF,CAAC"}
@@ -0,0 +1,7 @@
1
+ export interface LoginOptions {
2
+ email?: string;
3
+ clientName?: string;
4
+ token?: string;
5
+ }
6
+ export declare function runLogin(opts: LoginOptions): Promise<void>;
7
+ //# sourceMappingURL=login.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,YAAY;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,wBAAsB,QAAQ,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAiEhE"}
@@ -0,0 +1,63 @@
1
+ import { setCloudToken } from '@slatesvideo/shared';
2
+ import { promptInput } from '../util/prompt.js';
3
+ const CLOUD_BASE_URL = process.env.SLATES_CLOUD_BASE_URL ?? 'https://slates-api.fly.dev';
4
+ export async function runLogin(opts) {
5
+ // Manual paste path — power users who minted a key on the web UI
6
+ // (or want to bypass email entirely for CI).
7
+ if (opts.token) {
8
+ if (!opts.token.startsWith('slates_sk_')) {
9
+ console.error('Token must start with slates_sk_. Aborting.');
10
+ process.exit(1);
11
+ }
12
+ setCloudToken(opts.token);
13
+ console.log('Cloud token saved. You\'re logged in.');
14
+ return;
15
+ }
16
+ const email = opts.email ?? (await promptInput('Slates account email: '));
17
+ const trimmed = email.trim().toLowerCase();
18
+ if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(trimmed)) {
19
+ console.error('Not a valid email address. Aborting.');
20
+ process.exit(1);
21
+ }
22
+ const clientName = (opts.clientName ?? 'Slates CLI').slice(0, 80);
23
+ const reqRes = await fetch(`${CLOUD_BASE_URL}/auth/agent/request`, {
24
+ method: 'POST',
25
+ headers: { 'Content-Type': 'application/json' },
26
+ body: JSON.stringify({ email: trimmed, client_name: clientName }),
27
+ });
28
+ const reqBody = (await reqRes.json());
29
+ if (!reqBody.success || !reqBody.challengeId) {
30
+ console.error(`Failed to request sign-in: ${reqBody.error ?? 'unknown error'}`);
31
+ process.exit(1);
32
+ }
33
+ console.log(`\nA sign-in link is on its way to ${trimmed}.`);
34
+ console.log('Click the link, then return here. Waiting (10 min max)...\n');
35
+ const start = Date.now();
36
+ const timeoutMs = 10 * 60 * 1000;
37
+ while (Date.now() - start < timeoutMs) {
38
+ await sleep(1500);
39
+ try {
40
+ const pollRes = await fetch(`${CLOUD_BASE_URL}/auth/agent/poll?challengeId=${encodeURIComponent(reqBody.challengeId)}`);
41
+ const pollBody = (await pollRes.json());
42
+ if (pollBody.status === 'expired') {
43
+ console.error('Sign-in link expired. Run `slates login` again.');
44
+ process.exit(1);
45
+ }
46
+ if (pollBody.status === 'completed' && pollBody.token) {
47
+ setCloudToken(pollBody.token);
48
+ console.log('Connected to Slates. Token saved to ~/.slates/agent-connection.json');
49
+ return;
50
+ }
51
+ process.stdout.write('.');
52
+ }
53
+ catch {
54
+ // Network blip — keep polling.
55
+ }
56
+ }
57
+ console.error('\nTimed out waiting for sign-in. Run `slates login` again.');
58
+ process.exit(1);
59
+ }
60
+ function sleep(ms) {
61
+ return new Promise((r) => setTimeout(r, ms));
62
+ }
63
+ //# sourceMappingURL=login.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare function runLogout(): void;
2
+ //# sourceMappingURL=logout.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logout.d.ts","sourceRoot":"","sources":["../../src/commands/logout.ts"],"names":[],"mappings":"AAEA,wBAAgB,SAAS,IAAI,IAAI,CAGhC"}
@@ -0,0 +1,6 @@
1
+ import { clearCloudToken } from '@slatesvideo/shared';
2
+ export function runLogout() {
3
+ clearCloudToken();
4
+ console.log('Cloud token cleared.');
5
+ }
6
+ //# sourceMappingURL=logout.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logout.js","sourceRoot":"","sources":["../../src/commands/logout.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAA;AAErD,MAAM,UAAU,SAAS;IACvB,eAAe,EAAE,CAAA;IACjB,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAC,CAAA;AACrC,CAAC"}
@@ -0,0 +1,8 @@
1
+ interface RunOpOptions {
2
+ opId: string;
3
+ rawArgs: string[];
4
+ json: boolean;
5
+ }
6
+ export declare function runOp(opts: RunOpOptions): Promise<void>;
7
+ export {};
8
+ //# sourceMappingURL=op.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"op.d.ts","sourceRoot":"","sources":["../../src/commands/op.ts"],"names":[],"mappings":"AAEA,UAAU,YAAY;IACpB,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,IAAI,EAAE,OAAO,CAAA;CACd;AAsDD,wBAAsB,KAAK,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CA+C7D"}
@@ -0,0 +1,118 @@
1
+ import { ALL_OPERATIONS, defaultContext } from '@slatesvideo/shared';
2
+ // Run-command flags that are CLI-side controls, not operation inputs.
3
+ // They get filtered out of rawArgs so they don't fail strict schemas
4
+ // like z.object({}).strict() on operations that take no input.
5
+ const RUN_COMMAND_FLAGS = new Set(['--json', '--list']);
6
+ // Parse `--key value` pairs into an object. Repeated `--key` becomes an
7
+ // array. Booleans accept `--flag` (true) and `--flag=false`. Numbers
8
+ // auto-coerce when the Zod schema expects one — done at parse time
9
+ // against the operation input shape.
10
+ function parseArgs(rawArgs) {
11
+ const out = {};
12
+ for (let i = 0; i < rawArgs.length; i++) {
13
+ const arg = rawArgs[i];
14
+ if (!arg.startsWith('--'))
15
+ continue;
16
+ if (RUN_COMMAND_FLAGS.has(arg) || RUN_COMMAND_FLAGS.has(arg.split('=')[0])) {
17
+ // Skip the value too if it doesn't start with --
18
+ const next = rawArgs[i + 1];
19
+ if (next != null && !next.startsWith('--'))
20
+ i++;
21
+ continue;
22
+ }
23
+ const eqIdx = arg.indexOf('=');
24
+ if (eqIdx > 0) {
25
+ const key = arg.slice(2, eqIdx);
26
+ const value = arg.slice(eqIdx + 1);
27
+ assign(out, key, value);
28
+ }
29
+ else {
30
+ const key = arg.slice(2);
31
+ const next = rawArgs[i + 1];
32
+ if (next == null || next.startsWith('--')) {
33
+ assign(out, key, true);
34
+ }
35
+ else {
36
+ assign(out, key, next);
37
+ i++;
38
+ }
39
+ }
40
+ }
41
+ return out;
42
+ }
43
+ function assign(obj, key, value) {
44
+ const existing = obj[key];
45
+ if (existing == null) {
46
+ obj[key] = value;
47
+ return;
48
+ }
49
+ if (Array.isArray(existing)) {
50
+ existing.push(value);
51
+ return;
52
+ }
53
+ obj[key] = [existing, value];
54
+ }
55
+ export async function runOp(opts) {
56
+ const ops = ALL_OPERATIONS;
57
+ const op = ops.find((o) => o.id === opts.opId);
58
+ if (!op) {
59
+ console.error(`Unknown operation: ${opts.opId}`);
60
+ console.error('Run `slates run --list` to see all operations.');
61
+ process.exit(1);
62
+ }
63
+ const raw = parseArgs(opts.rawArgs);
64
+ const coerced = coerceForSchema(op.input, raw);
65
+ let parsed;
66
+ try {
67
+ parsed = op.input.parse(coerced);
68
+ }
69
+ catch (err) {
70
+ console.error('Invalid arguments for', op.id);
71
+ console.error(err instanceof Error ? err.message : String(err));
72
+ process.exit(1);
73
+ }
74
+ const result = await op.run(parsed, defaultContext());
75
+ if (opts.json) {
76
+ process.stdout.write(JSON.stringify({
77
+ text: result.text,
78
+ data: result.data,
79
+ images: result.images?.map((i) => ({
80
+ mimeType: i.mimeType,
81
+ bytes: Buffer.from(i.data, 'base64').byteLength,
82
+ })),
83
+ }, null, 2));
84
+ process.stdout.write('\n');
85
+ return;
86
+ }
87
+ console.log(result.text);
88
+ if (result.images && result.images.length > 0) {
89
+ console.log(`\nReturned ${result.images.length} inline image(s).`);
90
+ console.log('(use --json to get the full payload, or use the MCP server for vision-aware tools)');
91
+ }
92
+ }
93
+ function coerceForSchema(schema, raw) {
94
+ const zodObj = schema;
95
+ if (zodObj?._def?.typeName !== 'ZodObject')
96
+ return raw;
97
+ const shape = typeof zodObj._def.shape === 'function'
98
+ ? zodObj._def.shape()
99
+ : zodObj.shape ?? {};
100
+ const out = {};
101
+ for (const [key, value] of Object.entries(raw)) {
102
+ const fieldType = (shape[key]?._def?.typeName ?? '');
103
+ if (typeof value === 'string') {
104
+ if (fieldType === 'ZodNumber') {
105
+ const n = Number(value);
106
+ out[key] = Number.isFinite(n) ? n : value;
107
+ continue;
108
+ }
109
+ if (fieldType === 'ZodBoolean') {
110
+ out[key] = value === 'true' || value === '1';
111
+ continue;
112
+ }
113
+ }
114
+ out[key] = value;
115
+ }
116
+ return out;
117
+ }
118
+ //# sourceMappingURL=op.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"op.js","sourceRoot":"","sources":["../../src/commands/op.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare function runStatus(): Promise<void>;
2
+ //# sourceMappingURL=status.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"AAGA,wBAAsB,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC,CAmC/C"}
@@ -0,0 +1,34 @@
1
+ import { readConnection, SlatesCloudClient, SlatesDesktopClient } from '@slatesvideo/shared';
2
+ export async function runStatus() {
3
+ const c = readConnection();
4
+ console.log('=== Slates connection status ===');
5
+ console.log(`Cloud token: ${c.cloud.token ? 'present' : 'missing — run `slates login`'}`);
6
+ console.log(`Desktop server: ${c.desktop.enabled
7
+ ? `enabled on 127.0.0.1:${c.desktop.port}`
8
+ : 'disabled — open Slates → Settings → Agent Control'}`);
9
+ if (c.cloud.token) {
10
+ try {
11
+ const cloud = new SlatesCloudClient();
12
+ const me = await cloud.get('/api/agent/me');
13
+ console.log(`\nLogged in as: ${me.user.email}`);
14
+ console.log(`License: ${me.user.license_status}`);
15
+ console.log(`Tier: ${me.user.tier}`);
16
+ console.log(`Credit balance: $${me.user.credit_balance_dollars}`);
17
+ console.log(`Scopes: ${me.scopes.join(', ')}`);
18
+ }
19
+ catch (err) {
20
+ console.warn(`\nCloud check failed: ${err instanceof Error ? err.message : String(err)}`);
21
+ }
22
+ }
23
+ if (c.desktop.enabled && c.desktop.port && c.desktop.token) {
24
+ try {
25
+ const desktop = new SlatesDesktopClient();
26
+ const h = await desktop.healthz();
27
+ console.log(`\nDesktop reachable: ${h.ok ? 'yes' : 'no'}`);
28
+ }
29
+ catch (err) {
30
+ console.warn(`Desktop reachable: no (${err instanceof Error ? err.message : err})`);
31
+ }
32
+ }
33
+ }
34
+ //# sourceMappingURL=status.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAA;AAG5F,MAAM,CAAC,KAAK,UAAU,SAAS;IAC7B,MAAM,CAAC,GAAG,cAAc,EAAE,CAAA;IAC1B,OAAO,CAAC,GAAG,CAAC,kCAAkC,CAAC,CAAA;IAC/C,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,8BAA8B,EAAE,CAAC,CAAA;IAC/F,OAAO,CAAC,GAAG,CACT,sBACE,CAAC,CAAC,OAAO,CAAC,OAAO;QACf,CAAC,CAAC,wBAAwB,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE;QAC1C,CAAC,CAAC,mDACN,EAAE,CACH,CAAA;IAED,IAAI,CAAC,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QAClB,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,IAAI,iBAAiB,EAAE,CAAA;YACrC,MAAM,EAAE,GAAG,MAAM,KAAK,CAAC,GAAG,CAAiB,eAAe,CAAC,CAAA;YAC3D,OAAO,CAAC,GAAG,CAAC,wBAAwB,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAA;YACpD,OAAO,CAAC,GAAG,CAAC,sBAAsB,EAAE,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC,CAAA;YAC3D,OAAO,CAAC,GAAG,CAAC,sBAAsB,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAA;YACjD,OAAO,CAAC,GAAG,CAAC,uBAAuB,EAAE,CAAC,IAAI,CAAC,sBAAsB,EAAE,CAAC,CAAA;YACpE,OAAO,CAAC,GAAG,CAAC,sBAAsB,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;QAC3D,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,IAAI,CAAC,yBAAyB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;QAC3F,CAAC;IACH,CAAC;IAED,IAAI,CAAC,CAAC,OAAO,CAAC,OAAO,IAAI,CAAC,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;QAC3D,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,IAAI,mBAAmB,EAAE,CAAA;YACzC,MAAM,CAAC,GAAG,MAAM,OAAO,CAAC,OAAO,EAAE,CAAA;YACjC,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAA;QAC5D,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,IAAI,CAAC,0BAA0B,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAA;QACrF,CAAC;IACH,CAAC;AACH,CAAC"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,106 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from 'commander';
3
+ import { readConnection, setCloudToken, clearCloudToken } from '@slatesvideo/shared';
4
+ import { runLogin } from './commands/login.js';
5
+ import { runLogout } from './commands/logout.js';
6
+ import { runStatus } from './commands/status.js';
7
+ import { runOp } from './commands/op.js';
8
+ import { runInstallSkills } from './commands/install-skills.js';
9
+ import { ALL_OPERATIONS } from '@slatesvideo/shared';
10
+ // Slates CLI. The CLI mirrors the MCP tool surface as commands so Claude
11
+ // Code can shell out to them and skip the cost of loading 20+ tool
12
+ // schemas into its context window. Skills (in packages/skills) tell the
13
+ // agent how to compose them.
14
+ //
15
+ // First-time use:
16
+ // 1. Open Slates desktop → Settings → Agent Control → toggle on.
17
+ // 2. Run `slates login` — sends a magic link to your account email.
18
+ // 3. Click the link in your email.
19
+ // 4. The CLI auto-detects the connection and writes the cloud token
20
+ // into ~/.slates/agent-connection.json. The desktop app's local
21
+ // port + token are already there from step 1.
22
+ //
23
+ // Inside Claude Code:
24
+ // `/slates run slates_create_project --name "neon samurai"`
25
+ // `/slates run slates_generate_image --prompt "..." --resolution 2k`
26
+ //
27
+ // Or import @slatesvideo/skills/*.md into .claude/skills/ for higher-
28
+ // level recipes.
29
+ const program = new Command();
30
+ program
31
+ .name('slates')
32
+ .description('Slates CLI — drive AI Video Creation Studio from your terminal.')
33
+ .version('0.1.0')
34
+ .enablePositionalOptions();
35
+ program
36
+ .command('login')
37
+ .description('Authorize this CLI to use your Slates account (magic link).')
38
+ .option('--email <email>', 'Slates account email (otherwise prompted)')
39
+ .option('--client-name <name>', 'Label this connection (shows in Slates Settings)', 'Slates CLI')
40
+ .option('--token <token>', 'Skip the magic-link flow and paste a slates_sk_ token directly.')
41
+ .action((opts) => runLogin(opts));
42
+ program
43
+ .command('logout')
44
+ .description('Clear the stored Slates cloud token.')
45
+ .action(() => runLogout());
46
+ program
47
+ .command('status')
48
+ .description('Show the current Slates connection status.')
49
+ .action(() => runStatus());
50
+ program
51
+ .command('install-skills')
52
+ .description('Copy the bundled skills into your local .claude/skills directory.')
53
+ .option('--force', 'Overwrite existing skill files', false)
54
+ .action((opts) => runInstallSkills(opts));
55
+ const runCmd = program
56
+ .command('run')
57
+ .description('Invoke a Slates operation by id (use `slates run --list` to see them).')
58
+ .option('--list', 'List all available operations', false)
59
+ .option('--json', 'Emit raw JSON response instead of formatted text', false)
60
+ .allowUnknownOption()
61
+ .passThroughOptions()
62
+ .argument('[opId]', 'Operation id, e.g. slates_create_project')
63
+ .argument('[args...]', '--key value pairs for the operation input')
64
+ .action(async (opId, args, options) => {
65
+ // With passThroughOptions enabled, --json/--list AFTER the opId end
66
+ // up in `args` instead of `options`. Detect both positions.
67
+ const jsonFlag = !!options.json || args.some((a) => a === '--json' || a.startsWith('--json='));
68
+ const listFlag = !!options.list || args.some((a) => a === '--list');
69
+ if (listFlag || !opId) {
70
+ const ops = ALL_OPERATIONS;
71
+ for (const op of ops) {
72
+ console.log(`${op.id}\n ${op.description}\n`);
73
+ }
74
+ return;
75
+ }
76
+ await runOp({ opId, rawArgs: args, json: jsonFlag });
77
+ });
78
+ // Hidden helper for skills that just need the cloud token.
79
+ program
80
+ .command('print-cloud-token', { hidden: true })
81
+ .action(() => {
82
+ const c = readConnection();
83
+ if (!c.cloud.token) {
84
+ console.error('No cloud token. Run `slates login`.');
85
+ process.exit(1);
86
+ }
87
+ process.stdout.write(c.cloud.token);
88
+ });
89
+ // Hidden helper for tests / scripting.
90
+ program
91
+ .command('set-cloud-token <token>', { hidden: true })
92
+ .action((token) => {
93
+ setCloudToken(token);
94
+ console.log('Cloud token written.');
95
+ });
96
+ program
97
+ .command('clear-cloud-token', { hidden: true })
98
+ .action(() => {
99
+ clearCloudToken();
100
+ console.log('Cloud token cleared.');
101
+ });
102
+ program.parseAsync(process.argv).catch((err) => {
103
+ console.error(err instanceof Error ? err.message : String(err));
104
+ process.exit(1);
105
+ });
106
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare function promptInput(question: string): Promise<string>;
2
+ //# sourceMappingURL=prompt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt.d.ts","sourceRoot":"","sources":["../../src/util/prompt.ts"],"names":[],"mappings":"AAEA,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAQ7D"}
@@ -0,0 +1,11 @@
1
+ import { createInterface } from 'node:readline';
2
+ export function promptInput(question) {
3
+ return new Promise((resolve) => {
4
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
5
+ rl.question(question, (answer) => {
6
+ rl.close();
7
+ resolve(answer);
8
+ });
9
+ });
10
+ }
11
+ //# sourceMappingURL=prompt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt.js","sourceRoot":"","sources":["../../src/util/prompt.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAE/C,MAAM,UAAU,WAAW,CAAC,QAAgB;IAC1C,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,MAAM,EAAE,GAAG,eAAe,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAA;QAC5E,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,EAAE;YAC/B,EAAE,CAAC,KAAK,EAAE,CAAA;YACV,OAAO,CAAC,MAAM,CAAC,CAAA;QACjB,CAAC,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;AACJ,CAAC"}
package/package.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "@slatesvideo/cli",
3
+ "version": "0.1.0",
4
+ "description": "Slates CLI — drive Slates from your terminal or Claude Code via shell-out.",
5
+ "license": "UNLICENSED",
6
+ "type": "module",
7
+ "bin": {
8
+ "slates": "./dist/index.js"
9
+ },
10
+ "main": "./dist/index.js",
11
+ "files": ["dist", "skills", "README.md"],
12
+ "scripts": {
13
+ "build": "tsc",
14
+ "start": "node dist/index.js",
15
+ "typecheck": "tsc --noEmit"
16
+ },
17
+ "dependencies": {
18
+ "@slatesvideo/shared": "0.1.0",
19
+ "commander": "^12.1.0",
20
+ "open": "^10.0.0",
21
+ "zod": "^3.23.0"
22
+ },
23
+ "devDependencies": {
24
+ "@types/node": "^20.0.0",
25
+ "typescript": "^5.7.0"
26
+ }
27
+ }
@@ -0,0 +1,53 @@
1
+ ---
2
+ name: slates-character-turnaround
3
+ description: Build a Slates character from a reference image — generate the turnaround sheet and expression sheet, bind both to the character's slots so the user sees the character card update live. Use when the user wants to "create a character", "build a character from this image", "generate a turnaround for X", or starts any storyboard-flow that needs consistent character references.
4
+ ---
5
+
6
+ # Character turnaround — Slates workflow
7
+
8
+ Slates stores characters with two image slots: turnaround (full-body multi-angle) and expression sheet (face emotions). The whole storyboard generation pipeline pulls these via `@character` mentions for visual consistency. Building them well = consistent character across every frame.
9
+
10
+ ## Workflow
11
+
12
+ ### 1. Get the reference
13
+ The user has either:
14
+ - Pasted/uploaded an image of the character (real person, drawing, AI render).
15
+ - Described the character in text only.
16
+
17
+ If image: upload it as a reference (`slates_upload_reference_image`).
18
+ If text only: skip step 2 and generate the turnaround from prompt-only (less consistent — warn the user).
19
+
20
+ ### 2. Create the character record
21
+ `slates_create_character` with:
22
+ - `name` (ask if not given)
23
+ - `style` — match the project's overall style (`realistic`, `anime`, `pixar`, `comic-book`).
24
+ - `description` — 1-2 sentences, *visual* only ("tall, dark hair, scar over left eye"), not personality.
25
+
26
+ ### 3. Generate the turnaround sheet
27
+ - Use `slates_generate_image` with a prompt like:
28
+ > "Character turnaround sheet: 5 angles (front, 3/4 left, profile left, 3/4 right, back). Neutral pose. Even studio lighting. White background. {character description}"
29
+ - Pass the reference image URL/ID as a reference.
30
+ - Resolution: **2k** (default). 4k only if the user specifies.
31
+ - Estimate cost first.
32
+
33
+ When the result returns inline:
34
+ - Evaluate against the reference. Is it the same character?
35
+ - If wrong angle count / inconsistent: refine prompt with explicit angle list, regenerate once.
36
+ - When right: bind via `slates_set_character_turnaround_asset` (the user sees the character card update).
37
+
38
+ ### 4. Generate the expression sheet
39
+ - Prompt:
40
+ > "Expression sheet: 6 expressions (neutral, happy, angry, sad, surprised, determined). Same character, same lighting as the turnaround. {character description}"
41
+ - Pass BOTH the original reference AND the just-generated turnaround as references for max consistency.
42
+ - Same model, same resolution.
43
+ - On success bind via `slates_set_character_expression_asset`.
44
+
45
+ ### 5. Hand back
46
+ > "Character {name} ready. Turnaround + expressions bound. Use `@{name}` in any prompt to attach these references."
47
+
48
+ ## Anti-patterns
49
+
50
+ - **Don't** generate turnaround and expressions in one prompt. Slates expects them as two separate assets in two separate slots.
51
+ - **Don't** skip binding. The slots are what the storyboard pipeline reads — an unbound asset doesn't help downstream.
52
+ - **Don't** invent character details. Stick to what's in the reference image and the user's description.
53
+ - **Don't** use 4k unless asked — wastes credits, no quality gain at sheet scale.
@@ -0,0 +1,62 @@
1
+ ---
2
+ name: slates-direct-response-ad
3
+ description: Build a 30-second hyper-motion direct-response ad in Slates from a product image and brief. Composes upload → storyboard → frame gen → motion gen → timeline → export. Use when the user drops a product image and asks for "an ad", "a promo video", "a TikTok ad", "an Instagram ad", a launch video, or any short-form direct-response video built around a product.
4
+ ---
5
+
6
+ # Direct-response ad — Slates workflow
7
+
8
+ You are building a 30-second hyper-motion direct-response ad. The user has handed you a product image (or product URL) and a short brief. Slates desktop is open on the second monitor; the user watches it populate as you work.
9
+
10
+ **Hard rules**
11
+
12
+ - Always estimate cost before generating. Use `slates_estimate_generation_cost` and surface the total.
13
+ - All MCP/CLI generation routes through Slates Credits, period. BYOK is desktop-UI only by design — don't suggest "use your own keys" workarounds.
14
+ - Default model: `nano-banana-2-2k`. For close-up product hero frames step up to `4k` only if the user asks.
15
+ - Hyper-motion = punchy cuts, 4 frames in 30 seconds, ~7s each. Don't over-storyboard.
16
+
17
+ ## Workflow
18
+
19
+ ### 1. Set up the project
20
+ - Create a project named for the product (`slates_create_project`).
21
+ - If the user gave a product image as a file path, upload it (`slates_upload_reference_image`).
22
+ - If they pasted base64 / a data URL, use the same op with `dataUrl`.
23
+
24
+ ### 2. Generate the storyboard frames
25
+ Build exactly 4 frames in this order:
26
+
27
+ | # | Beat | Visual goal |
28
+ |---|------|-------------|
29
+ | 1 | Hook | Hyper-close-up of the product, dramatic light, motion blur edge |
30
+ | 2 | Lifestyle | Real person using/wearing/holding the product, eye contact |
31
+ | 3 | Problem→solution | The before/after moment that justifies the buy |
32
+ | 4 | CTA | Clean product hero with mental room for an overlaid CTA in editing |
33
+
34
+ For each frame:
35
+ 1. Draft a tight 1-2 sentence prompt (visual only — no copy text in the image).
36
+ 2. Reference the product upload's URL or asset ID for visual fidelity.
37
+ 3. Call `slates_generate_image` with that prompt + reference. **You see the result inline — evaluate it.**
38
+ 4. If it's wrong: refine prompt, regenerate. If it's right: bind it as a frame in the storyboard (`slates_add_frame`).
39
+
40
+ ### 3. Build the storyboard
41
+ - `slates_create_storyboard` named "30s ad — v1".
42
+ - Default scene already exists. Add 3 more scenes ("Hook", "Lifestyle", "Problem-Solution", "CTA") via `slates_add_scene`, or just add all 4 frames to the default scene.
43
+ - For each generated image, add a frame referencing the asset id (`slates_add_frame`).
44
+
45
+ ### 4. Hand back to the user
46
+ - Surface estimated total credits spent.
47
+ - Tell the user the storyboard is ready and they can either:
48
+ - **In Slates desktop:** click each frame to generate motion (the existing UI handles motion generation).
49
+ - **Continue here:** ask you to keep going.
50
+
51
+ If they say keep going, generate motion for each frame using the highest-confidence model the budget allows (Veo 3.1 Fast 8s by default). Then call the export op.
52
+
53
+ ## Anti-patterns
54
+
55
+ - **Don't** generate text overlays in the image. Slates renders captions/CTAs at the editor stage.
56
+ - **Don't** burn credits on slot-machine prompting. If the first generation is off, refine the prompt; don't just regenerate.
57
+ - **Don't** skip the cost estimate. Confirm with the user above $0.50.
58
+ - **Don't** invent visual specifics about the product (colors, textures, angles) that aren't in the reference image. Reference-anchored prompts only.
59
+
60
+ ## Voice
61
+
62
+ The ad lives or dies on the hook frame. Tight, sensory, no fluff. Match the user's brand. Default tone is "scroll-stopping" not "informative."
@@ -0,0 +1,46 @@
1
+ ---
2
+ name: slates-edit-and-iterate
3
+ description: Iterate on an existing Slates asset — re-evaluate, refine prompt, regenerate or edit. Use when the user has an existing generated image in Slates and wants to "tweak it", "change one thing", "make it warmer", "remove the second person", or any other surgical refinement instead of full regeneration.
4
+ ---
5
+
6
+ # Edit and iterate — Slates workflow
7
+
8
+ The user already has a generated image in Slates and wants to refine it. The vision-feedback-loop skill defines the general pattern; this skill is the specific recipe for "I have asset X, here's what's wrong with it."
9
+
10
+ ## Workflow
11
+
12
+ ### 1. Pull the current asset back into context
13
+ - The user references an asset by id, frame number, or "the latest one."
14
+ - Resolve to an asset id (`slates_list_assets` if needed).
15
+ - `slates_get_asset_image` with that id to load it inline. **You see the image.**
16
+
17
+ ### 2. Identify the delta
18
+ The user's request is one of:
19
+ - **Surgical** — "remove the second figure", "make the sword red", "swap the background to a bamboo forest".
20
+ - **Aesthetic** — "warmer light", "more dramatic", "softer focus".
21
+ - **Compositional** — "wider shot", "lower angle", "centered subject".
22
+ - **Wholesale** — "actually let's try a totally different look."
23
+
24
+ ### 3. Pick the right tool
25
+ | Delta type | Approach |
26
+ |---|---|
27
+ | Surgical | `slates_edit_image` with a focused edit prompt + the original as the source. |
28
+ | Aesthetic / compositional | `slates_generate_image` with the original as a reference + a refined prompt. Don't re-roll from scratch. |
29
+ | Wholesale | New prompt, no reference, fresh generation. Treat as a new brief. |
30
+
31
+ ### 4. Generate, evaluate, decide
32
+ - Estimate cost first.
33
+ - After generation, the result is inline. Compare side-by-side with the original (`slates_get_asset_image` again).
34
+ - If the delta is correct: bind to the same slot (frame, character turnaround, etc.) the original was bound to.
35
+ - If the delta missed: one focused refinement, then regenerate. Cap at 3 tries.
36
+
37
+ ### 5. Hand back
38
+ - "Asset updated. Frame 3 now uses {new_asset_id}."
39
+ - Always note what changed and what didn't, so the user can see the surgery worked: "Lighting shifted to warmer, composition unchanged."
40
+
41
+ ## Anti-patterns
42
+
43
+ - **Don't** delete the original asset until the user confirms the new one. Slates keeps both; the user picks.
44
+ - **Don't** mix surgical and wholesale changes in one regeneration. The user said "make it warmer" — don't also reframe the shot.
45
+ - **Don't** re-generate when `edit_image` would work. Edits preserve composition and identity; full regen rolls the dice.
46
+ - **Don't** chain >3 iterations without checking in. If three tries didn't land, the brief is wrong, not the model.
@@ -0,0 +1,49 @@
1
+ ---
2
+ name: slates-storyboard-from-script
3
+ description: Turn a script or treatment into a Slates storyboard with scenes and frames. Use when the user has a script, treatment, shot list, or scene-by-scene description and wants to materialize it as a Slates storyboard, optionally generating frame images per shot.
4
+ ---
5
+
6
+ # Storyboard from script — Slates workflow
7
+
8
+ The user has a script, treatment, or shot list. You're turning it into a Slates storyboard with scene → frame structure, optionally generating images for each frame.
9
+
10
+ ## Workflow
11
+
12
+ ### 1. Parse the script
13
+ Read the user's script. Decide:
14
+
15
+ - **Scene count** — usually 1 scene per location/setting change. Don't fragment into one-frame scenes.
16
+ - **Frames per scene** — match the shot list. Default is 3-6 frames per scene unless the script specifies more.
17
+ - **Shot labels** — pull them from the script (e.g., "Wide", "Close-up", "Over-the-shoulder").
18
+
19
+ If the user hasn't named the storyboard, suggest one based on the project tone.
20
+
21
+ ### 2. Materialize the structure first (no generation yet)
22
+ - `slates_create_storyboard` with the chosen name.
23
+ - For each scene: `slates_add_scene` with a descriptive name and order.
24
+ - For each frame: write a *visual-only* prompt (image, not action), the shot label, and any director notes. **Don't generate yet.**
25
+
26
+ Surface the planned structure back to the user as a tight summary:
27
+ > Storyboard "X" • 4 scenes • 12 frames total
28
+ > Scene 1: Forest opening (3 frames)
29
+ > Scene 2: Confrontation (4 frames)
30
+ > ...
31
+
32
+ Ask: **"Generate frame images now? (y/N)"**
33
+
34
+ ### 3. Generate frames if requested
35
+ For each frame:
36
+ - Estimate cost (`slates_estimate_generation_cost`, `count = total frames`). Confirm with user if total > $0.50.
37
+ - Generate sequentially, with character/environment/style references attached when present in the project (`slates_list_characters`, `slates_list_environments`).
38
+ - Each result returns inline. Evaluate. If wrong, refine prompt + regenerate (charge once, not multiple).
39
+ - Bind to the frame via `slates_add_frame`.
40
+
41
+ ### 4. Hand back
42
+ - Total frames generated, total credits spent, storyboard id.
43
+ - Suggest next steps: motion generation (in Slates UI), or `slates_get_storyboard_with_frames` to review.
44
+
45
+ ## Anti-patterns
46
+
47
+ - **Don't** auto-generate without asking. Generation is the expensive step. Always confirm first.
48
+ - **Don't** invent shot details the script doesn't mention. If the script says "they argue," ask what the shot looks like, don't fabricate "she clenches her fists in a wide shot."
49
+ - **Don't** mix scene structure and frame generation in one pass — building the skeleton first lets the user catch errors before spending credits.
@@ -0,0 +1,42 @@
1
+ ---
2
+ name: slates-vision-feedback-loop
3
+ description: Lower-level utility skill for any Slates workflow that needs to "generate, look at the result, refine, regenerate." Defines the standard inline-vision pattern. Other Slates skills compose this. Use when generating images and you need to confirm they match the brief before moving on, or when the user asks to "iterate" on an image.
4
+ ---
5
+
6
+ # Vision feedback loop — Slates utility skill
7
+
8
+ Slates returns generated images inline as base64. You see the actual pixels. Use that — don't trust prompt-following blindly.
9
+
10
+ ## The pattern
11
+
12
+ 1. **Generate.** Call `slates_generate_image` with a prompt. The result is in your context as an image content block.
13
+ 2. **Evaluate against the brief.** What did the user actually want? Are the elements right? Composition? Lighting? Subject identity?
14
+ 3. **One of three outcomes:**
15
+ - **Right** → save it (bind to a frame, character slot, etc.) and move on.
16
+ - **Close, but adjustable** → refine the prompt with a specific delta ("wider shot", "warmer light", "remove the second figure"), regenerate **once**.
17
+ - **Wrong direction** → ask the user before regenerating. Don't burn credits on prompt-thrashing.
18
+
19
+ ## Refinement rules
20
+
21
+ - **One specific delta per regeneration.** Don't change five things at once — you won't know what helped.
22
+ - **Anchor with references.** If the result drifted from the user's intent, attach the *previous best* generation as a reference image alongside the original brief.
23
+ - **Use `slates_get_asset_image`** to pull a previously-generated image back into context if you need to compare against a fresh generation.
24
+ - **Use `slates_edit_image`** (when available) for surgical tweaks instead of full regeneration when 90% of the image is right.
25
+
26
+ ## Cost discipline
27
+
28
+ - Track total credits spent across the loop. Surface to the user every 3 iterations.
29
+ - Stop after 3 failed iterations on the same prompt — escalate to the user with what you tried and what's not working. The slot machine never converges.
30
+ - For high-cost generations (`> $0.50`), confirm before *every* attempt, not just the first.
31
+
32
+ ## When to break the loop
33
+
34
+ - The user said "good enough" or "ship it." Stop iterating.
35
+ - You've burned >5 generations on one frame. Hand back and ask.
36
+ - The user changes brief mid-loop. Treat it as a new brief, not a continuation.
37
+
38
+ ## Voice when narrating to the user
39
+
40
+ Tight, observational, no editorializing.
41
+ - ✅ "Frame 2 has the wrong lighting direction — back-lit instead of side. Regenerating with side light."
42
+ - ❌ "I notice that the lighting in frame 2 isn't quite what we were going for. I'll go ahead and try again with a different approach."