@amg555/frameforge-mcp 2.4.0 → 2.4.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/CHANGELOG.md CHANGED
@@ -4,6 +4,19 @@ All notable changes to FrameForge are documented here.
4
4
 
5
5
  ---
6
6
 
7
+ ## [2.4.1] — 2026-11-09
8
+
9
+ ### Fixed
10
+ - **CLI & npx Invocation Handling (`src/mcp-server/stdio.js`, `package.json`)**:
11
+ - Registered `frameforge-make` binary alias in `package.json` so `npx --package=@amg555/frameforge-mcp frameforge-make` works immediately.
12
+ - Added CLI dispatch interception in `stdio.js` when flags or subcommands (`make`, `--topic`, `doctor`, `setup`, `harness`) are passed, preventing npx CLI invocations from hanging on stdio JSON-RPC stdin.
13
+ - **Language & Orientation Forwarding in CLI (`src/cli/make.js`)**:
14
+ - Added `--orientation <landscape|portrait>` and `--portrait` flags.
15
+ - Forwarded `language` and auto-selected `voice` (`ml-IN-MidhunNeural`, `hi-IN-MadhurNeural`, etc.) to `runAssets` and `runRender`.
16
+ - Forwarded `renderWidth` and `renderHeight` to `runRender` so canvas dimensions match requested orientation.
17
+
18
+ ---
19
+
7
20
  ## [2.4.0] — 2026-11-09
8
21
 
9
22
  ### Added
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@amg555/frameforge-mcp",
3
- "version": "2.4.0",
3
+ "version": "2.4.1",
4
4
  "description": "FrameForge — AI video production engine for agentic IDEs. npx @amg555/frameforge-mcp. FFmpeg + TTS run on your machine. 21 MCP tools.",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -8,6 +8,7 @@
8
8
  "main": "src/mcp-server/server.js",
9
9
  "bin": {
10
10
  "frameforge": "src/cli/make.js",
11
+ "frameforge-make": "src/cli/make.js",
11
12
  "frameforge-mcp": "src/mcp-server/stdio.js",
12
13
  "frameforge-setup": "src/cli/setup-keys.js",
13
14
  "frameforge-doctor": "src/cli/doctor.js",
@@ -20,7 +20,7 @@ check('batch handling', code.includes('Array.isArray(body)'));
20
20
  check('ping handler', code.includes("method === 'ping'"));
21
21
  check('notifications/init', code.includes("notifications/initialized"));
22
22
  check('body size limit', code.includes('MAX_BODY_BYTES'));
23
- check('SERVER_INFO version 2.4.0', code.includes("version: '2.4.0'"));
23
+ check('SERVER_INFO version 2.4.1', code.includes("version: '2.4.1'"));
24
24
  check('18 tools defined (incl get_job_status)', code.includes('get_job_status'));
25
25
  check('export_to_editor tool defined', code.includes('export_to_editor'));
26
26
  check('edit_video tool defined', code.includes('edit_video'));
@@ -299,7 +299,7 @@ check('cf-worker has checkRateLimit', cfWorker.includes('checkRateLimit'));
299
299
  check('cf-worker hashes token (SHA-256)', cfWorker.includes('SHA-256'));
300
300
  check('cf-worker uses RATE_LIMITS KV', cfWorker.includes('env.RATE_LIMITS'));
301
301
  check('cf-worker returns 429 on limit', cfWorker.includes('429'));
302
- check('cf-worker version 2.4.0', cfWorker.includes("'2.4.0'"));
302
+ check('cf-worker version 2.4.1', cfWorker.includes("'2.4.1'"));
303
303
  check('wrangler.toml has kv_namespaces',fs.readFileSync('./cloudflare-mcp/wrangler.toml','utf8').includes('kv_namespaces'));
304
304
 
305
305
  // ── plugins/skills present ────────────────────────────────────────────────
package/src/cli/make.js CHANGED
@@ -98,6 +98,7 @@ function parseArgs(argv) {
98
98
  mode: 'full-auto',
99
99
  topic: '', topicsFile: '', language: 'en',
100
100
  style: 'educational comedy', channels: ['file'],
101
+ orientation: 'landscape',
101
102
  scenesFile: '', scriptFile: '', mediaDir: '',
102
103
  shorts: true, dryRun: false,
103
104
  noResearch: false, noScript: false, noImages: false, noTts: false, noPublish: false,
@@ -112,6 +113,8 @@ function parseArgs(argv) {
112
113
  case '--script-file': args.scriptFile = argv[++i] || ''; break;
113
114
  case '--media-dir': args.mediaDir = argv[++i] || ''; break;
114
115
  case '--language': args.language = (argv[++i] || 'en').toLowerCase(); break;
116
+ case '--orientation': args.orientation = (argv[++i] || 'landscape').toLowerCase(); break;
117
+ case '--portrait': args.orientation = 'portrait'; break;
115
118
  case '--style': args.style = argv[++i] || args.style; break;
116
119
  case '--channels': args.channels = (argv[++i] || 'file').split(',').map(s => s.trim()); break;
117
120
  case '--no-research': args.noResearch = true; break;
@@ -399,10 +402,13 @@ async function runSingleVideo(args, topic, outDir, workflows) {
399
402
  // Pass stage-skip flags into scenes so runAssets can honour them
400
403
  const preparedScenes = scenes.map(s => ({
401
404
  ...s,
405
+ language: s.language || args.language,
402
406
  _skipImageGen: skipImages,
403
407
  _skipTts: args.noTts,
404
408
  }));
405
409
 
410
+ const resolvedVoice = LANGUAGE_VOICES[args.language] || null;
411
+
406
412
  const assetsResult = await runAssets({
407
413
  productionId,
408
414
  scenes: preparedScenes,
@@ -413,17 +419,25 @@ async function runSingleVideo(args, topic, outDir, workflows) {
413
419
  topic,
414
420
  title,
415
421
  style: args.style || '',
422
+ language: args.language,
423
+ voice: resolvedVoice,
416
424
  });
417
425
  log('Assets', `✅ ${assetsResult.sceneCount} scenes prepared`);
418
426
 
419
427
  // ── Stage D: Render ────────────────────────────────────────────────────────
420
428
  logSection('Stage D: Video Render (FFmpeg)');
429
+ const renderWidth = args.orientation === 'portrait' ? 1080 : 1920;
430
+ const renderHeight = args.orientation === 'portrait' ? 1920 : 1080;
431
+
421
432
  const renderResult = await runRender({
422
433
  productionId,
423
434
  sceneAssets: assetsResult.sceneAssets,
424
435
  topic,
425
436
  title,
426
437
  style: args.style || '',
438
+ language: args.language,
439
+ renderWidth,
440
+ renderHeight,
427
441
  });
428
442
  if (!renderResult.passed) {
429
443
  throw new Error(`Quality gate FAILED: ${JSON.stringify(renderResult.qcReport?.checks?.filter(c => !c.passed).map(c => c.name))}`);
@@ -1633,7 +1633,7 @@ Allows the agent in Claude Code, Cursor, or any IDE to inspect the visual output
1633
1633
 
1634
1634
  const SERVER_INFO = {
1635
1635
  name: 'video-pipeline',
1636
- version: '2.4.0',
1636
+ version: '2.4.1',
1637
1637
  };
1638
1638
 
1639
1639
  const CAPABILITIES = {
@@ -34,6 +34,34 @@
34
34
  // Load config BEFORE requiring server.js so env vars are set when providers initialise
35
35
  require('../config/load-config').loadConfig();
36
36
 
37
+ // ── CLI Dispatch Fallback ──────────────────────────────────────────────────
38
+ // When run via `npx @amg555/frameforge-mcp` with CLI arguments (e.g. `make`, `--topic`,
39
+ // `doctor`, `setup`, `harness`, or `--help`), dispatch to the CLI tool instead of
40
+ // hanging on stdin JSON-RPC.
41
+ const firstArg = process.argv[2];
42
+ if (firstArg && (firstArg === 'make' || firstArg.startsWith('-') || firstArg === 'doctor' || firstArg === 'setup' || firstArg === 'harness')) {
43
+ if (firstArg === 'doctor') {
44
+ require('../cli/doctor');
45
+ return;
46
+ }
47
+ if (firstArg === 'setup') {
48
+ require('../cli/setup-keys');
49
+ return;
50
+ }
51
+ if (firstArg === 'harness') {
52
+ const harnessBin = require('path').resolve(__dirname, '../harness/bin/harness.mjs');
53
+ const { spawnSync } = require('child_process');
54
+ const res = spawnSync(process.execPath, [harnessBin, ...process.argv.slice(3)], { stdio: 'inherit' });
55
+ process.exit(res.status ?? 0);
56
+ }
57
+ // Otherwise dispatch to make.js (stripping 'make' if present)
58
+ if (firstArg === 'make') {
59
+ process.argv.splice(2, 1);
60
+ }
61
+ require('../cli/make');
62
+ return;
63
+ }
64
+
37
65
  const readline = require('readline');
38
66
  const { handleMCPRequest } = require('./server');
39
67