@ericdisero/aurora-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.
package/README.md ADDED
@@ -0,0 +1,37 @@
1
+ # @ericdisero/aurora-cli
2
+
3
+ `aurora` — terminal CLI for **Aurora**, the AI audio workbench. Drive music generation, covers, sample manufacturing, 7-stem separation, and stack export from your shell, or let Claude Code shell out to it instead of loading 27 MCP tool schemas into context.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm i -g @ericdisero/aurora-cli
9
+ aurora keys --suno-api-key <key> --mvsep-api-key <key> # once
10
+ aurora status
11
+ ```
12
+
13
+ ## Commands
14
+
15
+ ```bash
16
+ aurora run --list # every operation + description
17
+ aurora run <op> --key value # run any operation (same surface as the MCP)
18
+ aurora install-skills [--global] # bundled agent recipes → .claude/skills/<name>/SKILL.md
19
+ aurora mcp # print the MCP client config for this machine
20
+ aurora keys # provider key status (masked)
21
+ aurora status # userData / database / projects root / keys
22
+ ```
23
+
24
+ ## Examples
25
+
26
+ ```bash
27
+ aurora run aurora_create_project --name "Midnight Drive"
28
+ aurora run aurora_sounds --prompt "huge cinematic braam, dark low brass" --soundKey Cm --tempo 140
29
+ aurora run aurora_generate --prompt "dark synthwave, driving bass" --background
30
+ aurora run aurora_get_job_status --jobId gen-xxxx # streamUrls while cooking
31
+ aurora run aurora_split --assetId <id> --background # 7 stems, progressive landing
32
+ aurora run aurora_stack_export --projectId <id> # aligned multi-WAV bundle
33
+ ```
34
+
35
+ Server sibling: `@ericdisero/aurora-mcp-server`. Full docs: [github.com/EricDisero/aurora-mcp](https://github.com/EricDisero/aurora-mcp).
36
+
37
+ MIT. Copyright Blueprint Online Learning Inc.
@@ -0,0 +1,5 @@
1
+ interface InstallSkillsOptions {
2
+ global?: boolean;
3
+ }
4
+ export declare function runInstallSkills(opts: InstallSkillsOptions): void;
5
+ export {};
@@ -0,0 +1,47 @@
1
+ // Install the bundled skill markdown into Claude Code's skill directories.
2
+ // Claude Code discovers skills as DIRECTORIES: .claude/skills/<name>/SKILL.md —
3
+ // a loose .md dropped into skills/ is silently never loaded (the slates-mcp
4
+ // 6/9 audit lesson, baked in from day one here).
5
+ import { join } from 'node:path';
6
+ import { homedir } from 'node:os';
7
+ import { existsSync, writeFileSync, mkdirSync } from 'node:fs';
8
+ import { SKILLS } from '@ericdisero/aurora-shared';
9
+ function frontmatterName(markdown, fallback) {
10
+ if (!markdown.startsWith('---'))
11
+ return fallback;
12
+ const end = markdown.indexOf('\n---', 3);
13
+ if (end === -1)
14
+ return fallback;
15
+ const m = markdown.slice(3, end).match(/^name:\s*(.+?)\s*$/m);
16
+ return m ? m[1].trim() : fallback;
17
+ }
18
+ export function runInstallSkills(opts) {
19
+ const skillEntries = Object.entries(SKILLS);
20
+ if (skillEntries.length === 0) {
21
+ console.error('No bundled skills found in @ericdisero/aurora-shared — broken build?');
22
+ process.exit(1);
23
+ }
24
+ const target = opts.global
25
+ ? join(homedir(), '.claude', 'skills')
26
+ : join(process.cwd(), '.claude', 'skills');
27
+ let fresh = 0;
28
+ let updated = 0;
29
+ for (const [key, content] of skillEntries) {
30
+ const name = frontmatterName(content, key);
31
+ const dir = join(target, name);
32
+ const isUpdate = existsSync(dir);
33
+ mkdirSync(dir, { recursive: true });
34
+ writeFileSync(join(dir, 'SKILL.md'), content);
35
+ if (isUpdate) {
36
+ updated++;
37
+ console.log(`update ${name}`);
38
+ }
39
+ else {
40
+ fresh++;
41
+ console.log(`install ${name}`);
42
+ }
43
+ }
44
+ console.log(`\nInstalled ${fresh + updated} skill(s) (${fresh} new, ${updated} updated) into ${target}`);
45
+ console.log(`Restart Claude Code, then ask: 'what aurora skills do you have?' to verify.`);
46
+ }
47
+ //# sourceMappingURL=install-skills.js.map
@@ -0,0 +1,7 @@
1
+ interface KeysOptions {
2
+ sunoApiKey?: string;
3
+ kieApiKey?: string;
4
+ mvsepApiKey?: string;
5
+ }
6
+ export declare function runKeys(opts: KeysOptions): void;
7
+ export {};
@@ -0,0 +1,33 @@
1
+ // `aurora keys` — provider key configuration (~/.aurora/config.json).
2
+ // No flags = status (masked). Aurora itself has no auth (testing build);
3
+ // these are the dev-direct provider keys.
4
+ import { getConfigPath, getKieKey, getMvsepKey, getSunoKey, writeKeyConfig } from '@ericdisero/aurora-shared';
5
+ function mask(key) {
6
+ if (!key)
7
+ return 'NOT SET';
8
+ if (key.length <= 8)
9
+ return '****';
10
+ return `${key.slice(0, 4)}…${key.slice(-4)}`;
11
+ }
12
+ export function runKeys(opts) {
13
+ const updates = {};
14
+ if (opts.sunoApiKey)
15
+ updates.sunoApiKey = opts.sunoApiKey;
16
+ if (opts.kieApiKey)
17
+ updates.kieApiKey = opts.kieApiKey;
18
+ if (opts.mvsepApiKey)
19
+ updates.mvsepApiKey = opts.mvsepApiKey;
20
+ if (Object.keys(updates).length > 0) {
21
+ writeKeyConfig(updates);
22
+ console.log(`Saved to ${getConfigPath()}`);
23
+ }
24
+ console.log('Provider keys (env vars override the config file):');
25
+ console.log(` suno (SUNO_API_KEY): ${mask(getSunoKey())} ← PRIMARY (sunoapi.org)`);
26
+ console.log(` kie (KIE_API_KEY): ${mask(getKieKey())} ← fallback (api.kie.ai)`);
27
+ console.log(` mvsep (MVSEP_API_KEY): ${mask(getMvsepKey())} ← stem separation`);
28
+ if (!getSunoKey() && !getKieKey()) {
29
+ console.log('\nNo Suno provider key — generation/cover/sounds ops will fail.');
30
+ console.log('Set one: aurora keys --suno-api-key <key>');
31
+ }
32
+ }
33
+ //# sourceMappingURL=keys.js.map
@@ -0,0 +1 @@
1
+ export declare function runMcpConfig(): void;
@@ -0,0 +1,41 @@
1
+ // `aurora mcp` — print the MCP client config for this machine, with the key
2
+ // envs inlined when configured (keys in ~/.aurora/config.json are read by the
3
+ // server directly, so the env block is optional in that case).
4
+ import { getKieKey, getMvsepKey, getSunoKey, readKeyConfig } from '@ericdisero/aurora-shared';
5
+ export function runMcpConfig() {
6
+ const fileConfig = readKeyConfig();
7
+ const needsEnvBlock = !fileConfig.sunoApiKey && !fileConfig.kieApiKey && !fileConfig.mvsepApiKey;
8
+ const env = {};
9
+ if (needsEnvBlock) {
10
+ if (getSunoKey())
11
+ env.SUNO_API_KEY = '<your sunoapi.org key>';
12
+ if (getKieKey())
13
+ env.KIE_API_KEY = '<your kie.ai key>';
14
+ if (getMvsepKey())
15
+ env.MVSEP_API_KEY = '<your mvsep key>';
16
+ if (Object.keys(env).length === 0) {
17
+ env.SUNO_API_KEY = '<your sunoapi.org key>';
18
+ env.MVSEP_API_KEY = '<your mvsep key>';
19
+ }
20
+ }
21
+ const server = {
22
+ command: 'npx',
23
+ args: ['-y', '@ericdisero/aurora-mcp-server']
24
+ };
25
+ if (Object.keys(env).length > 0)
26
+ server.env = env;
27
+ const config = { mcpServers: { aurora: server } };
28
+ console.log('Add to your MCP config (Claude Desktop: claude_desktop_config.json,');
29
+ console.log('Claude Code: `claude mcp add-json aurora ...` or .mcp.json, Cursor: mcp.json):\n');
30
+ console.log(JSON.stringify(config, null, 2));
31
+ if (!needsEnvBlock) {
32
+ console.log('\nKeys found in ~/.aurora/config.json — the server reads them directly, no env block needed.');
33
+ }
34
+ else {
35
+ console.log('\nTip: `aurora keys --suno-api-key <key> --mvsep-api-key <key>` stores keys in');
36
+ console.log('~/.aurora/config.json so the env block becomes unnecessary.');
37
+ }
38
+ console.log('\nClaude Code one-liner:');
39
+ console.log(' claude mcp add aurora -- npx -y @ericdisero/aurora-mcp-server');
40
+ }
41
+ //# sourceMappingURL=mcp.js.map
@@ -0,0 +1,8 @@
1
+ interface RunOpOptions {
2
+ opId: string;
3
+ rawArgs: string[];
4
+ json: boolean;
5
+ }
6
+ export declare function listOps(): void;
7
+ export declare function runOp(opts: RunOpOptions): Promise<void>;
8
+ export {};
@@ -0,0 +1,158 @@
1
+ // `aurora run <op> --key value` dispatcher — port of the slates-mcp CLI's op
2
+ // command, including its best-effort Zod-aware scalar/array/null coercion.
3
+ import { ALL_OPERATIONS } from '@ericdisero/aurora-shared';
4
+ const RUN_COMMAND_FLAGS = new Set(['--json', '--list']);
5
+ export function listOps() {
6
+ const ops = ALL_OPERATIONS;
7
+ for (const op of ops) {
8
+ console.log(`${op.id}\n ${op.description}\n`);
9
+ }
10
+ console.log(`${ops.length} operations. Run one with: aurora run <op> --key value`);
11
+ }
12
+ function parseArgs(rawArgs) {
13
+ const out = {};
14
+ for (let i = 0; i < rawArgs.length; i++) {
15
+ const arg = rawArgs[i];
16
+ if (!arg.startsWith('--'))
17
+ continue;
18
+ if (RUN_COMMAND_FLAGS.has(arg) || RUN_COMMAND_FLAGS.has(arg.split('=')[0])) {
19
+ const next = rawArgs[i + 1];
20
+ if (next != null && !next.startsWith('--'))
21
+ i++;
22
+ continue;
23
+ }
24
+ const eqIdx = arg.indexOf('=');
25
+ if (eqIdx > 0) {
26
+ assign(out, arg.slice(2, eqIdx), arg.slice(eqIdx + 1));
27
+ }
28
+ else {
29
+ const key = arg.slice(2);
30
+ const next = rawArgs[i + 1];
31
+ if (next == null || next.startsWith('--')) {
32
+ assign(out, key, true);
33
+ }
34
+ else {
35
+ assign(out, key, next);
36
+ i++;
37
+ }
38
+ }
39
+ }
40
+ return out;
41
+ }
42
+ function assign(obj, key, value) {
43
+ const existing = obj[key];
44
+ if (existing == null) {
45
+ obj[key] = value;
46
+ return;
47
+ }
48
+ if (Array.isArray(existing)) {
49
+ existing.push(value);
50
+ return;
51
+ }
52
+ obj[key] = [existing, value];
53
+ }
54
+ export async function runOp(opts) {
55
+ const ops = ALL_OPERATIONS;
56
+ const op = ops.find((o) => o.id === opts.opId);
57
+ if (!op) {
58
+ console.error(`Unknown operation: ${opts.opId}`);
59
+ console.error('Run `aurora run --list` to see all operations.');
60
+ process.exit(1);
61
+ }
62
+ const raw = parseArgs(opts.rawArgs);
63
+ const coerced = coerceForSchema(op.input, raw);
64
+ let parsed;
65
+ try {
66
+ parsed = op.input.parse(coerced);
67
+ }
68
+ catch (err) {
69
+ console.error('Invalid arguments for', op.id);
70
+ console.error(err instanceof Error ? err.message : String(err));
71
+ process.exit(1);
72
+ }
73
+ const result = await op.run(parsed);
74
+ if (opts.json) {
75
+ process.stdout.write(JSON.stringify({ text: result.text, data: result.data }, null, 2));
76
+ process.stdout.write('\n');
77
+ return;
78
+ }
79
+ console.log(result.text);
80
+ }
81
+ function coerceForSchema(schema, raw) {
82
+ const zodObj = schema;
83
+ if (zodObj?._def?.typeName !== 'ZodObject')
84
+ return raw;
85
+ const shape = typeof zodObj._def.shape === 'function' ? zodObj._def.shape() : (zodObj.shape ?? {});
86
+ const wrappers = new Set([
87
+ 'ZodOptional',
88
+ 'ZodDefault',
89
+ 'ZodNullable',
90
+ 'ZodReadonly',
91
+ 'ZodEffects',
92
+ 'ZodCatch',
93
+ 'ZodBranded'
94
+ ]);
95
+ const unwrap = (t) => {
96
+ let cur = t;
97
+ let depth = 0;
98
+ while (cur?._def?.typeName && wrappers.has(cur._def.typeName) && depth < 8) {
99
+ cur = cur._def.innerType ?? cur._def.schema;
100
+ depth++;
101
+ }
102
+ return cur;
103
+ };
104
+ const isNullable = (t) => {
105
+ let cur = t;
106
+ let depth = 0;
107
+ while (cur?._def?.typeName && depth < 8) {
108
+ if (cur._def.typeName === 'ZodNullable')
109
+ return true;
110
+ if (wrappers.has(cur._def.typeName)) {
111
+ cur = cur._def.innerType ?? cur._def.schema;
112
+ depth++;
113
+ continue;
114
+ }
115
+ break;
116
+ }
117
+ return false;
118
+ };
119
+ const coerceScalar = (fieldType, value) => {
120
+ if (fieldType === 'ZodNumber') {
121
+ const n = Number(value);
122
+ return Number.isFinite(n) ? n : value;
123
+ }
124
+ if (fieldType === 'ZodBoolean')
125
+ return value === 'true' || value === '1';
126
+ return value;
127
+ };
128
+ const out = {};
129
+ for (const [key, value] of Object.entries(raw)) {
130
+ const declared = shape[key];
131
+ const leaf = unwrap(declared);
132
+ const fieldType = (leaf?._def?.typeName ?? '');
133
+ if (value === 'null' && isNullable(declared)) {
134
+ out[key] = null;
135
+ continue;
136
+ }
137
+ if (fieldType === 'ZodArray') {
138
+ const elemType = (leaf?._def?.type?._def?.typeName ?? '');
139
+ const arr = Array.isArray(value)
140
+ ? value
141
+ : typeof value === 'string'
142
+ ? value
143
+ .split(',')
144
+ .map((s) => s.trim())
145
+ .filter((s) => s.length > 0)
146
+ : [value];
147
+ out[key] = arr.map((v) => (typeof v === 'string' ? coerceScalar(elemType, v) : v));
148
+ continue;
149
+ }
150
+ if (typeof value === 'string') {
151
+ out[key] = coerceScalar(fieldType, value);
152
+ continue;
153
+ }
154
+ out[key] = value;
155
+ }
156
+ return out;
157
+ }
158
+ //# sourceMappingURL=op.js.map
@@ -0,0 +1 @@
1
+ export declare function runStatus(): Promise<void>;
@@ -0,0 +1,20 @@
1
+ // `aurora status` — where Aurora's data lives + key state, with a clear
2
+ // message when the app has never run on this machine.
3
+ import { existsSync } from 'node:fs';
4
+ import { getDbPath, getKieKey, getMvsepKey, getProjectsDirectory, getSunoKey, getUserDataDir } from '@ericdisero/aurora-shared';
5
+ export async function runStatus() {
6
+ const userData = getUserDataDir();
7
+ const dbPath = getDbPath();
8
+ const dbExists = existsSync(dbPath);
9
+ console.log(`userData: ${userData}`);
10
+ console.log(`database: ${dbPath} ${dbExists ? '(exists)' : '(MISSING — created on first op)'}`);
11
+ console.log(`projects root: ${getProjectsDirectory()}`);
12
+ console.log(`suno key: ${getSunoKey() ? 'configured' : getKieKey() ? 'kie fallback configured' : 'NOT SET'}`);
13
+ console.log(`mvsep key: ${getMvsepKey() ? 'configured' : 'NOT SET'}`);
14
+ if (!dbExists) {
15
+ console.log('\nNo Aurora database yet — either the Aurora app has never run on this machine, or it uses a ' +
16
+ 'different userData dir (set AURORA_USER_DATA to override). The first project/asset op will ' +
17
+ 'create a fresh library here.');
18
+ }
19
+ }
20
+ //# sourceMappingURL=status.js.map
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from 'commander';
3
+ import { readFileSync } from 'node:fs';
4
+ import { join, dirname } from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
6
+ import { runOp, listOps } from './commands/op.js';
7
+ import { runInstallSkills } from './commands/install-skills.js';
8
+ import { runKeys } from './commands/keys.js';
9
+ import { runStatus } from './commands/status.js';
10
+ import { runMcpConfig } from './commands/mcp.js';
11
+ const pkg = JSON.parse(readFileSync(join(dirname(fileURLToPath(import.meta.url)), '..', 'package.json'), 'utf8'));
12
+ const program = new Command();
13
+ program
14
+ .name('aurora')
15
+ .description('Aurora AI audio workbench CLI — generation, covers, sounds, 7-stem splits, stack export. ' +
16
+ 'Agent-first: `aurora run <op> --key value` mirrors the MCP tool surface.')
17
+ .version(pkg.version);
18
+ program
19
+ .command('run [op]')
20
+ .description('Run an operation, e.g. `aurora run aurora_create_project --name "Midnight Drive"`')
21
+ .option('--list', 'List every operation with its description')
22
+ .option('--json', 'Machine-readable output ({text, data})')
23
+ .allowUnknownOption(true)
24
+ .action(async (opId, opts, cmd) => {
25
+ if (opts.list || !opId) {
26
+ listOps();
27
+ return;
28
+ }
29
+ await runOp({ opId, rawArgs: cmd.args.slice(1), json: Boolean(opts.json) });
30
+ });
31
+ program
32
+ .command('install-skills')
33
+ .description("Install the bundled agent skills into Claude Code's skill directory (.claude/skills/<name>/SKILL.md)")
34
+ .option('--global', 'Install to ~/.claude/skills instead of ./.claude/skills')
35
+ .action((opts) => {
36
+ runInstallSkills({ global: Boolean(opts.global) });
37
+ });
38
+ program
39
+ .command('keys')
40
+ .description('Configure provider API keys (~/.aurora/config.json). `aurora keys` shows status.')
41
+ .option('--suno-api-key <key>', 'sunoapi.org key (PRIMARY Suno provider)')
42
+ .option('--kie-api-key <key>', 'kie.ai key (fallback Suno provider)')
43
+ .option('--mvsep-api-key <key>', 'MVSEP key (stem separation)')
44
+ .action((opts) => {
45
+ runKeys(opts);
46
+ });
47
+ program
48
+ .command('status')
49
+ .description('Show Aurora connection state: userData, database, projects root, key status')
50
+ .action(async () => {
51
+ await runStatus();
52
+ });
53
+ program
54
+ .command('mcp')
55
+ .description('Print the MCP client config for this machine (Claude Code / Claude Desktop / Cursor)')
56
+ .action(() => {
57
+ runMcpConfig();
58
+ });
59
+ program.parseAsync(process.argv).catch((err) => {
60
+ console.error(err instanceof Error ? err.message : String(err));
61
+ process.exit(1);
62
+ });
63
+ //# sourceMappingURL=index.js.map
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@ericdisero/aurora-cli",
3
+ "version": "0.1.0",
4
+ "description": "Aurora CLI: drive the Aurora AI audio workbench from your terminal or let Claude Code shell out to it. Music generation, covers, sample manufacturing, 7-stem separation, stack export — plus bundled agent skills and an MCP configurator.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "bin": {
8
+ "aurora": "dist/index.js"
9
+ },
10
+ "main": "./dist/index.js",
11
+ "files": ["dist", "!dist/**/*.map", "README.md"],
12
+ "scripts": {
13
+ "build": "tsc",
14
+ "start": "node dist/index.js",
15
+ "typecheck": "tsc --noEmit",
16
+ "prepublishOnly": "npm run build"
17
+ },
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/EricDisero/aurora-mcp.git",
21
+ "directory": "packages/cli"
22
+ },
23
+ "keywords": [
24
+ "aurora",
25
+ "cli",
26
+ "mcp",
27
+ "model-context-protocol",
28
+ "ai-music",
29
+ "music-generation",
30
+ "stem-separation",
31
+ "suno",
32
+ "mvsep",
33
+ "claude",
34
+ "claude-code",
35
+ "agent-skills"
36
+ ],
37
+ "engines": {
38
+ "node": ">=18"
39
+ },
40
+ "dependencies": {
41
+ "@ericdisero/aurora-shared": "0.1.0",
42
+ "commander": "^12.1.0",
43
+ "zod": "^3.23.0"
44
+ },
45
+ "devDependencies": {
46
+ "@types/node": "^20.0.0",
47
+ "typescript": "^5.7.0"
48
+ }
49
+ }