@vibeframe/cli 0.27.0 → 0.30.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/LICENSE +21 -0
- package/dist/agent/adapters/index.d.ts +1 -0
- package/dist/agent/adapters/index.d.ts.map +1 -1
- package/dist/agent/adapters/index.js +5 -0
- package/dist/agent/adapters/index.js.map +1 -1
- package/dist/agent/adapters/openrouter.d.ts +16 -0
- package/dist/agent/adapters/openrouter.d.ts.map +1 -0
- package/dist/agent/adapters/openrouter.js +100 -0
- package/dist/agent/adapters/openrouter.js.map +1 -0
- package/dist/agent/types.d.ts +1 -1
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/commands/agent.d.ts.map +1 -1
- package/dist/commands/agent.js +3 -1
- package/dist/commands/agent.js.map +1 -1
- package/dist/commands/ai-edit-cli.d.ts.map +1 -1
- package/dist/commands/ai-edit-cli.js +18 -0
- package/dist/commands/ai-edit-cli.js.map +1 -1
- package/dist/commands/generate.js +14 -0
- package/dist/commands/generate.js.map +1 -1
- package/dist/commands/schema.d.ts +1 -0
- package/dist/commands/schema.d.ts.map +1 -1
- package/dist/commands/schema.js +122 -21
- package/dist/commands/schema.js.map +1 -1
- package/dist/commands/setup.js +5 -2
- package/dist/commands/setup.js.map +1 -1
- package/dist/config/schema.d.ts +2 -1
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +2 -0
- package/dist/config/schema.js.map +1 -1
- package/dist/index.js +0 -0
- package/package.json +16 -12
- package/.turbo/turbo-build.log +0 -4
- package/.turbo/turbo-lint.log +0 -21
- package/.turbo/turbo-test.log +0 -689
- package/src/agent/adapters/claude.ts +0 -143
- package/src/agent/adapters/gemini.ts +0 -159
- package/src/agent/adapters/index.ts +0 -61
- package/src/agent/adapters/ollama.ts +0 -231
- package/src/agent/adapters/openai.ts +0 -116
- package/src/agent/adapters/xai.ts +0 -119
- package/src/agent/index.ts +0 -251
- package/src/agent/memory/index.ts +0 -151
- package/src/agent/prompts/system.ts +0 -106
- package/src/agent/tools/ai-editing.ts +0 -845
- package/src/agent/tools/ai-generation.ts +0 -1073
- package/src/agent/tools/ai-pipeline.ts +0 -1055
- package/src/agent/tools/ai.ts +0 -21
- package/src/agent/tools/batch.ts +0 -429
- package/src/agent/tools/e2e.test.ts +0 -545
- package/src/agent/tools/export.ts +0 -184
- package/src/agent/tools/filesystem.ts +0 -237
- package/src/agent/tools/index.ts +0 -150
- package/src/agent/tools/integration.test.ts +0 -775
- package/src/agent/tools/media.ts +0 -697
- package/src/agent/tools/project.ts +0 -313
- package/src/agent/tools/timeline.ts +0 -951
- package/src/agent/types.ts +0 -68
- package/src/commands/agent.ts +0 -340
- package/src/commands/ai-analyze.ts +0 -429
- package/src/commands/ai-animated-caption.ts +0 -390
- package/src/commands/ai-audio.ts +0 -941
- package/src/commands/ai-broll.ts +0 -490
- package/src/commands/ai-edit-cli.ts +0 -658
- package/src/commands/ai-edit.ts +0 -1542
- package/src/commands/ai-fill-gaps.ts +0 -566
- package/src/commands/ai-helpers.ts +0 -65
- package/src/commands/ai-highlights.ts +0 -1303
- package/src/commands/ai-image.ts +0 -761
- package/src/commands/ai-motion.ts +0 -347
- package/src/commands/ai-narrate.ts +0 -451
- package/src/commands/ai-review.ts +0 -309
- package/src/commands/ai-script-pipeline-cli.ts +0 -1710
- package/src/commands/ai-script-pipeline.ts +0 -1365
- package/src/commands/ai-suggest-edit.ts +0 -264
- package/src/commands/ai-video-fx.ts +0 -445
- package/src/commands/ai-video.ts +0 -915
- package/src/commands/ai-viral.ts +0 -595
- package/src/commands/ai-visual-fx.ts +0 -601
- package/src/commands/ai.test.ts +0 -627
- package/src/commands/ai.ts +0 -307
- package/src/commands/analyze.ts +0 -282
- package/src/commands/audio.ts +0 -644
- package/src/commands/batch.test.ts +0 -279
- package/src/commands/batch.ts +0 -440
- package/src/commands/detect.ts +0 -329
- package/src/commands/doctor.ts +0 -237
- package/src/commands/edit-cmd.ts +0 -1014
- package/src/commands/export.ts +0 -918
- package/src/commands/generate.ts +0 -2146
- package/src/commands/media.ts +0 -177
- package/src/commands/output.ts +0 -142
- package/src/commands/pipeline.ts +0 -398
- package/src/commands/project.test.ts +0 -127
- package/src/commands/project.ts +0 -149
- package/src/commands/sanitize.ts +0 -60
- package/src/commands/schema.ts +0 -130
- package/src/commands/setup.ts +0 -509
- package/src/commands/timeline.test.ts +0 -499
- package/src/commands/timeline.ts +0 -529
- package/src/commands/validate.ts +0 -77
- package/src/config/config.test.ts +0 -197
- package/src/config/index.ts +0 -125
- package/src/config/schema.ts +0 -82
- package/src/engine/index.ts +0 -2
- package/src/engine/project.test.ts +0 -702
- package/src/engine/project.ts +0 -439
- package/src/index.ts +0 -146
- package/src/utils/api-key.test.ts +0 -41
- package/src/utils/api-key.ts +0 -247
- package/src/utils/audio.ts +0 -83
- package/src/utils/exec-safe.ts +0 -75
- package/src/utils/first-run.ts +0 -52
- package/src/utils/provider-resolver.ts +0 -56
- package/src/utils/remotion.ts +0 -951
- package/src/utils/subtitle.test.ts +0 -227
- package/src/utils/subtitle.ts +0 -169
- package/src/utils/tty.ts +0 -196
- package/tsconfig.json +0 -20
|
@@ -1,951 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Timeline Tools - Add sources, clips, tracks, effects, and edit timeline
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { readFile, writeFile, stat } from "node:fs/promises";
|
|
6
|
-
import { resolve, basename, extname } from "node:path";
|
|
7
|
-
import { Project, type ProjectFile } from "../../engine/index.js";
|
|
8
|
-
import type { ToolRegistry, ToolHandler } from "./index.js";
|
|
9
|
-
import type { ToolDefinition, ToolResult } from "../types.js";
|
|
10
|
-
import type { MediaType, EffectType } from "@vibeframe/core/timeline";
|
|
11
|
-
import { ffprobeDuration } from "../../utils/exec-safe.js";
|
|
12
|
-
|
|
13
|
-
// Helper to detect media type from file extension
|
|
14
|
-
function detectMediaType(path: string): MediaType {
|
|
15
|
-
const ext = extname(path).toLowerCase();
|
|
16
|
-
const videoExts = [".mp4", ".mov", ".webm", ".avi", ".mkv"];
|
|
17
|
-
const audioExts = [".mp3", ".wav", ".ogg", ".m4a", ".aac"];
|
|
18
|
-
const imageExts = [".jpg", ".jpeg", ".png", ".gif", ".webp"];
|
|
19
|
-
|
|
20
|
-
if (videoExts.includes(ext)) return "video";
|
|
21
|
-
if (audioExts.includes(ext)) return "audio";
|
|
22
|
-
if (imageExts.includes(ext)) return "image";
|
|
23
|
-
return "video";
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// Helper to get media duration using ffprobe
|
|
27
|
-
async function getMediaDuration(filePath: string, mediaType: MediaType): Promise<number> {
|
|
28
|
-
if (mediaType === "image") {
|
|
29
|
-
return 5; // Default 5 seconds for images
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
try {
|
|
33
|
-
return await ffprobeDuration(filePath);
|
|
34
|
-
} catch {
|
|
35
|
-
return 5;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// Helper to resolve project path
|
|
40
|
-
async function resolveProjectPath(inputPath: string, cwd: string): Promise<string> {
|
|
41
|
-
const filePath = resolve(cwd, inputPath);
|
|
42
|
-
try {
|
|
43
|
-
const stats = await stat(filePath);
|
|
44
|
-
if (stats.isDirectory()) {
|
|
45
|
-
return resolve(filePath, "project.vibe.json");
|
|
46
|
-
}
|
|
47
|
-
} catch {
|
|
48
|
-
// Path doesn't exist
|
|
49
|
-
}
|
|
50
|
-
return filePath;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// Helper to load project
|
|
54
|
-
async function loadProject(path: string, cwd: string): Promise<{ project: Project; filePath: string }> {
|
|
55
|
-
const filePath = await resolveProjectPath(path, cwd);
|
|
56
|
-
const content = await readFile(filePath, "utf-8");
|
|
57
|
-
const data: ProjectFile = JSON.parse(content);
|
|
58
|
-
const project = Project.fromJSON(data);
|
|
59
|
-
return { project, filePath };
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// Helper to save project
|
|
63
|
-
async function saveProject(project: Project, filePath: string): Promise<void> {
|
|
64
|
-
await writeFile(filePath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// Tool Definitions
|
|
68
|
-
const addSourceDef: ToolDefinition = {
|
|
69
|
-
name: "timeline_add_source",
|
|
70
|
-
description: "Add a media source (video, audio, or image) to the project",
|
|
71
|
-
parameters: {
|
|
72
|
-
type: "object",
|
|
73
|
-
properties: {
|
|
74
|
-
project: {
|
|
75
|
-
type: "string",
|
|
76
|
-
description: "Project file path",
|
|
77
|
-
},
|
|
78
|
-
media: {
|
|
79
|
-
type: "string",
|
|
80
|
-
description: "Media file path to add",
|
|
81
|
-
},
|
|
82
|
-
name: {
|
|
83
|
-
type: "string",
|
|
84
|
-
description: "Source name (defaults to filename)",
|
|
85
|
-
},
|
|
86
|
-
type: {
|
|
87
|
-
type: "string",
|
|
88
|
-
description: "Media type (video, audio, image) - auto-detected if not specified",
|
|
89
|
-
enum: ["video", "audio", "image"],
|
|
90
|
-
},
|
|
91
|
-
duration: {
|
|
92
|
-
type: "number",
|
|
93
|
-
description: "Duration in seconds (required for images, auto-detected for video/audio)",
|
|
94
|
-
},
|
|
95
|
-
},
|
|
96
|
-
required: ["project", "media"],
|
|
97
|
-
},
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
const addClipDef: ToolDefinition = {
|
|
101
|
-
name: "timeline_add_clip",
|
|
102
|
-
description: "Add a clip to the timeline from an existing source",
|
|
103
|
-
parameters: {
|
|
104
|
-
type: "object",
|
|
105
|
-
properties: {
|
|
106
|
-
project: {
|
|
107
|
-
type: "string",
|
|
108
|
-
description: "Project file path",
|
|
109
|
-
},
|
|
110
|
-
sourceId: {
|
|
111
|
-
type: "string",
|
|
112
|
-
description: "Source ID to create clip from",
|
|
113
|
-
},
|
|
114
|
-
trackId: {
|
|
115
|
-
type: "string",
|
|
116
|
-
description: "Track ID (defaults to first matching track)",
|
|
117
|
-
},
|
|
118
|
-
startTime: {
|
|
119
|
-
type: "number",
|
|
120
|
-
description: "Start time in timeline (seconds)",
|
|
121
|
-
},
|
|
122
|
-
duration: {
|
|
123
|
-
type: "number",
|
|
124
|
-
description: "Clip duration (defaults to source duration)",
|
|
125
|
-
},
|
|
126
|
-
offset: {
|
|
127
|
-
type: "number",
|
|
128
|
-
description: "Source start offset (seconds)",
|
|
129
|
-
},
|
|
130
|
-
},
|
|
131
|
-
required: ["project", "sourceId"],
|
|
132
|
-
},
|
|
133
|
-
};
|
|
134
|
-
|
|
135
|
-
const addTrackDef: ToolDefinition = {
|
|
136
|
-
name: "timeline_add_track",
|
|
137
|
-
description: "Add a new track to the timeline",
|
|
138
|
-
parameters: {
|
|
139
|
-
type: "object",
|
|
140
|
-
properties: {
|
|
141
|
-
project: {
|
|
142
|
-
type: "string",
|
|
143
|
-
description: "Project file path",
|
|
144
|
-
},
|
|
145
|
-
type: {
|
|
146
|
-
type: "string",
|
|
147
|
-
description: "Track type (video or audio)",
|
|
148
|
-
enum: ["video", "audio"],
|
|
149
|
-
},
|
|
150
|
-
name: {
|
|
151
|
-
type: "string",
|
|
152
|
-
description: "Track name",
|
|
153
|
-
},
|
|
154
|
-
},
|
|
155
|
-
required: ["project", "type"],
|
|
156
|
-
},
|
|
157
|
-
};
|
|
158
|
-
|
|
159
|
-
const addEffectDef: ToolDefinition = {
|
|
160
|
-
name: "timeline_add_effect",
|
|
161
|
-
description: "Add an effect to a clip",
|
|
162
|
-
parameters: {
|
|
163
|
-
type: "object",
|
|
164
|
-
properties: {
|
|
165
|
-
project: {
|
|
166
|
-
type: "string",
|
|
167
|
-
description: "Project file path",
|
|
168
|
-
},
|
|
169
|
-
clipId: {
|
|
170
|
-
type: "string",
|
|
171
|
-
description: "Clip ID to add effect to",
|
|
172
|
-
},
|
|
173
|
-
effectType: {
|
|
174
|
-
type: "string",
|
|
175
|
-
description: "Effect type",
|
|
176
|
-
enum: ["fadeIn", "fadeOut", "blur", "brightness", "contrast", "saturation", "speed", "volume"],
|
|
177
|
-
},
|
|
178
|
-
startTime: {
|
|
179
|
-
type: "number",
|
|
180
|
-
description: "Effect start time relative to clip (seconds)",
|
|
181
|
-
},
|
|
182
|
-
duration: {
|
|
183
|
-
type: "number",
|
|
184
|
-
description: "Effect duration (defaults to clip duration)",
|
|
185
|
-
},
|
|
186
|
-
params: {
|
|
187
|
-
type: "object",
|
|
188
|
-
description: "Effect-specific parameters",
|
|
189
|
-
},
|
|
190
|
-
},
|
|
191
|
-
required: ["project", "clipId", "effectType"],
|
|
192
|
-
},
|
|
193
|
-
};
|
|
194
|
-
|
|
195
|
-
const trimDef: ToolDefinition = {
|
|
196
|
-
name: "timeline_trim",
|
|
197
|
-
description: "Trim a clip (adjust start time or duration)",
|
|
198
|
-
parameters: {
|
|
199
|
-
type: "object",
|
|
200
|
-
properties: {
|
|
201
|
-
project: {
|
|
202
|
-
type: "string",
|
|
203
|
-
description: "Project file path",
|
|
204
|
-
},
|
|
205
|
-
clipId: {
|
|
206
|
-
type: "string",
|
|
207
|
-
description: "Clip ID to trim",
|
|
208
|
-
},
|
|
209
|
-
startTime: {
|
|
210
|
-
type: "number",
|
|
211
|
-
description: "New start time",
|
|
212
|
-
},
|
|
213
|
-
duration: {
|
|
214
|
-
type: "number",
|
|
215
|
-
description: "New duration",
|
|
216
|
-
},
|
|
217
|
-
},
|
|
218
|
-
required: ["project", "clipId"],
|
|
219
|
-
},
|
|
220
|
-
};
|
|
221
|
-
|
|
222
|
-
const splitDef: ToolDefinition = {
|
|
223
|
-
name: "timeline_split",
|
|
224
|
-
description: "Split a clip at a specific time, creating two clips",
|
|
225
|
-
parameters: {
|
|
226
|
-
type: "object",
|
|
227
|
-
properties: {
|
|
228
|
-
project: {
|
|
229
|
-
type: "string",
|
|
230
|
-
description: "Project file path",
|
|
231
|
-
},
|
|
232
|
-
clipId: {
|
|
233
|
-
type: "string",
|
|
234
|
-
description: "Clip ID to split",
|
|
235
|
-
},
|
|
236
|
-
time: {
|
|
237
|
-
type: "number",
|
|
238
|
-
description: "Split time relative to clip start (seconds)",
|
|
239
|
-
},
|
|
240
|
-
},
|
|
241
|
-
required: ["project", "clipId", "time"],
|
|
242
|
-
},
|
|
243
|
-
};
|
|
244
|
-
|
|
245
|
-
const moveDef: ToolDefinition = {
|
|
246
|
-
name: "timeline_move",
|
|
247
|
-
description: "Move a clip to a new position or track",
|
|
248
|
-
parameters: {
|
|
249
|
-
type: "object",
|
|
250
|
-
properties: {
|
|
251
|
-
project: {
|
|
252
|
-
type: "string",
|
|
253
|
-
description: "Project file path",
|
|
254
|
-
},
|
|
255
|
-
clipId: {
|
|
256
|
-
type: "string",
|
|
257
|
-
description: "Clip ID to move",
|
|
258
|
-
},
|
|
259
|
-
startTime: {
|
|
260
|
-
type: "number",
|
|
261
|
-
description: "New start time",
|
|
262
|
-
},
|
|
263
|
-
trackId: {
|
|
264
|
-
type: "string",
|
|
265
|
-
description: "New track ID",
|
|
266
|
-
},
|
|
267
|
-
},
|
|
268
|
-
required: ["project", "clipId"],
|
|
269
|
-
},
|
|
270
|
-
};
|
|
271
|
-
|
|
272
|
-
const deleteDef: ToolDefinition = {
|
|
273
|
-
name: "timeline_delete",
|
|
274
|
-
description: "Delete a clip from the timeline",
|
|
275
|
-
parameters: {
|
|
276
|
-
type: "object",
|
|
277
|
-
properties: {
|
|
278
|
-
project: {
|
|
279
|
-
type: "string",
|
|
280
|
-
description: "Project file path",
|
|
281
|
-
},
|
|
282
|
-
clipId: {
|
|
283
|
-
type: "string",
|
|
284
|
-
description: "Clip ID to delete",
|
|
285
|
-
},
|
|
286
|
-
},
|
|
287
|
-
required: ["project", "clipId"],
|
|
288
|
-
},
|
|
289
|
-
};
|
|
290
|
-
|
|
291
|
-
const duplicateDef: ToolDefinition = {
|
|
292
|
-
name: "timeline_duplicate",
|
|
293
|
-
description: "Duplicate a clip",
|
|
294
|
-
parameters: {
|
|
295
|
-
type: "object",
|
|
296
|
-
properties: {
|
|
297
|
-
project: {
|
|
298
|
-
type: "string",
|
|
299
|
-
description: "Project file path",
|
|
300
|
-
},
|
|
301
|
-
clipId: {
|
|
302
|
-
type: "string",
|
|
303
|
-
description: "Clip ID to duplicate",
|
|
304
|
-
},
|
|
305
|
-
startTime: {
|
|
306
|
-
type: "number",
|
|
307
|
-
description: "Start time for duplicate (default: after original)",
|
|
308
|
-
},
|
|
309
|
-
},
|
|
310
|
-
required: ["project", "clipId"],
|
|
311
|
-
},
|
|
312
|
-
};
|
|
313
|
-
|
|
314
|
-
const listDef: ToolDefinition = {
|
|
315
|
-
name: "timeline_list",
|
|
316
|
-
description: "List timeline contents (sources, tracks, clips)",
|
|
317
|
-
parameters: {
|
|
318
|
-
type: "object",
|
|
319
|
-
properties: {
|
|
320
|
-
project: {
|
|
321
|
-
type: "string",
|
|
322
|
-
description: "Project file path",
|
|
323
|
-
},
|
|
324
|
-
type: {
|
|
325
|
-
type: "string",
|
|
326
|
-
description: "What to list (all, sources, tracks, clips)",
|
|
327
|
-
enum: ["all", "sources", "tracks", "clips"],
|
|
328
|
-
},
|
|
329
|
-
},
|
|
330
|
-
required: ["project"],
|
|
331
|
-
},
|
|
332
|
-
};
|
|
333
|
-
|
|
334
|
-
const clearDef: ToolDefinition = {
|
|
335
|
-
name: "timeline_clear",
|
|
336
|
-
description: "Clear timeline contents (remove clips, tracks, or sources)",
|
|
337
|
-
parameters: {
|
|
338
|
-
type: "object",
|
|
339
|
-
properties: {
|
|
340
|
-
project: {
|
|
341
|
-
type: "string",
|
|
342
|
-
description: "Project file path",
|
|
343
|
-
},
|
|
344
|
-
what: {
|
|
345
|
-
type: "string",
|
|
346
|
-
description: "What to clear: clips (default), tracks, sources, or all",
|
|
347
|
-
enum: ["clips", "tracks", "sources", "all"],
|
|
348
|
-
},
|
|
349
|
-
keepTracks: {
|
|
350
|
-
type: "boolean",
|
|
351
|
-
description: "When clearing 'all', keep default empty tracks (default: true)",
|
|
352
|
-
},
|
|
353
|
-
},
|
|
354
|
-
required: ["project"],
|
|
355
|
-
},
|
|
356
|
-
};
|
|
357
|
-
|
|
358
|
-
// Tool Handlers
|
|
359
|
-
const addSource: ToolHandler = async (args, context): Promise<ToolResult> => {
|
|
360
|
-
const projectPath = (args.project || args.path) as string;
|
|
361
|
-
const mediaPath = args.media as string;
|
|
362
|
-
|
|
363
|
-
try {
|
|
364
|
-
const { project, filePath } = await loadProject(projectPath, context.workingDirectory);
|
|
365
|
-
const absMediaPath = resolve(context.workingDirectory, mediaPath);
|
|
366
|
-
|
|
367
|
-
const mediaName = (args.name as string) || basename(mediaPath);
|
|
368
|
-
const mediaType = (args.type as MediaType) || detectMediaType(mediaPath);
|
|
369
|
-
const duration = (args.duration as number) || await getMediaDuration(absMediaPath, mediaType);
|
|
370
|
-
|
|
371
|
-
const source = project.addSource({
|
|
372
|
-
name: mediaName,
|
|
373
|
-
type: mediaType,
|
|
374
|
-
url: absMediaPath,
|
|
375
|
-
duration,
|
|
376
|
-
});
|
|
377
|
-
|
|
378
|
-
await saveProject(project, filePath);
|
|
379
|
-
|
|
380
|
-
return {
|
|
381
|
-
toolCallId: "",
|
|
382
|
-
success: true,
|
|
383
|
-
output: `Source added: ${source.id}\nName: ${mediaName}\nType: ${mediaType}\nDuration: ${duration.toFixed(1)}s`,
|
|
384
|
-
};
|
|
385
|
-
} catch (error) {
|
|
386
|
-
return {
|
|
387
|
-
toolCallId: "",
|
|
388
|
-
success: false,
|
|
389
|
-
output: "",
|
|
390
|
-
error: `Failed to add source: ${error instanceof Error ? error.message : String(error)}`,
|
|
391
|
-
};
|
|
392
|
-
}
|
|
393
|
-
};
|
|
394
|
-
|
|
395
|
-
const addClip: ToolHandler = async (args, context): Promise<ToolResult> => {
|
|
396
|
-
const projectPath = (args.project || args.path) as string;
|
|
397
|
-
const sourceId = args.sourceId as string;
|
|
398
|
-
|
|
399
|
-
try {
|
|
400
|
-
const { project, filePath } = await loadProject(projectPath, context.workingDirectory);
|
|
401
|
-
|
|
402
|
-
const source = project.getSource(sourceId);
|
|
403
|
-
if (!source) {
|
|
404
|
-
return {
|
|
405
|
-
toolCallId: "",
|
|
406
|
-
success: false,
|
|
407
|
-
output: "",
|
|
408
|
-
error: `Source not found: ${sourceId}`,
|
|
409
|
-
};
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
// Find track
|
|
413
|
-
let trackId = args.trackId as string | undefined;
|
|
414
|
-
if (!trackId) {
|
|
415
|
-
const trackType = source.type === "audio" ? "audio" : "video";
|
|
416
|
-
const tracks = project.getTracksByType(trackType);
|
|
417
|
-
if (tracks.length === 0) {
|
|
418
|
-
return {
|
|
419
|
-
toolCallId: "",
|
|
420
|
-
success: false,
|
|
421
|
-
output: "",
|
|
422
|
-
error: `No ${trackType} track found. Create one first with timeline_add_track.`,
|
|
423
|
-
};
|
|
424
|
-
}
|
|
425
|
-
trackId = tracks[0].id;
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
const startTime = (args.startTime as number) || 0;
|
|
429
|
-
const offset = (args.offset as number) || 0;
|
|
430
|
-
const duration = (args.duration as number) || source.duration;
|
|
431
|
-
|
|
432
|
-
const clip = project.addClip({
|
|
433
|
-
sourceId,
|
|
434
|
-
trackId,
|
|
435
|
-
startTime,
|
|
436
|
-
duration,
|
|
437
|
-
sourceStartOffset: offset,
|
|
438
|
-
sourceEndOffset: offset + duration,
|
|
439
|
-
});
|
|
440
|
-
|
|
441
|
-
await saveProject(project, filePath);
|
|
442
|
-
|
|
443
|
-
return {
|
|
444
|
-
toolCallId: "",
|
|
445
|
-
success: true,
|
|
446
|
-
output: `Clip added: ${clip.id}\nSource: ${source.name}\nTrack: ${trackId}\nStart: ${startTime}s\nDuration: ${duration.toFixed(1)}s`,
|
|
447
|
-
};
|
|
448
|
-
} catch (error) {
|
|
449
|
-
return {
|
|
450
|
-
toolCallId: "",
|
|
451
|
-
success: false,
|
|
452
|
-
output: "",
|
|
453
|
-
error: `Failed to add clip: ${error instanceof Error ? error.message : String(error)}`,
|
|
454
|
-
};
|
|
455
|
-
}
|
|
456
|
-
};
|
|
457
|
-
|
|
458
|
-
const addTrack: ToolHandler = async (args, context): Promise<ToolResult> => {
|
|
459
|
-
const projectPath = (args.project || args.path) as string;
|
|
460
|
-
const type = args.type as MediaType;
|
|
461
|
-
|
|
462
|
-
try {
|
|
463
|
-
const { project, filePath } = await loadProject(projectPath, context.workingDirectory);
|
|
464
|
-
|
|
465
|
-
const existingTracks = project.getTracksByType(type);
|
|
466
|
-
const trackName = (args.name as string) || `${type.charAt(0).toUpperCase() + type.slice(1)} ${existingTracks.length + 1}`;
|
|
467
|
-
const order = project.getTracks().length;
|
|
468
|
-
|
|
469
|
-
const track = project.addTrack({
|
|
470
|
-
name: trackName,
|
|
471
|
-
type,
|
|
472
|
-
order,
|
|
473
|
-
isMuted: false,
|
|
474
|
-
isLocked: false,
|
|
475
|
-
isVisible: true,
|
|
476
|
-
});
|
|
477
|
-
|
|
478
|
-
await saveProject(project, filePath);
|
|
479
|
-
|
|
480
|
-
return {
|
|
481
|
-
toolCallId: "",
|
|
482
|
-
success: true,
|
|
483
|
-
output: `Track added: ${track.id}\nName: ${trackName}\nType: ${type}`,
|
|
484
|
-
};
|
|
485
|
-
} catch (error) {
|
|
486
|
-
return {
|
|
487
|
-
toolCallId: "",
|
|
488
|
-
success: false,
|
|
489
|
-
output: "",
|
|
490
|
-
error: `Failed to add track: ${error instanceof Error ? error.message : String(error)}`,
|
|
491
|
-
};
|
|
492
|
-
}
|
|
493
|
-
};
|
|
494
|
-
|
|
495
|
-
const addEffect: ToolHandler = async (args, context): Promise<ToolResult> => {
|
|
496
|
-
const projectPath = (args.project || args.path) as string;
|
|
497
|
-
const clipId = args.clipId as string;
|
|
498
|
-
const effectType = args.effectType as EffectType;
|
|
499
|
-
|
|
500
|
-
try {
|
|
501
|
-
const { project, filePath } = await loadProject(projectPath, context.workingDirectory);
|
|
502
|
-
|
|
503
|
-
const clip = project.getClip(clipId);
|
|
504
|
-
if (!clip) {
|
|
505
|
-
return {
|
|
506
|
-
toolCallId: "",
|
|
507
|
-
success: false,
|
|
508
|
-
output: "",
|
|
509
|
-
error: `Clip not found: ${clipId}`,
|
|
510
|
-
};
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
const startTime = (args.startTime as number) || 0;
|
|
514
|
-
const duration = (args.duration as number) || clip.duration;
|
|
515
|
-
const rawParams = (args.params as Record<string, unknown>) || {};
|
|
516
|
-
const params: Record<string, string | number | boolean> = {};
|
|
517
|
-
for (const [key, value] of Object.entries(rawParams)) {
|
|
518
|
-
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
519
|
-
params[key] = value;
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
const effect = project.addEffect(clipId, {
|
|
524
|
-
type: effectType,
|
|
525
|
-
startTime,
|
|
526
|
-
duration,
|
|
527
|
-
params,
|
|
528
|
-
});
|
|
529
|
-
|
|
530
|
-
if (!effect) {
|
|
531
|
-
return {
|
|
532
|
-
toolCallId: "",
|
|
533
|
-
success: false,
|
|
534
|
-
output: "",
|
|
535
|
-
error: "Failed to add effect",
|
|
536
|
-
};
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
await saveProject(project, filePath);
|
|
540
|
-
|
|
541
|
-
return {
|
|
542
|
-
toolCallId: "",
|
|
543
|
-
success: true,
|
|
544
|
-
output: `Effect added: ${effect.id}\nType: ${effectType}\nStart: ${startTime}s\nDuration: ${duration.toFixed(1)}s`,
|
|
545
|
-
};
|
|
546
|
-
} catch (error) {
|
|
547
|
-
return {
|
|
548
|
-
toolCallId: "",
|
|
549
|
-
success: false,
|
|
550
|
-
output: "",
|
|
551
|
-
error: `Failed to add effect: ${error instanceof Error ? error.message : String(error)}`,
|
|
552
|
-
};
|
|
553
|
-
}
|
|
554
|
-
};
|
|
555
|
-
|
|
556
|
-
const trim: ToolHandler = async (args, context): Promise<ToolResult> => {
|
|
557
|
-
const projectPath = (args.project || args.path) as string;
|
|
558
|
-
const clipId = args.clipId as string;
|
|
559
|
-
|
|
560
|
-
try {
|
|
561
|
-
const { project, filePath } = await loadProject(projectPath, context.workingDirectory);
|
|
562
|
-
|
|
563
|
-
const clip = project.getClip(clipId);
|
|
564
|
-
if (!clip) {
|
|
565
|
-
return {
|
|
566
|
-
toolCallId: "",
|
|
567
|
-
success: false,
|
|
568
|
-
output: "",
|
|
569
|
-
error: `Clip not found: ${clipId}`,
|
|
570
|
-
};
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
if (args.startTime !== undefined) {
|
|
574
|
-
project.trimClipStart(clipId, args.startTime as number);
|
|
575
|
-
}
|
|
576
|
-
if (args.duration !== undefined) {
|
|
577
|
-
project.trimClipEnd(clipId, args.duration as number);
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
await saveProject(project, filePath);
|
|
581
|
-
|
|
582
|
-
const updated = project.getClip(clipId)!;
|
|
583
|
-
return {
|
|
584
|
-
toolCallId: "",
|
|
585
|
-
success: true,
|
|
586
|
-
output: `Clip trimmed: ${clipId}\nStart: ${updated.startTime}s\nDuration: ${updated.duration.toFixed(1)}s`,
|
|
587
|
-
};
|
|
588
|
-
} catch (error) {
|
|
589
|
-
return {
|
|
590
|
-
toolCallId: "",
|
|
591
|
-
success: false,
|
|
592
|
-
output: "",
|
|
593
|
-
error: `Failed to trim clip: ${error instanceof Error ? error.message : String(error)}`,
|
|
594
|
-
};
|
|
595
|
-
}
|
|
596
|
-
};
|
|
597
|
-
|
|
598
|
-
const split: ToolHandler = async (args, context): Promise<ToolResult> => {
|
|
599
|
-
const projectPath = (args.project || args.path) as string;
|
|
600
|
-
const clipId = args.clipId as string;
|
|
601
|
-
const splitTime = args.time as number;
|
|
602
|
-
|
|
603
|
-
try {
|
|
604
|
-
const { project, filePath } = await loadProject(projectPath, context.workingDirectory);
|
|
605
|
-
|
|
606
|
-
const clip = project.getClip(clipId);
|
|
607
|
-
if (!clip) {
|
|
608
|
-
return {
|
|
609
|
-
toolCallId: "",
|
|
610
|
-
success: false,
|
|
611
|
-
output: "",
|
|
612
|
-
error: `Clip not found: ${clipId}`,
|
|
613
|
-
};
|
|
614
|
-
}
|
|
615
|
-
|
|
616
|
-
if (splitTime <= 0 || splitTime >= clip.duration) {
|
|
617
|
-
return {
|
|
618
|
-
toolCallId: "",
|
|
619
|
-
success: false,
|
|
620
|
-
output: "",
|
|
621
|
-
error: `Invalid split time. Must be between 0 and ${clip.duration}s`,
|
|
622
|
-
};
|
|
623
|
-
}
|
|
624
|
-
|
|
625
|
-
const result = project.splitClip(clipId, splitTime);
|
|
626
|
-
if (!result) {
|
|
627
|
-
return {
|
|
628
|
-
toolCallId: "",
|
|
629
|
-
success: false,
|
|
630
|
-
output: "",
|
|
631
|
-
error: "Failed to split clip",
|
|
632
|
-
};
|
|
633
|
-
}
|
|
634
|
-
|
|
635
|
-
await saveProject(project, filePath);
|
|
636
|
-
|
|
637
|
-
const [first, second] = result;
|
|
638
|
-
return {
|
|
639
|
-
toolCallId: "",
|
|
640
|
-
success: true,
|
|
641
|
-
output: `Clip split:\nFirst: ${first.id} (${first.duration.toFixed(2)}s)\nSecond: ${second.id} (${second.duration.toFixed(2)}s)`,
|
|
642
|
-
};
|
|
643
|
-
} catch (error) {
|
|
644
|
-
return {
|
|
645
|
-
toolCallId: "",
|
|
646
|
-
success: false,
|
|
647
|
-
output: "",
|
|
648
|
-
error: `Failed to split clip: ${error instanceof Error ? error.message : String(error)}`,
|
|
649
|
-
};
|
|
650
|
-
}
|
|
651
|
-
};
|
|
652
|
-
|
|
653
|
-
const move: ToolHandler = async (args, context): Promise<ToolResult> => {
|
|
654
|
-
const projectPath = (args.project || args.path) as string;
|
|
655
|
-
const clipId = args.clipId as string;
|
|
656
|
-
|
|
657
|
-
try {
|
|
658
|
-
const { project, filePath } = await loadProject(projectPath, context.workingDirectory);
|
|
659
|
-
|
|
660
|
-
const clip = project.getClip(clipId);
|
|
661
|
-
if (!clip) {
|
|
662
|
-
return {
|
|
663
|
-
toolCallId: "",
|
|
664
|
-
success: false,
|
|
665
|
-
output: "",
|
|
666
|
-
error: `Clip not found: ${clipId}`,
|
|
667
|
-
};
|
|
668
|
-
}
|
|
669
|
-
|
|
670
|
-
const newTime = args.startTime !== undefined ? (args.startTime as number) : clip.startTime;
|
|
671
|
-
const newTrack = (args.trackId as string) || clip.trackId;
|
|
672
|
-
|
|
673
|
-
const moved = project.moveClip(clipId, newTrack, newTime);
|
|
674
|
-
if (!moved) {
|
|
675
|
-
return {
|
|
676
|
-
toolCallId: "",
|
|
677
|
-
success: false,
|
|
678
|
-
output: "",
|
|
679
|
-
error: "Failed to move clip",
|
|
680
|
-
};
|
|
681
|
-
}
|
|
682
|
-
|
|
683
|
-
await saveProject(project, filePath);
|
|
684
|
-
|
|
685
|
-
const updated = project.getClip(clipId)!;
|
|
686
|
-
return {
|
|
687
|
-
toolCallId: "",
|
|
688
|
-
success: true,
|
|
689
|
-
output: `Clip moved: ${clipId}\nTrack: ${updated.trackId}\nStart: ${updated.startTime}s`,
|
|
690
|
-
};
|
|
691
|
-
} catch (error) {
|
|
692
|
-
return {
|
|
693
|
-
toolCallId: "",
|
|
694
|
-
success: false,
|
|
695
|
-
output: "",
|
|
696
|
-
error: `Failed to move clip: ${error instanceof Error ? error.message : String(error)}`,
|
|
697
|
-
};
|
|
698
|
-
}
|
|
699
|
-
};
|
|
700
|
-
|
|
701
|
-
const deleteClip: ToolHandler = async (args, context): Promise<ToolResult> => {
|
|
702
|
-
const projectPath = (args.project || args.path) as string;
|
|
703
|
-
const clipId = args.clipId as string;
|
|
704
|
-
|
|
705
|
-
try {
|
|
706
|
-
const { project, filePath } = await loadProject(projectPath, context.workingDirectory);
|
|
707
|
-
|
|
708
|
-
const clip = project.getClip(clipId);
|
|
709
|
-
if (!clip) {
|
|
710
|
-
return {
|
|
711
|
-
toolCallId: "",
|
|
712
|
-
success: false,
|
|
713
|
-
output: "",
|
|
714
|
-
error: `Clip not found: ${clipId}`,
|
|
715
|
-
};
|
|
716
|
-
}
|
|
717
|
-
|
|
718
|
-
const removed = project.removeClip(clipId);
|
|
719
|
-
if (!removed) {
|
|
720
|
-
return {
|
|
721
|
-
toolCallId: "",
|
|
722
|
-
success: false,
|
|
723
|
-
output: "",
|
|
724
|
-
error: "Failed to delete clip",
|
|
725
|
-
};
|
|
726
|
-
}
|
|
727
|
-
|
|
728
|
-
await saveProject(project, filePath);
|
|
729
|
-
|
|
730
|
-
return {
|
|
731
|
-
toolCallId: "",
|
|
732
|
-
success: true,
|
|
733
|
-
output: `Clip deleted: ${clipId}`,
|
|
734
|
-
};
|
|
735
|
-
} catch (error) {
|
|
736
|
-
return {
|
|
737
|
-
toolCallId: "",
|
|
738
|
-
success: false,
|
|
739
|
-
output: "",
|
|
740
|
-
error: `Failed to delete clip: ${error instanceof Error ? error.message : String(error)}`,
|
|
741
|
-
};
|
|
742
|
-
}
|
|
743
|
-
};
|
|
744
|
-
|
|
745
|
-
const duplicate: ToolHandler = async (args, context): Promise<ToolResult> => {
|
|
746
|
-
const projectPath = (args.project || args.path) as string;
|
|
747
|
-
const clipId = args.clipId as string;
|
|
748
|
-
|
|
749
|
-
try {
|
|
750
|
-
const { project, filePath } = await loadProject(projectPath, context.workingDirectory);
|
|
751
|
-
|
|
752
|
-
const clip = project.getClip(clipId);
|
|
753
|
-
if (!clip) {
|
|
754
|
-
return {
|
|
755
|
-
toolCallId: "",
|
|
756
|
-
success: false,
|
|
757
|
-
output: "",
|
|
758
|
-
error: `Clip not found: ${clipId}`,
|
|
759
|
-
};
|
|
760
|
-
}
|
|
761
|
-
|
|
762
|
-
const offsetTime = args.startTime as number | undefined;
|
|
763
|
-
const duplicated = project.duplicateClip(clipId, offsetTime);
|
|
764
|
-
|
|
765
|
-
if (!duplicated) {
|
|
766
|
-
return {
|
|
767
|
-
toolCallId: "",
|
|
768
|
-
success: false,
|
|
769
|
-
output: "",
|
|
770
|
-
error: "Failed to duplicate clip",
|
|
771
|
-
};
|
|
772
|
-
}
|
|
773
|
-
|
|
774
|
-
await saveProject(project, filePath);
|
|
775
|
-
|
|
776
|
-
return {
|
|
777
|
-
toolCallId: "",
|
|
778
|
-
success: true,
|
|
779
|
-
output: `Clip duplicated: ${duplicated.id}\nStart: ${duplicated.startTime}s\nDuration: ${duplicated.duration.toFixed(1)}s`,
|
|
780
|
-
};
|
|
781
|
-
} catch (error) {
|
|
782
|
-
return {
|
|
783
|
-
toolCallId: "",
|
|
784
|
-
success: false,
|
|
785
|
-
output: "",
|
|
786
|
-
error: `Failed to duplicate clip: ${error instanceof Error ? error.message : String(error)}`,
|
|
787
|
-
};
|
|
788
|
-
}
|
|
789
|
-
};
|
|
790
|
-
|
|
791
|
-
const list: ToolHandler = async (args, context): Promise<ToolResult> => {
|
|
792
|
-
// Accept both 'project' and 'path' for robustness (LLMs sometimes use alternative names)
|
|
793
|
-
const projectPath = (args.project || args.path) as string;
|
|
794
|
-
const listType = (args.type as string) || "all";
|
|
795
|
-
|
|
796
|
-
try {
|
|
797
|
-
const { project } = await loadProject(projectPath, context.workingDirectory);
|
|
798
|
-
const output: string[] = [];
|
|
799
|
-
|
|
800
|
-
if (listType === "all" || listType === "sources") {
|
|
801
|
-
output.push("## Sources");
|
|
802
|
-
const sources = project.getSources();
|
|
803
|
-
if (sources.length === 0) {
|
|
804
|
-
output.push(" (none)");
|
|
805
|
-
} else {
|
|
806
|
-
for (const source of sources) {
|
|
807
|
-
output.push(` ${source.id}: ${source.name} (${source.type}, ${source.duration.toFixed(1)}s)`);
|
|
808
|
-
}
|
|
809
|
-
}
|
|
810
|
-
}
|
|
811
|
-
|
|
812
|
-
if (listType === "all" || listType === "tracks") {
|
|
813
|
-
output.push("\n## Tracks");
|
|
814
|
-
const tracks = project.getTracks();
|
|
815
|
-
for (const track of tracks) {
|
|
816
|
-
const status = [
|
|
817
|
-
track.isMuted ? "muted" : null,
|
|
818
|
-
track.isLocked ? "locked" : null,
|
|
819
|
-
!track.isVisible ? "hidden" : null,
|
|
820
|
-
].filter(Boolean).join(", ");
|
|
821
|
-
output.push(` ${track.id}: ${track.name} (${track.type})${status ? ` [${status}]` : ""}`);
|
|
822
|
-
}
|
|
823
|
-
}
|
|
824
|
-
|
|
825
|
-
if (listType === "all" || listType === "clips") {
|
|
826
|
-
output.push("\n## Clips");
|
|
827
|
-
const clips = project.getClips();
|
|
828
|
-
if (clips.length === 0) {
|
|
829
|
-
output.push(" (none)");
|
|
830
|
-
} else {
|
|
831
|
-
for (const clip of clips) {
|
|
832
|
-
const source = project.getSource(clip.sourceId);
|
|
833
|
-
const effects = clip.effects.length > 0 ? ` [effects: ${clip.effects.map((e) => e.type).join(", ")}]` : "";
|
|
834
|
-
output.push(` ${clip.id}: ${source?.name || "unknown"} @ ${clip.startTime}s (${clip.duration.toFixed(1)}s)${effects}`);
|
|
835
|
-
}
|
|
836
|
-
}
|
|
837
|
-
}
|
|
838
|
-
|
|
839
|
-
return {
|
|
840
|
-
toolCallId: "",
|
|
841
|
-
success: true,
|
|
842
|
-
output: output.join("\n"),
|
|
843
|
-
};
|
|
844
|
-
} catch (error) {
|
|
845
|
-
return {
|
|
846
|
-
toolCallId: "",
|
|
847
|
-
success: false,
|
|
848
|
-
output: "",
|
|
849
|
-
error: `Failed to list timeline: ${error instanceof Error ? error.message : String(error)}`,
|
|
850
|
-
};
|
|
851
|
-
}
|
|
852
|
-
};
|
|
853
|
-
|
|
854
|
-
const clear: ToolHandler = async (args, context): Promise<ToolResult> => {
|
|
855
|
-
const projectPath = (args.project || args.path) as string;
|
|
856
|
-
const what = (args.what as string) || "clips";
|
|
857
|
-
const keepTracks = args.keepTracks !== false; // default true
|
|
858
|
-
|
|
859
|
-
try {
|
|
860
|
-
const { project, filePath } = await loadProject(projectPath, context.workingDirectory);
|
|
861
|
-
|
|
862
|
-
const removed = {
|
|
863
|
-
clips: 0,
|
|
864
|
-
tracks: 0,
|
|
865
|
-
sources: 0,
|
|
866
|
-
};
|
|
867
|
-
|
|
868
|
-
// Remove clips
|
|
869
|
-
if (what === "clips" || what === "all") {
|
|
870
|
-
const clips = project.getClips();
|
|
871
|
-
for (const clip of clips) {
|
|
872
|
-
project.removeClip(clip.id);
|
|
873
|
-
removed.clips++;
|
|
874
|
-
}
|
|
875
|
-
}
|
|
876
|
-
|
|
877
|
-
// Remove tracks
|
|
878
|
-
if (what === "tracks" || what === "all") {
|
|
879
|
-
const tracks = project.getTracks();
|
|
880
|
-
for (const track of tracks) {
|
|
881
|
-
project.removeTrack(track.id);
|
|
882
|
-
removed.tracks++;
|
|
883
|
-
}
|
|
884
|
-
|
|
885
|
-
// Re-add default tracks if keepTracks
|
|
886
|
-
if (what === "all" && keepTracks) {
|
|
887
|
-
project.addTrack({
|
|
888
|
-
name: "Video 1",
|
|
889
|
-
type: "video",
|
|
890
|
-
order: 1,
|
|
891
|
-
isMuted: false,
|
|
892
|
-
isLocked: false,
|
|
893
|
-
isVisible: true,
|
|
894
|
-
});
|
|
895
|
-
project.addTrack({
|
|
896
|
-
name: "Audio 1",
|
|
897
|
-
type: "audio",
|
|
898
|
-
order: 0,
|
|
899
|
-
isMuted: false,
|
|
900
|
-
isLocked: false,
|
|
901
|
-
isVisible: true,
|
|
902
|
-
});
|
|
903
|
-
}
|
|
904
|
-
}
|
|
905
|
-
|
|
906
|
-
// Remove sources
|
|
907
|
-
if (what === "sources" || what === "all") {
|
|
908
|
-
const sources = project.getSources();
|
|
909
|
-
for (const source of sources) {
|
|
910
|
-
project.removeSource(source.id);
|
|
911
|
-
removed.sources++;
|
|
912
|
-
}
|
|
913
|
-
}
|
|
914
|
-
|
|
915
|
-
await saveProject(project, filePath);
|
|
916
|
-
|
|
917
|
-
// Build output message
|
|
918
|
-
const parts: string[] = [];
|
|
919
|
-
if (removed.clips > 0) parts.push(`${removed.clips} clips`);
|
|
920
|
-
if (removed.tracks > 0) parts.push(`${removed.tracks} tracks`);
|
|
921
|
-
if (removed.sources > 0) parts.push(`${removed.sources} sources`);
|
|
922
|
-
|
|
923
|
-
return {
|
|
924
|
-
toolCallId: "",
|
|
925
|
-
success: true,
|
|
926
|
-
output: parts.length > 0 ? `Cleared: ${parts.join(", ")}` : "Nothing to clear",
|
|
927
|
-
};
|
|
928
|
-
} catch (error) {
|
|
929
|
-
return {
|
|
930
|
-
toolCallId: "",
|
|
931
|
-
success: false,
|
|
932
|
-
output: "",
|
|
933
|
-
error: `Failed to clear timeline: ${error instanceof Error ? error.message : String(error)}`,
|
|
934
|
-
};
|
|
935
|
-
}
|
|
936
|
-
};
|
|
937
|
-
|
|
938
|
-
// Registration function
|
|
939
|
-
export function registerTimelineTools(registry: ToolRegistry): void {
|
|
940
|
-
registry.register(addSourceDef, addSource);
|
|
941
|
-
registry.register(addClipDef, addClip);
|
|
942
|
-
registry.register(addTrackDef, addTrack);
|
|
943
|
-
registry.register(addEffectDef, addEffect);
|
|
944
|
-
registry.register(trimDef, trim);
|
|
945
|
-
registry.register(splitDef, split);
|
|
946
|
-
registry.register(moveDef, move);
|
|
947
|
-
registry.register(deleteDef, deleteClip);
|
|
948
|
-
registry.register(duplicateDef, duplicate);
|
|
949
|
-
registry.register(listDef, list);
|
|
950
|
-
registry.register(clearDef, clear);
|
|
951
|
-
}
|