@doubledigit/cli 0.9.0 → 0.10.1

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 CHANGED
@@ -21,7 +21,7 @@ pnpm dd doctor
21
21
  pnpm onboard
22
22
  pnpm onboard -- --no-run
23
23
  pnpm dd actions remotion-hub
24
- pnpm dd actions remotion-hub init temp-project
24
+ pnpm dd actions remotion-hub init temp-project --framework remotion
25
25
  ```
26
26
 
27
27
  If installed globally, the binary is available as `dd`:
@@ -32,7 +32,7 @@ dd doctor
32
32
  dd onboard
33
33
  dd onboard --no-run
34
34
  dd actions remotion-hub
35
- dd actions remotion-hub init temp-project
35
+ dd actions remotion-hub init temp-project --framework remotion
36
36
  ```
37
37
 
38
38
  ## Requirements
@@ -117,24 +117,28 @@ Use `dd actions` to discover or call enabled micro-app actions through the runni
117
117
 
118
118
  ```bash
119
119
  npx @doubledigit/cli@latest actions remotion-hub
120
- npx @doubledigit/cli@latest actions remotion-hub init temp-project --yes
121
- npx @doubledigit/cli@latest actions remotion-hub list-components --limit 5
122
- npx @doubledigit/cli@latest actions remotion-hub search-components --query "animated chart" --limit 5
120
+ npx @doubledigit/cli@latest actions remotion-hub init temp-project --framework remotion --yes
121
+ npx @doubledigit/cli@latest actions remotion-hub init html-project --framework hyperframe --yes
122
+ npx @doubledigit/cli@latest actions remotion-hub list-components --framework remotion --limit 5
123
+ npx @doubledigit/cli@latest actions remotion-hub search-components --framework hyperframe --query "animated chart" --limit 5
123
124
  npx @doubledigit/cli@latest actions remotion-hub register-component --json-file component.json
124
125
  ```
125
126
 
126
- Remotion Hub `init` is handled locally by the CLI because it creates files on the caller's machine. Other Remotion Hub actions, such as search and registry lookup, call the configured Double Digit app over HTTP.
127
+ Component Hub currently uses the `remotion-hub` compatibility command. `init` and `add` are handled locally by the CLI because they create or modify files on the caller's machine. Other actions, such as search and registry lookup, call the configured Double Digit app over HTTP.
127
128
 
128
- By default, Remotion Hub `init` creates Remotion's Hello World starter so `remotion studio` opens with a visible composition. Pass `--skip-remotion-create` or `--hub-only` only when adding Remotion Hub files to an existing Remotion project.
129
+ Pass `--framework remotion|hyperframe` when the target framework is known. Remotion remains the default for compatibility and creates Remotion's Hello World starter so `remotion studio` opens with a visible composition. HyperFrames init requires Node.js 22 or newer and uses `npx hyperframes preview` as its dev command.
129
130
 
130
- After init, the CLI attempts to install both Remotion's project skills and the Double Digit Remotion Hub auxiliary skills from the created project directory:
131
+ Pass `--skip-framework-create` when adding Component Hub files to an existing project. `--skip-remotion-create` and `--hub-only` remain compatibility aliases.
132
+
133
+ After init, the CLI attempts to install framework skills and the Double Digit Component Hub skill from the created project directory. The retry command list depends on the selected framework; examples include:
131
134
 
132
135
  ```bash
133
136
  npx skills add remotion-dev/skills --all
137
+ npx skills add heygen-com/hyperframes
134
138
  npx skills add crystalphantom/double-digit --skill dd-remotion-hub --agent '*' --yes
135
139
  ```
136
140
 
137
- Pass `--skip-skills` to skip skill installation. If skill installation fails, init still completes the project scaffold and prints exact retry commands. Use `--yes` for agent-run or scripted init flows; `-y` and `--non-interactive` are accepted compatibility aliases.
141
+ Pass `--skip-skills` to skip skill installation. If skill installation fails, init still completes the project scaffold and prints exact retry commands. Use `--yes` for agent-run or scripted init flows; `-y` and `--non-interactive` are accepted compatibility aliases and are forwarded to nested framework scaffolds where supported.
138
142
 
139
143
  Register a component with simple JSON:
140
144
 
@@ -149,6 +153,7 @@ Minimal `component.json`:
149
153
  "title": "Animated Chart",
150
154
  "slug": "animated-chart",
151
155
  "namespace": "doubledigit",
156
+ "framework": "remotion",
152
157
  "kind": "component",
153
158
  "description": "Animated chart scene.",
154
159
  "tags": ["chart", "animation"],
@@ -157,7 +162,7 @@ Minimal `component.json`:
157
162
  }
158
163
  ```
159
164
 
160
- When no `--registry`, `--url`, or `--app-url` is passed, Remotion Hub init writes a registry URL from exported `DD_APP_URL`, `APP_URL`, `NEXT_PUBLIC_APP_URL`, or `BETTER_AUTH_URL`; then the release-configured hosted app URL; then `http://localhost:3111` for local development builds.
165
+ When no `--registry`, `--url`, or `--app-url` is passed, Component Hub init writes a registry URL from exported `DD_APP_URL`, `APP_URL`, `NEXT_PUBLIC_APP_URL`, or `BETTER_AUTH_URL`; then the release-configured hosted app URL; then `http://localhost:3111` for local development builds.
161
166
 
162
167
  For HTTP-backed actions, the command resolves the app URL from exported `DD_APP_URL`, `APP_URL`, or `BETTER_AUTH_URL`; then local env-file `DD_APP_URL`; then the release-configured hosted app URL. It prints JSON responses to stdout. It also reads `.env`, `.env.local`, `apps/main-app/.env`, and `apps/main-app/.env.local`; exported shell values take precedence.
163
168
 
@@ -171,7 +176,7 @@ npx @doubledigit/cli@latest actions remotion-hub
171
176
 
172
177
  This hosted default does not require a local checkout or running app.
173
178
 
174
- Before relying on the hosted default in automation, run discovery as a preflight. If the hosted action endpoint returns HTTP 500 or is unreachable, use `DD_APP_URL` or `--url` to target a local or self-hosted Double Digit app until the hosted Remotion Hub is healthy.
179
+ Before relying on the hosted default in automation, run discovery as a preflight. If the hosted action endpoint returns HTTP 500 or is unreachable, use `DD_APP_URL` or `--url` to target a local or self-hosted Double Digit app until the hosted Component Hub endpoint is healthy.
175
180
 
176
181
  Local development or self-hosted environments should override the target explicitly:
177
182
 
@@ -1 +1 @@
1
- {"version":3,"file":"actions.d.ts","sourceRoot":"","sources":["../../src/commands/actions.ts"],"names":[],"mappings":"AAoCA,wBAAgB,uBAAuB,WAKtC;AAoED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE;WACf,MAAM;WAAS,MAAM;IAuBxD;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,EAAE,WAGrD;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,EAAE,WAGpD;AAED,wBAAgB,+BAA+B,CAAC,IAAI,EAAE,MAAM,EAAE,YAI7D;AAED,wBAAgB,8BAA8B,CAAC,IAAI,EAAE,MAAM,EAAE,YAI5D;AAED,wBAAsB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAoC3D"}
1
+ {"version":3,"file":"actions.d.ts","sourceRoot":"","sources":["../../src/commands/actions.ts"],"names":[],"mappings":"AAqCA,wBAAgB,uBAAuB,WAKtC;AAsED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE;WACf,MAAM;WAAS,MAAM;IAuBxD;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,EAAE,WAGrD;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,EAAE,WAGpD;AAED,wBAAgB,+BAA+B,CAAC,IAAI,EAAE,MAAM,EAAE,YAI7D;AAED,wBAAgB,8BAA8B,CAAC,IAAI,EAAE,MAAM,EAAE,YAI5D;AAED,wBAAsB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAoC3D"}
@@ -10,6 +10,7 @@ import { runRemotionHubInitCommand } from './remotion-hub-init.js';
10
10
  const actionValueOptions = new Set([
11
11
  '--app-url',
12
12
  '--dir',
13
+ '--framework',
13
14
  '--id-or-slug',
14
15
  '--kind',
15
16
  '--limit',
@@ -44,6 +45,7 @@ Options:
44
45
  --query <text> Set input.query
45
46
  --limit <number> Set input.limit
46
47
  --namespace <namespace> Set input namespace/filter
48
+ --framework <framework> Set input framework/filter
47
49
  --slug <slug> Set input.slug
48
50
  --id-or-slug <value> Set input.idOrSlug
49
51
  --kind <kind> Set input kind/filter
@@ -52,13 +54,14 @@ Options:
52
54
 
53
55
  Examples:
54
56
  npx @doubledigit/cli@latest actions remotion-hub
55
- npx @doubledigit/cli@latest actions remotion-hub init my-video --yes
56
- npx @doubledigit/cli@latest actions remotion-hub init my-video --skip-skills
57
- npx @doubledigit/cli@latest actions remotion-hub list-components --limit 5
58
- npx @doubledigit/cli@latest actions remotion-hub add terminal-scene --dir my-video
59
- npx @doubledigit/cli@latest actions remotion-hub search-components --query "animated chart" --limit 5
57
+ npx @doubledigit/cli@latest actions remotion-hub init my-video --framework remotion --yes
58
+ npx @doubledigit/cli@latest actions remotion-hub init my-video --framework hyperframe --yes
59
+ npx @doubledigit/cli@latest actions remotion-hub init my-video --framework remotion --skip-skills
60
+ npx @doubledigit/cli@latest actions remotion-hub list-components --framework remotion --limit 5
61
+ npx @doubledigit/cli@latest actions remotion-hub add terminal-scene --framework remotion --dir my-video
62
+ npx @doubledigit/cli@latest actions remotion-hub search-components --framework remotion --query "animated chart" --limit 5
60
63
  npx @doubledigit/cli@latest actions remotion-hub register-component --json-file component.json
61
- npx @doubledigit/cli@latest actions remotion-hub get-registry-payload --json '{"namespace":"doubledigit","slug":"motion-strip"}'
64
+ npx @doubledigit/cli@latest actions remotion-hub get-registry-payload --json '{"namespace":"doubledigit","slug":"motion-strip","framework":"remotion"}'
62
65
  `;
63
66
  }
64
67
  function readActionEnvFiles(paths) {
@@ -1 +1 @@
1
- {"version":3,"file":"remotion-hub-add.d.ts","sourceRoot":"","sources":["../../src/commands/remotion-hub-add.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,KAAK,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;AA8E9F,wBAAgB,uBAAuB,WAsBtC;AAkBD,wBAAsB,wBAAwB,CAC5C,IAAI,EAAE,MAAM,EAAE,GACb,OAAO,CAAC,uBAAuB,GAAG,SAAS,CAAC,CA2B9C"}
1
+ {"version":3,"file":"remotion-hub-add.d.ts","sourceRoot":"","sources":["../../src/commands/remotion-hub-add.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,KAAK,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;AA+E9F,wBAAgB,uBAAuB,WAwBtC;AAkBD,wBAAsB,wBAAwB,CAC5C,IAAI,EAAE,MAAM,EAAE,GACb,OAAO,CAAC,uBAAuB,GAAG,SAAS,CAAC,CA4B9C"}
@@ -5,6 +5,7 @@ const valueFlags = new Set([
5
5
  'namespace',
6
6
  'registry',
7
7
  'url',
8
+ 'framework',
8
9
  ]);
9
10
  function parseArgs(args) {
10
11
  const positional = [];
@@ -72,6 +73,7 @@ Options:
72
73
  --registry <url> Double Digit app URL or /api/remotion-hub/r endpoint
73
74
  --url, --app-url <url> Alias for --registry
74
75
  --namespace <name> Registry namespace when item is not namespace/slug
76
+ --framework <name> Framework tag (e.g. remotion, hyperframe)
75
77
  --force Overwrite existing generated files
76
78
  --skip-install Do not install npm dependencies
77
79
  --no-install Alias for --skip-install
@@ -80,6 +82,7 @@ Options:
80
82
  Examples:
81
83
  npx @doubledigit/cli@latest actions remotion-hub add terminal-scene
82
84
  npx @doubledigit/cli@latest actions remotion-hub add doubledigit/terminal-scene --dir ./my-video
85
+ npx @doubledigit/cli@latest actions remotion-hub add doubledigit/terminal-scene --framework hyperframe
83
86
  npx @doubledigit/cli@latest actions remotion-hub add terminal-scene --registry https://double-digit.example --skip-install
84
87
  `;
85
88
  }
@@ -116,6 +119,7 @@ export async function runRemotionHubAddCommand(args) {
116
119
  force: booleanFlag(parsed.flags, 'force'),
117
120
  skipInstall: booleanFlag(parsed.flags, 'skip-install', 'no-install'),
118
121
  quiet: json,
122
+ framework: stringFlag(parsed.flags, 'framework'),
119
123
  });
120
124
  if (json)
121
125
  console.log(JSON.stringify(result, null, 2));
@@ -1 +1 @@
1
- {"version":3,"file":"remotion-hub-init.d.ts","sourceRoot":"","sources":["../../src/commands/remotion-hub-init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,KAAK,wBAAwB,EAAE,MAAM,6BAA6B,CAAC;AAqFhG,wBAAgB,wBAAwB,WA6BvC;AAgCD,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,wBAAwB,GAAG,SAAS,CA4B9F"}
1
+ {"version":3,"file":"remotion-hub-init.d.ts","sourceRoot":"","sources":["../../src/commands/remotion-hub-init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,KAAK,wBAAwB,EAAE,MAAM,6BAA6B,CAAC;AAuFhG,wBAAgB,wBAAwB,WAgCvC;AAkCD,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,wBAAwB,GAAG,SAAS,CA8B9F"}
@@ -2,6 +2,7 @@ import { runRemotionHubInit } from '../lib/remotion-hub-init.js';
2
2
  const valueFlags = new Set([
3
3
  'app-url',
4
4
  'dir',
5
+ 'framework',
5
6
  'namespace',
6
7
  'registry',
7
8
  'url',
@@ -46,6 +47,7 @@ function parseArgs(args) {
46
47
  || rawKey === 'hub-only'
47
48
  || rawKey === 'json'
48
49
  || rawKey === 'non-interactive'
50
+ || rawKey === 'skip-framework-create'
49
51
  || rawKey === 'skip-skills'
50
52
  || rawKey === 'skip-remotion-create'
51
53
  || rawKey === 'yes') {
@@ -69,9 +71,9 @@ function booleanFlag(flags, ...names) {
69
71
  }
70
72
  export function buildRemotionHubInitHelp() {
71
73
  return `
72
- dd actions remotion-hub init - Initialize a local Remotion Hub project
74
+ dd actions remotion-hub init - Initialize a local Component Hub project
73
75
 
74
- Creates a Remotion Hello World starter by default, then adds Remotion Hub config files.
76
+ Creates a Remotion starter by default, then adds Component Hub config files.
75
77
 
76
78
  Usage:
77
79
  dd actions remotion-hub init [project-dir] [options]
@@ -81,8 +83,10 @@ Options:
81
83
  --registry <url> Double Digit app URL or /api/remotion-hub/r endpoint
82
84
  --url, --app-url <url> Alias for --registry
83
85
  --namespace <name> Registry namespace (default: doubledigit)
84
- --skip-remotion-create Only add Remotion Hub files; do not run create-video
85
- --hub-only Alias for --skip-remotion-create
86
+ --framework <name> Framework to scaffold (remotion or hyperframe; default: remotion)
87
+ --skip-framework-create Only add Component Hub files; do not run framework scaffold
88
+ --skip-remotion-create Alias for --skip-framework-create
89
+ --hub-only Alias for --skip-framework-create
86
90
  --skip-skills Do not install agent skills
87
91
  --yes, -y Skip prompts in nested tools and use defaults
88
92
  --non-interactive Alias for --yes
@@ -92,15 +96,18 @@ Options:
92
96
  Examples:
93
97
  npx @doubledigit/cli@latest actions remotion-hub init
94
98
  npx @doubledigit/cli@latest actions remotion-hub init my-video
95
- npx @doubledigit/cli@latest actions remotion-hub init my-video --yes
96
- npx @doubledigit/cli@latest actions remotion-hub init my-video --skip-skills
99
+ npx @doubledigit/cli@latest actions remotion-hub init my-video --framework remotion --yes
100
+ npx @doubledigit/cli@latest actions remotion-hub init my-video --framework hyperframe --yes
101
+ npx @doubledigit/cli@latest actions remotion-hub init my-video --framework remotion --skip-skills
97
102
  npx @doubledigit/cli@latest actions remotion-hub init my-video --registry https://double-digit.example
98
103
  `;
99
104
  }
100
105
  function printInit(result) {
101
- console.log(`Initialized Remotion Hub in ${result.projectDir}`);
102
- if (!result.remotionCreate.skipped) {
103
- console.log(` Remotion project: ${result.remotionCreate.command} ${result.remotionCreate.args.join(' ')}`);
106
+ console.log(`Initialized Component Hub in ${result.projectDir}`);
107
+ console.log(`Framework: ${result.framework}`);
108
+ console.log(`Dev command: ${result.devCommand}`);
109
+ if (!result.create.skipped) {
110
+ console.log(` Framework project: ${result.create.command} ${result.create.args.join(' ')}`);
104
111
  }
105
112
  for (const file of result.created) {
106
113
  console.log(` ${file}`);
@@ -141,6 +148,8 @@ export function runRemotionHubInitCommand(args) {
141
148
  registry: stringFlag(parsed.flags, 'registry', 'url', 'app-url'),
142
149
  namespace: stringFlag(parsed.flags, 'namespace'),
143
150
  force: booleanFlag(parsed.flags, 'force'),
151
+ framework: stringFlag(parsed.flags, 'framework'),
152
+ skipFrameworkCreate: booleanFlag(parsed.flags, 'skip-framework-create', 'skip-remotion-create', 'hub-only'),
144
153
  skipRemotionCreate: booleanFlag(parsed.flags, 'skip-remotion-create', 'hub-only'),
145
154
  skipSkills: booleanFlag(parsed.flags, 'skip-skills'),
146
155
  nonInteractive: booleanFlag(parsed.flags, 'yes', 'y', 'non-interactive'),
package/dist/index.d.ts CHANGED
@@ -6,7 +6,7 @@
6
6
  * without importing the full extension-management dependency graph first.
7
7
  */
8
8
  declare const command: string, args: string[];
9
- declare const HELP = "\n@doubledigit/cli \u2014 Manage extensions and local setup\n\nCommands:\n doctor Check local prerequisites and project health\n onboard Prepare local development and start the app\n run Start the local app with automatic DB bootstrap\n dev Start DB + app without migrations or seed data\n db <subcommand> Database helpers (status, migrate, create)\n actions <app> [action] Discover and invoke micro-app actions\n create <name> Scaffold a new micro-app from the template\n init <project-name> Scaffold a new Double Digit project\n add|install <source> Install an extension from GitHub or a marketplace\n sync Regenerate micro-apps.ts from dd-apps.config.json\n enable <name> Enable a micro-app (updates config + runs sync)\n disable <name> Disable a micro-app (updates config + runs sync)\n uninstall|remove <name> Completely remove a micro-app\n list List all discovered micro-apps with enabled/disabled status\n info <name> Show detailed info about an installed extension\n outdated Check for outdated marketplace extensions\n reconcile Detect drift between lock file, marketplace, and local files\n marketplace <sub> Manage marketplace registrations (add/list/update/remove)\n browse [marketplace] Browse available extensions in registered marketplaces\n\nOptions:\n --help, -h Show this help message\n\nExamples:\n dd doctor\n dd onboard --yes\n dd onboard # setup + start the dev server\n dd onboard --no-run # setup only\n dd init my-project # scaffold and bootstrap a fresh project\n dd init my-project --run # scaffold + bootstrap + start\n dd init my-project --skip-install --no-git\n dd run\n dd dev\n dd db status\n npx @doubledigit/cli@latest actions remotion-hub init my-video --yes\n npx @doubledigit/cli@latest actions remotion-hub init my-video --skip-skills\n npx @doubledigit/cli@latest actions remotion-hub search-components --query \"animated chart\"\n dd create invoice-tracker\n dd add gh:owner/repo/extensions/micro-apps/habit-tracker\n dd add habit-tracker@community\n dd info habit-tracker\n dd reconcile\n dd marketplace add digitaldouble/dd-marketplace\n dd browse community\n DD_APPS=tasks,agent-v2 dd sync\n";
9
+ declare const HELP = "\n@doubledigit/cli \u2014 Manage extensions and local setup\n\nCommands:\n doctor Check local prerequisites and project health\n onboard Prepare local development and start the app\n run Start the local app with automatic DB bootstrap\n dev Start DB + app without migrations or seed data\n db <subcommand> Database helpers (status, migrate, create)\n actions <app> [action] Discover and invoke micro-app actions\n create <name> Scaffold a new micro-app from the template\n init <project-name> Scaffold a new Double Digit project\n add|install <source> Install an extension from GitHub or a marketplace\n sync Regenerate micro-apps.ts from dd-apps.config.json\n enable <name> Enable a micro-app (updates config + runs sync)\n disable <name> Disable a micro-app (updates config + runs sync)\n uninstall|remove <name> Completely remove a micro-app\n list List all discovered micro-apps with enabled/disabled status\n info <name> Show detailed info about an installed extension\n outdated Check for outdated marketplace extensions\n reconcile Detect drift between lock file, marketplace, and local files\n marketplace <sub> Manage marketplace registrations (add/list/update/remove)\n browse [marketplace] Browse available extensions in registered marketplaces\n\nOptions:\n --help, -h Show this help message\n\nExamples:\n dd doctor\n dd onboard --yes\n dd onboard # setup + start the dev server\n dd onboard --no-run # setup only\n dd init my-project # scaffold and bootstrap a fresh project\n dd init my-project --run # scaffold + bootstrap + start\n dd init my-project --skip-install --no-git\n dd run\n dd dev\n dd db status\n npx @doubledigit/cli@latest actions remotion-hub init my-video --framework remotion --yes\n npx @doubledigit/cli@latest actions remotion-hub init my-video --framework remotion --skip-skills\n npx @doubledigit/cli@latest actions remotion-hub search-components --framework remotion --query \"animated chart\"\n dd create invoice-tracker\n dd add gh:owner/repo/extensions/micro-apps/habit-tracker\n dd add habit-tracker@community\n dd info habit-tracker\n dd reconcile\n dd marketplace add digitaldouble/dd-marketplace\n dd browse community\n DD_APPS=tasks,agent-v2 dd sync\n";
10
10
  declare function requireArg(value: string | undefined, usage: string): string;
11
11
  declare function runAddCommand(rawArgs: string[]): Promise<void>;
12
12
  declare function main(): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA;;;;;GAKG;AAEH,QAAA,MAAW,OAAO,UAAK,IAAI,UAAgB,CAAC;AAE5C,QAAA,MAAM,IAAI,03EAiDT,CAAC;AAEF,iBAAS,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAOpE;AAED,iBAAe,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAuB7D;AAED,iBAAe,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAsInC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA;;;;;GAKG;AAEH,QAAA,MAAW,OAAO,UAAK,IAAI,UAAgB,CAAC;AAE5C,QAAA,MAAM,IAAI,y7EAiDT,CAAC;AAEF,iBAAS,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAOpE;AAED,iBAAe,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAuB7D;AAED,iBAAe,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAsInC"}
package/dist/index.js CHANGED
@@ -45,9 +45,9 @@ Examples:
45
45
  dd run
46
46
  dd dev
47
47
  dd db status
48
- npx @doubledigit/cli@latest actions remotion-hub init my-video --yes
49
- npx @doubledigit/cli@latest actions remotion-hub init my-video --skip-skills
50
- npx @doubledigit/cli@latest actions remotion-hub search-components --query "animated chart"
48
+ npx @doubledigit/cli@latest actions remotion-hub init my-video --framework remotion --yes
49
+ npx @doubledigit/cli@latest actions remotion-hub init my-video --framework remotion --skip-skills
50
+ npx @doubledigit/cli@latest actions remotion-hub search-components --framework remotion --query "animated chart"
51
51
  dd create invoice-tracker
52
52
  dd add gh:owner/repo/extensions/micro-apps/habit-tracker
53
53
  dd add habit-tracker@community
@@ -1 +1 @@
1
- {"version":3,"file":"actions-client.d.ts","sourceRoot":"","sources":["../../src/lib/actions-client.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,IAAI,EAAE,OAAO,CAAC;CACf;AAED,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,WAAW,CAAC;CACnB;AAED,MAAM,WAAW,0BAA0B;IACzC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IACzC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IAC7C,UAAU,EAAE,MAAM,CAAC;CACpB;AAOD,MAAM,WAAW,iCAAiC;IAChD,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IACzC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,wBAAgB,0BAA0B,CAAC,EACzC,GAAiB,EACjB,YAAY,EACZ,eAAe,GAChB,EAAE,iCAAiC,UAKnC;AAED,wBAAgB,mBAAmB,CAAC,EAClC,WAAW,EACX,GAAiB,EACjB,OAAY,EACZ,UAAU,GACX,EAAE,0BAA0B,UAS5B;AA4ED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,iBAAiB,CA6DlE;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,UAS7C;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,WAK3C;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,IAAI,CAAC,iBAAiB,EAAE,UAAU,GAAG,YAAY,GAAG,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,GAAG,aAAa,CAoBtI;AAED,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,OAAO,CAAC,CAiCjF"}
1
+ {"version":3,"file":"actions-client.d.ts","sourceRoot":"","sources":["../../src/lib/actions-client.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,IAAI,EAAE,OAAO,CAAC;CACf;AAED,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,WAAW,CAAC;CACnB;AAED,MAAM,WAAW,0BAA0B;IACzC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IACzC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IAC7C,UAAU,EAAE,MAAM,CAAC;CACpB;AAOD,MAAM,WAAW,iCAAiC;IAChD,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IACzC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,wBAAgB,0BAA0B,CAAC,EACzC,GAAiB,EACjB,YAAY,EACZ,eAAe,GAChB,EAAE,iCAAiC,UAKnC;AAED,wBAAgB,mBAAmB,CAAC,EAClC,WAAW,EACX,GAAiB,EACjB,OAAY,EACZ,UAAU,GACX,EAAE,0BAA0B,UAS5B;AA4ED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,iBAAiB,CA+DlE;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,UAS7C;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,WAK3C;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,IAAI,CAAC,iBAAiB,EAAE,UAAU,GAAG,YAAY,GAAG,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,GAAG,aAAa,CAoBtI;AAED,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,OAAO,CAAC,CAiCjF"}
@@ -68,7 +68,7 @@ function setInputValue(input, key, value) {
68
68
  input.limit = numeric;
69
69
  return;
70
70
  }
71
- if (key === 'namespace' || key === 'kind' || key === 'previewType') {
71
+ if (key === 'namespace' || key === 'kind' || key === 'previewType' || key === 'framework') {
72
72
  input[key] = value;
73
73
  asFilters(input)[key] = value;
74
74
  return;
@@ -127,6 +127,9 @@ export function parseActionsArgs(args) {
127
127
  else if (flag === '--namespace') {
128
128
  setInputValue(input, 'namespace', readValue());
129
129
  }
130
+ else if (flag === '--framework') {
131
+ setInputValue(input, 'framework', readValue());
132
+ }
130
133
  else if (flag === '--slug') {
131
134
  setInputValue(input, 'slug', readValue());
132
135
  }
@@ -0,0 +1,34 @@
1
+ export type ComponentFrameworkId = 'remotion' | 'hyperframe';
2
+ export interface ComponentFrameworkAdapter {
3
+ id: ComponentFrameworkId;
4
+ aliases: string[];
5
+ displayName: string;
6
+ minNodeMajor?: number;
7
+ defaultDevCommand: string;
8
+ defaultFileExtension: string;
9
+ buildCreateCommand: (projectDir: string, options: {
10
+ nonInteractive: boolean;
11
+ }) => {
12
+ command: string;
13
+ args: string[];
14
+ };
15
+ skillsCommands: (options: {
16
+ nonInteractive: boolean;
17
+ }) => Array<{
18
+ command: 'npx';
19
+ args: string[];
20
+ display: string;
21
+ }>;
22
+ buildPostAddSnippet: (input: {
23
+ name: string;
24
+ primaryFile: string;
25
+ source: string;
26
+ projectDir: string;
27
+ config: unknown;
28
+ }) => string;
29
+ }
30
+ export declare const componentFrameworkAdapters: ComponentFrameworkAdapter[];
31
+ export declare function normalizeFrameworkId(value?: string): ComponentFrameworkId;
32
+ export declare function getComponentFrameworkAdapter(value?: string): ComponentFrameworkAdapter;
33
+ export declare function assertFrameworkNodeRequirement(adapter: ComponentFrameworkAdapter, nodeVersion?: string): void;
34
+ //# sourceMappingURL=component-frameworks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"component-frameworks.d.ts","sourceRoot":"","sources":["../../src/lib/component-frameworks.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,oBAAoB,GAAG,UAAU,GAAG,YAAY,CAAC;AAE7D,MAAM,WAAW,yBAAyB;IACxC,EAAE,EAAE,oBAAoB,CAAC;IACzB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,oBAAoB,EAAE,MAAM,CAAC;IAC7B,kBAAkB,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE;QAAE,cAAc,EAAE,OAAO,CAAA;KAAE,KAAK;QAChF,OAAO,EAAE,MAAM,CAAC;QAChB,IAAI,EAAE,MAAM,EAAE,CAAC;KAChB,CAAC;IACF,cAAc,EAAE,CAAC,OAAO,EAAE;QAAE,cAAc,EAAE,OAAO,CAAA;KAAE,KAAK,KAAK,CAAC;QAC9D,OAAO,EAAE,KAAK,CAAC;QACf,IAAI,EAAE,MAAM,EAAE,CAAC;QACf,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC,CAAC;IACH,mBAAmB,EAAE,CAAC,KAAK,EAAE;QAC3B,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,EAAE,MAAM,CAAC;QACpB,MAAM,EAAE,MAAM,CAAC;QACf,UAAU,EAAE,MAAM,CAAC;QACnB,MAAM,EAAE,OAAO,CAAC;KACjB,KAAK,MAAM,CAAC;CACd;AAsDD,eAAO,MAAM,0BAA0B,EAAE,yBAAyB,EAuDjE,CAAC;AAEF,wBAAgB,oBAAoB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,oBAAoB,CASzE;AAED,wBAAgB,4BAA4B,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,yBAAyB,CAKtF;AAED,wBAAgB,8BAA8B,CAAC,OAAO,EAAE,yBAAyB,EAAE,WAAW,SAAwB,QAKrH"}
@@ -0,0 +1,121 @@
1
+ import path from 'node:path';
2
+ function addNonInteractiveAgentFlags(command, nonInteractive) {
3
+ if (!nonInteractive || !command.args.includes('--skill'))
4
+ return command;
5
+ return {
6
+ ...command,
7
+ args: [...command.args, '--agent', '*', '--yes'],
8
+ display: `${command.display} --agent '*' --yes`,
9
+ };
10
+ }
11
+ function normalizeSlashes(value) {
12
+ return value.split(path.sep).join('/');
13
+ }
14
+ function exportNameFromSource(source, fallback) {
15
+ const match = source.match(/export\s+(?:function|const|class)\s+([A-Z][A-Za-z0-9_]*)/);
16
+ if (match?.[1])
17
+ return match[1];
18
+ return fallback
19
+ .split(/[^a-zA-Z0-9]+/)
20
+ .filter(Boolean)
21
+ .map((part) => `${part.charAt(0).toUpperCase()}${part.slice(1)}`)
22
+ .join('') || 'ComponentHubComponent';
23
+ }
24
+ function importPath(fromFile, toFile) {
25
+ const withoutExtension = toFile.replace(/\.[cm]?[tj]sx?$/, '');
26
+ let relative = normalizeSlashes(path.relative(path.dirname(fromFile), withoutExtension));
27
+ if (!relative.startsWith('.'))
28
+ relative = `./${relative}`;
29
+ return relative;
30
+ }
31
+ function buildRemotionSnippet(input) {
32
+ const config = input.config;
33
+ const root = config.remotion?.root || 'src/Root.tsx';
34
+ const rootFile = path.resolve(input.projectDir, root);
35
+ const exportName = exportNameFromSource(input.source, input.name);
36
+ const importFrom = importPath(rootFile, input.primaryFile);
37
+ return [
38
+ `import { ${exportName} } from '${importFrom}';`,
39
+ '',
40
+ `<Composition id="${input.name}" component={${exportName}} durationInFrames={180} fps={30} width={1920} height={1080} />`,
41
+ ].join('\n');
42
+ }
43
+ export const componentFrameworkAdapters = [
44
+ {
45
+ id: 'remotion',
46
+ aliases: ['remotion', 'remotionjs'],
47
+ displayName: 'Remotion',
48
+ defaultDevCommand: 'npx remotion studio',
49
+ defaultFileExtension: 'tsx',
50
+ buildCreateCommand: (projectDir) => ({
51
+ command: 'npx',
52
+ args: ['create-video@latest', '--yes', '--hello-world', projectDir],
53
+ }),
54
+ skillsCommands: ({ nonInteractive }) => [
55
+ {
56
+ command: 'npx',
57
+ args: ['skills', 'add', 'remotion-dev/skills', '--all'],
58
+ display: 'npx skills add remotion-dev/skills --all',
59
+ },
60
+ addNonInteractiveAgentFlags({
61
+ command: 'npx',
62
+ args: ['skills', 'add', 'crystalphantom/double-digit', '--skill', 'dd-remotion-hub'],
63
+ display: 'npx skills add crystalphantom/double-digit --skill dd-remotion-hub',
64
+ }, nonInteractive),
65
+ ],
66
+ buildPostAddSnippet: buildRemotionSnippet,
67
+ },
68
+ {
69
+ id: 'hyperframe',
70
+ aliases: ['hyperframe', 'hyperframes'],
71
+ displayName: 'HyperFrames',
72
+ minNodeMajor: 22,
73
+ defaultDevCommand: 'npx hyperframes preview',
74
+ defaultFileExtension: 'html',
75
+ buildCreateCommand: (projectDir, { nonInteractive }) => ({
76
+ command: 'npx',
77
+ args: [
78
+ 'hyperframes',
79
+ 'init',
80
+ projectDir,
81
+ ...(nonInteractive ? ['--non-interactive'] : []),
82
+ ],
83
+ }),
84
+ skillsCommands: ({ nonInteractive }) => [
85
+ addNonInteractiveAgentFlags({
86
+ command: 'npx',
87
+ args: ['skills', 'add', 'heygen-com/hyperframes'],
88
+ display: 'npx skills add heygen-com/hyperframes',
89
+ }, nonInteractive),
90
+ addNonInteractiveAgentFlags({
91
+ command: 'npx',
92
+ args: ['skills', 'add', 'crystalphantom/double-digit', '--skill', 'dd-remotion-hub'],
93
+ display: 'npx skills add crystalphantom/double-digit --skill dd-remotion-hub',
94
+ }, nonInteractive),
95
+ ],
96
+ buildPostAddSnippet: () => '',
97
+ },
98
+ ];
99
+ export function normalizeFrameworkId(value) {
100
+ const normalized = (value || 'remotion').trim().toLowerCase();
101
+ const adapter = componentFrameworkAdapters.find((candidate) => candidate.id === normalized || candidate.aliases.includes(normalized));
102
+ if (!adapter) {
103
+ throw new Error(`Unsupported framework: ${value}. Supported frameworks: remotion, hyperframe`);
104
+ }
105
+ return adapter.id;
106
+ }
107
+ export function getComponentFrameworkAdapter(value) {
108
+ const id = normalizeFrameworkId(value);
109
+ const adapter = componentFrameworkAdapters.find((candidate) => candidate.id === id);
110
+ if (!adapter)
111
+ throw new Error(`Unsupported framework: ${value}`);
112
+ return adapter;
113
+ }
114
+ export function assertFrameworkNodeRequirement(adapter, nodeVersion = process.versions.node) {
115
+ if (!adapter.minNodeMajor)
116
+ return;
117
+ const major = Number(nodeVersion.split('.')[0]);
118
+ if (Number.isFinite(major) && major >= adapter.minNodeMajor)
119
+ return;
120
+ throw new Error(`${adapter.displayName} requires Node.js ${adapter.minNodeMajor}+`);
121
+ }
@@ -1,10 +1,11 @@
1
1
  import { spawnSync } from 'node:child_process';
2
- type RemotionSkillsCommand = {
2
+ import { type ComponentFrameworkId } from './component-frameworks.js';
3
+ type ComponentHubSkillsCommand = {
3
4
  command: 'npx';
4
5
  args: string[];
5
6
  display: string;
6
7
  };
7
- type RemotionSkillsInstallResult = RemotionSkillsCommand & {
8
+ type ComponentHubSkillsInstallResult = ComponentHubSkillsCommand & {
8
9
  skipped: boolean;
9
10
  success: boolean;
10
11
  status: number | null;
@@ -14,6 +15,13 @@ export interface RemotionHubConfig {
14
15
  $schema: string;
15
16
  registry: string;
16
17
  namespace: string;
18
+ defaultFramework?: ComponentFrameworkId;
19
+ frameworks?: Partial<Record<ComponentFrameworkId, {
20
+ devCommand: string;
21
+ entrypoint?: string;
22
+ root?: string;
23
+ publicDir: string;
24
+ }>>;
17
25
  paths: {
18
26
  scenes: string;
19
27
  components: string;
@@ -43,6 +51,7 @@ export interface RemotionHubLock {
43
51
  files: string[];
44
52
  dependencies: string[];
45
53
  installedAt: string;
54
+ framework: string;
46
55
  }>;
47
56
  }
48
57
  export interface RunRemotionHubInitOptions {
@@ -51,11 +60,14 @@ export interface RunRemotionHubInitOptions {
51
60
  namespace?: string;
52
61
  force?: boolean;
53
62
  skipRemotionCreate?: boolean;
63
+ skipFrameworkCreate?: boolean;
54
64
  skipSkills?: boolean;
55
65
  nonInteractive?: boolean;
56
66
  yes?: boolean;
57
67
  quiet?: boolean;
58
68
  spawnImpl?: typeof spawnSync;
69
+ framework?: string;
70
+ nodeVersion?: string;
59
71
  }
60
72
  export interface RunRemotionHubInitResult {
61
73
  projectDir: string;
@@ -63,6 +75,13 @@ export interface RunRemotionHubInitResult {
63
75
  lockPath: string;
64
76
  created: string[];
65
77
  config: RemotionHubConfig;
78
+ framework: ComponentFrameworkId;
79
+ devCommand: string;
80
+ create: {
81
+ skipped: boolean;
82
+ command: string;
83
+ args: string[];
84
+ };
66
85
  remotionCreate: {
67
86
  skipped: boolean;
68
87
  command: string;
@@ -70,7 +89,7 @@ export interface RunRemotionHubInitResult {
70
89
  };
71
90
  skills: {
72
91
  skipped: boolean;
73
- commands: RemotionSkillsInstallResult[];
92
+ commands: ComponentHubSkillsInstallResult[];
74
93
  };
75
94
  }
76
95
  export interface RunRemotionHubAddOptions {
@@ -83,6 +102,7 @@ export interface RunRemotionHubAddOptions {
83
102
  quiet?: boolean;
84
103
  fetchImpl?: typeof fetch;
85
104
  spawnImpl?: typeof spawnSync;
105
+ framework?: string;
86
106
  }
87
107
  export interface RunRemotionHubAddResult {
88
108
  name: string;
@@ -94,6 +114,7 @@ export interface RunRemotionHubAddResult {
94
114
  dependencies: string[];
95
115
  installedDependencies: string[];
96
116
  snippet: string;
117
+ framework: ComponentFrameworkId;
97
118
  }
98
119
  export interface ResolveRemotionHubRegistryOptions {
99
120
  registry?: string;
@@ -103,9 +124,9 @@ export interface ResolveRemotionHubRegistryOptions {
103
124
  }
104
125
  export declare function resolveRemotionHubRegistry({ registry, env, generatedDefaultRegistry, localDefaultRegistry, }?: ResolveRemotionHubRegistryOptions): string;
105
126
  export declare function buildRemotionSkillsCommands(options?: Pick<RunRemotionHubInitOptions, 'nonInteractive' | 'yes'>): {
127
+ command: "npx";
106
128
  args: string[];
107
129
  display: string;
108
- command: "npx";
109
130
  }[];
110
131
  export declare function runRemotionHubInit(options?: RunRemotionHubInitOptions): RunRemotionHubInitResult;
111
132
  export declare function runRemotionHubAdd(options: RunRemotionHubAddOptions): Promise<RunRemotionHubAddResult>;
@@ -1 +1 @@
1
- {"version":3,"file":"remotion-hub-init.d.ts","sourceRoot":"","sources":["../../src/lib/remotion-hub-init.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"remotion-hub-init.d.ts","sourceRoot":"","sources":["../../src/lib/remotion-hub-init.ts"],"names":[],"mappings":"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"}
@@ -1,24 +1,13 @@
1
- import { spawnSync } from 'node:child_process';
1
+ import { spawnSync, } from 'node:child_process';
2
2
  import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync, } from 'node:fs';
3
3
  import path from 'node:path';
4
4
  import { GENERATED_DEFAULT_ACTIONS_APP_URL } from '../generated/defaults.js';
5
5
  import { isLocalAppUrl } from './actions-client.js';
6
+ import { assertFrameworkNodeRequirement, getComponentFrameworkAdapter, normalizeFrameworkId, } from './component-frameworks.js';
6
7
  const CONFIG_FILE = 'remotion-hub.json';
7
8
  const LOCK_FILE = 'remotion-hub.lock.json';
8
9
  const DEFAULT_REGISTRY = 'http://localhost:3111';
9
10
  const DEFAULT_NAMESPACE = 'doubledigit';
10
- const REMOTION_SKILLS_COMMANDS = [
11
- {
12
- command: 'npx',
13
- args: ['skills', 'add', 'remotion-dev/skills', '--all'],
14
- display: 'npx skills add remotion-dev/skills --all',
15
- },
16
- {
17
- command: 'npx',
18
- args: ['skills', 'add', 'crystalphantom/double-digit', '--skill', 'dd-remotion-hub'],
19
- display: 'npx skills add crystalphantom/double-digit --skill dd-remotion-hub',
20
- },
21
- ];
22
11
  function toJson(value) {
23
12
  return `${JSON.stringify(value, null, 2)}\n`;
24
13
  }
@@ -107,19 +96,35 @@ export function resolveRemotionHubRegistry({ registry, env = process.env, genera
107
96
  ?? localDefaultRegistry;
108
97
  }
109
98
  function defaultConfig(projectDir, options) {
99
+ const framework = normalizeFrameworkId(options.framework);
110
100
  const language = detectLanguage(projectDir);
111
101
  const rootExtension = language === 'typescript' ? 'tsx' : 'jsx';
112
102
  const entryExtension = language === 'typescript' ? 'ts' : 'js';
103
+ const remotionAdapter = getComponentFrameworkAdapter('remotion');
104
+ const hyperframeAdapter = getComponentFrameworkAdapter('hyperframe');
113
105
  return {
114
106
  $schema: 'https://remotion-hub.dev/schema/remotion-hub.json',
115
107
  registry: resolveRemotionHubRegistry({ registry: options.registry }),
116
108
  namespace: options.namespace?.trim() || DEFAULT_NAMESPACE,
109
+ defaultFramework: framework,
110
+ frameworks: {
111
+ remotion: {
112
+ devCommand: remotionAdapter.defaultDevCommand,
113
+ entrypoint: firstExisting(projectDir, ['src/index.ts', 'src/index.tsx', 'src/index.js', 'src/index.jsx'], `src/index.${entryExtension}`),
114
+ root: firstExisting(projectDir, ['src/Root.tsx', 'src/Root.jsx', 'src/root.tsx', 'src/root.jsx'], `src/Root.${rootExtension}`),
115
+ publicDir: 'public',
116
+ },
117
+ hyperframe: {
118
+ devCommand: hyperframeAdapter.defaultDevCommand,
119
+ publicDir: 'public',
120
+ },
121
+ },
117
122
  paths: {
118
- scenes: 'src/remotion-hub/scenes',
119
- components: 'src/remotion-hub/components',
120
- overlays: 'src/remotion-hub/overlays',
121
- transitions: 'src/remotion-hub/transitions',
122
- assets: 'public/remotion-hub/assets',
123
+ scenes: 'src/remotion-hub/[framework]/scenes',
124
+ components: 'src/remotion-hub/[framework]/components',
125
+ overlays: 'src/remotion-hub/[framework]/overlays',
126
+ transitions: 'src/remotion-hub/[framework]/transitions',
127
+ assets: 'public/remotion-hub/[framework]/assets',
123
128
  registry: `src/remotion-hub/registry.${entryExtension}`,
124
129
  },
125
130
  remotion: {
@@ -133,6 +138,18 @@ function defaultConfig(projectDir, options) {
133
138
  },
134
139
  };
135
140
  }
141
+ function getFrameworkConfig(config, framework) {
142
+ const adapter = getComponentFrameworkAdapter(framework);
143
+ return {
144
+ devCommand: config.frameworks?.[framework]?.devCommand ?? adapter.defaultDevCommand,
145
+ publicDir: config.frameworks?.[framework]?.publicDir ?? config.remotion?.publicDir ?? 'public',
146
+ entrypoint: config.frameworks?.[framework]?.entrypoint,
147
+ root: config.frameworks?.[framework]?.root,
148
+ };
149
+ }
150
+ function getConfigDefaultFramework(config) {
151
+ return normalizeFrameworkId(config.defaultFramework);
152
+ }
136
153
  function defaultLock() {
137
154
  return {
138
155
  version: 1,
@@ -140,6 +157,11 @@ function defaultLock() {
140
157
  };
141
158
  }
142
159
  function ensureDir(projectDir, relativePath, created) {
160
+ if (relativePath.includes('[framework]')) {
161
+ ensureDir(projectDir, relativePath.replace('[framework]', 'remotion'), created);
162
+ ensureDir(projectDir, relativePath.replace('[framework]', 'hyperframe'), created);
163
+ return;
164
+ }
143
165
  const target = resolveInsideProject(projectDir, relativePath, relativePath);
144
166
  if (!existsSync(target)) {
145
167
  mkdirSync(target, { recursive: true });
@@ -155,6 +177,7 @@ function registrySource(config, lock) {
155
177
  namespace: entry.namespace,
156
178
  slug: entry.slug,
157
179
  files: entry.files,
180
+ framework: entry.framework ?? 'remotion',
158
181
  };
159
182
  return acc;
160
183
  }, {});
@@ -180,29 +203,50 @@ function hasPackageJson(projectDir) {
180
203
  function hasEntries(projectDir) {
181
204
  return existsSync(projectDir) && readdirSync(projectDir).length > 0;
182
205
  }
183
- function buildRemotionCreateCommand(projectDir) {
206
+ function withNonInteractiveNpx(command, nonInteractive) {
207
+ if (!nonInteractive || command.command !== 'npx')
208
+ return command;
209
+ const args = command.args[0] === '--yes'
210
+ ? command.args
211
+ : ['--yes', ...command.args];
212
+ const display = command.display
213
+ ? command.display.startsWith('npx --yes ')
214
+ ? command.display
215
+ : command.display.startsWith('npx ')
216
+ ? `npx --yes ${command.display.slice('npx '.length)}`
217
+ : `npx --yes ${args.slice(1).join(' ')}`
218
+ : command.display;
184
219
  return {
185
- command: 'npx',
186
- args: ['create-video@latest', '--yes', '--hello-world', projectDir],
220
+ ...command,
221
+ args,
222
+ ...(display ? { display } : {}),
223
+ };
224
+ }
225
+ function componentHubChildSpawnOptions(command, options) {
226
+ return {
227
+ cwd: options.cwd,
228
+ encoding: 'buffer',
229
+ stdio: options.quiet ? 'pipe' : 'inherit',
230
+ ...(options.nonInteractive && command === 'npx'
231
+ ? { env: { ...process.env, npm_config_yes: 'true' } }
232
+ : {}),
187
233
  };
188
234
  }
235
+ function spawnComponentHubChild(command, options) {
236
+ return (options.spawnImpl ?? spawnSync)(command.command, command.args, componentHubChildSpawnOptions(command.command, options));
237
+ }
189
238
  export function buildRemotionSkillsCommands(options = {}) {
190
239
  const nonInteractive = options.yes === true || options.nonInteractive === true;
191
- return REMOTION_SKILLS_COMMANDS.map((command) => ({
192
- ...command,
193
- args: nonInteractive && command.args.includes('--skill')
194
- ? [...command.args, '--agent', '*', '--yes']
195
- : [...command.args],
196
- display: nonInteractive && command.args.includes('--skill')
197
- ? `${command.display} --agent '*' --yes`
198
- : command.display,
199
- }));
200
- }
201
- function runRemotionSkillsInstall(projectDir, options) {
202
- const commands = buildRemotionSkillsCommands({
203
- nonInteractive: options.nonInteractive,
204
- yes: options.yes,
205
- });
240
+ return getComponentFrameworkAdapter('remotion')
241
+ .skillsCommands({ nonInteractive })
242
+ .map((command) => withNonInteractiveNpx(command, nonInteractive));
243
+ }
244
+ function runFrameworkSkillsInstall(projectDir, options) {
245
+ const adapter = getComponentFrameworkAdapter(options.framework);
246
+ const nonInteractive = options.yes === true || options.nonInteractive === true;
247
+ const commands = adapter
248
+ .skillsCommands({ nonInteractive })
249
+ .map((command) => withNonInteractiveNpx(command, nonInteractive));
206
250
  if (options.skipSkills) {
207
251
  return commands.map((command) => ({
208
252
  ...command,
@@ -212,9 +256,11 @@ function runRemotionSkillsInstall(projectDir, options) {
212
256
  }));
213
257
  }
214
258
  return commands.map((command) => {
215
- const result = (options.spawnImpl ?? spawnSync)(command.command, command.args, {
259
+ const result = spawnComponentHubChild(command, {
216
260
  cwd: projectDir,
217
- stdio: options.quiet ? 'pipe' : 'inherit',
261
+ nonInteractive,
262
+ quiet: options.quiet,
263
+ spawnImpl: options.spawnImpl,
218
264
  });
219
265
  return {
220
266
  ...command,
@@ -225,33 +271,41 @@ function runRemotionSkillsInstall(projectDir, options) {
225
271
  };
226
272
  });
227
273
  }
228
- function assertRemotionCreateSucceeded(result, command, args) {
274
+ function assertFrameworkCreateSucceeded(result, adapterName, command, args) {
229
275
  if (result.error) {
230
- throw new Error(`Remotion project creation failed: ${result.error.message}`);
276
+ throw new Error(`${adapterName} project creation failed: ${result.error.message}`);
231
277
  }
232
278
  if (result.status !== 0) {
233
- throw new Error(`Remotion project creation failed: ${command} ${args.join(' ')}`);
279
+ throw new Error(`${adapterName} project creation failed: ${command} ${args.join(' ')}`);
234
280
  }
235
281
  }
236
- function ensureBaseRemotionProject(projectDir, options) {
237
- const remotionCreate = buildRemotionCreateCommand(projectDir);
238
- if (options.skipRemotionCreate || hasPackageJson(projectDir)) {
282
+ function shouldSkipFrameworkCreate(options) {
283
+ return options.skipFrameworkCreate === true || options.skipRemotionCreate === true;
284
+ }
285
+ function ensureBaseFrameworkProject(projectDir, options) {
286
+ const adapter = getComponentFrameworkAdapter(options.framework);
287
+ const nonInteractive = options.yes === true || options.nonInteractive === true;
288
+ const frameworkCreate = withNonInteractiveNpx(adapter.buildCreateCommand(projectDir, { nonInteractive }), nonInteractive);
289
+ if (shouldSkipFrameworkCreate(options) || hasPackageJson(projectDir)) {
239
290
  return {
240
- ...remotionCreate,
291
+ ...frameworkCreate,
241
292
  skipped: true,
242
293
  };
243
294
  }
295
+ assertFrameworkNodeRequirement(adapter, options.nodeVersion);
244
296
  if (hasEntries(projectDir)) {
245
- throw new Error(`Project directory exists but is not a Remotion project: ${projectDir}. `
246
- + 'Remove it, choose an empty path, or run Remotion setup manually before Remotion Hub init.');
297
+ throw new Error(`Project directory exists but is not a ${adapter.displayName} project: ${projectDir}. `
298
+ + `Remove it, choose an empty path, or run ${adapter.displayName} setup manually before Component Hub init.`);
247
299
  }
248
- const result = (options.spawnImpl ?? spawnSync)(remotionCreate.command, remotionCreate.args, {
300
+ const result = spawnComponentHubChild(frameworkCreate, {
249
301
  cwd: process.cwd(),
250
- stdio: options.quiet ? 'pipe' : 'inherit',
302
+ nonInteractive,
303
+ quiet: options.quiet,
304
+ spawnImpl: options.spawnImpl,
251
305
  });
252
- assertRemotionCreateSucceeded(result, remotionCreate.command, remotionCreate.args);
306
+ assertFrameworkCreateSucceeded(result, adapter.displayName, frameworkCreate.command, frameworkCreate.args);
253
307
  return {
254
- ...remotionCreate,
308
+ ...frameworkCreate,
255
309
  skipped: false,
256
310
  };
257
311
  }
@@ -281,25 +335,35 @@ function parseItemRef(item, namespace) {
281
335
  }
282
336
  return { namespace: namespace || DEFAULT_NAMESPACE, slug: trimmed };
283
337
  }
284
- function registryPayloadUrl(config, namespace, slug, registryOverride) {
338
+ function registryPayloadUrl(config, namespace, slug, registryOverride, framework) {
285
339
  const base = normalizeRegistryBase(registryOverride || config.registry || DEFAULT_REGISTRY);
286
340
  const encodedNamespace = encodeURIComponent(namespace);
287
341
  const encodedSlug = encodeURIComponent(slug);
342
+ let url = '';
288
343
  if (base.endsWith('/api/remotion-hub/r')) {
289
- return `${base}/${encodedNamespace}/${encodedSlug}`;
344
+ url = `${base}/${encodedNamespace}/${encodedSlug}`;
345
+ }
346
+ else {
347
+ url = `${base}/api/remotion-hub/r/${encodedNamespace}/${encodedSlug}`;
348
+ }
349
+ if (framework) {
350
+ url += `?framework=${encodeURIComponent(framework)}`;
290
351
  }
291
- return `${base}/api/remotion-hub/r/${encodedNamespace}/${encodedSlug}`;
352
+ return url;
292
353
  }
293
354
  function asStringArray(value) {
294
355
  if (!Array.isArray(value))
295
356
  return [];
296
357
  return value.filter((item) => typeof item === 'string' && item.trim().length > 0);
297
358
  }
298
- function readRegistryPayload(value) {
359
+ function readRegistryPayload(value, fallbackFramework) {
299
360
  const name = typeof value.name === 'string' && value.name.trim() ? value.name.trim() : '';
300
361
  const type = typeof value.type === 'string' && value.type.trim() ? value.type.trim() : 'registry:component';
301
362
  const version = typeof value.version === 'string' && value.version.trim() ? value.version.trim() : null;
302
363
  const files = Array.isArray(value.files) ? value.files : [];
364
+ const framework = typeof value.framework === 'string' && value.framework.trim()
365
+ ? normalizeFrameworkId(value.framework)
366
+ : fallbackFramework;
303
367
  if (!name)
304
368
  throw new Error('Registry payload is missing name');
305
369
  if (files.length === 0)
@@ -310,9 +374,10 @@ function readRegistryPayload(value) {
310
374
  version,
311
375
  files,
312
376
  dependencies: asStringArray(value.dependencies),
377
+ framework,
313
378
  };
314
379
  }
315
- async function fetchRegistryPayload(url, fetchImpl) {
380
+ async function fetchRegistryPayload(url, fetchImpl, fallbackFramework) {
316
381
  const response = await fetchImpl(url);
317
382
  const text = await response.text();
318
383
  const parsed = text ? JSON.parse(text) : {};
@@ -322,14 +387,19 @@ async function fetchRegistryPayload(url, fetchImpl) {
322
387
  : `Request failed with HTTP ${response.status}`;
323
388
  throw new Error(error);
324
389
  }
325
- return readRegistryPayload(parsed);
390
+ return readRegistryPayload(parsed, fallbackFramework);
326
391
  }
327
- function targetForRegistryFile(config, file) {
392
+ function targetForRegistryFile(config, file, framework) {
328
393
  const explicitTarget = typeof file.target === 'string' ? file.target.trim() : '';
329
394
  if (explicitTarget)
330
- return explicitTarget;
395
+ return explicitTarget.replace(/\[framework\]/g, framework);
331
396
  const sourcePath = ensureRelativePath(typeof file.path === 'string' ? file.path : '', 'file.path');
332
397
  const mappings = [
398
+ [`components/${framework}/`, config.paths.components],
399
+ [`scenes/${framework}/`, config.paths.scenes],
400
+ [`overlays/${framework}/`, config.paths.overlays],
401
+ [`transitions/${framework}/`, config.paths.transitions],
402
+ [`assets/${framework}/`, config.paths.assets],
333
403
  ['components/', config.paths.components],
334
404
  ['scenes/', config.paths.scenes],
335
405
  ['overlays/', config.paths.overlays],
@@ -338,10 +408,11 @@ function targetForRegistryFile(config, file) {
338
408
  ];
339
409
  for (const [prefix, targetRoot] of mappings) {
340
410
  if (sourcePath.startsWith(prefix)) {
341
- return path.posix.join(targetRoot.replace(/\/+$/, ''), sourcePath.slice(prefix.length));
411
+ const rootPath = targetRoot.replace(/\[framework\]/g, framework);
412
+ return path.posix.join(rootPath.replace(/\/+$/, ''), sourcePath.slice(prefix.length));
342
413
  }
343
414
  }
344
- return path.posix.join('src/remotion-hub', sourcePath);
415
+ return path.posix.join('src/remotion-hub', sourcePath).replace(/\[framework\]/g, framework);
345
416
  }
346
417
  function packageJsonDependencies(projectDir) {
347
418
  const packageJsonPath = path.join(projectDir, 'package.json');
@@ -381,45 +452,21 @@ function installDependencies(projectDir, packageManager, dependencies, spawnImpl
381
452
  }
382
453
  return missing;
383
454
  }
384
- function exportNameFromSource(source, fallback) {
385
- const match = source.match(/export\s+(?:function|const|class)\s+([A-Z][A-Za-z0-9_]*)/);
386
- if (match?.[1])
387
- return match[1];
388
- return fallback
389
- .split(/[^a-zA-Z0-9]+/)
390
- .filter(Boolean)
391
- .map((part) => `${part.charAt(0).toUpperCase()}${part.slice(1)}`)
392
- .join('') || 'RemotionHubComponent';
393
- }
394
- function importPath(fromFile, toFile) {
395
- const withoutExtension = toFile.replace(/\.[cm]?[tj]sx?$/, '');
396
- let relative = normalizeSlashes(path.relative(path.dirname(fromFile), withoutExtension));
397
- if (!relative.startsWith('.'))
398
- relative = `./${relative}`;
399
- return relative;
400
- }
401
- function buildSnippet(config, projectDir, name, primaryFile, source) {
402
- const rootFile = resolveInsideProject(projectDir, config.remotion.root, 'remotion.root');
403
- const exportName = exportNameFromSource(source, name);
404
- const importFrom = importPath(rootFile, primaryFile);
405
- return [
406
- `import { ${exportName} } from '${importFrom}';`,
407
- '',
408
- `<Composition id="${name}" component={${exportName}} durationInFrames={180} fps={30} width={1920} height={1080} />`,
409
- ].join('\n');
410
- }
411
455
  export function runRemotionHubInit(options = {}) {
456
+ const framework = normalizeFrameworkId(options.framework);
457
+ const adapter = getComponentFrameworkAdapter(framework);
458
+ const normalizedOptions = { ...options, framework };
412
459
  const projectDir = resolveProjectDir(options.projectDir);
413
460
  const configPath = path.join(projectDir, CONFIG_FILE);
414
461
  const lockPath = path.join(projectDir, LOCK_FILE);
415
462
  const created = [];
416
- const remotionCreate = ensureBaseRemotionProject(projectDir, options);
463
+ const create = ensureBaseFrameworkProject(projectDir, normalizedOptions);
417
464
  mkdirSync(projectDir, { recursive: true });
418
465
  const configExists = existsSync(configPath);
419
466
  const lockExists = existsSync(lockPath);
420
467
  const config = configExists && !options.force
421
468
  ? loadConfig(projectDir)
422
- : defaultConfig(projectDir, options);
469
+ : defaultConfig(projectDir, normalizedOptions);
423
470
  if (!configExists || options.force) {
424
471
  writeJsonFile(configPath, config);
425
472
  created.push(CONFIG_FILE);
@@ -440,14 +487,17 @@ export function runRemotionHubInit(options = {}) {
440
487
  const lock = readJsonFile(lockPath, defaultLock());
441
488
  writeRegistryFile(projectDir, config, lock);
442
489
  created.push(config.paths.registry);
443
- const skills = runRemotionSkillsInstall(projectDir, options);
490
+ const skills = runFrameworkSkillsInstall(projectDir, normalizedOptions);
444
491
  return {
445
492
  projectDir,
446
493
  configPath,
447
494
  lockPath,
448
495
  created,
449
496
  config,
450
- remotionCreate,
497
+ framework,
498
+ devCommand: getFrameworkConfig(config, framework).devCommand ?? adapter.defaultDevCommand,
499
+ create,
500
+ remotionCreate: create,
451
501
  skills: {
452
502
  skipped: options.skipSkills === true,
453
503
  commands: skills,
@@ -457,9 +507,12 @@ export function runRemotionHubInit(options = {}) {
457
507
  export async function runRemotionHubAdd(options) {
458
508
  const projectDir = resolveProjectDir(options.projectDir);
459
509
  const config = loadConfig(projectDir);
510
+ const framework = normalizeFrameworkId(options.framework ?? getConfigDefaultFramework(config));
460
511
  const itemRef = parseItemRef(options.item, options.namespace || config.namespace);
461
- const resolved = registryPayloadUrl(config, itemRef.namespace, itemRef.slug, options.registry);
462
- const payload = await fetchRegistryPayload(resolved, options.fetchImpl ?? fetch);
512
+ const resolved = registryPayloadUrl(config, itemRef.namespace, itemRef.slug, options.registry, framework);
513
+ const payload = await fetchRegistryPayload(resolved, options.fetchImpl ?? fetch, framework);
514
+ const payloadFramework = normalizeFrameworkId(payload.framework || framework);
515
+ const payloadAdapter = getComponentFrameworkAdapter(payloadFramework);
463
516
  const lockPath = path.join(projectDir, LOCK_FILE);
464
517
  const lock = readJsonFile(lockPath, defaultLock());
465
518
  const written = [];
@@ -471,7 +524,7 @@ export async function runRemotionHubAdd(options) {
471
524
  const content = typeof file.content === 'string' ? file.content : '';
472
525
  if (!content)
473
526
  throw new Error(`Registry file for ${payload.name} is missing content`);
474
- const relativeTarget = targetForRegistryFile(config, file);
527
+ const relativeTarget = targetForRegistryFile(config, file, payloadFramework);
475
528
  const destination = resolveInsideProject(projectDir, relativeTarget, 'file.target');
476
529
  const relativePath = normalizeSlashes(path.relative(projectDir, destination));
477
530
  if (seenTargets.has(relativePath)) {
@@ -500,7 +553,8 @@ export async function runRemotionHubAdd(options) {
500
553
  throw new Error(`Failed to write file: ${normalizeSlashes(path.relative(projectDir, destination))}`);
501
554
  }
502
555
  }
503
- lock.installed[payload.name] = {
556
+ const lockKey = `${itemRef.namespace}/${itemRef.slug}/${payloadFramework}`;
557
+ lock.installed[lockKey] = {
504
558
  version: payload.version,
505
559
  type: payload.type,
506
560
  namespace: itemRef.namespace,
@@ -509,11 +563,18 @@ export async function runRemotionHubAdd(options) {
509
563
  files: written,
510
564
  dependencies: payload.dependencies,
511
565
  installedAt: new Date().toISOString(),
566
+ framework: payloadFramework,
512
567
  };
513
568
  writeJsonFile(lockPath, lock);
514
569
  writeRegistryFile(projectDir, config, lock);
515
570
  const snippet = primaryFile
516
- ? buildSnippet(config, projectDir, payload.name, primaryFile, primarySource)
571
+ ? payloadAdapter.buildPostAddSnippet({
572
+ name: payload.name,
573
+ primaryFile,
574
+ source: primarySource,
575
+ projectDir,
576
+ config,
577
+ })
517
578
  : '';
518
579
  return {
519
580
  name: payload.name,
@@ -525,5 +586,6 @@ export async function runRemotionHubAdd(options) {
525
586
  dependencies: payload.dependencies,
526
587
  installedDependencies,
527
588
  snippet,
589
+ framework: payloadFramework,
528
590
  };
529
591
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@doubledigit/cli",
3
- "version": "0.9.0",
3
+ "version": "0.10.1",
4
4
  "private": false,
5
5
  "description": "CLI for Double Digit local setup and extension management.",
6
6
  "license": "MIT",