AI ビデオエディタ — エージェント制御リファレンス
⚠️ editorctl の検索 — 必須(最初に読むこと)
editorctl は Monet CLI です。常に直接試してください — Monet は生成するすべてのシェルで PATH に保持し、bin ディレクトリを ~/.zshrc / ~/.bash_profile に永続化するため、サブシェルや PATH を書き換えるラッパーでも機能します。command -v editorctl が何も返さない場合は、以下の順序でフォールバックしてください。
"$HOME/Library/Application Support/Monet/bin/editorctl"— macOS インストール(ほとんどのユーザー)node /Applications/Monet.app/Contents/Resources/app.asar.unpacked/out/cli/cli/editorctl.js …node ./out/cli/cli/editorctl.js …— 開発ツリー
最初の「command not found」で諦めないでください。curl localhost:51847 の代わりに editorctl を優先してください。常に現在のコマンドサーフェスと引数の形状を公開します。
⚠️ 出力ファイルの命名 — 必須(最初に読むこと)
ビデオや画像を編集・再生成する際に、ファイル名を再利用しないでください。 アセットキャッシュはパスで前のファイルを保持するため、同じ名前で書き込むと古いコンテンツが静かに表示されます。編集・再生成のたびに新しい一意のファイル名(例:clip_v1.mp4、clip_v2.mp4、またはタイムスタンプのサフィックス)を生成する必要があります。すべてのビデオレンダリング、画像生成、キャンバスエクスポート、サムネイルに適用されます。ターゲットパスがすでに存在する場合は、_v2、_v3、… を追加してください — 上書きしないでください。
⚠️ マルチクリップタイムラインでのオーディオ — 必須
複数のビデオクリップがあるタイムラインにオーディオを追加する場合、オーディオはクリップ境界ごとに途切れます。必要なフロー:
- ユーザーに伝える:「クリップ間でオーディオが途切れないように、最初にすべてのビデオクリップを1つの結合ビデオにマージしてから、オーディオを追加します。続行してもよろしいですか?」
- 確認を待つ — 自動マージしないでください。
- すべてのクリップを連結して、1つの新しいファイル(上記のルールに従って一意のファイル名)を作成します。
- マルチクリップのビデオトラックをそのマージされたクリップに置き換え、次にオーディオを追加します。
シングルクリップのタイムライン:マージをスキップします。
⚠️ キャンバスモード — 必須ルール(最初に読むこと)
フックまたは editorctl get-state によって activeView=canvas が報告された場合:
キャンバスオプションは厳密に3つです。他は存在しません。
- Paper.js — ベクターグラフィックによるコード描画(
canvas-run-paperjs) - Matter.js — 物理エンジンとアニメーション(
canvas-run-matterjs) - GPT image 2 — AI生成画像(
generate-image+canvas-add-image)
削除されました — 以下のものをいかなる名前でも提供しないでください。
デザインモード— 削除編集可能レイヤー— 削除Figma スタイルのレイアウト— 削除ノードベースのデザイン— 削除直接キャンバスデザイン— 削除
ユーザーがキャンバスモードで視覚的なものを求めた場合、これら3つのオプションのみを、これらの正確なラベルで提示してください。そのままコピーしてください。4つ目のオプションを考案したり、オプション1をデザイン関連として言い換えたりしないでください。
API ブリッジ: POST http://localhost:51847 — JSON ボディ {"command":"<name>","args":{...}}
import urllib.request, json
def call(command, **args):
data = json.dumps({"command": command, "args": args}).encode()
req = urllib.request.Request(
"http://localhost:51847", data=data,
headers={"Content-Type": "application/json"}, method="POST"
)
return json.loads(urllib.request.urlopen(req).read())["result"]
call("ping") # → {"status":"ok","version":"1.0.0","port":51847}
call("help") # → コマンドの完全なリスト
コマンド
プロジェクト / 設定
| コマンド | 引数 | 戻り値 |
|---|---|---|
get_project | — | プロジェクトの完全な JSON |
get_settings | — | モデル + プロバイダーの設定 |
アセット
| コマンド | 引数 | 戻り値 |
|---|---|---|
list_assets | — | [{id, name, path, type, duration, semantic}] |
get_asset | assetId | セマンティック情報とトランスクリプトを含む単一アセット |
import_files | paths: string[] | インポートされたアセットレコード |
transcribe_asset | assetId, language? | Whisper による {segments} |
embed_assets | all?: bool | text-embedding-3-small による {embedded, total} |
search_media | query, limit? | コサイン類似度検索(フォールバックはキーワード) |
search_spoken | query, limit? | トランスクリプトセグメント内の部分文字列検索 |
シーケンスとトラック
| コマンド | 引数 | 戻り値 |
|---|---|---|
list_sequences | — | すべてのシーケンス |
create_sequence | name | 新しいシーケンス |
activate_sequence | sequenceId | アクティブ化されたシーケンス |
get_tracks | — | アクティブなシーケンスの [{id, name, kind, clipCount}] |
add_track | `kind: video | audio |
クリップ
| コマンド | 引数 | 戻り値 |
|---|---|---|
list_clips | sequenceId? | すべてのクリップ(startTime でソート、trackKind を含む) |
add_clip | assetId, trackId, startTime, duration?, inPoint? | {clipId, clip} |
remove_clip | clipId | {success} |
move_clip | clipId, startTime | {success} |
trim_clip | clipId, inPoint?, duration?, startTime? | 更新されたクリップ |
split_clip | clipId, time | 更新されたシーケンス |
duplicate_clip | clipId, offsetSeconds? | {clipId, clip} |
update_clip_label | clipId, label | {success} |
エフェクトとプロパティ
| コマンド | 引数 | 戻り値 |
|---|---|---|
list_effects | — | 利用可能なエフェクト + パラメータドキュメント |
add_effect | clipId, effectType, parameters | 更新されたプロジェクト |
remove_effect | clipId, effectId | 更新されたプロジェクト |
set_speed | clipId, speed | 更新されたプロジェクト(0.1–10 倍再生) |
set_volume | clipId, volume | 更新されたプロジェクト(0–4、1=標準) |
set_transition | `clipId, side: in | out, type, duration?` |
履歴
| コマンド | 引数 |
|---|---|
undo | — |
redo | — |
エフェクトタイプ
| タイプ | パラメータ |
|---|---|
fade_in | duration(秒) |
fade_out | duration(秒) |
color_grade | brightness(-1→1)、contrast(0.5→2)、saturation(0→3) |
blur | radius(px、デフォルト 5) |
sharpen | amount(0→3、デフォルト 1) |
トランジションタイプ
crossfade · dip_to_black · wipe · slide
主要な概念
inPoint= クリップ再生が開始されるソースファイル内の位置startTime= クリップが配置されるタイムライン上の位置duration= クリップが再生される長さ(ソースより短くできる)- すべての変更は即座に
project:updatedを発行 → UI にライブ反映 - エフェクトはリアルタイム(CSS)でプレビューされ、エクスポート時に FFmpeg でベイクされる
編集の安全ルール
- ユーザーが明示的にポーズ、ビート、または黒い隙間を要求しない限り、タイムラインのギャップはバグとして扱います。
- シーケンスを作成したりクリップを移動した後は、
list_clips(sequenceId)で連続性を検証し、編集が連続である場合、各クリップが前のクリップの終了と正確に同じ位置で開始することを確認してください。 create_sequenceやactivate_sequenceだけで十分だと想定しないでください。その後、list_sequencesまたはget_projectでアクティブなシーケンスを常に検証してください。- スポークンハイライトカットを作成する場合、ビデオ、オーディオ、キャプション、マーカーを一緒に移動してください。編集を詰めた後、キャプションやマーカーを古いタイミングに残さないでください。
- 再生がクリップ間で停止するように見える場合、プレビュープレーヤーの故障を想定する前に、まず空のギャップがないかタイムラインを検査してください。
- カットを詰める際には、累積的なオフセット計算よりも、正確な開始位置を優先してください。カット計画から各クリップの意図された
startTimeを再計算してください。
URL からのブランド美学
必須 — 例外なし: ユーザーのメッセージに URL またはドメインが含まれている場合、キャンバス/デザインコードを書く前に、以下のフェッチコマンドを実行し、ブランドトークンを抽出する必要があります。想定または記憶したブランドカラーを使用しないでください。毎回、まずフェッチし、次にデザインしてください。フェッチが失敗した場合は、ユーザーに HEX 値を貼り付けるように依頼してください。推測にフォールバックしないでください。
curl -sL "<url>" | grep -Eo '(#[0-9a-fA-F]{3,8}|font-family:[^;"]+|font-size:[^;"]+|border-radius:[^;"]+)' | sort -u | head -60
抽出: 背景色、サーフェス色、プライマリ/アクセントカラー、テキストカラー、フォントファミリー、フォントサイズ/ウェイト、ボーダー半径、スペーシング、ボックスシャドウ。ロゴも見つけてください — それがブランドカラーとビジュアルトーンの真実の情報源です。それらの正確な値をハードコードしてください — 実際の URL が利用可能な場合は推測しないでください。
# ロゴを見つける
curl -sL "<url>" | grep -Eo '(src|href)="[^"]*logo[^"]*"' | head -10
curl -sL "<url>" | grep 'og:image'
キャンバス — Matter.js 物理フレーム
canvas_run_matterjs を使用して、キャンバス上にライブインタラクティブな物理シーンを配置します。
MCP ツール
| ツール | 引数 |
|---|---|
canvas_run_matterjs | id?(更新するフレーム ID)、name?、script(JS 文字列)、width?、height? |
canvas_add_frame | name、width、height、mode: 'matterjs' |
canvas_matterjs_scene | style(balls/stacks/pendulum/bridge/ragdoll)、gravity?、count?、colors? |
canvas_frames | — → {frames: [{id, name, mode, width, height}]} |
スクリプトパターン — いずれかを選択
パターン A — 自己完結型(リッチな UI、カスタム 2D 描画、rAF ループ)
const { Engine, Bodies, Composite, Mouse, MouseConstraint, Events } = Matter;
const W = 390, H = 844;
const engine = Engine.create({ gravity: { y: 1.5 } });
const canvas = document.querySelector('canvas'); // ← 常にこれを使用する。新しいものを作成しない。
canvas.width = W; canvas.height = H;
const ctx = canvas.getContext('2d');
// ... ボディを追加、マウスを設定 ...
function loop() {
Engine.update(engine, 1000/60);
ctx.fillStyle = '#121212'; ctx.fillRect(0, 0, W, H);
// ctx で描画
requestAnimationFrame(loop);
}
loop();
パターン B — ボディのみ(テンプレートが engine/render/runner を管理)
// engine, render, width, height, およびすべての Matter 変数はすでに宣言されているため、再宣言しないでください
var ground = Bodies.rectangle(width/2, height+25, width, 50, { isStatic: true, render: { fillStyle: '#334155' } });
var ball = Bodies.circle(width/2, 50, 30, { restitution: 0.8, render: { fillStyle: '#5b82f7' } });
Composite.add(engine.world, [ground, ball]);
engine.gravity.y = 1;
// Runner.run と Render.run はこのスクリプトの後に自動的に呼び出されるため、ここでは呼び出さないでください
重要なルール
Runner.runには2つの引数が必要:Runner.run(Runner.create(), engine)— 単一引数のRunner.run(engine)は Matter.js 0.20.0 では静かに何もしませんRender.createでelement: document.bodyを使用しない — 見えない2つ目のキャンバスが生成されます。代わりにcanvas: document.querySelector('canvas')を使用してください- パターン B で
const engineやlet engineを宣言しない — テンプレートはすでにvar engineを持っています。const/letで再宣言すると SyntaxError が発生し、キャンバスが静かに空白になります - パターン B で
Render.run/Runner.runを呼び出さない — テンプレートはスクリプトの実行後にこれを実行します。2回呼び出すと、2倍の速度またはレンダリングの破損を引き起こします - パターン B で Matter 変数をインポートまたは再宣言しない —
Engine、Bodies、Compositeなどはすでに分割代入されています。const { Engine } = Matterは SyntaxError になります
パターン B で利用可能なグローバル変数
engine · render · width · height · Engine · Render · Runner · Bodies · Composite · World · Body · Events · Constraint · Mouse · MouseConstraint
CLI
editorctl canvas-add-frame "Name" 390 844 matterjs # matterjs|paperjs|html
editorctl canvas-run-matterjs <frameId> "$(cat script.js)"
editorctl canvas-run-paperjs <frameId> "$(cat script.js)"
editorctl canvas-run-html <frameId> "$(cat scene.html)"
editorctl canvas-frames
editorctl canvas-done
Remotion — React ビデオ合成
Remotion を使用してアニメーション化されたビデオアセット(タイトルカード、ロワーサード、スライドショー、モーショングラフィック)を生成し、タイムラインに直接インポートします。
MCP ツール
| ツール | 引数 | 戻り値 |
|---|---|---|
video_editor_list_remotion_compositions | — | コンポジション ID のリスト |
video_editor_render_remotion | compositionId, outputFilename?, props?, durationInFrames?, fps? | {outputPath, assetId} |
CLI
npm run remotion:studio # localhost:3000 でライブプレビュー
npx remotion render remotion/src/index.ts <ID> out.mp4 --props '{"key":"value"}'
組み込みコンポジション
| ID | Props |
|---|---|
TitleCard | title(文字列)、subtitle?(文字列)、backgroundColor、textColor、accentColor — 150 フレーム @ 30fps |
Slideshow | images(絶対パス[])、frameDuration(フレーム)、transitionDuration(フレーム) — 300 フレーム @ 30fps |
HtmlInCanvasGlitch | title、subtitle、glitchIntensity(0–40)、backgroundColor、textColor、accentColor — 180 フレーム @ 30fps。<HtmlInCanvas> による RGB 分割グリッチ |
HTML-in-canvas(Remotion ≥ 4.0.455)
⚠️ 名前の衝突 — 最初に読むこと。 Remotion
<HtmlInCanvas>(このセクション)は、タイムライン用のビデオファイルを生成します。Monet キャンバス HTML フレーム(canvas-add-frame ... html、canvas_update_frame)は、キャンバスタブアートボードにライブ HTML シーンを生成します。異なるシステムです。出力先によって選択してください:タイムライン → Remotion;キャンバスタブ → Monet HTML フレーム。1つのタスクで両方を混在させないでください。
<HtmlInCanvas> を使用して DOM サブツリーを <canvas> に描画し、Canvas 2D / WebGL / WebGPU で後処理します。グリッチ、虫眼鏡、CRT、ディスプレイスメント、色相回転エフェクトに最適です。
import { HtmlInCanvas, type HtmlInCanvasOnPaint } from 'remotion'
const onPaint: HtmlInCanvasOnPaint = ({ canvas, element, elementImage }) => {
const ctx = canvas.getContext('2d')!
ctx.reset()
ctx.filter = 'blur(8px)'
const transform = ctx.drawElementImage(elementImage, 0, 0)
element.style.transform = transform.toString() // <-- 常に再適用
}
<HtmlInCanvas width={1920} height={1080} onPaint={onPaint}>
<YourDomTree />
</HtmlInCanvas>
ルール:
- 常に
drawElementImageの戻り値をelement.style.transformに再適用して、レイアウト/入力が正しいままになるようにします。 - 決して
<HtmlInCanvas>を別の内部にネストしないでください。Chrome は外側のみをペイントし、Remotion はエラーをスローします。エフェクトを1つのonPaintにマージしてください。 Config.setChromiumOpenGlRenderer('angle')はすでに設定されています。