@vibeframe/cli 0.58.0 → 0.59.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/dist/index.js +1601 -402
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -6746,7 +6746,10 @@ var init_output = __esm({
|
|
|
6746
6746
|
"pipeline highlights": { min: 0.05, max: 1, unit: "per analysis" },
|
|
6747
6747
|
"pipeline auto-shorts": { min: 0.1, max: 2, unit: "per batch" },
|
|
6748
6748
|
"pipeline animated-caption": { min: 0.01, max: 0.1, unit: "per video" },
|
|
6749
|
-
"pipeline regenerate-scene": { min: 0.5, max: 5, unit: "per scene" }
|
|
6749
|
+
"pipeline regenerate-scene": { min: 0.5, max: 5, unit: "per scene" },
|
|
6750
|
+
// Scene composition (v0.59+) — per-beat composer call ≈ $0.06 (PR #111
|
|
6751
|
+
// pre-flight). Range covers a 1-beat preview to a 10-beat long-form.
|
|
6752
|
+
"compose scenes with skills": { min: 0.05, max: 1.5, unit: "per pipeline" }
|
|
6750
6753
|
};
|
|
6751
6754
|
}
|
|
6752
6755
|
});
|
|
@@ -14966,7 +14969,7 @@ var init_GeminiProvider = __esm({
|
|
|
14966
14969
|
* Sleep helper
|
|
14967
14970
|
*/
|
|
14968
14971
|
sleep(ms) {
|
|
14969
|
-
return new Promise((
|
|
14972
|
+
return new Promise((resolve54) => setTimeout(resolve54, ms));
|
|
14970
14973
|
}
|
|
14971
14974
|
/**
|
|
14972
14975
|
* Extend a previously generated Veo video
|
|
@@ -19053,7 +19056,7 @@ var init_RunwayProvider = __esm({
|
|
|
19053
19056
|
* Sleep helper
|
|
19054
19057
|
*/
|
|
19055
19058
|
sleep(ms) {
|
|
19056
|
-
return new Promise((
|
|
19059
|
+
return new Promise((resolve54) => setTimeout(resolve54, ms));
|
|
19057
19060
|
}
|
|
19058
19061
|
};
|
|
19059
19062
|
RunwayProvider.API_VERSION = "2024-11-06";
|
|
@@ -19485,7 +19488,7 @@ var init_KlingProvider = __esm({
|
|
|
19485
19488
|
* Sleep helper
|
|
19486
19489
|
*/
|
|
19487
19490
|
sleep(ms) {
|
|
19488
|
-
return new Promise((
|
|
19491
|
+
return new Promise((resolve54) => setTimeout(resolve54, ms));
|
|
19489
19492
|
}
|
|
19490
19493
|
};
|
|
19491
19494
|
klingProvider = new KlingProvider();
|
|
@@ -19794,7 +19797,7 @@ var init_GrokProvider = __esm({
|
|
|
19794
19797
|
}
|
|
19795
19798
|
}
|
|
19796
19799
|
sleep(ms) {
|
|
19797
|
-
return new Promise((
|
|
19800
|
+
return new Promise((resolve54) => setTimeout(resolve54, ms));
|
|
19798
19801
|
}
|
|
19799
19802
|
};
|
|
19800
19803
|
grokProvider = new GrokProvider();
|
|
@@ -19815,11 +19818,11 @@ var require_middleware = __commonJS({
|
|
|
19815
19818
|
"use strict";
|
|
19816
19819
|
var __awaiter = exports && exports.__awaiter || function(thisArg, _arguments, P, generator) {
|
|
19817
19820
|
function adopt(value) {
|
|
19818
|
-
return value instanceof P ? value : new P(function(
|
|
19819
|
-
|
|
19821
|
+
return value instanceof P ? value : new P(function(resolve54) {
|
|
19822
|
+
resolve54(value);
|
|
19820
19823
|
});
|
|
19821
19824
|
}
|
|
19822
|
-
return new (P || (P = Promise))(function(
|
|
19825
|
+
return new (P || (P = Promise))(function(resolve54, reject) {
|
|
19823
19826
|
function fulfilled(value) {
|
|
19824
19827
|
try {
|
|
19825
19828
|
step(generator.next(value));
|
|
@@ -19835,7 +19838,7 @@ var require_middleware = __commonJS({
|
|
|
19835
19838
|
}
|
|
19836
19839
|
}
|
|
19837
19840
|
function step(result) {
|
|
19838
|
-
result.done ?
|
|
19841
|
+
result.done ? resolve54(result.value) : adopt(result.value).then(fulfilled, rejected);
|
|
19839
19842
|
}
|
|
19840
19843
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
19841
19844
|
});
|
|
@@ -19920,11 +19923,11 @@ var require_response = __commonJS({
|
|
|
19920
19923
|
"use strict";
|
|
19921
19924
|
var __awaiter = exports && exports.__awaiter || function(thisArg, _arguments, P, generator) {
|
|
19922
19925
|
function adopt(value) {
|
|
19923
|
-
return value instanceof P ? value : new P(function(
|
|
19924
|
-
|
|
19926
|
+
return value instanceof P ? value : new P(function(resolve54) {
|
|
19927
|
+
resolve54(value);
|
|
19925
19928
|
});
|
|
19926
19929
|
}
|
|
19927
|
-
return new (P || (P = Promise))(function(
|
|
19930
|
+
return new (P || (P = Promise))(function(resolve54, reject) {
|
|
19928
19931
|
function fulfilled(value) {
|
|
19929
19932
|
try {
|
|
19930
19933
|
step(generator.next(value));
|
|
@@ -19940,7 +19943,7 @@ var require_response = __commonJS({
|
|
|
19940
19943
|
}
|
|
19941
19944
|
}
|
|
19942
19945
|
function step(result) {
|
|
19943
|
-
result.done ?
|
|
19946
|
+
result.done ? resolve54(result.value) : adopt(result.value).then(fulfilled, rejected);
|
|
19944
19947
|
}
|
|
19945
19948
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
19946
19949
|
});
|
|
@@ -20047,11 +20050,11 @@ var require_utils = __commonJS({
|
|
|
20047
20050
|
"use strict";
|
|
20048
20051
|
var __awaiter = exports && exports.__awaiter || function(thisArg, _arguments, P, generator) {
|
|
20049
20052
|
function adopt(value) {
|
|
20050
|
-
return value instanceof P ? value : new P(function(
|
|
20051
|
-
|
|
20053
|
+
return value instanceof P ? value : new P(function(resolve54) {
|
|
20054
|
+
resolve54(value);
|
|
20052
20055
|
});
|
|
20053
20056
|
}
|
|
20054
|
-
return new (P || (P = Promise))(function(
|
|
20057
|
+
return new (P || (P = Promise))(function(resolve54, reject) {
|
|
20055
20058
|
function fulfilled(value) {
|
|
20056
20059
|
try {
|
|
20057
20060
|
step(generator.next(value));
|
|
@@ -20067,7 +20070,7 @@ var require_utils = __commonJS({
|
|
|
20067
20070
|
}
|
|
20068
20071
|
}
|
|
20069
20072
|
function step(result) {
|
|
20070
|
-
result.done ?
|
|
20073
|
+
result.done ? resolve54(result.value) : adopt(result.value).then(fulfilled, rejected);
|
|
20071
20074
|
}
|
|
20072
20075
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
20073
20076
|
});
|
|
@@ -20160,7 +20163,7 @@ var require_utils = __commonJS({
|
|
|
20160
20163
|
}
|
|
20161
20164
|
function sleep2(ms) {
|
|
20162
20165
|
return __awaiter(this, void 0, void 0, function* () {
|
|
20163
|
-
return new Promise((
|
|
20166
|
+
return new Promise((resolve54) => setTimeout(resolve54, ms));
|
|
20164
20167
|
});
|
|
20165
20168
|
}
|
|
20166
20169
|
}
|
|
@@ -20172,11 +20175,11 @@ var require_retry = __commonJS({
|
|
|
20172
20175
|
"use strict";
|
|
20173
20176
|
var __awaiter = exports && exports.__awaiter || function(thisArg, _arguments, P, generator) {
|
|
20174
20177
|
function adopt(value) {
|
|
20175
|
-
return value instanceof P ? value : new P(function(
|
|
20176
|
-
|
|
20178
|
+
return value instanceof P ? value : new P(function(resolve54) {
|
|
20179
|
+
resolve54(value);
|
|
20177
20180
|
});
|
|
20178
20181
|
}
|
|
20179
|
-
return new (P || (P = Promise))(function(
|
|
20182
|
+
return new (P || (P = Promise))(function(resolve54, reject) {
|
|
20180
20183
|
function fulfilled(value) {
|
|
20181
20184
|
try {
|
|
20182
20185
|
step(generator.next(value));
|
|
@@ -20192,7 +20195,7 @@ var require_retry = __commonJS({
|
|
|
20192
20195
|
}
|
|
20193
20196
|
}
|
|
20194
20197
|
function step(result) {
|
|
20195
|
-
result.done ?
|
|
20198
|
+
result.done ? resolve54(result.value) : adopt(result.value).then(fulfilled, rejected);
|
|
20196
20199
|
}
|
|
20197
20200
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
20198
20201
|
});
|
|
@@ -20399,11 +20402,11 @@ var require_request = __commonJS({
|
|
|
20399
20402
|
"use strict";
|
|
20400
20403
|
var __awaiter = exports && exports.__awaiter || function(thisArg, _arguments, P, generator) {
|
|
20401
20404
|
function adopt(value) {
|
|
20402
|
-
return value instanceof P ? value : new P(function(
|
|
20403
|
-
|
|
20405
|
+
return value instanceof P ? value : new P(function(resolve54) {
|
|
20406
|
+
resolve54(value);
|
|
20404
20407
|
});
|
|
20405
20408
|
}
|
|
20406
|
-
return new (P || (P = Promise))(function(
|
|
20409
|
+
return new (P || (P = Promise))(function(resolve54, reject) {
|
|
20407
20410
|
function fulfilled(value) {
|
|
20408
20411
|
try {
|
|
20409
20412
|
step(generator.next(value));
|
|
@@ -20419,7 +20422,7 @@ var require_request = __commonJS({
|
|
|
20419
20422
|
}
|
|
20420
20423
|
}
|
|
20421
20424
|
function step(result) {
|
|
20422
|
-
result.done ?
|
|
20425
|
+
result.done ? resolve54(result.value) : adopt(result.value).then(fulfilled, rejected);
|
|
20423
20426
|
}
|
|
20424
20427
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
20425
20428
|
});
|
|
@@ -20506,11 +20509,11 @@ var require_storage = __commonJS({
|
|
|
20506
20509
|
"use strict";
|
|
20507
20510
|
var __awaiter = exports && exports.__awaiter || function(thisArg, _arguments, P, generator) {
|
|
20508
20511
|
function adopt(value) {
|
|
20509
|
-
return value instanceof P ? value : new P(function(
|
|
20510
|
-
|
|
20512
|
+
return value instanceof P ? value : new P(function(resolve54) {
|
|
20513
|
+
resolve54(value);
|
|
20511
20514
|
});
|
|
20512
20515
|
}
|
|
20513
|
-
return new (P || (P = Promise))(function(
|
|
20516
|
+
return new (P || (P = Promise))(function(resolve54, reject) {
|
|
20514
20517
|
function fulfilled(value) {
|
|
20515
20518
|
try {
|
|
20516
20519
|
step(generator.next(value));
|
|
@@ -20526,7 +20529,7 @@ var require_storage = __commonJS({
|
|
|
20526
20529
|
}
|
|
20527
20530
|
}
|
|
20528
20531
|
function step(result) {
|
|
20529
|
-
result.done ?
|
|
20532
|
+
result.done ? resolve54(result.value) : adopt(result.value).then(fulfilled, rejected);
|
|
20530
20533
|
}
|
|
20531
20534
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
20532
20535
|
});
|
|
@@ -20854,11 +20857,11 @@ var require_auth = __commonJS({
|
|
|
20854
20857
|
"use strict";
|
|
20855
20858
|
var __awaiter = exports && exports.__awaiter || function(thisArg, _arguments, P, generator) {
|
|
20856
20859
|
function adopt(value) {
|
|
20857
|
-
return value instanceof P ? value : new P(function(
|
|
20858
|
-
|
|
20860
|
+
return value instanceof P ? value : new P(function(resolve54) {
|
|
20861
|
+
resolve54(value);
|
|
20859
20862
|
});
|
|
20860
20863
|
}
|
|
20861
|
-
return new (P || (P = Promise))(function(
|
|
20864
|
+
return new (P || (P = Promise))(function(resolve54, reject) {
|
|
20862
20865
|
function fulfilled(value) {
|
|
20863
20866
|
try {
|
|
20864
20867
|
step(generator.next(value));
|
|
@@ -20874,7 +20877,7 @@ var require_auth = __commonJS({
|
|
|
20874
20877
|
}
|
|
20875
20878
|
}
|
|
20876
20879
|
function step(result) {
|
|
20877
|
-
result.done ?
|
|
20880
|
+
result.done ? resolve54(result.value) : adopt(result.value).then(fulfilled, rejected);
|
|
20878
20881
|
}
|
|
20879
20882
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
20880
20883
|
});
|
|
@@ -20913,11 +20916,11 @@ var require_streaming = __commonJS({
|
|
|
20913
20916
|
"use strict";
|
|
20914
20917
|
var __awaiter = exports && exports.__awaiter || function(thisArg, _arguments, P, generator) {
|
|
20915
20918
|
function adopt(value) {
|
|
20916
|
-
return value instanceof P ? value : new P(function(
|
|
20917
|
-
|
|
20919
|
+
return value instanceof P ? value : new P(function(resolve54) {
|
|
20920
|
+
resolve54(value);
|
|
20918
20921
|
});
|
|
20919
20922
|
}
|
|
20920
|
-
return new (P || (P = Promise))(function(
|
|
20923
|
+
return new (P || (P = Promise))(function(resolve54, reject) {
|
|
20921
20924
|
function fulfilled(value) {
|
|
20922
20925
|
try {
|
|
20923
20926
|
step(generator.next(value));
|
|
@@ -20933,7 +20936,7 @@ var require_streaming = __commonJS({
|
|
|
20933
20936
|
}
|
|
20934
20937
|
}
|
|
20935
20938
|
function step(result) {
|
|
20936
|
-
result.done ?
|
|
20939
|
+
result.done ? resolve54(result.value) : adopt(result.value).then(fulfilled, rejected);
|
|
20937
20940
|
}
|
|
20938
20941
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
20939
20942
|
});
|
|
@@ -21166,7 +21169,7 @@ var require_streaming = __commonJS({
|
|
|
21166
21169
|
query: options.queryParams
|
|
21167
21170
|
});
|
|
21168
21171
|
this.options = options;
|
|
21169
|
-
this.donePromise = new Promise((
|
|
21172
|
+
this.donePromise = new Promise((resolve54, reject) => {
|
|
21170
21173
|
if (this.streamClosed) {
|
|
21171
21174
|
reject(new response_1.ApiError({
|
|
21172
21175
|
message: "Streaming connection is already closed.",
|
|
@@ -21177,11 +21180,11 @@ var require_streaming = __commonJS({
|
|
|
21177
21180
|
}
|
|
21178
21181
|
this.signal.addEventListener("abort", () => {
|
|
21179
21182
|
var _a2;
|
|
21180
|
-
|
|
21183
|
+
resolve54((_a2 = this.currentData) !== null && _a2 !== void 0 ? _a2 : {});
|
|
21181
21184
|
});
|
|
21182
21185
|
this.on("done", (data) => {
|
|
21183
21186
|
this.streamClosed = true;
|
|
21184
|
-
|
|
21187
|
+
resolve54(data);
|
|
21185
21188
|
});
|
|
21186
21189
|
this.on("error", (error) => {
|
|
21187
21190
|
this.streamClosed = true;
|
|
@@ -21206,7 +21209,7 @@ var require_streaming = __commonJS({
|
|
|
21206
21209
|
if (data) {
|
|
21207
21210
|
yield yield __await(data);
|
|
21208
21211
|
}
|
|
21209
|
-
yield __await(new Promise((
|
|
21212
|
+
yield __await(new Promise((resolve54) => setTimeout(resolve54, 16)));
|
|
21210
21213
|
}
|
|
21211
21214
|
});
|
|
21212
21215
|
}
|
|
@@ -21251,11 +21254,11 @@ var require_queue = __commonJS({
|
|
|
21251
21254
|
"use strict";
|
|
21252
21255
|
var __awaiter = exports && exports.__awaiter || function(thisArg, _arguments, P, generator) {
|
|
21253
21256
|
function adopt(value) {
|
|
21254
|
-
return value instanceof P ? value : new P(function(
|
|
21255
|
-
|
|
21257
|
+
return value instanceof P ? value : new P(function(resolve54) {
|
|
21258
|
+
resolve54(value);
|
|
21256
21259
|
});
|
|
21257
21260
|
}
|
|
21258
|
-
return new (P || (P = Promise))(function(
|
|
21261
|
+
return new (P || (P = Promise))(function(resolve54, reject) {
|
|
21259
21262
|
function fulfilled(value) {
|
|
21260
21263
|
try {
|
|
21261
21264
|
step(generator.next(value));
|
|
@@ -21271,7 +21274,7 @@ var require_queue = __commonJS({
|
|
|
21271
21274
|
}
|
|
21272
21275
|
}
|
|
21273
21276
|
function step(result) {
|
|
21274
|
-
result.done ?
|
|
21277
|
+
result.done ? resolve54(result.value) : adopt(result.value).then(fulfilled, rejected);
|
|
21275
21278
|
}
|
|
21276
21279
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
21277
21280
|
});
|
|
@@ -21406,7 +21409,7 @@ var require_queue = __commonJS({
|
|
|
21406
21409
|
}
|
|
21407
21410
|
return doneStatus;
|
|
21408
21411
|
}
|
|
21409
|
-
return new Promise((
|
|
21412
|
+
return new Promise((resolve54, reject) => {
|
|
21410
21413
|
var _a;
|
|
21411
21414
|
let pollingTimeoutId;
|
|
21412
21415
|
const pollInterval = "pollInterval" in options && typeof options.pollInterval === "number" ? (_a = options.pollInterval) !== null && _a !== void 0 ? _a : DEFAULT_POLL_INTERVAL : DEFAULT_POLL_INTERVAL;
|
|
@@ -21438,7 +21441,7 @@ var require_queue = __commonJS({
|
|
|
21438
21441
|
}
|
|
21439
21442
|
if (requestStatus.status === "COMPLETED") {
|
|
21440
21443
|
clearScheduledTasks();
|
|
21441
|
-
|
|
21444
|
+
resolve54(requestStatus);
|
|
21442
21445
|
return;
|
|
21443
21446
|
}
|
|
21444
21447
|
pollingTimeoutId = setTimeout(poll, pollInterval);
|
|
@@ -23399,11 +23402,11 @@ var require_realtime = __commonJS({
|
|
|
23399
23402
|
"use strict";
|
|
23400
23403
|
var __awaiter = exports && exports.__awaiter || function(thisArg, _arguments, P, generator) {
|
|
23401
23404
|
function adopt(value) {
|
|
23402
|
-
return value instanceof P ? value : new P(function(
|
|
23403
|
-
|
|
23405
|
+
return value instanceof P ? value : new P(function(resolve54) {
|
|
23406
|
+
resolve54(value);
|
|
23404
23407
|
});
|
|
23405
23408
|
}
|
|
23406
|
-
return new (P || (P = Promise))(function(
|
|
23409
|
+
return new (P || (P = Promise))(function(resolve54, reject) {
|
|
23407
23410
|
function fulfilled(value) {
|
|
23408
23411
|
try {
|
|
23409
23412
|
step(generator.next(value));
|
|
@@ -23419,7 +23422,7 @@ var require_realtime = __commonJS({
|
|
|
23419
23422
|
}
|
|
23420
23423
|
}
|
|
23421
23424
|
function step(result) {
|
|
23422
|
-
result.done ?
|
|
23425
|
+
result.done ? resolve54(result.value) : adopt(result.value).then(fulfilled, rejected);
|
|
23423
23426
|
}
|
|
23424
23427
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
23425
23428
|
});
|
|
@@ -23744,11 +23747,11 @@ var require_client = __commonJS({
|
|
|
23744
23747
|
"use strict";
|
|
23745
23748
|
var __awaiter = exports && exports.__awaiter || function(thisArg, _arguments, P, generator) {
|
|
23746
23749
|
function adopt(value) {
|
|
23747
|
-
return value instanceof P ? value : new P(function(
|
|
23748
|
-
|
|
23750
|
+
return value instanceof P ? value : new P(function(resolve54) {
|
|
23751
|
+
resolve54(value);
|
|
23749
23752
|
});
|
|
23750
23753
|
}
|
|
23751
|
-
return new (P || (P = Promise))(function(
|
|
23754
|
+
return new (P || (P = Promise))(function(resolve54, reject) {
|
|
23752
23755
|
function fulfilled(value) {
|
|
23753
23756
|
try {
|
|
23754
23757
|
step(generator.next(value));
|
|
@@ -23764,7 +23767,7 @@ var require_client = __commonJS({
|
|
|
23764
23767
|
}
|
|
23765
23768
|
}
|
|
23766
23769
|
function step(result) {
|
|
23767
|
-
result.done ?
|
|
23770
|
+
result.done ? resolve54(result.value) : adopt(result.value).then(fulfilled, rejected);
|
|
23768
23771
|
}
|
|
23769
23772
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
23770
23773
|
});
|
|
@@ -24329,7 +24332,7 @@ var init_ReplicateProvider = __esm({
|
|
|
24329
24332
|
* Sleep helper
|
|
24330
24333
|
*/
|
|
24331
24334
|
sleep(ms) {
|
|
24332
|
-
return new Promise((
|
|
24335
|
+
return new Promise((resolve54) => setTimeout(resolve54, ms));
|
|
24333
24336
|
}
|
|
24334
24337
|
/**
|
|
24335
24338
|
* Generate music from text prompt using MusicGen
|
|
@@ -25315,7 +25318,7 @@ async function prompt(question, hidden = false) {
|
|
|
25315
25318
|
input: process.stdin,
|
|
25316
25319
|
output: process.stdout
|
|
25317
25320
|
});
|
|
25318
|
-
return new Promise((
|
|
25321
|
+
return new Promise((resolve54) => {
|
|
25319
25322
|
if (hidden && process.stdin.isTTY) {
|
|
25320
25323
|
process.stdout.write(question);
|
|
25321
25324
|
let input = "";
|
|
@@ -25329,7 +25332,7 @@ async function prompt(question, hidden = false) {
|
|
|
25329
25332
|
process.stdin.removeListener("data", onData);
|
|
25330
25333
|
process.stdout.write("\n");
|
|
25331
25334
|
rl.close();
|
|
25332
|
-
|
|
25335
|
+
resolve54(input);
|
|
25333
25336
|
} else if (char === "") {
|
|
25334
25337
|
process.exit(1);
|
|
25335
25338
|
} else if (char === "\x7F" || char === "\b") {
|
|
@@ -25344,7 +25347,7 @@ async function prompt(question, hidden = false) {
|
|
|
25344
25347
|
} else {
|
|
25345
25348
|
rl.question(question, (answer) => {
|
|
25346
25349
|
rl.close();
|
|
25347
|
-
|
|
25350
|
+
resolve54(answer);
|
|
25348
25351
|
});
|
|
25349
25352
|
}
|
|
25350
25353
|
});
|
|
@@ -26088,31 +26091,6 @@ function fitKineticFontSize(words) {
|
|
|
26088
26091
|
function kenBurnsTween(scope, dur, endScale = 1.08) {
|
|
26089
26092
|
return `tl.fromTo('${scope} .backdrop', { scale: 1.0 }, { scale: ${endScale}, duration: ${dur.toFixed(2)}, ease: 'none' }, 0);`;
|
|
26090
26093
|
}
|
|
26091
|
-
function idleHeroPulse(scope, selector, idleStart, dur) {
|
|
26092
|
-
const cycleDur = 1.5;
|
|
26093
|
-
const idleEnd = dur - SCENE_OVERLAP_SECONDS;
|
|
26094
|
-
const window2 = idleEnd - idleStart;
|
|
26095
|
-
if (window2 < cycleDur) return "";
|
|
26096
|
-
const plays = Math.max(1, Math.floor(window2 / cycleDur));
|
|
26097
|
-
const repeat = plays - 1;
|
|
26098
|
-
return `tl.to('${scope} ${selector}', { scale: 1.04, y: -8, duration: ${cycleDur}, yoyo: true, repeat: ${repeat}, ease: 'sine.inOut' }, ${idleStart.toFixed(2)});`;
|
|
26099
|
-
}
|
|
26100
|
-
function kineticWordBobs(scope, wordCount, idleStart, dur) {
|
|
26101
|
-
const cycleDur = 1.8;
|
|
26102
|
-
const idleEnd = dur - SCENE_OVERLAP_SECONDS;
|
|
26103
|
-
const window2 = idleEnd - idleStart;
|
|
26104
|
-
if (window2 < cycleDur) return "";
|
|
26105
|
-
const plays = Math.max(1, Math.floor(window2 / cycleDur));
|
|
26106
|
-
const repeat = plays - 1;
|
|
26107
|
-
const lines = [];
|
|
26108
|
-
for (let i = 0; i < wordCount; i++) {
|
|
26109
|
-
const start = (idleStart + i * 0.18).toFixed(2);
|
|
26110
|
-
lines.push(
|
|
26111
|
-
`tl.to('${scope} #w-${i}', { y: -12, duration: ${cycleDur}, yoyo: true, repeat: ${repeat}, ease: 'sine.inOut' }, ${start});`
|
|
26112
|
-
);
|
|
26113
|
-
}
|
|
26114
|
-
return lines.join("\n ");
|
|
26115
|
-
}
|
|
26116
26094
|
function buildPreset(input) {
|
|
26117
26095
|
const id = input.id;
|
|
26118
26096
|
const scope = `[data-composition-id="${id}"]`;
|
|
@@ -26173,8 +26151,7 @@ function buildPreset(input) {
|
|
|
26173
26151
|
<div class="caption" id="caption">${captionInner}</div>`,
|
|
26174
26152
|
timeline: `${crossfadeTweens(scope, dur)}
|
|
26175
26153
|
${kenBurnsTween(scope, dur)}
|
|
26176
|
-
${captionTween}
|
|
26177
|
-
${idleHeroPulse(scope, "#caption", 1, dur)}`
|
|
26154
|
+
${captionTween}`
|
|
26178
26155
|
};
|
|
26179
26156
|
}
|
|
26180
26157
|
case "announcement": {
|
|
@@ -26213,8 +26190,7 @@ function buildPreset(input) {
|
|
|
26213
26190
|
// Ken-Burns fills what used to be 80% dead static frame.
|
|
26214
26191
|
timeline: `${crossfadeTweens(scope, dur)}
|
|
26215
26192
|
${kenBurnsTween(scope, dur)}
|
|
26216
|
-
tl.from('${scope} #headline', { opacity: 0, scale: 0.92, duration: 0.55, ease: 'power3.out' }, 0.05)
|
|
26217
|
-
${idleHeroPulse(scope, "#headline", 1, dur)}`
|
|
26193
|
+
tl.from('${scope} #headline', { opacity: 0, scale: 0.92, duration: 0.55, ease: 'power3.out' }, 0.05);`
|
|
26218
26194
|
};
|
|
26219
26195
|
}
|
|
26220
26196
|
case "explainer": {
|
|
@@ -26263,8 +26239,7 @@ function buildPreset(input) {
|
|
|
26263
26239
|
${kenBurnsTween(scope, dur)}
|
|
26264
26240
|
tl.from('${scope} #kicker', { opacity: 0, y: 16, duration: 0.4, ease: 'power2.out' }, 0.1);
|
|
26265
26241
|
tl.from('${scope} #title', { opacity: 0, y: 60, duration: 0.7, ease: 'power3.out' }, 0.25);
|
|
26266
|
-
${subtitleTween}
|
|
26267
|
-
${idleHeroPulse(scope, "#title", 1.2, dur)}`
|
|
26242
|
+
${subtitleTween}`
|
|
26268
26243
|
};
|
|
26269
26244
|
}
|
|
26270
26245
|
case "kinetic-type": {
|
|
@@ -26281,8 +26256,6 @@ function buildPreset(input) {
|
|
|
26281
26256
|
return `tl.from('${scope} #w-${i}', { opacity: 0, y: 80, scale: 0.8, duration: 0.45, ease: 'back.out(1.8)' }, ${start});`;
|
|
26282
26257
|
}).join("\n ");
|
|
26283
26258
|
const kineticFontPx = fitKineticFontSize(words);
|
|
26284
|
-
const lastWordEntryEnd = useWordSync ? Math.max(...transcript.map((w) => w.end + 0.2)) : 0.05 + (words.length - 1) * stagger + 0.45 + 0.2;
|
|
26285
|
-
const idleStart = Math.max(1, Number(lastWordEntryEnd.toFixed(2)));
|
|
26286
26259
|
return {
|
|
26287
26260
|
css: `${scope} {
|
|
26288
26261
|
position: absolute; inset: 0; width: 100%; height: 100%;
|
|
@@ -26305,8 +26278,7 @@ function buildPreset(input) {
|
|
|
26305
26278
|
<div class="kinetic">${wordSpans}</div>`,
|
|
26306
26279
|
timeline: `${crossfadeTweens(scope, dur)}
|
|
26307
26280
|
${kenBurnsTween(scope, dur)}
|
|
26308
|
-
${tweens}
|
|
26309
|
-
${kineticWordBobs(scope, words.length, idleStart, dur)}`
|
|
26281
|
+
${tweens}`
|
|
26310
26282
|
};
|
|
26311
26283
|
}
|
|
26312
26284
|
case "product-shot": {
|
|
@@ -26347,8 +26319,7 @@ function buildPreset(input) {
|
|
|
26347
26319
|
tl.fromTo('${scope} .backdrop', { scale: 1.0 }, { scale: 1.12, duration: ${dur.toFixed(2)}, ease: 'none' }, 0);
|
|
26348
26320
|
tl.from('${scope} #label', { opacity: 0, x: -30, duration: 0.5, ease: 'power3.out' }, 0.2);
|
|
26349
26321
|
tl.from('${scope} #headline', { opacity: 0, y: 40, duration: 0.6, ease: 'power3.out' }, 0.4);${subhead ? `
|
|
26350
|
-
tl.from('${scope} #subhead', { opacity: 0, y: 20, duration: 0.5, ease: 'power3.out' }, 0.65);` : ""}
|
|
26351
|
-
${idleHeroPulse(scope, "#headline", 1.2, dur)}`
|
|
26322
|
+
tl.from('${scope} #subhead', { opacity: 0, y: 20, duration: 0.5, ease: 'power3.out' }, 0.65);` : ""}`
|
|
26352
26323
|
};
|
|
26353
26324
|
}
|
|
26354
26325
|
}
|
|
@@ -28820,16 +28791,16 @@ async function prompt2(question) {
|
|
|
28820
28791
|
input,
|
|
28821
28792
|
output: process.stdout
|
|
28822
28793
|
});
|
|
28823
|
-
return new Promise((
|
|
28794
|
+
return new Promise((resolve54) => {
|
|
28824
28795
|
rl.question(question, (answer) => {
|
|
28825
28796
|
rl.close();
|
|
28826
|
-
|
|
28797
|
+
resolve54(answer);
|
|
28827
28798
|
});
|
|
28828
28799
|
});
|
|
28829
28800
|
}
|
|
28830
28801
|
async function promptHidden(question) {
|
|
28831
28802
|
const input = getTTYInputStream();
|
|
28832
|
-
return new Promise((
|
|
28803
|
+
return new Promise((resolve54) => {
|
|
28833
28804
|
process.stdout.write(question);
|
|
28834
28805
|
let value = "";
|
|
28835
28806
|
if (typeof input.setRawMode === "function") {
|
|
@@ -28841,7 +28812,7 @@ async function promptHidden(question) {
|
|
|
28841
28812
|
input.setRawMode(false);
|
|
28842
28813
|
input.removeListener("data", onData);
|
|
28843
28814
|
process.stdout.write("\n");
|
|
28844
|
-
|
|
28815
|
+
resolve54(value);
|
|
28845
28816
|
} else if (char === "") {
|
|
28846
28817
|
input.setRawMode(false);
|
|
28847
28818
|
process.stdout.write("\n");
|
|
@@ -28861,7 +28832,7 @@ async function promptHidden(question) {
|
|
|
28861
28832
|
const rl = createInterface2({ input, output: process.stdout });
|
|
28862
28833
|
rl.question("", (answer) => {
|
|
28863
28834
|
rl.close();
|
|
28864
|
-
|
|
28835
|
+
resolve54(answer);
|
|
28865
28836
|
});
|
|
28866
28837
|
}
|
|
28867
28838
|
});
|
|
@@ -28869,7 +28840,7 @@ async function promptHidden(question) {
|
|
|
28869
28840
|
async function promptSelect(_question, options, defaultIndex = 0) {
|
|
28870
28841
|
const input = getTTYInputStream();
|
|
28871
28842
|
if (typeof input.setRawMode === "function") {
|
|
28872
|
-
return new Promise((
|
|
28843
|
+
return new Promise((resolve54) => {
|
|
28873
28844
|
let selected = defaultIndex;
|
|
28874
28845
|
const render = () => {
|
|
28875
28846
|
if (renderCount > 0) {
|
|
@@ -28893,7 +28864,7 @@ async function promptSelect(_question, options, defaultIndex = 0) {
|
|
|
28893
28864
|
input.setRawMode(false);
|
|
28894
28865
|
input.removeListener("data", onData);
|
|
28895
28866
|
process.stdout.write("\n");
|
|
28896
|
-
|
|
28867
|
+
resolve54(selected);
|
|
28897
28868
|
} else if (char === "") {
|
|
28898
28869
|
input.setRawMode(false);
|
|
28899
28870
|
process.stdout.write("\n");
|
|
@@ -28910,7 +28881,7 @@ async function promptSelect(_question, options, defaultIndex = 0) {
|
|
|
28910
28881
|
input.removeListener("data", onData);
|
|
28911
28882
|
render();
|
|
28912
28883
|
process.stdout.write("\n");
|
|
28913
|
-
|
|
28884
|
+
resolve54(selected);
|
|
28914
28885
|
}
|
|
28915
28886
|
};
|
|
28916
28887
|
input.on("data", onData);
|
|
@@ -29080,13 +29051,10 @@ var init_subtitle = __esm({
|
|
|
29080
29051
|
// src/utils/remotion.ts
|
|
29081
29052
|
var remotion_exports = {};
|
|
29082
29053
|
__export(remotion_exports, {
|
|
29083
|
-
compositeOverlay: () => compositeOverlay,
|
|
29084
|
-
compositeWithImage: () => compositeWithImage,
|
|
29085
29054
|
ensureRemotionInstalled: () => ensureRemotionInstalled,
|
|
29086
29055
|
generateAnimatedCaptionComponent: () => generateAnimatedCaptionComponent,
|
|
29087
29056
|
generateCaptionComponent: () => generateCaptionComponent,
|
|
29088
29057
|
generateTextOverlayComponent: () => generateTextOverlayComponent,
|
|
29089
|
-
renderAndComposite: () => renderAndComposite,
|
|
29090
29058
|
renderMotion: () => renderMotion,
|
|
29091
29059
|
renderWithEmbeddedImage: () => renderWithEmbeddedImage,
|
|
29092
29060
|
renderWithEmbeddedVideo: () => renderWithEmbeddedVideo,
|
|
@@ -29834,92 +29802,6 @@ export const ${name} = () => {
|
|
|
29834
29802
|
`;
|
|
29835
29803
|
return { code, name };
|
|
29836
29804
|
}
|
|
29837
|
-
async function compositeOverlay(options) {
|
|
29838
|
-
try {
|
|
29839
|
-
await execSafe("ffmpeg", [
|
|
29840
|
-
"-y",
|
|
29841
|
-
"-i",
|
|
29842
|
-
options.baseVideo,
|
|
29843
|
-
"-i",
|
|
29844
|
-
options.overlayPath,
|
|
29845
|
-
"-filter_complex",
|
|
29846
|
-
"[0:v][1:v]overlay=0:0:shortest=1[out]",
|
|
29847
|
-
"-map",
|
|
29848
|
-
"[out]",
|
|
29849
|
-
"-map",
|
|
29850
|
-
"0:a?",
|
|
29851
|
-
"-c:a",
|
|
29852
|
-
"copy",
|
|
29853
|
-
"-c:v",
|
|
29854
|
-
"libx264",
|
|
29855
|
-
"-crf",
|
|
29856
|
-
"18",
|
|
29857
|
-
"-pix_fmt",
|
|
29858
|
-
"yuv420p",
|
|
29859
|
-
options.outputPath
|
|
29860
|
-
], { timeout: 3e5 });
|
|
29861
|
-
return { success: true, outputPath: options.outputPath };
|
|
29862
|
-
} catch (error) {
|
|
29863
|
-
const msg = error instanceof Error ? error.message : String(error);
|
|
29864
|
-
return { success: false, error: `FFmpeg composite failed: ${msg}` };
|
|
29865
|
-
}
|
|
29866
|
-
}
|
|
29867
|
-
async function compositeWithImage(options) {
|
|
29868
|
-
try {
|
|
29869
|
-
const fps = options.fps || 30;
|
|
29870
|
-
await execSafe("ffmpeg", [
|
|
29871
|
-
"-y",
|
|
29872
|
-
"-loop",
|
|
29873
|
-
"1",
|
|
29874
|
-
"-framerate",
|
|
29875
|
-
String(fps),
|
|
29876
|
-
"-i",
|
|
29877
|
-
options.baseImage,
|
|
29878
|
-
"-i",
|
|
29879
|
-
options.overlayPath,
|
|
29880
|
-
"-filter_complex",
|
|
29881
|
-
"[0:v]scale=iw:ih[base];[base][1:v]overlay=0:0:shortest=1[out]",
|
|
29882
|
-
"-map",
|
|
29883
|
-
"[out]",
|
|
29884
|
-
"-c:v",
|
|
29885
|
-
"libx264",
|
|
29886
|
-
"-crf",
|
|
29887
|
-
"18",
|
|
29888
|
-
"-pix_fmt",
|
|
29889
|
-
"yuv420p",
|
|
29890
|
-
"-t",
|
|
29891
|
-
String(options.durationSeconds),
|
|
29892
|
-
options.outputPath
|
|
29893
|
-
], { timeout: 3e5 });
|
|
29894
|
-
return { success: true, outputPath: options.outputPath };
|
|
29895
|
-
} catch (error) {
|
|
29896
|
-
const msg = error instanceof Error ? error.message : String(error);
|
|
29897
|
-
return { success: false, error: `FFmpeg image composite failed: ${msg}` };
|
|
29898
|
-
}
|
|
29899
|
-
}
|
|
29900
|
-
async function renderAndComposite(motionOpts, baseVideo, finalOutput) {
|
|
29901
|
-
const renderOpts = {
|
|
29902
|
-
...motionOpts,
|
|
29903
|
-
transparent: !!baseVideo,
|
|
29904
|
-
outputPath: baseVideo ? motionOpts.outputPath.replace(/\.\w+$/, "_overlay.webm") : motionOpts.outputPath
|
|
29905
|
-
};
|
|
29906
|
-
const renderResult = await renderMotion(renderOpts);
|
|
29907
|
-
if (!renderResult.success || !renderResult.outputPath) {
|
|
29908
|
-
return renderResult;
|
|
29909
|
-
}
|
|
29910
|
-
if (!baseVideo) {
|
|
29911
|
-
return renderResult;
|
|
29912
|
-
}
|
|
29913
|
-
const output = finalOutput || motionOpts.outputPath;
|
|
29914
|
-
const compositeResult = await compositeOverlay({
|
|
29915
|
-
baseVideo,
|
|
29916
|
-
overlayPath: renderResult.outputPath,
|
|
29917
|
-
outputPath: output
|
|
29918
|
-
});
|
|
29919
|
-
await rm(renderResult.outputPath, { force: true }).catch(() => {
|
|
29920
|
-
});
|
|
29921
|
-
return compositeResult;
|
|
29922
|
-
}
|
|
29923
29805
|
var REMOTION_VERSION, REMOTION_CACHE_DIR;
|
|
29924
29806
|
var init_remotion = __esm({
|
|
29925
29807
|
"src/utils/remotion.ts"() {
|
|
@@ -31418,7 +31300,7 @@ import { readFile as readFile13, writeFile as writeFile11, mkdir as mkdir8, unli
|
|
|
31418
31300
|
import { resolve as resolve15, basename as basename8, dirname as dirname6, extname as extname4 } from "node:path";
|
|
31419
31301
|
import { existsSync as existsSync9 } from "node:fs";
|
|
31420
31302
|
function sleep(ms) {
|
|
31421
|
-
return new Promise((
|
|
31303
|
+
return new Promise((resolve54) => setTimeout(resolve54, ms));
|
|
31422
31304
|
}
|
|
31423
31305
|
async function uploadToImgbb(imageBuffer, apiKey) {
|
|
31424
31306
|
try {
|
|
@@ -31783,7 +31665,6 @@ async function executeScriptToVideo(options) {
|
|
|
31783
31665
|
outputDir: absOutputDir,
|
|
31784
31666
|
scenes: segments.length,
|
|
31785
31667
|
storyboardPath,
|
|
31786
|
-
narrations: [],
|
|
31787
31668
|
narrationEntries: [],
|
|
31788
31669
|
images: [],
|
|
31789
31670
|
videos: [],
|
|
@@ -31844,7 +31725,6 @@ async function executeScriptToVideo(options) {
|
|
|
31844
31725
|
);
|
|
31845
31726
|
}
|
|
31846
31727
|
segment.duration = actualDuration;
|
|
31847
|
-
result.narrations.push(audioPath);
|
|
31848
31728
|
result.narrationEntries.push({
|
|
31849
31729
|
path: audioPath,
|
|
31850
31730
|
duration: actualDuration,
|
|
@@ -32827,6 +32707,34 @@ var init_provider_resolver = __esm({
|
|
|
32827
32707
|
}
|
|
32828
32708
|
});
|
|
32829
32709
|
|
|
32710
|
+
// src/commands/_shared/openai-image.ts
|
|
32711
|
+
function resolveOpenAIImageModel(modelAlias) {
|
|
32712
|
+
const isGptImage2 = modelAlias === "2" || modelAlias === "gpt-image-2";
|
|
32713
|
+
return {
|
|
32714
|
+
openaiModel: isGptImage2 ? "gpt-image-2" : void 0,
|
|
32715
|
+
modelLabel: isGptImage2 ? "GPT Image 2" : "GPT Image 1.5"
|
|
32716
|
+
};
|
|
32717
|
+
}
|
|
32718
|
+
async function executeOpenAIImageGenerate(prompt3, options, ctx) {
|
|
32719
|
+
const provider = new OpenAIImageProvider();
|
|
32720
|
+
await provider.initialize({ apiKey: ctx.apiKey });
|
|
32721
|
+
const { openaiModel, modelLabel } = resolveOpenAIImageModel(options.model);
|
|
32722
|
+
const result = await provider.generateImage(prompt3, {
|
|
32723
|
+
model: openaiModel,
|
|
32724
|
+
size: options.size,
|
|
32725
|
+
quality: options.quality,
|
|
32726
|
+
style: options.style,
|
|
32727
|
+
n: options.count !== void 0 ? parseInt(options.count, 10) : void 0
|
|
32728
|
+
});
|
|
32729
|
+
return { result, openaiModel, modelLabel };
|
|
32730
|
+
}
|
|
32731
|
+
var init_openai_image2 = __esm({
|
|
32732
|
+
"src/commands/_shared/openai-image.ts"() {
|
|
32733
|
+
"use strict";
|
|
32734
|
+
init_dist2();
|
|
32735
|
+
}
|
|
32736
|
+
});
|
|
32737
|
+
|
|
32830
32738
|
// src/commands/ai-image.ts
|
|
32831
32739
|
var ai_image_exports = {};
|
|
32832
32740
|
__export(ai_image_exports, {
|
|
@@ -32879,22 +32787,11 @@ function _registerImageCommands(aiCommand) {
|
|
|
32879
32787
|
}
|
|
32880
32788
|
const spinner2 = ora(`Generating image with ${providerName}...`).start();
|
|
32881
32789
|
if (provider === "dalle" || provider === "openai") {
|
|
32882
|
-
const
|
|
32883
|
-
await openaiImage.initialize({ apiKey });
|
|
32884
|
-
const modelAlias = options.model;
|
|
32885
|
-
const openaiModel = modelAlias === "2" || modelAlias === "gpt-image-2" ? "gpt-image-2" : void 0;
|
|
32886
|
-
const result = await openaiImage.generateImage(prompt3, {
|
|
32887
|
-
model: openaiModel,
|
|
32888
|
-
size: options.size,
|
|
32889
|
-
quality: options.quality,
|
|
32890
|
-
style: options.style,
|
|
32891
|
-
n: parseInt(options.count)
|
|
32892
|
-
});
|
|
32790
|
+
const { result, modelLabel } = await executeOpenAIImageGenerate(prompt3, options, { apiKey });
|
|
32893
32791
|
if (!result.success || !result.images) {
|
|
32894
32792
|
spinner2.fail("Image generation failed");
|
|
32895
32793
|
exitWithError(apiError(result.error || "Image generation failed", true));
|
|
32896
32794
|
}
|
|
32897
|
-
const modelLabel = openaiModel === "gpt-image-2" ? "GPT Image 2" : "GPT Image 1.5";
|
|
32898
32795
|
spinner2.succeed(source_default.green(`Generated ${result.images.length} image(s) with OpenAI ${modelLabel}`));
|
|
32899
32796
|
console.log();
|
|
32900
32797
|
console.log(source_default.bold.cyan("Generated Images"));
|
|
@@ -33581,6 +33478,7 @@ var init_ai_image = __esm({
|
|
|
33581
33478
|
init_exec_safe();
|
|
33582
33479
|
init_output();
|
|
33583
33480
|
init_validate();
|
|
33481
|
+
init_openai_image2();
|
|
33584
33482
|
}
|
|
33585
33483
|
});
|
|
33586
33484
|
|
|
@@ -34021,6 +33919,7 @@ var init_generate = __esm({
|
|
|
34021
33919
|
init_provider_resolver();
|
|
34022
33920
|
init_ai_image();
|
|
34023
33921
|
init_ai_motion();
|
|
33922
|
+
init_openai_image2();
|
|
34024
33923
|
generateCommand = new Command("generate").alias("gen").description(
|
|
34025
33924
|
"Generate assets using AI (images, videos, speech, music, motion)"
|
|
34026
33925
|
).addHelpText(
|
|
@@ -34120,22 +34019,11 @@ Examples:
|
|
|
34120
34019
|
const apiKey = await requireApiKey(envKey, providerName, options.apiKey);
|
|
34121
34020
|
const spinner2 = ora(`Generating image with ${providerName}...`).start();
|
|
34122
34021
|
if (provider === "dalle" || provider === "openai") {
|
|
34123
|
-
const
|
|
34124
|
-
await openaiImage.initialize({ apiKey });
|
|
34125
|
-
const modelAlias = options.model;
|
|
34126
|
-
const openaiModel = modelAlias === "2" || modelAlias === "gpt-image-2" ? "gpt-image-2" : void 0;
|
|
34127
|
-
const result = await openaiImage.generateImage(prompt3, {
|
|
34128
|
-
model: openaiModel,
|
|
34129
|
-
size: options.size,
|
|
34130
|
-
quality: options.quality,
|
|
34131
|
-
style: options.style,
|
|
34132
|
-
n: parseInt(options.count)
|
|
34133
|
-
});
|
|
34022
|
+
const { result, modelLabel } = await executeOpenAIImageGenerate(prompt3, options, { apiKey });
|
|
34134
34023
|
if (!result.success || !result.images) {
|
|
34135
34024
|
spinner2.fail(result.error || "Image generation failed");
|
|
34136
34025
|
exitWithError(apiError(result.error || "Image generation failed", true));
|
|
34137
34026
|
}
|
|
34138
|
-
const modelLabel = openaiModel === "gpt-image-2" ? "GPT Image 2" : "GPT Image 1.5";
|
|
34139
34027
|
spinner2.succeed(source_default.green(`Generated ${result.images.length} image(s) with OpenAI ${modelLabel}`));
|
|
34140
34028
|
if (isJsonMode()) {
|
|
34141
34029
|
const outputPath = options.output ? resolve18(process.cwd(), options.output) : void 0;
|
|
@@ -49201,7 +49089,7 @@ function buildFFmpegArgs(clips, sources, presetSettings, outputPath, options, so
|
|
|
49201
49089
|
return args;
|
|
49202
49090
|
}
|
|
49203
49091
|
function runFFmpegProcess(ffmpegPath, args, onProgress) {
|
|
49204
|
-
return new Promise((
|
|
49092
|
+
return new Promise((resolve54, reject) => {
|
|
49205
49093
|
const ffmpeg = spawn(ffmpegPath, args, {
|
|
49206
49094
|
stdio: ["pipe", "pipe", "pipe"]
|
|
49207
49095
|
});
|
|
@@ -49225,7 +49113,7 @@ function runFFmpegProcess(ffmpegPath, args, onProgress) {
|
|
|
49225
49113
|
});
|
|
49226
49114
|
ffmpeg.on("close", (code) => {
|
|
49227
49115
|
if (code === 0) {
|
|
49228
|
-
|
|
49116
|
+
resolve54();
|
|
49229
49117
|
} else {
|
|
49230
49118
|
const errorMatch = stderr.match(/Error.*$/m);
|
|
49231
49119
|
const errorMsg = errorMatch ? errorMatch[0] : `FFmpeg exited with code ${code}`;
|
|
@@ -49361,17 +49249,17 @@ function getPresetSettings(preset, aspectRatio) {
|
|
|
49361
49249
|
}
|
|
49362
49250
|
async function runHyperframesExport(projectPath, options, spinner2) {
|
|
49363
49251
|
spinner2.text = "Loading project...";
|
|
49364
|
-
const { readFile:
|
|
49365
|
-
const { resolve:
|
|
49252
|
+
const { readFile: readFile44 } = await import("node:fs/promises");
|
|
49253
|
+
const { resolve: resolve54, basename: basename28 } = await import("node:path");
|
|
49366
49254
|
const { Project: Project2 } = await Promise.resolve().then(() => (init_engine(), engine_exports));
|
|
49367
49255
|
const { createHyperframesBackend: createHyperframesBackend2 } = await Promise.resolve().then(() => (init_hyperframes(), hyperframes_exports));
|
|
49368
49256
|
const { exitWithError: exitWithError2, generalError: generalError2, outputResult: outputResult2 } = await Promise.resolve().then(() => (init_output(), output_exports));
|
|
49369
49257
|
const chalk2 = (await Promise.resolve().then(() => (init_source(), source_exports))).default;
|
|
49370
|
-
const filePath =
|
|
49371
|
-
const content = await
|
|
49258
|
+
const filePath = resolve54(process.cwd(), projectPath);
|
|
49259
|
+
const content = await readFile44(filePath, "utf-8");
|
|
49372
49260
|
const project = Project2.fromJSON(JSON.parse(content));
|
|
49373
49261
|
const state = project.getState();
|
|
49374
|
-
const outputPath = options.output ?
|
|
49262
|
+
const outputPath = options.output ? resolve54(process.cwd(), options.output) : resolve54(process.cwd(), `${basename28(projectPath, ".vibe.json")}.${options.format ?? "mp4"}`);
|
|
49375
49263
|
const quality = ["draft", "standard", "high"].includes(options.preset ?? "") ? options.preset : "standard";
|
|
49376
49264
|
const backend = createHyperframesBackend2();
|
|
49377
49265
|
spinner2.text = "Rendering with Hyperframes...";
|
|
@@ -51900,6 +51788,1295 @@ var init_ai_audio = __esm({
|
|
|
51900
51788
|
}
|
|
51901
51789
|
});
|
|
51902
51790
|
|
|
51791
|
+
// src/commands/_shared/hf-skill-bundle/bundle-content.ts
|
|
51792
|
+
var SKILL_MD, HOUSE_STYLE_MD, MOTION_PRINCIPLES_MD, TYPOGRAPHY_MD, TRANSITIONS_MD;
|
|
51793
|
+
var init_bundle_content = __esm({
|
|
51794
|
+
"src/commands/_shared/hf-skill-bundle/bundle-content.ts"() {
|
|
51795
|
+
"use strict";
|
|
51796
|
+
SKILL_MD = `---
|
|
51797
|
+
name: hyperframes
|
|
51798
|
+
description: Create video compositions, animations, title cards, overlays, captions, voiceovers, audio-reactive visuals, and scene transitions in HyperFrames HTML. Use when asked to build any HTML-based video content, add captions or subtitles synced to audio, generate text-to-speech narration, create audio-reactive animation (beat sync, glow, pulse driven by music), add animated text highlighting (marker sweeps, hand-drawn circles, burst lines, scribble, sketchout), or add transitions between scenes (crossfades, wipes, reveals, shader transitions). Covers composition authoring, timing, media, and the full video production workflow. For CLI commands (init, lint, preview, render, transcribe, tts) see the hyperframes-cli skill.
|
|
51799
|
+
---
|
|
51800
|
+
|
|
51801
|
+
# HyperFrames
|
|
51802
|
+
|
|
51803
|
+
HTML is the source of truth for video. A composition is an HTML file with \`data-*\` attributes for timing, a GSAP timeline for animation, and CSS for appearance. The framework handles clip visibility, media playback, and timeline sync.
|
|
51804
|
+
|
|
51805
|
+
## Approach
|
|
51806
|
+
|
|
51807
|
+
Before writing HTML, think at a high level:
|
|
51808
|
+
|
|
51809
|
+
1. **What** \u2014 what should the viewer experience? Identify the narrative arc, key moments, and emotional beats.
|
|
51810
|
+
2. **Structure** \u2014 how many compositions, which are sub-compositions vs inline, what tracks carry what (video, audio, overlays, captions).
|
|
51811
|
+
3. **Timing** \u2014 which clips drive the duration, where do transitions land, what's the pacing.
|
|
51812
|
+
4. **Layout** \u2014 build the end-state first. See "Layout Before Animation" below.
|
|
51813
|
+
5. **Animate** \u2014 then add motion using the rules below.
|
|
51814
|
+
|
|
51815
|
+
For small edits (fix a color, adjust timing, add one element), skip straight to the rules.
|
|
51816
|
+
|
|
51817
|
+
### Visual Identity Gate
|
|
51818
|
+
|
|
51819
|
+
<HARD-GATE>
|
|
51820
|
+
Before writing ANY composition HTML, you MUST have a visual identity defined. Do NOT write compositions with default or generic colors.
|
|
51821
|
+
|
|
51822
|
+
Check in this order:
|
|
51823
|
+
|
|
51824
|
+
1. **DESIGN.md exists in the project?** \u2192 Read it. Use its exact colors, fonts, motion rules, and "What NOT to Do" constraints.
|
|
51825
|
+
2. **visual-style.md exists?** \u2192 Read it. Apply its \`style_prompt_full\` and structured fields. (Note: \`visual-style.md\` is a project-specific file. \`visual-styles.md\` is the style library with 8 named presets \u2014 different files.)
|
|
51826
|
+
3. **User named a style** (e.g., "Swiss Pulse", "dark and techy", "luxury brand")? \u2192 Read [visual-styles.md](./visual-styles.md) for the 8 named presets. Generate a minimal DESIGN.md with: \`## Style Prompt\` (one paragraph), \`## Colors\` (3-5 hex values with roles), \`## Typography\` (1-2 font families), \`## What NOT to Do\` (3-5 anti-patterns).
|
|
51827
|
+
4. **None of the above?** \u2192 Ask 3 questions before writing any HTML:
|
|
51828
|
+
- What's the mood? (explosive / cinematic / fluid / technical / chaotic / warm)
|
|
51829
|
+
- Light or dark canvas?
|
|
51830
|
+
- Any specific brand colors, fonts, or visual references?
|
|
51831
|
+
Then generate a minimal DESIGN.md from the answers.
|
|
51832
|
+
|
|
51833
|
+
Every composition must trace its palette and typography back to a DESIGN.md, visual-style.md, or explicit user direction. If you're reaching for \`#333\`, \`#3b82f6\`, or \`Roboto\` \u2014 you skipped this step.
|
|
51834
|
+
</HARD-GATE>
|
|
51835
|
+
|
|
51836
|
+
For motion defaults, sizing, entrance patterns, and easing \u2014 follow [house-style.md](./house-style.md). The house style handles HOW things move. The DESIGN.md handles WHAT things look like.
|
|
51837
|
+
|
|
51838
|
+
## Layout Before Animation
|
|
51839
|
+
|
|
51840
|
+
Position every element where it should be at its **most visible moment** \u2014 the frame where it's fully entered, correctly placed, and not yet exiting. Write this as static HTML+CSS first. No GSAP yet.
|
|
51841
|
+
|
|
51842
|
+
**Why this matters:** If you position elements at their animated start state (offscreen, scaled to 0, opacity 0) and tween them to where you think they should land, you're guessing the final layout. Overlaps are invisible until the video renders. By building the end state first, you can see and fix layout problems before adding any motion.
|
|
51843
|
+
|
|
51844
|
+
### The process
|
|
51845
|
+
|
|
51846
|
+
1. **Identify the hero frame** for each scene \u2014 the moment when the most elements are simultaneously visible. This is the layout you build.
|
|
51847
|
+
2. **Write static CSS** for that frame. The \`.scene-content\` container MUST fill the full scene using \`width: 100%; height: 100%; padding: Npx;\` with \`display: flex; flex-direction: column; gap: Npx; box-sizing: border-box\`. Use padding to push content inward \u2014 NEVER \`position: absolute; top: Npx\` on a content container. Absolute-positioned content containers overflow when content is taller than the remaining space. Reserve \`position: absolute\` for decoratives only.
|
|
51848
|
+
3. **Add entrances with \`gsap.from()\`** \u2014 animate FROM offscreen/invisible TO the CSS position. The CSS position is the ground truth; the tween describes the journey to get there.
|
|
51849
|
+
4. **Add exits with \`gsap.to()\`** \u2014 animate TO offscreen/invisible FROM the CSS position.
|
|
51850
|
+
|
|
51851
|
+
### Example
|
|
51852
|
+
|
|
51853
|
+
\`\`\`css
|
|
51854
|
+
/* scene-content fills the scene, padding positions content */
|
|
51855
|
+
.scene-content {
|
|
51856
|
+
display: flex;
|
|
51857
|
+
flex-direction: column;
|
|
51858
|
+
justify-content: center;
|
|
51859
|
+
width: 100%;
|
|
51860
|
+
height: 100%;
|
|
51861
|
+
padding: 120px 160px;
|
|
51862
|
+
gap: 24px;
|
|
51863
|
+
box-sizing: border-box;
|
|
51864
|
+
}
|
|
51865
|
+
.title {
|
|
51866
|
+
font-size: 120px;
|
|
51867
|
+
}
|
|
51868
|
+
.subtitle {
|
|
51869
|
+
font-size: 42px;
|
|
51870
|
+
}
|
|
51871
|
+
/* Container fills any scene size (1920x1080, 1080x1920, etc).
|
|
51872
|
+
Padding positions content. Flex + gap handles spacing. */
|
|
51873
|
+
\`\`\`
|
|
51874
|
+
|
|
51875
|
+
**WRONG \u2014 hardcoded dimensions and absolute positioning:**
|
|
51876
|
+
|
|
51877
|
+
\`\`\`css
|
|
51878
|
+
.scene-content {
|
|
51879
|
+
position: absolute;
|
|
51880
|
+
top: 200px;
|
|
51881
|
+
left: 160px;
|
|
51882
|
+
width: 1920px;
|
|
51883
|
+
height: 1080px;
|
|
51884
|
+
display: flex; /* ... */
|
|
51885
|
+
}
|
|
51886
|
+
\`\`\`
|
|
51887
|
+
|
|
51888
|
+
\`\`\`js
|
|
51889
|
+
// Step 3: Animate INTO those positions
|
|
51890
|
+
tl.from(".title", { y: 60, opacity: 0, duration: 0.6, ease: "power3.out" }, 0);
|
|
51891
|
+
tl.from(".subtitle", { y: 40, opacity: 0, duration: 0.5, ease: "power3.out" }, 0.2);
|
|
51892
|
+
tl.from(".logo", { scale: 0.8, opacity: 0, duration: 0.4, ease: "power2.out" }, 0.3);
|
|
51893
|
+
|
|
51894
|
+
// Step 4: Animate OUT from those positions
|
|
51895
|
+
tl.to(".title", { y: -40, opacity: 0, duration: 0.4, ease: "power2.in" }, 3);
|
|
51896
|
+
tl.to(".subtitle", { y: -30, opacity: 0, duration: 0.3, ease: "power2.in" }, 3.1);
|
|
51897
|
+
tl.to(".logo", { scale: 0.9, opacity: 0, duration: 0.3, ease: "power2.in" }, 3.2);
|
|
51898
|
+
\`\`\`
|
|
51899
|
+
|
|
51900
|
+
### When elements share space across time
|
|
51901
|
+
|
|
51902
|
+
If element A exits before element B enters in the same area, both should have correct CSS positions for their respective hero frames. The timeline ordering guarantees they never visually coexist \u2014 but if you skip the layout step, you won't catch the case where they accidentally overlap due to a timing error.
|
|
51903
|
+
|
|
51904
|
+
### What counts as intentional overlap
|
|
51905
|
+
|
|
51906
|
+
Layered effects (glow behind text, shadow elements, background patterns) and z-stacked designs (card stacks, depth layers) are intentional. The layout step is about catching **unintentional** overlap \u2014 two headlines landing on top of each other, a stat covering a label, content bleeding off-frame.
|
|
51907
|
+
|
|
51908
|
+
## Data Attributes
|
|
51909
|
+
|
|
51910
|
+
### All Clips
|
|
51911
|
+
|
|
51912
|
+
| Attribute | Required | Values |
|
|
51913
|
+
| ------------------ | --------------------------------- | ------------------------------------------------------ |
|
|
51914
|
+
| \`id\` | Yes | Unique identifier |
|
|
51915
|
+
| \`data-start\` | Yes | Seconds or clip ID reference (\`"el-1"\`, \`"intro + 2"\`) |
|
|
51916
|
+
| \`data-duration\` | Required for img/div/compositions | Seconds. Video/audio defaults to media duration. |
|
|
51917
|
+
| \`data-track-index\` | Yes | Integer. Same-track clips cannot overlap. |
|
|
51918
|
+
| \`data-media-start\` | No | Trim offset into source (seconds) |
|
|
51919
|
+
| \`data-volume\` | No | 0-1 (default 1) |
|
|
51920
|
+
|
|
51921
|
+
\`data-track-index\` does **not** affect visual layering \u2014 use CSS \`z-index\`.
|
|
51922
|
+
|
|
51923
|
+
### Composition Clips
|
|
51924
|
+
|
|
51925
|
+
| Attribute | Required | Values |
|
|
51926
|
+
| ---------------------------- | -------- | -------------------------------------------- |
|
|
51927
|
+
| \`data-composition-id\` | Yes | Unique composition ID |
|
|
51928
|
+
| \`data-start\` | Yes | Start time (root composition: use \`"0"\`) |
|
|
51929
|
+
| \`data-duration\` | Yes | Takes precedence over GSAP timeline duration |
|
|
51930
|
+
| \`data-width\` / \`data-height\` | Yes | Pixel dimensions (1920x1080 or 1080x1920) |
|
|
51931
|
+
| \`data-composition-src\` | No | Path to external HTML file |
|
|
51932
|
+
|
|
51933
|
+
## Composition Structure
|
|
51934
|
+
|
|
51935
|
+
Sub-compositions loaded via \`data-composition-src\` use a \`<template>\` wrapper. **Standalone compositions (the main index.html) do NOT use \`<template>\`** \u2014 they put the \`data-composition-id\` div directly in \`<body>\`. Using \`<template>\` on a standalone file hides all content from the browser and breaks rendering.
|
|
51936
|
+
|
|
51937
|
+
Sub-composition structure:
|
|
51938
|
+
|
|
51939
|
+
\`\`\`html
|
|
51940
|
+
<template id="my-comp-template">
|
|
51941
|
+
<div data-composition-id="my-comp" data-width="1920" data-height="1080">
|
|
51942
|
+
<!-- content -->
|
|
51943
|
+
<style>
|
|
51944
|
+
[data-composition-id="my-comp"] {
|
|
51945
|
+
/* scoped styles */
|
|
51946
|
+
}
|
|
51947
|
+
</style>
|
|
51948
|
+
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
|
|
51949
|
+
<script>
|
|
51950
|
+
window.__timelines = window.__timelines || {};
|
|
51951
|
+
const tl = gsap.timeline({ paused: true });
|
|
51952
|
+
// tweens...
|
|
51953
|
+
window.__timelines["my-comp"] = tl;
|
|
51954
|
+
</script>
|
|
51955
|
+
</div>
|
|
51956
|
+
</template>
|
|
51957
|
+
\`\`\`
|
|
51958
|
+
|
|
51959
|
+
Load in root: \`<div id="el-1" data-composition-id="my-comp" data-composition-src="compositions/my-comp.html" data-start="0" data-duration="10" data-track-index="1"></div>\`
|
|
51960
|
+
|
|
51961
|
+
## Video and Audio
|
|
51962
|
+
|
|
51963
|
+
Video must be \`muted playsinline\`. Audio is always a separate \`<audio>\` element:
|
|
51964
|
+
|
|
51965
|
+
\`\`\`html
|
|
51966
|
+
<video
|
|
51967
|
+
id="el-v"
|
|
51968
|
+
data-start="0"
|
|
51969
|
+
data-duration="30"
|
|
51970
|
+
data-track-index="0"
|
|
51971
|
+
src="video.mp4"
|
|
51972
|
+
muted
|
|
51973
|
+
playsinline
|
|
51974
|
+
></video>
|
|
51975
|
+
<audio
|
|
51976
|
+
id="el-a"
|
|
51977
|
+
data-start="0"
|
|
51978
|
+
data-duration="30"
|
|
51979
|
+
data-track-index="2"
|
|
51980
|
+
src="video.mp4"
|
|
51981
|
+
data-volume="1"
|
|
51982
|
+
></audio>
|
|
51983
|
+
\`\`\`
|
|
51984
|
+
|
|
51985
|
+
## Timeline Contract
|
|
51986
|
+
|
|
51987
|
+
- All timelines start \`{ paused: true }\` \u2014 the player controls playback
|
|
51988
|
+
- Register every timeline: \`window.__timelines["<composition-id>"] = tl\`
|
|
51989
|
+
- Framework auto-nests sub-timelines \u2014 do NOT manually add them
|
|
51990
|
+
- Duration comes from \`data-duration\`, not from GSAP timeline length
|
|
51991
|
+
- Never create empty tweens to set duration
|
|
51992
|
+
|
|
51993
|
+
## Rules (Non-Negotiable)
|
|
51994
|
+
|
|
51995
|
+
**Deterministic:** No \`Math.random()\`, \`Date.now()\`, or time-based logic. Use a seeded PRNG if you need pseudo-random values (e.g. mulberry32).
|
|
51996
|
+
|
|
51997
|
+
**GSAP:** Only animate visual properties (\`opacity\`, \`x\`, \`y\`, \`scale\`, \`rotation\`, \`color\`, \`backgroundColor\`, \`borderRadius\`, transforms). Do NOT animate \`visibility\`, \`display\`, or call \`video.play()\`/\`audio.play()\`.
|
|
51998
|
+
|
|
51999
|
+
**Animation conflicts:** Never animate the same property on the same element from multiple timelines simultaneously.
|
|
52000
|
+
|
|
52001
|
+
**No \`repeat: -1\`:** Infinite-repeat timelines break the capture engine. Calculate the exact repeat count from composition duration: \`repeat: Math.ceil(duration / cycleDuration) - 1\`.
|
|
52002
|
+
|
|
52003
|
+
**Synchronous timeline construction:** Never build timelines inside \`async\`/\`await\`, \`setTimeout\`, or Promises. The capture engine reads \`window.__timelines\` synchronously after page load. Fonts are embedded by the compiler, so they're available immediately \u2014 no need to wait for font loading.
|
|
52004
|
+
|
|
52005
|
+
**Never do:**
|
|
52006
|
+
|
|
52007
|
+
1. Forget \`window.__timelines\` registration
|
|
52008
|
+
2. Use video for audio \u2014 always muted video + separate \`<audio>\`
|
|
52009
|
+
3. Nest video inside a timed div \u2014 use a non-timed wrapper
|
|
52010
|
+
4. Use \`data-layer\` (use \`data-track-index\`) or \`data-end\` (use \`data-duration\`)
|
|
52011
|
+
5. Animate video element dimensions \u2014 animate a wrapper div
|
|
52012
|
+
6. Call play/pause/seek on media \u2014 framework owns playback
|
|
52013
|
+
7. Create a top-level container without \`data-composition-id\`
|
|
52014
|
+
8. Use \`repeat: -1\` on any timeline or tween \u2014 always finite repeats
|
|
52015
|
+
9. Build timelines asynchronously (inside \`async\`, \`setTimeout\`, \`Promise\`)
|
|
52016
|
+
10. Use \`gsap.set()\` on clip elements from later scenes \u2014 they don't exist in the DOM at page load. Use \`tl.set(selector, vars, timePosition)\` inside the timeline at or after the clip's \`data-start\` time instead.
|
|
52017
|
+
11. Use \`<br>\` in content text \u2014 forced line breaks don't account for actual rendered font width. Text that wraps naturally + a \`<br>\` produces an extra unwanted break, causing overlap. Let text wrap via \`max-width\` instead. Exception: short display titles where each word is deliberately on its own line (e.g., "THE\\nIMMORTAL\\nGAME" at 130px).
|
|
52018
|
+
|
|
52019
|
+
## Scene Transitions (Non-Negotiable)
|
|
52020
|
+
|
|
52021
|
+
Every multi-scene composition MUST follow ALL of these rules. Violating any one of them is a broken composition.
|
|
52022
|
+
|
|
52023
|
+
1. **ALWAYS use transitions between scenes.** No jump cuts. No exceptions.
|
|
52024
|
+
2. **ALWAYS use entrance animations on every scene.** Every element animates IN via \`gsap.from()\`. No element may appear fully-formed. If a scene has 5 elements, it needs 5 entrance tweens.
|
|
52025
|
+
3. **NEVER use exit animations** except on the final scene. This means: NO \`gsap.to()\` that animates opacity to 0, y offscreen, scale to 0, or any other "out" animation before a transition fires. The transition IS the exit. The outgoing scene's content MUST be fully visible at the moment the transition starts.
|
|
52026
|
+
4. **Final scene only:** The last scene may fade elements out (e.g., fade to black). This is the ONLY scene where \`gsap.to(..., { opacity: 0 })\` is allowed.
|
|
52027
|
+
|
|
52028
|
+
**WRONG \u2014 exit animation before transition:**
|
|
52029
|
+
|
|
52030
|
+
\`\`\`js
|
|
52031
|
+
// BANNED \u2014 this empties the scene before the transition can use it
|
|
52032
|
+
tl.to("#s1-title", { opacity: 0, y: -40, duration: 0.4 }, 6.5);
|
|
52033
|
+
tl.to("#s1-subtitle", { opacity: 0, duration: 0.3 }, 6.7);
|
|
52034
|
+
// transition fires on empty frame
|
|
52035
|
+
\`\`\`
|
|
52036
|
+
|
|
52037
|
+
**RIGHT \u2014 entrance only, transition handles exit:**
|
|
52038
|
+
|
|
52039
|
+
\`\`\`js
|
|
52040
|
+
// Scene 1 entrance animations
|
|
52041
|
+
tl.from("#s1-title", { y: 50, opacity: 0, duration: 0.7, ease: "power3.out" }, 0.3);
|
|
52042
|
+
tl.from("#s1-subtitle", { y: 30, opacity: 0, duration: 0.5, ease: "power2.out" }, 0.6);
|
|
52043
|
+
// NO exit tweens \u2014 transition at 7.2s handles the scene change
|
|
52044
|
+
// Scene 2 entrance animations
|
|
52045
|
+
tl.from("#s2-heading", { x: -40, opacity: 0, duration: 0.6, ease: "expo.out" }, 8.0);
|
|
52046
|
+
\`\`\`
|
|
52047
|
+
|
|
52048
|
+
## Animation Guardrails
|
|
52049
|
+
|
|
52050
|
+
- Offset first animation 0.1-0.3s (not t=0)
|
|
52051
|
+
- Vary eases across entrance tweens \u2014 use at least 3 different eases per scene
|
|
52052
|
+
- Don't repeat an entrance pattern within a scene
|
|
52053
|
+
- Avoid full-screen linear gradients on dark backgrounds (H.264 banding \u2014 use radial or solid + localized glow)
|
|
52054
|
+
- 60px+ headlines, 20px+ body, 16px+ data labels for rendered video
|
|
52055
|
+
- \`font-variant-numeric: tabular-nums\` on number columns
|
|
52056
|
+
|
|
52057
|
+
When no \`visual-style.md\` or animation direction is provided, follow [house-style.md](./house-style.md) for aesthetic defaults.
|
|
52058
|
+
|
|
52059
|
+
## Typography and Assets
|
|
52060
|
+
|
|
52061
|
+
- **Fonts:** Just write the \`font-family\` you want in CSS \u2014 the compiler embeds supported fonts automatically. If a font isn't supported, the compiler warns.
|
|
52062
|
+
- Add \`crossorigin="anonymous"\` to external media
|
|
52063
|
+
- For dynamic text overflow, use \`window.__hyperframes.fitTextFontSize(text, { maxWidth, fontFamily, fontWeight })\`
|
|
52064
|
+
- All files live at the project root alongside \`index.html\`; sub-compositions use \`../\`
|
|
52065
|
+
|
|
52066
|
+
## Editing Existing Compositions
|
|
52067
|
+
|
|
52068
|
+
- Read the full composition first \u2014 match existing fonts, colors, animation patterns
|
|
52069
|
+
- Only change what was requested
|
|
52070
|
+
- Preserve timing of unrelated clips
|
|
52071
|
+
|
|
52072
|
+
## Output Checklist
|
|
52073
|
+
|
|
52074
|
+
- [ ] \`npx hyperframes lint\` and \`npx hyperframes validate\` both pass
|
|
52075
|
+
- [ ] \`npx hyperframes inspect\` passes, or every reported overflow is intentionally marked
|
|
52076
|
+
- [ ] Contrast warnings addressed (see Quality Checks below)
|
|
52077
|
+
- [ ] Layout issues addressed (see Quality Checks below)
|
|
52078
|
+
- [ ] Animation choreography verified (see Quality Checks below)
|
|
52079
|
+
|
|
52080
|
+
## Quality Checks
|
|
52081
|
+
|
|
52082
|
+
### Visual Inspect
|
|
52083
|
+
|
|
52084
|
+
\`hyperframes inspect\` runs the composition in headless Chrome, seeks through the timeline, and maps visual layout issues with timestamps, selectors, bounding boxes, and fix hints. Run it after \`lint\` and \`validate\`:
|
|
52085
|
+
|
|
52086
|
+
\`\`\`bash
|
|
52087
|
+
npx hyperframes inspect
|
|
52088
|
+
npx hyperframes inspect --json
|
|
52089
|
+
\`\`\`
|
|
52090
|
+
|
|
52091
|
+
Failures usually mean text is spilling out of a bubble/card, a fixed-size label is clipping dynamic copy, or text has moved off the canvas. Fix by increasing container size or padding, reducing font size or letter spacing, adding a real \`max-width\` so text wraps inside the container, or using \`window.__hyperframes.fitTextFontSize(...)\` for dynamic copy.
|
|
52092
|
+
|
|
52093
|
+
Use \`--samples 15\` for dense videos and \`--at 1.5,4,7.25\` for specific hero frames. Repeated static issues are collapsed by default to avoid flooding agent context. If overflow is intentional for an entrance/exit animation, mark the element or ancestor with \`data-layout-allow-overflow\`. If a decorative element should never be audited, mark it with \`data-layout-ignore\`.
|
|
52094
|
+
|
|
52095
|
+
\`hyperframes layout\` is the compatibility alias for the same check.
|
|
52096
|
+
|
|
52097
|
+
### Contrast
|
|
52098
|
+
|
|
52099
|
+
\`hyperframes validate\` runs a WCAG contrast audit by default. It seeks to 5 timestamps, screenshots the page, samples background pixels behind every text element, and computes contrast ratios. Failures appear as warnings:
|
|
52100
|
+
|
|
52101
|
+
\`\`\`
|
|
52102
|
+
\u26A0 WCAG AA contrast warnings (3):
|
|
52103
|
+
\xB7 .subtitle "secondary text" \u2014 2.67:1 (need 4.5:1, t=5.3s)
|
|
52104
|
+
\`\`\`
|
|
52105
|
+
|
|
52106
|
+
If warnings appear:
|
|
52107
|
+
|
|
52108
|
+
- On dark backgrounds: brighten the failing color until it clears 4.5:1 (normal text) or 3:1 (large text, 24px+ or 19px+ bold)
|
|
52109
|
+
- On light backgrounds: darken it
|
|
52110
|
+
- Stay within the palette family \u2014 don't invent a new color, adjust the existing one
|
|
52111
|
+
- Re-run \`hyperframes validate\` until clean
|
|
52112
|
+
|
|
52113
|
+
Use \`--no-contrast\` to skip if iterating rapidly and you'll check later.
|
|
52114
|
+
|
|
52115
|
+
### Animation Map
|
|
52116
|
+
|
|
52117
|
+
After authoring animations, run the animation map to verify choreography:
|
|
52118
|
+
|
|
52119
|
+
\`\`\`bash
|
|
52120
|
+
node skills/hyperframes/scripts/animation-map.mjs <composition-dir> \\
|
|
52121
|
+
--out <composition-dir>/.hyperframes/anim-map
|
|
52122
|
+
\`\`\`
|
|
52123
|
+
|
|
52124
|
+
Outputs a single \`animation-map.json\` with:
|
|
52125
|
+
|
|
52126
|
+
- **Per-tween summaries**: \`"#card1 animates opacity+y over 0.50s. moves 23px up. fades in. ends at (120, 200)"\`
|
|
52127
|
+
- **ASCII timeline**: Gantt chart of all tweens across the composition duration
|
|
52128
|
+
- **Stagger detection**: reports actual intervals (\`"3 elements stagger at 120ms"\`)
|
|
52129
|
+
- **Dead zones**: periods over 1s with no animation \u2014 intentional hold or missing entrance?
|
|
52130
|
+
- **Element lifecycles**: first/last animation time, final visibility
|
|
52131
|
+
- **Scene snapshots**: visible element state at 5 key timestamps
|
|
52132
|
+
- **Flags**: \`offscreen\`, \`collision\`, \`invisible\`, \`paced-fast\` (under 0.2s), \`paced-slow\` (over 2s)
|
|
52133
|
+
|
|
52134
|
+
Read the JSON. Scan summaries for anything unexpected. Check every flag \u2014 fix or justify. Verify the timeline shows the intended choreography rhythm. Re-run after fixes.
|
|
52135
|
+
|
|
52136
|
+
Skip on small edits (fixing a color, adjusting one duration). Run on new compositions and significant animation changes.
|
|
52137
|
+
|
|
52138
|
+
---
|
|
52139
|
+
|
|
52140
|
+
## References (loaded on demand)
|
|
52141
|
+
|
|
52142
|
+
- **[references/captions.md](references/captions.md)** \u2014 Captions, subtitles, lyrics, karaoke synced to audio. Tone-adaptive style detection, per-word styling, text overflow prevention, caption exit guarantees, word grouping. Read when adding any text synced to audio timing.
|
|
52143
|
+
- **[references/tts.md](references/tts.md)** \u2014 Text-to-speech with Kokoro-82M. Voice selection, speed tuning, TTS+captions workflow. Read when generating narration or voiceover.
|
|
52144
|
+
- **[references/audio-reactive.md](references/audio-reactive.md)** \u2014 Audio-reactive animation: map frequency bands and amplitude to GSAP properties. Read when visuals should respond to music, voice, or sound.
|
|
52145
|
+
- **[references/css-patterns.md](references/css-patterns.md)** \u2014 CSS+GSAP marker highlighting: highlight, circle, burst, scribble, sketchout. Deterministic, fully seekable. Read when adding visual emphasis to text.
|
|
52146
|
+
- **[references/typography.md](references/typography.md)** \u2014 Typography: font pairing, OpenType features, dark-background adjustments, font discovery script. **Always read** \u2014 every composition has text.
|
|
52147
|
+
- **[references/motion-principles.md](references/motion-principles.md)** \u2014 Motion design principles: easing as emotion, timing as weight, choreography as hierarchy, scene pacing, ambient motion, anti-patterns. Read when choreographing GSAP animations.
|
|
52148
|
+
- **[visual-styles.md](visual-styles.md)** \u2014 8 named visual styles (Swiss Pulse, Velvet Standard, Deconstructed, Maximalist Type, Data Drift, Soft Signal, Folk Frequency, Shadow Cut) with hex palettes, GSAP easing signatures, and shader pairings. Read when user names a style or when generating DESIGN.md.
|
|
52149
|
+
- **[house-style.md](house-style.md)** \u2014 Default motion, sizing, and color palettes when no style is specified.
|
|
52150
|
+
- **[patterns.md](patterns.md)** \u2014 PiP, title cards, slide show patterns.
|
|
52151
|
+
- **[data-in-motion.md](data-in-motion.md)** \u2014 Data, stats, and infographic patterns.
|
|
52152
|
+
- **[references/transcript-guide.md](references/transcript-guide.md)** \u2014 Transcription commands, whisper models, external APIs, troubleshooting.
|
|
52153
|
+
- **[references/dynamic-techniques.md](references/dynamic-techniques.md)** \u2014 Dynamic caption animation techniques (karaoke, clip-path, slam, scatter, elastic, 3D).
|
|
52154
|
+
|
|
52155
|
+
- **[references/transitions.md](references/transitions.md)** \u2014 Scene transitions: crossfades, wipes, reveals, shader transitions. Energy/mood selection, CSS vs WebGL guidance. **Always read for multi-scene compositions** \u2014 scenes without transitions feel like jump cuts.
|
|
52156
|
+
- [transitions/catalog.md](references/transitions/catalog.md) \u2014 Hard rules, scene template, and routing to per-type implementation code.
|
|
52157
|
+
- Shader transitions are in \`@hyperframes/shader-transitions\` (\`packages/shader-transitions/\`) \u2014 read package source, not skill files.
|
|
52158
|
+
|
|
52159
|
+
GSAP patterns and effects are in the \`/gsap\` skill.
|
|
52160
|
+
`;
|
|
52161
|
+
HOUSE_STYLE_MD = `# House Style
|
|
52162
|
+
|
|
52163
|
+
Creative direction for compositions when no \`visual-style.md\` is provided. These are starting points \u2014 override anything that doesn't serve the content.
|
|
52164
|
+
|
|
52165
|
+
## Before Writing HTML
|
|
52166
|
+
|
|
52167
|
+
1. **Interpret the prompt.** Generate real content. A recipe lists real ingredients. A HUD has real readouts.
|
|
52168
|
+
2. **Pick a palette.** Light or dark? Declare bg, fg, accent before writing code.
|
|
52169
|
+
3. **Pick typefaces.** Run the font discovery script in [references/typography.md](references/typography.md) \u2014 or pick a font you already know that fits the theme. The script broadens your options; it's not the only source.
|
|
52170
|
+
|
|
52171
|
+
## Lazy Defaults to Question
|
|
52172
|
+
|
|
52173
|
+
These patterns are AI design tells \u2014 the first thing every LLM reaches for. If you're about to use one, pause and ask: is this a deliberate choice for THIS content, or am I defaulting?
|
|
52174
|
+
|
|
52175
|
+
- Gradient text (\`background-clip: text\` + gradient)
|
|
52176
|
+
- Left-edge accent stripes on cards/callouts
|
|
52177
|
+
- Cyan-on-dark / purple-to-blue gradients / neon accents
|
|
52178
|
+
- Pure \`#000\` or \`#fff\` (tint toward your accent hue instead)
|
|
52179
|
+
- Identical card grids (same-size cards repeated)
|
|
52180
|
+
- Everything centered with equal weight (lead the eye somewhere)
|
|
52181
|
+
- Banned fonts (see [references/typography.md](references/typography.md) for full list)
|
|
52182
|
+
|
|
52183
|
+
If the content genuinely calls for one of these \u2014 centered layout for a solemn closing, cards for a real product UI mockup, a banned font because it's the perfect thematic match \u2014 use it. The goal is intentionality, not avoidance.
|
|
52184
|
+
|
|
52185
|
+
## Color
|
|
52186
|
+
|
|
52187
|
+
- Match light/dark to content: food, wellness, kids \u2192 light. Tech, cinema, finance \u2192 dark.
|
|
52188
|
+
- One accent hue. Same background across all scenes.
|
|
52189
|
+
- Tint neutrals toward your accent (even subtle warmth/coolness beats dead gray).
|
|
52190
|
+
- **Contrast:** enforced by \`hyperframes validate\` (WCAG AA). Text must be readable with decoratives removed.
|
|
52191
|
+
- Declare palette up front. Don't invent colors per-element.
|
|
52192
|
+
|
|
52193
|
+
## Background Layer
|
|
52194
|
+
|
|
52195
|
+
Every scene needs visual depth \u2014 persistent decorative elements that stay visible while content animates in. Without these, scenes feel empty during entrance staggering.
|
|
52196
|
+
|
|
52197
|
+
Ideas (mix and match, 2-5 per scene):
|
|
52198
|
+
|
|
52199
|
+
- Radial glows (accent-tinted, low opacity, breathing scale)
|
|
52200
|
+
- Ghost text (theme words at 3-8% opacity, very large, slow drift)
|
|
52201
|
+
- Accent lines (hairline rules, subtle pulse)
|
|
52202
|
+
- Grain/noise overlay, geometric shapes, grid patterns
|
|
52203
|
+
- Thematic decoratives (orbit rings for space, vinyl grooves for music, grid lines for data)
|
|
52204
|
+
|
|
52205
|
+
All decoratives should have slow ambient GSAP animation \u2014 breathing, drift, pulse. Static decoratives feel dead.
|
|
52206
|
+
|
|
52207
|
+
## Motion
|
|
52208
|
+
|
|
52209
|
+
See [references/motion-principles.md](references/motion-principles.md) for full rules. Quick: 0.3\u20130.6s, vary eases, combine transforms on entrances, overlap entries.
|
|
52210
|
+
|
|
52211
|
+
## Typography
|
|
52212
|
+
|
|
52213
|
+
See [references/typography.md](references/typography.md) for full rules. Quick: 700-900 headlines / 300-400 body, serif + sans (not two sans), 60px+ headlines / 20px+ body.
|
|
52214
|
+
|
|
52215
|
+
## Palettes
|
|
52216
|
+
|
|
52217
|
+
Declare one background, one foreground, one accent before writing HTML.
|
|
52218
|
+
|
|
52219
|
+
| Category | Use for | File |
|
|
52220
|
+
| ----------------- | --------------------------------------------- | ---------------------------------------------------------- |
|
|
52221
|
+
| Bold / Energetic | Product launches, social media, announcements | [palettes/bold-energetic.md](palettes/bold-energetic.md) |
|
|
52222
|
+
| Warm / Editorial | Storytelling, documentaries, case studies | [palettes/warm-editorial.md](palettes/warm-editorial.md) |
|
|
52223
|
+
| Dark / Premium | Tech, finance, luxury, cinematic | [palettes/dark-premium.md](palettes/dark-premium.md) |
|
|
52224
|
+
| Clean / Corporate | Explainers, tutorials, presentations | [palettes/clean-corporate.md](palettes/clean-corporate.md) |
|
|
52225
|
+
| Nature / Earth | Sustainability, outdoor, organic | [palettes/nature-earth.md](palettes/nature-earth.md) |
|
|
52226
|
+
| Neon / Electric | Gaming, tech, nightlife | [palettes/neon-electric.md](palettes/neon-electric.md) |
|
|
52227
|
+
| Pastel / Soft | Fashion, beauty, lifestyle, wellness | [palettes/pastel-soft.md](palettes/pastel-soft.md) |
|
|
52228
|
+
| Jewel / Rich | Luxury, events, sophisticated | [palettes/jewel-rich.md](palettes/jewel-rich.md) |
|
|
52229
|
+
| Monochrome | Dramatic, typography-focused | [palettes/monochrome.md](palettes/monochrome.md) |
|
|
52230
|
+
|
|
52231
|
+
Or derive from OKLCH \u2014 pick a hue, build bg/fg/accent at different lightnesses, tint everything toward that hue.
|
|
52232
|
+
`;
|
|
52233
|
+
MOTION_PRINCIPLES_MD = `# Motion Principles
|
|
52234
|
+
|
|
52235
|
+
## Guardrails
|
|
52236
|
+
|
|
52237
|
+
You know these rules but you violate them. Stop.
|
|
52238
|
+
|
|
52239
|
+
- **Don't use the same ease on every tween.** You default to \`power2.out\` on everything. Vary eases like you vary font weights \u2014 no more than 2 independent tweens with the same ease in a scene.
|
|
52240
|
+
- **Don't use the same speed on everything.** You default to 0.4-0.5s for everything. The slowest scene should be 3\xD7 slower than the fastest. Vary duration deliberately.
|
|
52241
|
+
- **Don't enter everything from the same direction.** You default to \`y: 30, opacity: 0\` on every element. Vary: from left, from right, from scale, opacity-only, letter-spacing.
|
|
52242
|
+
- **Don't use the same stagger on every scene.** Each scene needs its own rhythm.
|
|
52243
|
+
- **Don't use ambient zoom on every scene.** Pick different ambient motion per scene: slow pan, subtle rotation, scale push, color shift, or nothing. Stillness after motion is powerful.
|
|
52244
|
+
- **Don't start at t=0.** Offset the first animation 0.1-0.3s. Zero-delay feels like a jump cut.
|
|
52245
|
+
|
|
52246
|
+
## What You Don't Do Without Being Told
|
|
52247
|
+
|
|
52248
|
+
### Easing is emotion, not technique
|
|
52249
|
+
|
|
52250
|
+
The transition is the verb. The easing is the adverb. A slide-in with \`expo.out\` = confident. With \`sine.inOut\` = dreamy. With \`elastic.out\` = playful. Same motion, different meaning. Choose the adverb deliberately.
|
|
52251
|
+
|
|
52252
|
+
**Direction rules \u2014 these are not optional:**
|
|
52253
|
+
|
|
52254
|
+
- \`.out\` for elements entering. Starts fast, decelerates. Feels responsive. This is your default.
|
|
52255
|
+
- \`.in\` for elements leaving. Starts slow, accelerates away. Throws them off.
|
|
52256
|
+
- \`.inOut\` for elements moving between positions.
|
|
52257
|
+
|
|
52258
|
+
You get this backwards constantly. Ease-in for entrances feels sluggish. Ease-out for exits feels reluctant.
|
|
52259
|
+
|
|
52260
|
+
### Speed communicates weight
|
|
52261
|
+
|
|
52262
|
+
- Fast (0.15-0.3s) \u2014 energy, urgency, confidence
|
|
52263
|
+
- Medium (0.3-0.5s) \u2014 professional, most content
|
|
52264
|
+
- Slow (0.5-0.8s) \u2014 gravity, luxury, contemplation
|
|
52265
|
+
- Very slow (0.8-2.0s) \u2014 cinematic, emotional, atmospheric
|
|
52266
|
+
|
|
52267
|
+
### Scene structure: build / breathe / resolve
|
|
52268
|
+
|
|
52269
|
+
Every scene has three phases. You dump everything in the build and leave nothing for breathe or resolve.
|
|
52270
|
+
|
|
52271
|
+
- **Build (0-30%)** \u2014 elements enter, staggered. Don't dump everything at once.
|
|
52272
|
+
- **Breathe (30-70%)** \u2014 content visible, alive with ONE ambient motion.
|
|
52273
|
+
- **Resolve (70-100%)** \u2014 exit or decisive end. Exits are faster than entrances.
|
|
52274
|
+
|
|
52275
|
+
### Transitions are meaning
|
|
52276
|
+
|
|
52277
|
+
- **Crossfade** = "this continues"
|
|
52278
|
+
- **Hard cut** = "wake up" / disruption
|
|
52279
|
+
- **Slow dissolve** = "drift with me"
|
|
52280
|
+
|
|
52281
|
+
You crossfade everything. Use hard cuts for disruption and register shifts.
|
|
52282
|
+
|
|
52283
|
+
### Choreography is hierarchy
|
|
52284
|
+
|
|
52285
|
+
The element that moves first is perceived as most important. Stagger in order of importance, not DOM order. Don't wait for completion \u2014 overlap entries. Total stagger sequence under 500ms regardless of item count.
|
|
52286
|
+
|
|
52287
|
+
### Asymmetry
|
|
52288
|
+
|
|
52289
|
+
Entrances need longer than exits. A card takes 0.4s to appear but 0.25s to disappear.
|
|
52290
|
+
|
|
52291
|
+
## Visual Composition
|
|
52292
|
+
|
|
52293
|
+
You build for the web. Video frames are not pages.
|
|
52294
|
+
|
|
52295
|
+
- **Two focal points minimum per scene.** The eye needs somewhere to travel. Never a single text block floating in empty space.
|
|
52296
|
+
- **Fill the frame.** Hero text: 60-80% of width. You will try to use web-sized elements. Don't.
|
|
52297
|
+
- **Three layers minimum per scene.** Background treatment (glow, oversized faded type, color panel). Foreground content. Accent elements (dividers, labels, data bars).
|
|
52298
|
+
- **Background is not empty.** Radial glows, oversized faded type bleeding off-frame, subtle border panels, hairline rules. Pure solid #000 reads as "nothing loaded."
|
|
52299
|
+
- **Anchor to edges.** Pin content to left/top or right/bottom. Centered-and-floating is a web pattern.
|
|
52300
|
+
- **Split frames.** Data panel on the left, content on the right. Top bar with metadata, full-width below. Zone-based layouts, not centered stacks.
|
|
52301
|
+
- **Use structural elements.** Rules, dividers, border panels. They create paths for the eye and animate well (scaleX from 0).
|
|
52302
|
+
`;
|
|
52303
|
+
TYPOGRAPHY_MD = `# Typography
|
|
52304
|
+
|
|
52305
|
+
The compiler embeds supported fonts \u2014 just write \`font-family\` in CSS.
|
|
52306
|
+
|
|
52307
|
+
## Banned
|
|
52308
|
+
|
|
52309
|
+
Training-data defaults that every LLM reaches for. These produce monoculture across compositions.
|
|
52310
|
+
|
|
52311
|
+
Inter, Roboto, Open Sans, Noto Sans, Arimo, Lato, Source Sans, PT Sans, Nunito, Poppins, Outfit, Sora, Playfair Display, Cormorant Garamond, Bodoni Moda, EB Garamond, Cinzel, Prata, Syne
|
|
52312
|
+
|
|
52313
|
+
**Syne in particular** is the most overused "distinctive" display font. It is an instant AI design tell.
|
|
52314
|
+
|
|
52315
|
+
## Guardrails
|
|
52316
|
+
|
|
52317
|
+
You know these rules but you violate them. Stop.
|
|
52318
|
+
|
|
52319
|
+
- **Don't pair two sans-serifs.** You do this constantly \u2014 one for headlines, one for body. Cross the boundary: serif + sans, or sans + mono.
|
|
52320
|
+
- **One expressive font per scene.** You pick two interesting fonts trying to make it "better." One performs, one recedes.
|
|
52321
|
+
- **Weight contrast must be extreme.** You default to 400 vs 700. Video needs 300 vs 900. The difference must be visible in motion at a glance.
|
|
52322
|
+
- **Video sizes, not web sizes.** Body: 20px minimum. Headlines: 60px+. Data labels: 16px. You will try to use 14px. Don't.
|
|
52323
|
+
|
|
52324
|
+
## What You Don't Do Without Being Told
|
|
52325
|
+
|
|
52326
|
+
- **Tension should mean something.** Don't pattern-match pairings. Ask WHY these two fonts disagree. The pairing should embody the content's contradiction \u2014 mechanical vs human, public vs private, institutional vs personal. If you can't articulate the tension, it's arbitrary.
|
|
52327
|
+
- **Register switching.** Assign different fonts to different communicative modes \u2014 one voice for statements, another for data, another for attribution. Not hierarchy on a page. Voices in a conversation.
|
|
52328
|
+
- **Tension can live inside a single font.** A font that looks familiar but is secretly strange creates tension with the viewer's expectations, not with another font.
|
|
52329
|
+
- **One variable changed = dramatic contrast.** Same letterforms, monospaced vs proportional. Same family at different optical sizes. Changing only rhythm while everything else stays constant.
|
|
52330
|
+
- **Double personality works.** Two expressive fonts can coexist if they share an attitude (both irreverent, both precise) even when their forms are completely different.
|
|
52331
|
+
- **Time is hierarchy.** The first element to appear is the most important. In video, sequence replaces position.
|
|
52332
|
+
- **Motion is typography.** How a word enters carries as much meaning as the font. A 0.1s slam vs a 2s fade \u2014 same font, completely different message.
|
|
52333
|
+
- **Fixed reading time.** 3 seconds on screen = must be readable in 2. Fewer words, larger type.
|
|
52334
|
+
- **Tracking tighter than web.** -0.03em to -0.05em on display sizes. Video encoding compresses letter detail.
|
|
52335
|
+
|
|
52336
|
+
## Finding Fonts
|
|
52337
|
+
|
|
52338
|
+
Don't default to what you know. If the content is luxury, a grotesque sans might create more tension than the expected Didone serif. Decide the register first, then search.
|
|
52339
|
+
|
|
52340
|
+
Save this script to \`/tmp/fontquery.py\` and run with \`curl -s 'https://fonts.google.com/metadata/fonts' > /tmp/gfonts.json && python3 /tmp/fontquery.py /tmp/gfonts.json\`:
|
|
52341
|
+
|
|
52342
|
+
\`\`\`python
|
|
52343
|
+
import json, sys, random
|
|
52344
|
+
from collections import OrderedDict
|
|
52345
|
+
|
|
52346
|
+
random.seed() # true random each run
|
|
52347
|
+
|
|
52348
|
+
with open(sys.argv[1]) as f:
|
|
52349
|
+
data = json.load(f)
|
|
52350
|
+
fonts = data.get("familyMetadataList", [])
|
|
52351
|
+
|
|
52352
|
+
ban = {"Inter","Roboto","Open Sans","Noto Sans","Lato","Poppins","Source Sans 3",
|
|
52353
|
+
"PT Sans","Nunito","Outfit","Sora","Playfair Display","Cormorant Garamond",
|
|
52354
|
+
"Bodoni Moda","EB Garamond","Cinzel","Prata","Arimo","Source Sans Pro","Syne"}
|
|
52355
|
+
skip_pfx = ("Roboto","Noto ","Google Sans","Bpmf","Playwrite","Anek","BIZ ",
|
|
52356
|
+
"Nanum","Shippori","Sawarabi","Zen ","Kaisei","Kiwi ","Yuji ","Radio ")
|
|
52357
|
+
|
|
52358
|
+
def ok(f):
|
|
52359
|
+
if f["family"] in ban: return False
|
|
52360
|
+
if any(f["family"].startswith(b) for b in skip_pfx): return False
|
|
52361
|
+
if "latin" not in (f.get("subsets") or []): return False
|
|
52362
|
+
return True
|
|
52363
|
+
|
|
52364
|
+
seen = set()
|
|
52365
|
+
R = OrderedDict()
|
|
52366
|
+
|
|
52367
|
+
# Trending Sans \u2014 recent (2022+), popular (<300)
|
|
52368
|
+
R["Trending Sans"] = []
|
|
52369
|
+
for f in fonts:
|
|
52370
|
+
if not ok(f) or f["family"] in seen: continue
|
|
52371
|
+
if f.get("category") in ("Sans Serif","Display") and f.get("dateAdded","") >= "2022-01-01" and f.get("popularity",9999) < 300:
|
|
52372
|
+
R["Trending Sans"].append(f); seen.add(f["family"])
|
|
52373
|
+
|
|
52374
|
+
# Trending Serif \u2014 recent (2018+), popular (<600)
|
|
52375
|
+
R["Trending Serif"] = []
|
|
52376
|
+
for f in fonts:
|
|
52377
|
+
if not ok(f) or f["family"] in seen: continue
|
|
52378
|
+
if f.get("category") == "Serif" and f.get("dateAdded","") >= "2018-01-01" and f.get("popularity",9999) < 600:
|
|
52379
|
+
R["Trending Serif"].append(f); seen.add(f["family"])
|
|
52380
|
+
|
|
52381
|
+
# Monospace \u2014 recent (2018+), popular (<600)
|
|
52382
|
+
R["Monospace"] = []
|
|
52383
|
+
for f in fonts:
|
|
52384
|
+
if not ok(f) or f["family"] in seen: continue
|
|
52385
|
+
if f.get("category") == "Monospace" and f.get("dateAdded","") >= "2018-01-01" and f.get("popularity",9999) < 600:
|
|
52386
|
+
R["Monospace"].append(f); seen.add(f["family"])
|
|
52387
|
+
|
|
52388
|
+
# Impact & Condensed \u2014 heavy display fonts with 800+ weight
|
|
52389
|
+
R["Impact & Condensed"] = []
|
|
52390
|
+
for f in fonts:
|
|
52391
|
+
if not ok(f) or f["family"] in seen: continue
|
|
52392
|
+
has_heavy = any(k in list(f.get("fonts",{}).keys()) for k in ("800","900"))
|
|
52393
|
+
is_display = f.get("category") in ("Sans Serif","Display")
|
|
52394
|
+
if has_heavy and is_display and f.get("popularity",9999) < 400:
|
|
52395
|
+
R["Impact & Condensed"].append(f); seen.add(f["family"])
|
|
52396
|
+
|
|
52397
|
+
# Script & Handwriting \u2014 popular (<300)
|
|
52398
|
+
R["Script & Handwriting"] = []
|
|
52399
|
+
for f in fonts:
|
|
52400
|
+
if not ok(f) or f["family"] in seen: continue
|
|
52401
|
+
if f.get("category") == "Handwriting" and f.get("popularity",9999) < 300:
|
|
52402
|
+
R["Script & Handwriting"].append(f); seen.add(f["family"])
|
|
52403
|
+
|
|
52404
|
+
|
|
52405
|
+
# Randomize the top 5 in each category so the LLM doesn't always pick the same first result
|
|
52406
|
+
for cat in R:
|
|
52407
|
+
R[cat].sort(key=lambda x: x.get("popularity",9999))
|
|
52408
|
+
top5 = R[cat][:5]
|
|
52409
|
+
rest = R[cat][5:]
|
|
52410
|
+
random.shuffle(top5)
|
|
52411
|
+
R[cat] = top5 + rest
|
|
52412
|
+
limits = {"Trending Sans":15,"Trending Serif":12,"Monospace":8,
|
|
52413
|
+
"Impact & Condensed":12,"Script & Handwriting":10}
|
|
52414
|
+
for cat in R:
|
|
52415
|
+
items = R[cat][:limits.get(cat,10)]
|
|
52416
|
+
if not items: continue
|
|
52417
|
+
print(f"--- {cat} ({len(items)}) ---")
|
|
52418
|
+
for ff in items:
|
|
52419
|
+
var = "VAR" if ff.get("axes") else " "
|
|
52420
|
+
print(f' {ff.get("popularity"):4d} | {var} | {ff["family"]}')
|
|
52421
|
+
print()
|
|
52422
|
+
\`\`\`
|
|
52423
|
+
|
|
52424
|
+
Five categories: trending sans, trending serif, monospace, impact/condensed, script/handwriting. All dynamically filtered from Google Fonts metadata \u2014 no hardcoded font names. Cross classification boundaries when pairing.
|
|
52425
|
+
|
|
52426
|
+
## Selection Thinking
|
|
52427
|
+
|
|
52428
|
+
Don't pick fonts by category reflex (editorial \u2192 serif, tech \u2192 mono, modern \u2192 geometric sans). That's pattern matching, not design.
|
|
52429
|
+
|
|
52430
|
+
1. **Name the register.** What voice is the content speaking in? Institutional authority? Personal confession? Technical precision? Casual irreverence? The register narrows the field more than the category.
|
|
52431
|
+
2. **Think physically.** Imagine the font as a physical object the brand could ship \u2014 a museum exhibit caption, a hand-painted shop sign, a 1970s mainframe terminal manual, a fabric label inside a coat, a children's book printed on cheap newsprint, a tax form. Whichever physical object fits the register is pointing at the right _kind_ of typeface.
|
|
52432
|
+
3. **Reject your first instinct.** The first font that feels right is usually your training-data default for that register. If you picked it last time too, find something else.
|
|
52433
|
+
4. **Cross-check the assumption.** An editorial brief does NOT need a serif. A technical brief does NOT need a sans. A children's product does NOT need a rounded display font. The most distinctive choice often contradicts the category expectation.
|
|
52434
|
+
|
|
52435
|
+
## Similar-Font Pairing
|
|
52436
|
+
|
|
52437
|
+
Never pair two fonts that are similar but not identical \u2014 two geometric sans-serifs, two transitional serifs, two humanist sans. They create visual friction without clear hierarchy. The viewer senses something is "off" but can't articulate it. Either use one font at two weights, or pair fonts that contrast on multiple axes: serif + sans, condensed + wide, geometric + humanist.
|
|
52438
|
+
|
|
52439
|
+
## Dark Backgrounds
|
|
52440
|
+
|
|
52441
|
+
Light text on dark backgrounds creates two optical illusions you need to compensate for:
|
|
52442
|
+
|
|
52443
|
+
- **Increased apparent weight.** Light-on-dark reads heavier than dark-on-light at the same \`font-weight\`. Use 350 instead of 400 for body text. Headlines are less affected because size compensates.
|
|
52444
|
+
- **Tighter apparent spacing.** Light halos around letterforms reduce perceived gaps. Increase \`line-height\` by 0.05-0.1 beyond your light-background value. For display sizes, add 0.01em \`letter-spacing\` to counteract.
|
|
52445
|
+
|
|
52446
|
+
## OpenType Features for Data
|
|
52447
|
+
|
|
52448
|
+
Most fonts ship with OpenType features that are off by default. Turn them on for data compositions:
|
|
52449
|
+
|
|
52450
|
+
\`\`\`css
|
|
52451
|
+
/* Tabular numbers \u2014 digits align vertically in columns */
|
|
52452
|
+
.stat-value,
|
|
52453
|
+
.timer,
|
|
52454
|
+
.data-column {
|
|
52455
|
+
font-variant-numeric: tabular-nums;
|
|
52456
|
+
}
|
|
52457
|
+
|
|
52458
|
+
/* Diagonal fractions \u2014 renders 1/2 as \xBD */
|
|
52459
|
+
.recipe-amount,
|
|
52460
|
+
.ratio {
|
|
52461
|
+
font-variant-numeric: diagonal-fractions;
|
|
52462
|
+
}
|
|
52463
|
+
|
|
52464
|
+
/* Small caps for abbreviations \u2014 less visual shouting */
|
|
52465
|
+
.abbreviation,
|
|
52466
|
+
.unit {
|
|
52467
|
+
font-variant-caps: all-small-caps;
|
|
52468
|
+
}
|
|
52469
|
+
|
|
52470
|
+
/* Disable ligatures in code \u2014 fi, fl, ffi should stay separate */
|
|
52471
|
+
code,
|
|
52472
|
+
.code {
|
|
52473
|
+
font-variant-ligatures: none;
|
|
52474
|
+
}
|
|
52475
|
+
\`\`\`
|
|
52476
|
+
|
|
52477
|
+
\`tabular-nums\` is essential any time numbers are stacked vertically \u2014 stat callouts, timers, scoreboards, data tables. Without it, digits have proportional widths and columns don't align.
|
|
52478
|
+
`;
|
|
52479
|
+
TRANSITIONS_MD = `# Scene Transitions
|
|
52480
|
+
|
|
52481
|
+
A transition tells the viewer how two scenes relate. A crossfade says "this continues." A push slide says "next point." A blur crossfade says "drift with me." Choose transitions that match what the content is doing emotionally, not just technically.
|
|
52482
|
+
|
|
52483
|
+
## Animation Rules for Multi-Scene Compositions
|
|
52484
|
+
|
|
52485
|
+
These are non-negotiable for every multi-scene composition:
|
|
52486
|
+
|
|
52487
|
+
1. **Every composition uses transitions.** No exceptions. Scenes without transitions feel like jump cuts.
|
|
52488
|
+
2. **Every scene uses entrance animations.** Elements animate IN via \`gsap.from()\` \u2014 opacity, position, scale, etc. No scene should pop fully-formed onto screen.
|
|
52489
|
+
3. **Exit animations are BANNED** except on the final scene. Do NOT use \`gsap.to()\` to animate elements out before a transition fires. The transition IS the exit. Outgoing scene content must be fully visible when the transition starts \u2014 the transition handles the visual handoff.
|
|
52490
|
+
4. **Final scene exception:** The last scene MAY fade elements out (e.g., fade to black at the end of the composition). This is the only scene where exit animations are allowed.
|
|
52491
|
+
|
|
52492
|
+
## Energy \u2192 Primary Transition
|
|
52493
|
+
|
|
52494
|
+
| Energy | CSS Primary | Shader Primary | Accent | Duration | Easing |
|
|
52495
|
+
| ---------------------------------------- | ---------------------------- | ------------------------------------ | ------------------------------ | --------- | ---------------------- |
|
|
52496
|
+
| **Calm** (wellness, brand story, luxury) | Blur crossfade, focus pull | Cross-warp morph, thermal distortion | Light leak, circle iris | 0.5-0.8s | \`sine.inOut\`, \`power1\` |
|
|
52497
|
+
| **Medium** (corporate, SaaS, explainer) | Push slide, staggered blocks | Whip pan, cinematic zoom | Squeeze, vertical push | 0.3-0.5s | \`power2\`, \`power3\` |
|
|
52498
|
+
| **High** (promos, sports, music, launch) | Zoom through, overexposure | Ridged burn, glitch, chromatic split | Staggered blocks, gravity drop | 0.15-0.3s | \`power4\`, \`expo\` |
|
|
52499
|
+
|
|
52500
|
+
Pick ONE primary (60-70% of scene changes) + 1-2 accents. Never use a different transition for every scene.
|
|
52501
|
+
|
|
52502
|
+
## Mood \u2192 Transition Type
|
|
52503
|
+
|
|
52504
|
+
Think about what the transition _communicates_, not just what it looks like.
|
|
52505
|
+
|
|
52506
|
+
| Mood | Transitions | Why it works |
|
|
52507
|
+
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------- |
|
|
52508
|
+
| **Warm / inviting** | Light leak, blur crossfade, focus pull, film burn \xB7 **Shader:** thermal distortion, light leak, cross-warp morph | Soft edges, warm color washes. Nothing sharp or mechanical. |
|
|
52509
|
+
| **Cold / clinical** | Squeeze, zoom out, blinds, shutter, grid dissolve \xB7 **Shader:** gravitational lens | Content transforms mechanically \u2014 compressed, shrunk, sliced, gridded. |
|
|
52510
|
+
| **Editorial / magazine** | Push slide, vertical push, diagonal split, shutter \xB7 **Shader:** whip pan | Like turning a page or slicing a layout. Clean directional movement. |
|
|
52511
|
+
| **Tech / futuristic** | Grid dissolve, staggered blocks, blinds, chromatic aberration \xB7 **Shader:** glitch, chromatic split | Grid dissolve is the core "data" transition. Shader glitch adds posterization + scan lines. |
|
|
52512
|
+
| **Tense / edgy** | Glitch, VHS, chromatic aberration, ripple \xB7 **Shader:** ridged burn, glitch, domain warp | Instability, distortion, digital breakdown. Ridged burn adds sharp lightning-crack edges. |
|
|
52513
|
+
| **Playful / fun** | Elastic push, 3D flip, circle iris, morph circle, clock wipe \xB7 **Shader:** ripple waves, swirl vortex | Overshoot, bounce, rotation, expansion. Swirl vortex adds organic spiral distortion. |
|
|
52514
|
+
| **Dramatic / cinematic** | Zoom through, zoom out, gravity drop, overexposure, color dip to black \xB7 **Shader:** cinematic zoom, gravitational lens, domain warp | Scale, weight, light extremes. Shader transitions add per-pixel depth. |
|
|
52515
|
+
| **Premium / luxury** | Focus pull, blur crossfade, color dip to black \xB7 **Shader:** cross-warp morph, thermal distortion | Restraint. Cross-warp morph flows both scenes into each other organically. |
|
|
52516
|
+
| **Retro / analog** | Film burn, light leak, VHS, clock wipe \xB7 **Shader:** light leak | Organic imperfection. Warm color bleeds, scan line displacement. |
|
|
52517
|
+
|
|
52518
|
+
## Narrative Position
|
|
52519
|
+
|
|
52520
|
+
| Position | Use | Why |
|
|
52521
|
+
| -------------------------- | -------------------------------------------------------------------------- | ----------------------------------------------------- |
|
|
52522
|
+
| **Opening** | Your most distinctive transition. Match the mood. 0.4-0.6s | Sets the visual language for the entire piece. |
|
|
52523
|
+
| **Between related points** | Your primary transition. Consistent. 0.3s | Don't distract \u2014 the content is continuing. |
|
|
52524
|
+
| **Topic change** | Something different from your primary. Staggered blocks, shutter, squeeze. | Signals "new section" \u2014 the viewer's brain resets. |
|
|
52525
|
+
| **Climax / hero reveal** | Your boldest accent. Fastest or most dramatic. | This is the payoff \u2014 spend your best transition here. |
|
|
52526
|
+
| **Wind-down** | Return to gentle. Blur crossfade, crossfade. 0.5-0.7s | Let the viewer exhale after the climax. |
|
|
52527
|
+
| **Outro** | Slowest, simplest. Crossfade, color dip to black. 0.6-1.0s | Closure. Don't introduce new energy at the end. |
|
|
52528
|
+
|
|
52529
|
+
## Blur Intensity by Energy
|
|
52530
|
+
|
|
52531
|
+
| Energy | Blur | Duration | Hold at peak |
|
|
52532
|
+
| ---------- | ------- | -------- | ------------ |
|
|
52533
|
+
| **Calm** | 20-30px | 0.8-1.2s | 0.3-0.5s |
|
|
52534
|
+
| **Medium** | 8-15px | 0.4-0.6s | 0.1-0.2s |
|
|
52535
|
+
| **High** | 3-6px | 0.2-0.3s | 0s |
|
|
52536
|
+
|
|
52537
|
+
## Presets
|
|
52538
|
+
|
|
52539
|
+
| Preset | Duration | Easing |
|
|
52540
|
+
| ---------- | -------- | ----------------- |
|
|
52541
|
+
| \`snappy\` | 0.2s | \`power4.inOut\` |
|
|
52542
|
+
| \`smooth\` | 0.4s | \`power2.inOut\` |
|
|
52543
|
+
| \`gentle\` | 0.6s | \`sine.inOut\` |
|
|
52544
|
+
| \`dramatic\` | 0.5s | \`power3.in\` \u2192 out |
|
|
52545
|
+
| \`instant\` | 0.15s | \`expo.inOut\` |
|
|
52546
|
+
| \`luxe\` | 0.7s | \`power1.inOut\` |
|
|
52547
|
+
|
|
52548
|
+
## Implementation
|
|
52549
|
+
|
|
52550
|
+
Read [transitions/catalog.md](transitions/catalog.md) for GSAP code and hard rules for every transition type.
|
|
52551
|
+
|
|
52552
|
+
| Category | CSS | Shader (WebGL) |
|
|
52553
|
+
| ----------- | -------------------------------------------------------------- | ------------------------------------------------------------------------- |
|
|
52554
|
+
| Push/slide | Push slide, vertical push, elastic push, squeeze | Whip pan |
|
|
52555
|
+
| Scale/zoom | Zoom through, zoom out, gravity drop, 3D flip | Cinematic zoom, gravitational lens |
|
|
52556
|
+
| Reveal/mask | Circle iris, diamond iris, diagonal split, clock wipe, shutter | SDF iris |
|
|
52557
|
+
| Dissolve | Crossfade, blur crossfade, focus pull, color dip | Cross-warp morph, domain warp |
|
|
52558
|
+
| Cover | Staggered blocks, horizontal blinds, vertical blinds | \u2014 |
|
|
52559
|
+
| Light | Light leak, overexposure burn, film burn | Light leak (shader), thermal distortion |
|
|
52560
|
+
| Distortion | Glitch, chromatic aberration, ripple, VHS tape | Glitch (shader), chromatic split, ridged burn, ripple waves, swirl vortex |
|
|
52561
|
+
| Pattern | Grid dissolve, morph circle | \u2014 |
|
|
52562
|
+
|
|
52563
|
+
## Transitions That Don't Work in CSS
|
|
52564
|
+
|
|
52565
|
+
Avoid: star iris, tilt-shift, lens flare, hinge/door. See catalog.md for why.
|
|
52566
|
+
|
|
52567
|
+
## CSS vs Shader
|
|
52568
|
+
|
|
52569
|
+
CSS transitions animate scene containers with opacity, transforms, clip-path, and filters. Shader transitions composite both scene textures per-pixel on a WebGL canvas \u2014 they can warp, dissolve, and morph in ways CSS cannot.
|
|
52570
|
+
|
|
52571
|
+
**Both are first-class options.** Shaders are provided by the \`@hyperframes/shader-transitions\` package \u2014 import from the package instead of writing raw GLSL. CSS transitions are simpler to set up. Choose based on the effect you want, not based on which is easier.
|
|
52572
|
+
|
|
52573
|
+
When a composition uses shader transitions, ALL transitions in that composition should be shader-based (the WebGL canvas replaces DOM-based scene switching). Don't mix CSS and shader transitions in the same composition.
|
|
52574
|
+
|
|
52575
|
+
## Shader-Compatible CSS Rules
|
|
52576
|
+
|
|
52577
|
+
Shader transitions capture DOM scenes to WebGL textures via html2canvas. The canvas 2D rendering pipeline doesn't match CSS exactly. Follow these rules to avoid visible artifacts at transition boundaries:
|
|
52578
|
+
|
|
52579
|
+
1. **No \`transparent\` keyword in gradients.** Canvas interpolates \`transparent\` as \`rgba(0,0,0,0)\` (black at zero alpha), creating dark fringes. Always use the target color at zero alpha: \`rgba(200,117,51,0)\` not \`transparent\`.
|
|
52580
|
+
2. **No gradient backgrounds on elements thinner than 4px.** Canvas can't match CSS gradient rendering on 1-2px elements. Use solid \`background-color\` on thin accent lines.
|
|
52581
|
+
3. **No CSS variables (\`var()\`) on elements visible during capture.** html2canvas doesn't reliably resolve custom properties. Use literal color values in inline styles.
|
|
52582
|
+
4. **Mark uncapturable decorative elements with \`data-no-capture\`.** The capture function skips these. They're present on the live DOM but absent from the shader texture. Use for elements that can't follow the rules above.
|
|
52583
|
+
5. **No gradient opacity below 0.15.** Gradient elements below 10% opacity render differently in canvas vs CSS. Increase to 0.15+ or use a solid color at equivalent brightness.
|
|
52584
|
+
6. **Every \`.scene\` div must have explicit \`background-color\`, AND pass the same color as \`bgColor\` in the \`init()\` config.** The package captures scene elements via html2canvas. Both the CSS \`background-color\` on \`.scene\` and the \`bgColor\` config must match. Without either, the texture renders as black.
|
|
52585
|
+
|
|
52586
|
+
These rules only apply to shader transition compositions. CSS-only compositions have no restrictions.
|
|
52587
|
+
|
|
52588
|
+
## Visual Pattern Warning
|
|
52589
|
+
|
|
52590
|
+
Avoid transitions that create visible repeating geometric patterns \u2014 grids of tiles, hexagonal cells, uniform dot arrays, evenly-spaced blob circles. These look cheap and artificial regardless of the math behind them. Organic noise (FBM, domain warping) is good because it's irregular. Geometric repetition is bad because the eye instantly sees the grid.
|
|
52591
|
+
`;
|
|
52592
|
+
}
|
|
52593
|
+
});
|
|
52594
|
+
|
|
52595
|
+
// src/commands/_shared/hf-skill-bundle/bundle.ts
|
|
52596
|
+
import { readFileSync, existsSync as existsSync27, statSync } from "node:fs";
|
|
52597
|
+
import { join as join7 } from "node:path";
|
|
52598
|
+
import { homedir as homedir3 } from "node:os";
|
|
52599
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
52600
|
+
function installedSkillPath() {
|
|
52601
|
+
const candidate = join7(homedir3(), ".claude", "skills", "hyperframes");
|
|
52602
|
+
if (!existsSync27(candidate)) return null;
|
|
52603
|
+
if (!statSync(candidate).isDirectory()) return null;
|
|
52604
|
+
return candidate;
|
|
52605
|
+
}
|
|
52606
|
+
function joinSections(sections) {
|
|
52607
|
+
return sections.map((s) => `
|
|
52608
|
+
|
|
52609
|
+
=== ${s.label} ===
|
|
52610
|
+
|
|
52611
|
+
${s.content}`).join("\n");
|
|
52612
|
+
}
|
|
52613
|
+
function buildVendored() {
|
|
52614
|
+
return {
|
|
52615
|
+
content: joinSections(VENDORED_SECTIONS),
|
|
52616
|
+
hint: `[skill] using vendored Hyperframes snapshot ${BUNDLE_VERSION}. For latest, run \`npx skills add heygen-com/hyperframes\`.`
|
|
52617
|
+
};
|
|
52618
|
+
}
|
|
52619
|
+
function buildInstalled(skillRoot) {
|
|
52620
|
+
const installedPaths = {
|
|
52621
|
+
"hyperframes/SKILL.md": join7(skillRoot, "SKILL.md"),
|
|
52622
|
+
"hyperframes/house-style.md": join7(skillRoot, "house-style.md"),
|
|
52623
|
+
"hyperframes/motion-principles.md": join7(skillRoot, "references", "motion-principles.md"),
|
|
52624
|
+
"hyperframes/typography.md": join7(skillRoot, "references", "typography.md"),
|
|
52625
|
+
"hyperframes/transitions.md": join7(skillRoot, "references", "transitions.md")
|
|
52626
|
+
};
|
|
52627
|
+
const sections = [];
|
|
52628
|
+
for (const [label, path2] of Object.entries(installedPaths)) {
|
|
52629
|
+
if (!existsSync27(path2)) return null;
|
|
52630
|
+
sections.push({ label, content: readFileSync(path2, "utf-8") });
|
|
52631
|
+
}
|
|
52632
|
+
return {
|
|
52633
|
+
content: joinSections(sections),
|
|
52634
|
+
hint: `[skill] using installed Hyperframes skill from ${skillRoot}.`
|
|
52635
|
+
};
|
|
52636
|
+
}
|
|
52637
|
+
function loadHyperframesSkillBundle() {
|
|
52638
|
+
const installedRoot = installedSkillPath();
|
|
52639
|
+
if (installedRoot) {
|
|
52640
|
+
const r = buildInstalled(installedRoot);
|
|
52641
|
+
if (r) {
|
|
52642
|
+
return {
|
|
52643
|
+
content: r.content,
|
|
52644
|
+
source: "installed",
|
|
52645
|
+
hint: r.hint,
|
|
52646
|
+
hash: hashBundle(BUNDLE_VERSION, r.content)
|
|
52647
|
+
};
|
|
52648
|
+
}
|
|
52649
|
+
}
|
|
52650
|
+
const v = buildVendored();
|
|
52651
|
+
return {
|
|
52652
|
+
content: v.content,
|
|
52653
|
+
source: "vendored",
|
|
52654
|
+
hint: v.hint,
|
|
52655
|
+
hash: hashBundle(BUNDLE_VERSION, v.content)
|
|
52656
|
+
};
|
|
52657
|
+
}
|
|
52658
|
+
function hashBundle(version, content) {
|
|
52659
|
+
return createHash2("sha256").update(version).update(" ").update(content).digest("hex");
|
|
52660
|
+
}
|
|
52661
|
+
var BUNDLE_VERSION, VENDORED_SECTIONS;
|
|
52662
|
+
var init_bundle = __esm({
|
|
52663
|
+
"src/commands/_shared/hf-skill-bundle/bundle.ts"() {
|
|
52664
|
+
"use strict";
|
|
52665
|
+
init_bundle_content();
|
|
52666
|
+
BUNDLE_VERSION = "970367f-2026-04-25";
|
|
52667
|
+
VENDORED_SECTIONS = [
|
|
52668
|
+
{ label: "hyperframes/SKILL.md", content: SKILL_MD },
|
|
52669
|
+
{ label: "hyperframes/house-style.md", content: HOUSE_STYLE_MD },
|
|
52670
|
+
{ label: "hyperframes/motion-principles.md", content: MOTION_PRINCIPLES_MD },
|
|
52671
|
+
{ label: "hyperframes/typography.md", content: TYPOGRAPHY_MD },
|
|
52672
|
+
{ label: "hyperframes/transitions.md", content: TRANSITIONS_MD }
|
|
52673
|
+
];
|
|
52674
|
+
}
|
|
52675
|
+
});
|
|
52676
|
+
|
|
52677
|
+
// src/commands/_shared/storyboard-parse.ts
|
|
52678
|
+
function parseStoryboard(md) {
|
|
52679
|
+
const text = md.replace(/\r\n/g, "\n");
|
|
52680
|
+
const headings = [];
|
|
52681
|
+
HEADING_RE.lastIndex = 0;
|
|
52682
|
+
let m;
|
|
52683
|
+
while ((m = HEADING_RE.exec(text)) !== null) {
|
|
52684
|
+
headings.push({
|
|
52685
|
+
start: m.index,
|
|
52686
|
+
end: m.index + m[0].length,
|
|
52687
|
+
line: m[1].trim()
|
|
52688
|
+
});
|
|
52689
|
+
}
|
|
52690
|
+
if (headings.length === 0) {
|
|
52691
|
+
return { global: text.trim(), beats: [] };
|
|
52692
|
+
}
|
|
52693
|
+
const global2 = text.slice(0, headings[0].start).trim();
|
|
52694
|
+
const beats = headings.map((h, i) => {
|
|
52695
|
+
const bodyStart = h.end;
|
|
52696
|
+
const bodyEnd = i + 1 < headings.length ? headings[i + 1].start : text.length;
|
|
52697
|
+
const body = text.slice(bodyStart, bodyEnd).trim();
|
|
52698
|
+
const id = deriveBeatId(h.line);
|
|
52699
|
+
const duration = parseBeatDuration(body);
|
|
52700
|
+
return {
|
|
52701
|
+
id,
|
|
52702
|
+
heading: h.line,
|
|
52703
|
+
body,
|
|
52704
|
+
...duration !== void 0 ? { duration } : {}
|
|
52705
|
+
};
|
|
52706
|
+
});
|
|
52707
|
+
return { global: global2, beats };
|
|
52708
|
+
}
|
|
52709
|
+
function deriveBeatId(headingLine) {
|
|
52710
|
+
const beatPrefix = headingLine.match(BEAT_PREFIX_RE);
|
|
52711
|
+
if (beatPrefix) {
|
|
52712
|
+
return slugify(beatPrefix[1]);
|
|
52713
|
+
}
|
|
52714
|
+
return slugify(headingLine);
|
|
52715
|
+
}
|
|
52716
|
+
function slugify(s) {
|
|
52717
|
+
return s.toLowerCase().normalize("NFKD").replace(/[̀-ͯ]/g, "").replace(/\([^)]*\)/g, "").replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "beat";
|
|
52718
|
+
}
|
|
52719
|
+
function parseBeatDuration(body) {
|
|
52720
|
+
const sub = body.match(DURATION_SUBSECTION_RE);
|
|
52721
|
+
if (!sub) return void 0;
|
|
52722
|
+
const valueMatch = sub[1].match(DURATION_VALUE_RE);
|
|
52723
|
+
if (!valueMatch) return void 0;
|
|
52724
|
+
const n = parseFloat(valueMatch[1]);
|
|
52725
|
+
return Number.isFinite(n) && n > 0 ? n : void 0;
|
|
52726
|
+
}
|
|
52727
|
+
var HEADING_RE, BEAT_PREFIX_RE, DURATION_SUBSECTION_RE, DURATION_VALUE_RE;
|
|
52728
|
+
var init_storyboard_parse = __esm({
|
|
52729
|
+
"src/commands/_shared/storyboard-parse.ts"() {
|
|
52730
|
+
"use strict";
|
|
52731
|
+
HEADING_RE = /^##\s+(.+?)\s*$/gm;
|
|
52732
|
+
BEAT_PREFIX_RE = /^Beat\s+(.+?)\s+(?:—|:|-)\s+(.+)$/i;
|
|
52733
|
+
DURATION_SUBSECTION_RE = /###\s+Beat\s+duration\s*\n([\s\S]*?)(?=\n###\s|\n##\s|$)/i;
|
|
52734
|
+
DURATION_VALUE_RE = /(-?\d+(?:\.\d+)?)\s*(?:s|sec|seconds?)?/i;
|
|
52735
|
+
}
|
|
52736
|
+
});
|
|
52737
|
+
|
|
52738
|
+
// src/commands/_shared/compose-scenes-skills.ts
|
|
52739
|
+
var compose_scenes_skills_exports = {};
|
|
52740
|
+
__export(compose_scenes_skills_exports, {
|
|
52741
|
+
ComposeBeatError: () => ComposeBeatError,
|
|
52742
|
+
buildSystemPrompt: () => buildSystemPrompt,
|
|
52743
|
+
buildUserPrompt: () => buildUserPrompt,
|
|
52744
|
+
composeBeatHtml: () => composeBeatHtml,
|
|
52745
|
+
composeBeatWithRetry: () => composeBeatWithRetry,
|
|
52746
|
+
computeCacheKey: () => computeCacheKey,
|
|
52747
|
+
defaultCacheDir: () => defaultCacheDir,
|
|
52748
|
+
executeComposeScenesWithSkills: () => executeComposeScenesWithSkills,
|
|
52749
|
+
extractHtml: () => extractHtml,
|
|
52750
|
+
formatLintFeedback: () => formatLintFeedback,
|
|
52751
|
+
lintBeatHtml: () => lintBeatHtml
|
|
52752
|
+
});
|
|
52753
|
+
import Anthropic from "@anthropic-ai/sdk";
|
|
52754
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
52755
|
+
import { existsSync as existsSync28, mkdirSync } from "node:fs";
|
|
52756
|
+
import { mkdir as mkdir19, readFile as readFile35, writeFile as writeFile31 } from "node:fs/promises";
|
|
52757
|
+
import { homedir as homedir4 } from "node:os";
|
|
52758
|
+
import { dirname as dirname20, join as join8, resolve as resolve42 } from "node:path";
|
|
52759
|
+
import {
|
|
52760
|
+
runHyperframeLint as runHyperframeLint2
|
|
52761
|
+
} from "@hyperframes/producer";
|
|
52762
|
+
function buildSystemPrompt(ctx) {
|
|
52763
|
+
return `You are a Hyperframes composition author. The skill content below
|
|
52764
|
+
defines the framework rules, motion principles, and quality standards.
|
|
52765
|
+
Read it thoroughly before writing any HTML.
|
|
52766
|
+
|
|
52767
|
+
${ctx.skillBundle.content}
|
|
52768
|
+
|
|
52769
|
+
=== DESIGN.md (project-specific visual identity \u2014 HARD-GATE, every decision must trace back) ===
|
|
52770
|
+
|
|
52771
|
+
${ctx.designMd}`;
|
|
52772
|
+
}
|
|
52773
|
+
function buildUserPrompt(ctx) {
|
|
52774
|
+
const compositionId = `scene-${ctx.beat.id}`;
|
|
52775
|
+
const baseRequirements = `Build the Hyperframes sub-composition HTML for this beat. The composition
|
|
52776
|
+
will be loaded into a root index.html via
|
|
52777
|
+
\`data-composition-src="compositions/${compositionId}.html"\`.
|
|
52778
|
+
|
|
52779
|
+
Requirements (non-negotiable):
|
|
52780
|
+
- Use the \`<template>\` wrapper (this is a sub-composition, not standalone)
|
|
52781
|
+
- Composition id: \`${compositionId}\`
|
|
52782
|
+
- One paused GSAP timeline registered on \`window.__timelines["${compositionId}"]\`
|
|
52783
|
+
- All timed elements have \`class="clip"\` and \`data-start\`, \`data-duration\`, \`data-track-index\`
|
|
52784
|
+
- No \`Math.random()\`, \`Date.now()\`, \`repeat: -1\`, or \`<br>\` in content
|
|
52785
|
+
- Layout-before-animation: position elements at hero-frame state in CSS, animate FROM
|
|
52786
|
+
- No exit animations (transitions handle scene exits, except the final beat)
|
|
52787
|
+
- Strictly follow DESIGN.md palette, typography, motion signature
|
|
52788
|
+
|
|
52789
|
+
=== Storyboard \u2014 global direction ===
|
|
52790
|
+
|
|
52791
|
+
${ctx.storyboardGlobal || "(no global direction)"}
|
|
52792
|
+
|
|
52793
|
+
=== Beat to build ===
|
|
52794
|
+
|
|
52795
|
+
## ${ctx.beat.heading}
|
|
52796
|
+
|
|
52797
|
+
${ctx.beat.body}
|
|
52798
|
+
|
|
52799
|
+
=== Output format ===
|
|
52800
|
+
|
|
52801
|
+
Return ONE complete HTML file in a single \`\`\`html\`\`\` fenced code block.
|
|
52802
|
+
No prose, no explanations, no commentary outside the code block. Just the HTML.`;
|
|
52803
|
+
if (ctx.retryFeedback && ctx.retryFeedback.trim().length > 0) {
|
|
52804
|
+
return `${baseRequirements}
|
|
52805
|
+
|
|
52806
|
+
=== Previous attempt failed lint with the following findings \u2014 fix them ===
|
|
52807
|
+
|
|
52808
|
+
${ctx.retryFeedback.trim()}`;
|
|
52809
|
+
}
|
|
52810
|
+
return baseRequirements;
|
|
52811
|
+
}
|
|
52812
|
+
function computeCacheKey(parts) {
|
|
52813
|
+
return createHash3("sha256").update(parts.model).update("\u241E").update(parts.systemPrompt).update("\u241E").update(parts.userPrompt).digest("hex");
|
|
52814
|
+
}
|
|
52815
|
+
function defaultCacheDir() {
|
|
52816
|
+
return join8(homedir4(), ".vibeframe", "cache", "compose-scenes");
|
|
52817
|
+
}
|
|
52818
|
+
function extractHtml(responseText) {
|
|
52819
|
+
const fenced = responseText.match(/```html\s*\n([\s\S]*?)\n```/);
|
|
52820
|
+
if (fenced) return fenced[1].trim();
|
|
52821
|
+
const trimmed = responseText.trim();
|
|
52822
|
+
if (trimmed.startsWith("<")) return trimmed;
|
|
52823
|
+
throw new ComposeBeatError(
|
|
52824
|
+
"no-html-in-response",
|
|
52825
|
+
`Could not extract HTML from response. First 200 chars: ${trimmed.slice(0, 200)}`
|
|
52826
|
+
);
|
|
52827
|
+
}
|
|
52828
|
+
async function composeBeatHtml(ctx, overrides) {
|
|
52829
|
+
const effort = ctx.effort ?? "medium";
|
|
52830
|
+
const settings = MODEL_SETTINGS[effort];
|
|
52831
|
+
const systemPrompt = buildSystemPrompt(ctx);
|
|
52832
|
+
const userPrompt = buildUserPrompt(ctx);
|
|
52833
|
+
const cacheKey = computeCacheKey({
|
|
52834
|
+
systemPrompt,
|
|
52835
|
+
userPrompt,
|
|
52836
|
+
model: settings.model
|
|
52837
|
+
});
|
|
52838
|
+
const cacheDir = ctx.cacheDir ?? defaultCacheDir();
|
|
52839
|
+
const cachePath = join8(cacheDir, `${cacheKey}.html`);
|
|
52840
|
+
if (existsSync28(cachePath)) {
|
|
52841
|
+
const html2 = await readFile35(cachePath, "utf-8");
|
|
52842
|
+
return { html: html2, cached: true, cacheKey, model: settings.model };
|
|
52843
|
+
}
|
|
52844
|
+
if (!ctx.apiKey) {
|
|
52845
|
+
throw new ComposeBeatError(
|
|
52846
|
+
"missing-api-key",
|
|
52847
|
+
"ANTHROPIC_API_KEY required for compose-scenes-with-skills (set it in env or .env)."
|
|
52848
|
+
);
|
|
52849
|
+
}
|
|
52850
|
+
const client = overrides?.client ?? new Anthropic({ apiKey: ctx.apiKey });
|
|
52851
|
+
const now = overrides?.now ?? (() => Date.now());
|
|
52852
|
+
const t0 = now();
|
|
52853
|
+
let response;
|
|
52854
|
+
try {
|
|
52855
|
+
response = await client.messages.create({
|
|
52856
|
+
model: settings.model,
|
|
52857
|
+
max_tokens: settings.maxTokens,
|
|
52858
|
+
system: systemPrompt,
|
|
52859
|
+
messages: [{ role: "user", content: userPrompt }]
|
|
52860
|
+
});
|
|
52861
|
+
} catch (err) {
|
|
52862
|
+
throw new ComposeBeatError(
|
|
52863
|
+
"api-call-failed",
|
|
52864
|
+
`Anthropic API call failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
52865
|
+
err
|
|
52866
|
+
);
|
|
52867
|
+
}
|
|
52868
|
+
const latencyMs = now() - t0;
|
|
52869
|
+
if (!("content" in response) || !Array.isArray(response.content)) {
|
|
52870
|
+
throw new ComposeBeatError("unexpected-response-shape", "Anthropic response missing `content` array.");
|
|
52871
|
+
}
|
|
52872
|
+
const textBlock = response.content.find((b) => b.type === "text");
|
|
52873
|
+
if (!textBlock || textBlock.type !== "text") {
|
|
52874
|
+
throw new ComposeBeatError("no-text-block", "Anthropic response had no text block.");
|
|
52875
|
+
}
|
|
52876
|
+
const html = extractHtml(textBlock.text);
|
|
52877
|
+
try {
|
|
52878
|
+
mkdirSync(cacheDir, { recursive: true });
|
|
52879
|
+
await writeFile31(cachePath, html, "utf-8");
|
|
52880
|
+
} catch {
|
|
52881
|
+
}
|
|
52882
|
+
const inputTokens = response.usage?.input_tokens ?? 0;
|
|
52883
|
+
const outputTokens = response.usage?.output_tokens ?? 0;
|
|
52884
|
+
const costUsd = inputTokens / 1e6 * settings.costPerMTokIn + outputTokens / 1e6 * settings.costPerMTokOut;
|
|
52885
|
+
return {
|
|
52886
|
+
html,
|
|
52887
|
+
cached: false,
|
|
52888
|
+
cacheKey,
|
|
52889
|
+
inputTokens,
|
|
52890
|
+
outputTokens,
|
|
52891
|
+
costUsd: Number(costUsd.toFixed(4)),
|
|
52892
|
+
latencyMs,
|
|
52893
|
+
model: settings.model
|
|
52894
|
+
};
|
|
52895
|
+
}
|
|
52896
|
+
function lintBeatHtml(html, beatId) {
|
|
52897
|
+
const prepared = {
|
|
52898
|
+
html,
|
|
52899
|
+
entryFile: `compositions/scene-${beatId}.html`,
|
|
52900
|
+
source: "projectDir"
|
|
52901
|
+
};
|
|
52902
|
+
const raw = runHyperframeLint2(prepared);
|
|
52903
|
+
const findings = filterSubCompFalsePositives(raw.findings, true);
|
|
52904
|
+
return {
|
|
52905
|
+
errorCount: findings.filter((f) => f.severity === "error").length,
|
|
52906
|
+
warningCount: findings.filter((f) => f.severity === "warning").length,
|
|
52907
|
+
findings
|
|
52908
|
+
};
|
|
52909
|
+
}
|
|
52910
|
+
function formatLintFeedback(findings) {
|
|
52911
|
+
const errors = findings.filter((f) => f.severity === "error");
|
|
52912
|
+
if (errors.length === 0) return "";
|
|
52913
|
+
return errors.map((f) => {
|
|
52914
|
+
const fix = f.fixHint ? `
|
|
52915
|
+
Fix hint: ${f.fixHint}` : "";
|
|
52916
|
+
return `ERROR [${f.code}] ${f.message}${fix}`;
|
|
52917
|
+
}).join("\n");
|
|
52918
|
+
}
|
|
52919
|
+
async function composeBeatWithRetry(ctx, overrides) {
|
|
52920
|
+
const first = await composeBeatHtml(ctx, overrides);
|
|
52921
|
+
const lint1 = lintBeatHtml(first.html, ctx.beat.id);
|
|
52922
|
+
if (lint1.errorCount === 0) {
|
|
52923
|
+
return { ...first, lintAttempts: 1, lint: lint1 };
|
|
52924
|
+
}
|
|
52925
|
+
const feedback = formatLintFeedback(lint1.findings);
|
|
52926
|
+
const second = await composeBeatHtml({ ...ctx, retryFeedback: feedback }, overrides);
|
|
52927
|
+
const lint2 = lintBeatHtml(second.html, ctx.beat.id);
|
|
52928
|
+
if (lint2.errorCount === 0) {
|
|
52929
|
+
return { ...second, lintAttempts: 2, lint: lint2 };
|
|
52930
|
+
}
|
|
52931
|
+
throw new ComposeBeatError(
|
|
52932
|
+
"lint-failed-after-retry",
|
|
52933
|
+
`Beat "${ctx.beat.id}" failed lint after retry. Final findings:
|
|
52934
|
+
${formatLintFeedback(lint2.findings)}`
|
|
52935
|
+
);
|
|
52936
|
+
}
|
|
52937
|
+
async function executeComposeScenesWithSkills(params, outputDir, overrides) {
|
|
52938
|
+
const projectRoot = params.project ? resolve42(outputDir, params.project) : resolve42(outputDir);
|
|
52939
|
+
const designPath = resolve42(projectRoot, params.design ?? "DESIGN.md");
|
|
52940
|
+
const storyboardPath = resolve42(projectRoot, params.storyboard ?? "STORYBOARD.md");
|
|
52941
|
+
if (!existsSync28(designPath)) {
|
|
52942
|
+
return { success: false, error: `DESIGN.md not found at ${designPath}` };
|
|
52943
|
+
}
|
|
52944
|
+
if (!existsSync28(storyboardPath)) {
|
|
52945
|
+
return { success: false, error: `STORYBOARD.md not found at ${storyboardPath}` };
|
|
52946
|
+
}
|
|
52947
|
+
const designMd = await readFile35(designPath, "utf-8");
|
|
52948
|
+
const storyboardMd = await readFile35(storyboardPath, "utf-8");
|
|
52949
|
+
const { global: storyboardGlobal, beats } = parseStoryboard(storyboardMd);
|
|
52950
|
+
if (beats.length === 0) {
|
|
52951
|
+
return {
|
|
52952
|
+
success: false,
|
|
52953
|
+
error: `STORYBOARD.md at ${storyboardPath} contains no \`## Beat \u2026\` headings.`
|
|
52954
|
+
};
|
|
52955
|
+
}
|
|
52956
|
+
const skillBundleLoaded = loadHyperframesSkillBundle();
|
|
52957
|
+
const skillBundle = { content: skillBundleLoaded.content, hash: skillBundleLoaded.hash };
|
|
52958
|
+
const apiKey = process.env.ANTHROPIC_API_KEY ?? "";
|
|
52959
|
+
const compositionsDir = join8(projectRoot, "compositions");
|
|
52960
|
+
await mkdir19(compositionsDir, { recursive: true });
|
|
52961
|
+
const onProgress = params.onProgress ?? (() => {
|
|
52962
|
+
});
|
|
52963
|
+
const totalBeats = beats.length;
|
|
52964
|
+
const tasks = beats.map(async (beat, beatIndex) => {
|
|
52965
|
+
onProgress({ type: "beat-start", beatId: beat.id, beatIndex, totalBeats });
|
|
52966
|
+
try {
|
|
52967
|
+
const result = await composeBeatWithRetry(
|
|
52968
|
+
{
|
|
52969
|
+
beat,
|
|
52970
|
+
designMd,
|
|
52971
|
+
storyboardGlobal,
|
|
52972
|
+
skillBundle,
|
|
52973
|
+
apiKey,
|
|
52974
|
+
effort: params.effort,
|
|
52975
|
+
cacheDir: params.cacheDir
|
|
52976
|
+
},
|
|
52977
|
+
overrides
|
|
52978
|
+
);
|
|
52979
|
+
const compositionPath = join8(compositionsDir, `scene-${beat.id}.html`);
|
|
52980
|
+
await mkdir19(dirname20(compositionPath), { recursive: true });
|
|
52981
|
+
await writeFile31(compositionPath, result.html, "utf-8");
|
|
52982
|
+
if (result.cached) {
|
|
52983
|
+
onProgress({
|
|
52984
|
+
type: "beat-cached",
|
|
52985
|
+
beatId: beat.id,
|
|
52986
|
+
beatIndex,
|
|
52987
|
+
totalBeats,
|
|
52988
|
+
lintAttempts: result.lintAttempts
|
|
52989
|
+
});
|
|
52990
|
+
} else {
|
|
52991
|
+
onProgress({
|
|
52992
|
+
type: "beat-fresh",
|
|
52993
|
+
beatId: beat.id,
|
|
52994
|
+
beatIndex,
|
|
52995
|
+
totalBeats,
|
|
52996
|
+
lintAttempts: result.lintAttempts,
|
|
52997
|
+
costUsd: result.costUsd,
|
|
52998
|
+
latencyMs: result.latencyMs
|
|
52999
|
+
});
|
|
53000
|
+
}
|
|
53001
|
+
return { ok: true, beatId: beat.id, beatIndex, path: compositionPath, result };
|
|
53002
|
+
} catch (err) {
|
|
53003
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
53004
|
+
onProgress({ type: "beat-failed", beatId: beat.id, beatIndex, totalBeats, error: message });
|
|
53005
|
+
return { ok: false, beatId: beat.id, beatIndex, error: message };
|
|
53006
|
+
}
|
|
53007
|
+
});
|
|
53008
|
+
const outcomes = await Promise.all(tasks);
|
|
53009
|
+
const written = [];
|
|
53010
|
+
const failures = [];
|
|
53011
|
+
let totalCostUsd = 0;
|
|
53012
|
+
let totalTokensIn = 0;
|
|
53013
|
+
let totalTokensOut = 0;
|
|
53014
|
+
let cacheHits = 0;
|
|
53015
|
+
for (const outcome of outcomes) {
|
|
53016
|
+
if (!outcome.ok) {
|
|
53017
|
+
failures.push({ beatId: outcome.beatId, error: outcome.error });
|
|
53018
|
+
continue;
|
|
53019
|
+
}
|
|
53020
|
+
const r = outcome.result;
|
|
53021
|
+
written.push({
|
|
53022
|
+
beatId: outcome.beatId,
|
|
53023
|
+
path: outcome.path,
|
|
53024
|
+
cached: r.cached,
|
|
53025
|
+
lintAttempts: r.lintAttempts,
|
|
53026
|
+
costUsd: r.costUsd
|
|
53027
|
+
});
|
|
53028
|
+
if (r.cached) cacheHits++;
|
|
53029
|
+
if (r.costUsd) totalCostUsd += r.costUsd;
|
|
53030
|
+
if (r.inputTokens) totalTokensIn += r.inputTokens;
|
|
53031
|
+
if (r.outputTokens) totalTokensOut += r.outputTokens;
|
|
53032
|
+
}
|
|
53033
|
+
const aggregateData = {
|
|
53034
|
+
beats: beats.length,
|
|
53035
|
+
written,
|
|
53036
|
+
totalCostUsd: Number(totalCostUsd.toFixed(4)),
|
|
53037
|
+
totalTokensIn,
|
|
53038
|
+
totalTokensOut,
|
|
53039
|
+
cacheHits
|
|
53040
|
+
};
|
|
53041
|
+
if (failures.length > 0) {
|
|
53042
|
+
return {
|
|
53043
|
+
success: false,
|
|
53044
|
+
outputPath: projectRoot,
|
|
53045
|
+
error: failures.length === 1 ? `compose-scenes-with-skills failed at beat "${failures[0].beatId}": ${failures[0].error}` : `compose-scenes-with-skills failed at ${failures.length} beats:
|
|
53046
|
+
${failures.map((f) => ` - ${f.beatId}: ${f.error}`).join("\n")}`,
|
|
53047
|
+
data: aggregateData
|
|
53048
|
+
};
|
|
53049
|
+
}
|
|
53050
|
+
return {
|
|
53051
|
+
success: true,
|
|
53052
|
+
outputPath: projectRoot,
|
|
53053
|
+
data: aggregateData
|
|
53054
|
+
};
|
|
53055
|
+
}
|
|
53056
|
+
var MODEL_SETTINGS, ComposeBeatError;
|
|
53057
|
+
var init_compose_scenes_skills = __esm({
|
|
53058
|
+
"src/commands/_shared/compose-scenes-skills.ts"() {
|
|
53059
|
+
"use strict";
|
|
53060
|
+
init_bundle();
|
|
53061
|
+
init_scene_lint();
|
|
53062
|
+
init_storyboard_parse();
|
|
53063
|
+
MODEL_SETTINGS = {
|
|
53064
|
+
// Sonnet 4.6 — pre-flight validated default. $3/$15 per MTok.
|
|
53065
|
+
low: { model: "claude-sonnet-4-6", maxTokens: 4e3, costPerMTokIn: 3, costPerMTokOut: 15 },
|
|
53066
|
+
medium: { model: "claude-sonnet-4-6", maxTokens: 6e3, costPerMTokIn: 3, costPerMTokOut: 15 },
|
|
53067
|
+
high: { model: "claude-sonnet-4-6", maxTokens: 8e3, costPerMTokIn: 3, costPerMTokOut: 15 }
|
|
53068
|
+
};
|
|
53069
|
+
ComposeBeatError = class extends Error {
|
|
53070
|
+
constructor(code, message, cause) {
|
|
53071
|
+
super(message);
|
|
53072
|
+
this.code = code;
|
|
53073
|
+
this.cause = cause;
|
|
53074
|
+
this.name = "ComposeBeatError";
|
|
53075
|
+
}
|
|
53076
|
+
};
|
|
53077
|
+
}
|
|
53078
|
+
});
|
|
53079
|
+
|
|
51903
53080
|
// src/agent/adapters/openai.ts
|
|
51904
53081
|
var openai_exports = {};
|
|
51905
53082
|
__export(openai_exports, {
|
|
@@ -52001,7 +53178,7 @@ var claude_exports = {};
|
|
|
52001
53178
|
__export(claude_exports, {
|
|
52002
53179
|
ClaudeAdapter: () => ClaudeAdapter
|
|
52003
53180
|
});
|
|
52004
|
-
import
|
|
53181
|
+
import Anthropic2 from "@anthropic-ai/sdk";
|
|
52005
53182
|
var ClaudeAdapter;
|
|
52006
53183
|
var init_claude2 = __esm({
|
|
52007
53184
|
"src/agent/adapters/claude.ts"() {
|
|
@@ -52011,7 +53188,7 @@ var init_claude2 = __esm({
|
|
|
52011
53188
|
client = null;
|
|
52012
53189
|
model = "claude-sonnet-4-6";
|
|
52013
53190
|
async initialize(apiKey) {
|
|
52014
|
-
this.client = new
|
|
53191
|
+
this.client = new Anthropic2({ apiKey });
|
|
52015
53192
|
}
|
|
52016
53193
|
isInitialized() {
|
|
52017
53194
|
return this.client !== null;
|
|
@@ -52630,14 +53807,14 @@ var project_exports = {};
|
|
|
52630
53807
|
__export(project_exports, {
|
|
52631
53808
|
registerProjectTools: () => registerProjectTools
|
|
52632
53809
|
});
|
|
52633
|
-
import { readFile as
|
|
52634
|
-
import { resolve as
|
|
53810
|
+
import { readFile as readFile37, writeFile as writeFile33, stat as stat7 } from "node:fs/promises";
|
|
53811
|
+
import { resolve as resolve44 } from "node:path";
|
|
52635
53812
|
async function resolveProjectPath3(inputPath, cwd) {
|
|
52636
|
-
const filePath =
|
|
53813
|
+
const filePath = resolve44(cwd, inputPath);
|
|
52637
53814
|
try {
|
|
52638
53815
|
const stats = await stat7(filePath);
|
|
52639
53816
|
if (stats.isDirectory()) {
|
|
52640
|
-
return
|
|
53817
|
+
return resolve44(filePath, "project.vibe.json");
|
|
52641
53818
|
}
|
|
52642
53819
|
} catch {
|
|
52643
53820
|
}
|
|
@@ -52760,9 +53937,9 @@ var init_project2 = __esm({
|
|
|
52760
53937
|
const project = new Project(name);
|
|
52761
53938
|
project.setAspectRatio(aspectRatio);
|
|
52762
53939
|
project.setFrameRate(fps);
|
|
52763
|
-
const outputPath =
|
|
53940
|
+
const outputPath = resolve44(context.workingDirectory, output);
|
|
52764
53941
|
const data = JSON.stringify(project.toJSON(), null, 2);
|
|
52765
|
-
await
|
|
53942
|
+
await writeFile33(outputPath, data, "utf-8");
|
|
52766
53943
|
context.projectPath = outputPath;
|
|
52767
53944
|
return {
|
|
52768
53945
|
toolCallId: "",
|
|
@@ -52785,7 +53962,7 @@ Frame Rate: ${fps} fps`
|
|
|
52785
53962
|
const path2 = args.path || args.project;
|
|
52786
53963
|
try {
|
|
52787
53964
|
const filePath = await resolveProjectPath3(path2, context.workingDirectory);
|
|
52788
|
-
const content = await
|
|
53965
|
+
const content = await readFile37(filePath, "utf-8");
|
|
52789
53966
|
const data = JSON.parse(content);
|
|
52790
53967
|
const project = Project.fromJSON(data);
|
|
52791
53968
|
const summary = project.getSummary();
|
|
@@ -52820,7 +53997,7 @@ Frame Rate: ${fps} fps`
|
|
|
52820
53997
|
const updates = [];
|
|
52821
53998
|
try {
|
|
52822
53999
|
const filePath = await resolveProjectPath3(path2, context.workingDirectory);
|
|
52823
|
-
const content = await
|
|
54000
|
+
const content = await readFile37(filePath, "utf-8");
|
|
52824
54001
|
const data = JSON.parse(content);
|
|
52825
54002
|
const project = Project.fromJSON(data);
|
|
52826
54003
|
if (args.name) {
|
|
@@ -52835,7 +54012,7 @@ Frame Rate: ${fps} fps`
|
|
|
52835
54012
|
project.setFrameRate(args.fps);
|
|
52836
54013
|
updates.push(`Frame Rate: ${args.fps} fps`);
|
|
52837
54014
|
}
|
|
52838
|
-
await
|
|
54015
|
+
await writeFile33(filePath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
|
|
52839
54016
|
return {
|
|
52840
54017
|
toolCallId: "",
|
|
52841
54018
|
success: true,
|
|
@@ -52855,7 +54032,7 @@ ${updates.join("\n")}`
|
|
|
52855
54032
|
const path2 = args.path || args.project;
|
|
52856
54033
|
try {
|
|
52857
54034
|
const filePath = await resolveProjectPath3(path2, context.workingDirectory);
|
|
52858
|
-
const content = await
|
|
54035
|
+
const content = await readFile37(filePath, "utf-8");
|
|
52859
54036
|
const data = JSON.parse(content);
|
|
52860
54037
|
const project = Project.fromJSON(data);
|
|
52861
54038
|
context.projectPath = filePath;
|
|
@@ -52889,10 +54066,10 @@ Duration: ${summary.duration.toFixed(1)}s`
|
|
|
52889
54066
|
}
|
|
52890
54067
|
try {
|
|
52891
54068
|
const filePath = await resolveProjectPath3(path2, context.workingDirectory);
|
|
52892
|
-
const content = await
|
|
54069
|
+
const content = await readFile37(filePath, "utf-8");
|
|
52893
54070
|
const data = JSON.parse(content);
|
|
52894
54071
|
const project = Project.fromJSON(data);
|
|
52895
|
-
await
|
|
54072
|
+
await writeFile33(filePath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
|
|
52896
54073
|
return {
|
|
52897
54074
|
toolCallId: "",
|
|
52898
54075
|
success: true,
|
|
@@ -52915,8 +54092,8 @@ var timeline_exports = {};
|
|
|
52915
54092
|
__export(timeline_exports, {
|
|
52916
54093
|
registerTimelineTools: () => registerTimelineTools
|
|
52917
54094
|
});
|
|
52918
|
-
import { readFile as
|
|
52919
|
-
import { resolve as
|
|
54095
|
+
import { readFile as readFile38, writeFile as writeFile34, stat as stat8 } from "node:fs/promises";
|
|
54096
|
+
import { resolve as resolve45, basename as basename25, extname as extname15 } from "node:path";
|
|
52920
54097
|
function detectMediaType4(path2) {
|
|
52921
54098
|
const ext = extname15(path2).toLowerCase();
|
|
52922
54099
|
const videoExts = [".mp4", ".mov", ".webm", ".avi", ".mkv"];
|
|
@@ -52939,11 +54116,11 @@ async function getMediaDuration2(filePath, mediaType) {
|
|
|
52939
54116
|
}
|
|
52940
54117
|
}
|
|
52941
54118
|
async function resolveProjectPath4(inputPath, cwd) {
|
|
52942
|
-
const filePath =
|
|
54119
|
+
const filePath = resolve45(cwd, inputPath);
|
|
52943
54120
|
try {
|
|
52944
54121
|
const stats = await stat8(filePath);
|
|
52945
54122
|
if (stats.isDirectory()) {
|
|
52946
|
-
return
|
|
54123
|
+
return resolve45(filePath, "project.vibe.json");
|
|
52947
54124
|
}
|
|
52948
54125
|
} catch {
|
|
52949
54126
|
}
|
|
@@ -52951,13 +54128,13 @@ async function resolveProjectPath4(inputPath, cwd) {
|
|
|
52951
54128
|
}
|
|
52952
54129
|
async function loadProject(path2, cwd) {
|
|
52953
54130
|
const filePath = await resolveProjectPath4(path2, cwd);
|
|
52954
|
-
const content = await
|
|
54131
|
+
const content = await readFile38(filePath, "utf-8");
|
|
52955
54132
|
const data = JSON.parse(content);
|
|
52956
54133
|
const project = Project.fromJSON(data);
|
|
52957
54134
|
return { project, filePath };
|
|
52958
54135
|
}
|
|
52959
54136
|
async function saveProject(project, filePath) {
|
|
52960
|
-
await
|
|
54137
|
+
await writeFile34(filePath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
|
|
52961
54138
|
}
|
|
52962
54139
|
function registerTimelineTools(registry) {
|
|
52963
54140
|
registry.register(addSourceDef, addSource);
|
|
@@ -53262,7 +54439,7 @@ var init_timeline = __esm({
|
|
|
53262
54439
|
const mediaPath = args.media;
|
|
53263
54440
|
try {
|
|
53264
54441
|
const { project, filePath } = await loadProject(projectPath, context.workingDirectory);
|
|
53265
|
-
const absMediaPath =
|
|
54442
|
+
const absMediaPath = resolve45(context.workingDirectory, mediaPath);
|
|
53266
54443
|
const mediaName = args.name || basename25(mediaPath);
|
|
53267
54444
|
const mediaType = args.type || detectMediaType4(mediaPath);
|
|
53268
54445
|
const duration = args.duration || await getMediaDuration2(absMediaPath, mediaType);
|
|
@@ -53780,8 +54957,8 @@ var filesystem_exports = {};
|
|
|
53780
54957
|
__export(filesystem_exports, {
|
|
53781
54958
|
registerFilesystemTools: () => registerFilesystemTools
|
|
53782
54959
|
});
|
|
53783
|
-
import { readFile as
|
|
53784
|
-
import { resolve as
|
|
54960
|
+
import { readFile as readFile39, writeFile as writeFile35, readdir as readdir5, stat as stat9, access as access8 } from "node:fs/promises";
|
|
54961
|
+
import { resolve as resolve46, join as join9 } from "node:path";
|
|
53785
54962
|
function matchPattern(filename, pattern) {
|
|
53786
54963
|
const regex2 = new RegExp(
|
|
53787
54964
|
"^" + pattern.replace(/\./g, "\\.").replace(/\*/g, ".*").replace(/\?/g, ".") + "$",
|
|
@@ -53877,14 +55054,14 @@ var init_filesystem = __esm({
|
|
|
53877
55054
|
const dirPath = args.path || ".";
|
|
53878
55055
|
const pattern = args.pattern;
|
|
53879
55056
|
try {
|
|
53880
|
-
const absPath =
|
|
55057
|
+
const absPath = resolve46(context.workingDirectory, dirPath);
|
|
53881
55058
|
const entries = await readdir5(absPath, { withFileTypes: true });
|
|
53882
55059
|
const results = [];
|
|
53883
55060
|
for (const entry of entries) {
|
|
53884
55061
|
if (pattern && !matchPattern(entry.name, pattern)) {
|
|
53885
55062
|
continue;
|
|
53886
55063
|
}
|
|
53887
|
-
const fullPath =
|
|
55064
|
+
const fullPath = join9(absPath, entry.name);
|
|
53888
55065
|
const stats = await stat9(fullPath);
|
|
53889
55066
|
if (entry.isDirectory()) {
|
|
53890
55067
|
results.push(`[DIR] ${entry.name}/`);
|
|
@@ -53917,8 +55094,8 @@ ${results.join("\n")}`
|
|
|
53917
55094
|
read2 = async (args, context) => {
|
|
53918
55095
|
const filePath = args.path;
|
|
53919
55096
|
try {
|
|
53920
|
-
const absPath =
|
|
53921
|
-
const content = await
|
|
55097
|
+
const absPath = resolve46(context.workingDirectory, filePath);
|
|
55098
|
+
const content = await readFile39(absPath, "utf-8");
|
|
53922
55099
|
const maxLength = 4e3;
|
|
53923
55100
|
const truncated = content.length > maxLength;
|
|
53924
55101
|
const output = truncated ? content.substring(0, maxLength) + "\n... (truncated)" : content;
|
|
@@ -53941,8 +55118,8 @@ ${output}`
|
|
|
53941
55118
|
const filePath = args.path;
|
|
53942
55119
|
const content = args.content;
|
|
53943
55120
|
try {
|
|
53944
|
-
const absPath =
|
|
53945
|
-
await
|
|
55121
|
+
const absPath = resolve46(context.workingDirectory, filePath);
|
|
55122
|
+
await writeFile35(absPath, content, "utf-8");
|
|
53946
55123
|
return {
|
|
53947
55124
|
toolCallId: "",
|
|
53948
55125
|
success: true,
|
|
@@ -53960,7 +55137,7 @@ ${output}`
|
|
|
53960
55137
|
exists = async (args, context) => {
|
|
53961
55138
|
const filePath = args.path;
|
|
53962
55139
|
try {
|
|
53963
|
-
const absPath =
|
|
55140
|
+
const absPath = resolve46(context.workingDirectory, filePath);
|
|
53964
55141
|
await access8(absPath);
|
|
53965
55142
|
const stats = await stat9(absPath);
|
|
53966
55143
|
const type = stats.isDirectory() ? "directory" : "file";
|
|
@@ -53985,8 +55162,8 @@ var media_exports = {};
|
|
|
53985
55162
|
__export(media_exports, {
|
|
53986
55163
|
registerMediaTools: () => registerMediaTools
|
|
53987
55164
|
});
|
|
53988
|
-
import { readFile as
|
|
53989
|
-
import { resolve as
|
|
55165
|
+
import { readFile as readFile40, writeFile as writeFile36 } from "node:fs/promises";
|
|
55166
|
+
import { resolve as resolve47 } from "node:path";
|
|
53990
55167
|
function formatTimestamp2(seconds) {
|
|
53991
55168
|
const mins = Math.floor(seconds / 60);
|
|
53992
55169
|
const secs = (seconds % 60).toFixed(2);
|
|
@@ -54125,7 +55302,7 @@ var init_media = __esm({
|
|
|
54125
55302
|
mediaInfo = async (args, context) => {
|
|
54126
55303
|
const mediaPath = args.path;
|
|
54127
55304
|
try {
|
|
54128
|
-
const absPath =
|
|
55305
|
+
const absPath = resolve47(context.workingDirectory, mediaPath);
|
|
54129
55306
|
const { stdout } = await execSafe("ffprobe", ["-v", "quiet", "-print_format", "json", "-show_format", "-show_streams", absPath], { maxBuffer: 10 * 1024 * 1024 });
|
|
54130
55307
|
const info = JSON.parse(stdout);
|
|
54131
55308
|
const format = info.format || {};
|
|
@@ -54179,7 +55356,7 @@ Audio:`);
|
|
|
54179
55356
|
const videoPath = args.video;
|
|
54180
55357
|
const threshold = args.threshold || 0.3;
|
|
54181
55358
|
try {
|
|
54182
|
-
const absPath =
|
|
55359
|
+
const absPath = resolve47(context.workingDirectory, videoPath);
|
|
54183
55360
|
const { stdout, stderr } = await execSafe("ffmpeg", ["-i", absPath, "-filter:v", `select='gt(scene,${threshold})',showinfo`, "-f", "null", "-"], { maxBuffer: 50 * 1024 * 1024 });
|
|
54184
55361
|
const output = stdout + stderr;
|
|
54185
55362
|
const scenes = [];
|
|
@@ -54195,7 +55372,7 @@ Audio:`);
|
|
|
54195
55372
|
return `[${i + 1}] ${formatTimestamp2(s.timestamp)} - ${formatTimestamp2(end)} (${(end - s.timestamp).toFixed(1)}s)`;
|
|
54196
55373
|
}).join("\n");
|
|
54197
55374
|
if (args.output) {
|
|
54198
|
-
const outputPath =
|
|
55375
|
+
const outputPath = resolve47(context.workingDirectory, args.output);
|
|
54199
55376
|
const result = {
|
|
54200
55377
|
source: absPath,
|
|
54201
55378
|
totalDuration,
|
|
@@ -54207,7 +55384,7 @@ Audio:`);
|
|
|
54207
55384
|
duration: (i < scenes.length - 1 ? scenes[i + 1].timestamp : totalDuration) - s.timestamp
|
|
54208
55385
|
}))
|
|
54209
55386
|
};
|
|
54210
|
-
await
|
|
55387
|
+
await writeFile36(outputPath, JSON.stringify(result, null, 2), "utf-8");
|
|
54211
55388
|
}
|
|
54212
55389
|
return {
|
|
54213
55390
|
toolCallId: "",
|
|
@@ -54229,7 +55406,7 @@ ${sceneList}`
|
|
|
54229
55406
|
const noise = args.noise || "-30";
|
|
54230
55407
|
const duration = args.duration || 0.5;
|
|
54231
55408
|
try {
|
|
54232
|
-
const absPath =
|
|
55409
|
+
const absPath = resolve47(context.workingDirectory, mediaPath);
|
|
54233
55410
|
const { stdout, stderr } = await execSafe("ffmpeg", ["-i", absPath, "-af", `silencedetect=noise=${noise}dB:d=${duration}`, "-f", "null", "-"], { maxBuffer: 50 * 1024 * 1024 });
|
|
54234
55411
|
const output = stdout + stderr;
|
|
54235
55412
|
const silences = [];
|
|
@@ -54255,8 +55432,8 @@ ${sceneList}`
|
|
|
54255
55432
|
(s, idx) => `[${idx + 1}] ${formatTimestamp2(s.start)} - ${formatTimestamp2(s.end)} (${s.duration.toFixed(1)}s)`
|
|
54256
55433
|
).join("\n");
|
|
54257
55434
|
if (args.output) {
|
|
54258
|
-
const outputPath =
|
|
54259
|
-
await
|
|
55435
|
+
const outputPath = resolve47(context.workingDirectory, args.output);
|
|
55436
|
+
await writeFile36(
|
|
54260
55437
|
outputPath,
|
|
54261
55438
|
JSON.stringify({ source: absPath, silences }, null, 2),
|
|
54262
55439
|
"utf-8"
|
|
@@ -54280,7 +55457,7 @@ ${silenceList}` : "No silence periods detected."
|
|
|
54280
55457
|
detectBeats = async (args, context) => {
|
|
54281
55458
|
const audioPath = args.audio;
|
|
54282
55459
|
try {
|
|
54283
|
-
const absPath =
|
|
55460
|
+
const absPath = resolve47(context.workingDirectory, audioPath);
|
|
54284
55461
|
const totalDuration = await ffprobeDuration(absPath);
|
|
54285
55462
|
const estimatedBPM = 120;
|
|
54286
55463
|
const beatInterval = 60 / estimatedBPM;
|
|
@@ -54292,8 +55469,8 @@ ${silenceList}` : "No silence periods detected."
|
|
|
54292
55469
|
(t, i) => `[${i + 1}] ${formatTimestamp2(t)}`
|
|
54293
55470
|
).join("\n");
|
|
54294
55471
|
if (args.output) {
|
|
54295
|
-
const outputPath =
|
|
54296
|
-
await
|
|
55472
|
+
const outputPath = resolve47(context.workingDirectory, args.output);
|
|
55473
|
+
await writeFile36(
|
|
54297
55474
|
outputPath,
|
|
54298
55475
|
JSON.stringify({ source: absPath, beatCount: beats.length, beats }, null, 2),
|
|
54299
55476
|
"utf-8"
|
|
@@ -54328,8 +55505,8 @@ ${beatList}${beats.length > 20 ? `
|
|
|
54328
55505
|
error: "OpenAI API key required for transcription. Set OPENAI_API_KEY or configure via 'vibe setup'."
|
|
54329
55506
|
};
|
|
54330
55507
|
}
|
|
54331
|
-
const absPath =
|
|
54332
|
-
const audioBuffer = await
|
|
55508
|
+
const absPath = resolve47(context.workingDirectory, audioPath);
|
|
55509
|
+
const audioBuffer = await readFile40(absPath);
|
|
54333
55510
|
const { WhisperProvider: WhisperProvider2 } = await Promise.resolve().then(() => (init_dist2(), dist_exports2));
|
|
54334
55511
|
const whisper = new WhisperProvider2();
|
|
54335
55512
|
await whisper.initialize({ apiKey });
|
|
@@ -54357,11 +55534,11 @@ ${result.fullText}`;
|
|
|
54357
55534
|
}
|
|
54358
55535
|
}
|
|
54359
55536
|
if (args.output) {
|
|
54360
|
-
const outputPath =
|
|
55537
|
+
const outputPath = resolve47(context.workingDirectory, args.output);
|
|
54361
55538
|
const { detectFormat: detectFormat2, formatTranscript: formatTranscript2 } = await Promise.resolve().then(() => (init_subtitle(), subtitle_exports));
|
|
54362
55539
|
const format = detectFormat2(args.output, void 0);
|
|
54363
55540
|
const content = formatTranscript2(result, format);
|
|
54364
|
-
await
|
|
55541
|
+
await writeFile36(outputPath, content, "utf-8");
|
|
54365
55542
|
output += `
|
|
54366
55543
|
|
|
54367
55544
|
Saved to: ${args.output}`;
|
|
@@ -54460,10 +55637,10 @@ Saved to: ${args.output}`;
|
|
|
54460
55637
|
const inputPath = args.input;
|
|
54461
55638
|
const quality = args.quality || "medium";
|
|
54462
55639
|
try {
|
|
54463
|
-
const absInput =
|
|
55640
|
+
const absInput = resolve47(context.workingDirectory, inputPath);
|
|
54464
55641
|
const ext = inputPath.split(".").pop() || "mp4";
|
|
54465
55642
|
const baseName = inputPath.replace(/\.[^/.]+$/, "");
|
|
54466
|
-
const outputPath = args.output ?
|
|
55643
|
+
const outputPath = args.output ? resolve47(context.workingDirectory, args.output) : resolve47(context.workingDirectory, `${baseName}-compressed.${ext}`);
|
|
54467
55644
|
const crfValues = {
|
|
54468
55645
|
low: 28,
|
|
54469
55646
|
medium: 23,
|
|
@@ -54471,8 +55648,8 @@ Saved to: ${args.output}`;
|
|
|
54471
55648
|
};
|
|
54472
55649
|
const crf = crfValues[quality] || 23;
|
|
54473
55650
|
await execSafe("ffmpeg", ["-i", absInput, "-c:v", "libx264", "-crf", String(crf), "-preset", "medium", "-c:a", "aac", "-b:a", "128k", outputPath, "-y"], { maxBuffer: 50 * 1024 * 1024 });
|
|
54474
|
-
const inputStats = await
|
|
54475
|
-
const outputStats = await
|
|
55651
|
+
const inputStats = await readFile40(absInput);
|
|
55652
|
+
const outputStats = await readFile40(outputPath);
|
|
54476
55653
|
const inputSize = inputStats.length;
|
|
54477
55654
|
const outputSize = outputStats.length;
|
|
54478
55655
|
const reduction = ((inputSize - outputSize) / inputSize * 100).toFixed(1);
|
|
@@ -54497,8 +55674,8 @@ Size: ${formatSize2(inputSize)} \u2192 ${formatSize2(outputSize)} (${reduction}%
|
|
|
54497
55674
|
const codec = args.codec;
|
|
54498
55675
|
const audioCodec = args.audioCodec;
|
|
54499
55676
|
try {
|
|
54500
|
-
const absInput =
|
|
54501
|
-
const absOutput =
|
|
55677
|
+
const absInput = resolve47(context.workingDirectory, inputPath);
|
|
55678
|
+
const absOutput = resolve47(context.workingDirectory, outputPath);
|
|
54502
55679
|
let videoCodecName = "copy";
|
|
54503
55680
|
let audioCodecName = "copy";
|
|
54504
55681
|
if (codec) {
|
|
@@ -54546,19 +55723,19 @@ Size: ${formatSize2(inputSize)} \u2192 ${formatSize2(outputSize)} (${reduction}%
|
|
|
54546
55723
|
};
|
|
54547
55724
|
}
|
|
54548
55725
|
try {
|
|
54549
|
-
const absOutput =
|
|
55726
|
+
const absOutput = resolve47(context.workingDirectory, outputPath);
|
|
54550
55727
|
if (reencode) {
|
|
54551
55728
|
const ffmpegArgs = [];
|
|
54552
55729
|
for (const i of inputs) {
|
|
54553
|
-
ffmpegArgs.push("-i",
|
|
55730
|
+
ffmpegArgs.push("-i", resolve47(context.workingDirectory, i));
|
|
54554
55731
|
}
|
|
54555
55732
|
const filterComplex = inputs.map((_, i) => `[${i}:v][${i}:a]`).join("");
|
|
54556
55733
|
ffmpegArgs.push("-filter_complex", `${filterComplex}concat=n=${inputs.length}:v=1:a=1[outv][outa]`, "-map", "[outv]", "-map", "[outa]", absOutput, "-y");
|
|
54557
55734
|
await execSafe("ffmpeg", ffmpegArgs, { maxBuffer: 100 * 1024 * 1024 });
|
|
54558
55735
|
} else {
|
|
54559
|
-
const tempList =
|
|
54560
|
-
const listContent = inputs.map((i) => `file '${
|
|
54561
|
-
await
|
|
55736
|
+
const tempList = resolve47(context.workingDirectory, `concat-list-${Date.now()}.txt`);
|
|
55737
|
+
const listContent = inputs.map((i) => `file '${resolve47(context.workingDirectory, i)}'`).join("\n");
|
|
55738
|
+
await writeFile36(tempList, listContent, "utf-8");
|
|
54562
55739
|
await execSafe("ffmpeg", ["-f", "concat", "-safe", "0", "-i", tempList, "-c", "copy", absOutput, "-y"], { maxBuffer: 100 * 1024 * 1024 });
|
|
54563
55740
|
const { unlink: unlink3 } = await import("node:fs/promises");
|
|
54564
55741
|
await unlink3(tempList).catch(() => {
|
|
@@ -54582,8 +55759,8 @@ Size: ${formatSize2(inputSize)} \u2192 ${formatSize2(outputSize)} (${reduction}%
|
|
|
54582
55759
|
});
|
|
54583
55760
|
|
|
54584
55761
|
// src/agent/tools/ai-generation.ts
|
|
54585
|
-
import { writeFile as
|
|
54586
|
-
import { resolve as
|
|
55762
|
+
import { writeFile as writeFile37, readFile as readFile41 } from "node:fs/promises";
|
|
55763
|
+
import { resolve as resolve48 } from "node:path";
|
|
54587
55764
|
function getTimestamp() {
|
|
54588
55765
|
return Date.now().toString();
|
|
54589
55766
|
}
|
|
@@ -54852,7 +56029,7 @@ var init_ai_generation = __esm({
|
|
|
54852
56029
|
error: `API key required for ${provider}. Configure via 'vibe setup'.`
|
|
54853
56030
|
};
|
|
54854
56031
|
}
|
|
54855
|
-
const outputPath =
|
|
56032
|
+
const outputPath = resolve48(context.workingDirectory, output);
|
|
54856
56033
|
if (provider === "dalle" || provider === "openai") {
|
|
54857
56034
|
const { OpenAIImageProvider: OpenAIImageProvider2 } = await Promise.resolve().then(() => (init_dist2(), dist_exports2));
|
|
54858
56035
|
const openaiImage = new OpenAIImageProvider2();
|
|
@@ -54883,7 +56060,7 @@ var init_ai_generation = __esm({
|
|
|
54883
56060
|
error: "Image generated but no URL or base64 data returned"
|
|
54884
56061
|
};
|
|
54885
56062
|
}
|
|
54886
|
-
await
|
|
56063
|
+
await writeFile37(outputPath, buffer);
|
|
54887
56064
|
} else if (provider === "gemini") {
|
|
54888
56065
|
const { GeminiProvider: GeminiProvider2 } = await Promise.resolve().then(() => (init_dist2(), dist_exports2));
|
|
54889
56066
|
const gemini = new GeminiProvider2();
|
|
@@ -54900,7 +56077,7 @@ var init_ai_generation = __esm({
|
|
|
54900
56077
|
const image = result.images[0];
|
|
54901
56078
|
if (image.base64) {
|
|
54902
56079
|
const buffer = Buffer.from(image.base64, "base64");
|
|
54903
|
-
await
|
|
56080
|
+
await writeFile37(outputPath, buffer);
|
|
54904
56081
|
}
|
|
54905
56082
|
} else if (provider === "grok") {
|
|
54906
56083
|
const { GrokProvider: GrokProvider2 } = await Promise.resolve().then(() => (init_dist2(), dist_exports2));
|
|
@@ -54919,10 +56096,10 @@ var init_ai_generation = __esm({
|
|
|
54919
56096
|
if (image.url) {
|
|
54920
56097
|
const response = await fetch(image.url);
|
|
54921
56098
|
const buffer = Buffer.from(await response.arrayBuffer());
|
|
54922
|
-
await
|
|
56099
|
+
await writeFile37(outputPath, buffer);
|
|
54923
56100
|
} else if (image.base64) {
|
|
54924
56101
|
const buffer = Buffer.from(image.base64, "base64");
|
|
54925
|
-
await
|
|
56102
|
+
await writeFile37(outputPath, buffer);
|
|
54926
56103
|
}
|
|
54927
56104
|
}
|
|
54928
56105
|
return {
|
|
@@ -54958,8 +56135,8 @@ Size: ${size}`
|
|
|
54958
56135
|
};
|
|
54959
56136
|
}
|
|
54960
56137
|
async function prepareReferenceImage(imgPath) {
|
|
54961
|
-
const absImagePath =
|
|
54962
|
-
const imageBuffer = await
|
|
56138
|
+
const absImagePath = resolve48(context.workingDirectory, imgPath);
|
|
56139
|
+
const imageBuffer = await readFile41(absImagePath);
|
|
54963
56140
|
const base64 = imageBuffer.toString("base64");
|
|
54964
56141
|
const ext = imgPath.split(".").pop()?.toLowerCase() || "png";
|
|
54965
56142
|
const mimeType = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : `image/${ext}`;
|
|
@@ -55013,9 +56190,9 @@ Size: ${size}`
|
|
|
55013
56190
|
};
|
|
55014
56191
|
}
|
|
55015
56192
|
if (finalResult.videoUrl) {
|
|
55016
|
-
const outputPath =
|
|
56193
|
+
const outputPath = resolve48(context.workingDirectory, output);
|
|
55017
56194
|
const buffer = await downloadVideo(finalResult.videoUrl, apiKey);
|
|
55018
|
-
await
|
|
56195
|
+
await writeFile37(outputPath, buffer);
|
|
55019
56196
|
}
|
|
55020
56197
|
}
|
|
55021
56198
|
return {
|
|
@@ -55074,9 +56251,9 @@ Duration: ${duration}s`
|
|
|
55074
56251
|
};
|
|
55075
56252
|
}
|
|
55076
56253
|
if (finalResult.videoUrl) {
|
|
55077
|
-
const outputPath =
|
|
56254
|
+
const outputPath = resolve48(context.workingDirectory, output);
|
|
55078
56255
|
const buffer = await downloadVideo(finalResult.videoUrl, apiKey);
|
|
55079
|
-
await
|
|
56256
|
+
await writeFile37(outputPath, buffer);
|
|
55080
56257
|
}
|
|
55081
56258
|
}
|
|
55082
56259
|
return {
|
|
@@ -55130,9 +56307,9 @@ Duration: ${duration}s`
|
|
|
55130
56307
|
};
|
|
55131
56308
|
}
|
|
55132
56309
|
if (finalResult.videoUrl) {
|
|
55133
|
-
const outputPath =
|
|
56310
|
+
const outputPath = resolve48(context.workingDirectory, output);
|
|
55134
56311
|
const buffer = await downloadVideo(finalResult.videoUrl, apiKey);
|
|
55135
|
-
await
|
|
56312
|
+
await writeFile37(outputPath, buffer);
|
|
55136
56313
|
}
|
|
55137
56314
|
}
|
|
55138
56315
|
return {
|
|
@@ -55215,9 +56392,9 @@ Duration: ${duration}s`
|
|
|
55215
56392
|
};
|
|
55216
56393
|
}
|
|
55217
56394
|
if (finalResult.videoUrl) {
|
|
55218
|
-
const outputPath =
|
|
56395
|
+
const outputPath = resolve48(context.workingDirectory, output);
|
|
55219
56396
|
const buffer = await downloadVideo(finalResult.videoUrl, apiKey);
|
|
55220
|
-
await
|
|
56397
|
+
await writeFile37(outputPath, buffer);
|
|
55221
56398
|
}
|
|
55222
56399
|
}
|
|
55223
56400
|
return {
|
|
@@ -55267,8 +56444,8 @@ Mode: ${mode}`
|
|
|
55267
56444
|
error: `TTS generation failed: ${result.error || "No audio generated"}`
|
|
55268
56445
|
};
|
|
55269
56446
|
}
|
|
55270
|
-
const outputPath =
|
|
55271
|
-
await
|
|
56447
|
+
const outputPath = resolve48(context.workingDirectory, output);
|
|
56448
|
+
await writeFile37(outputPath, result.audioBuffer);
|
|
55272
56449
|
return {
|
|
55273
56450
|
toolCallId: "",
|
|
55274
56451
|
success: true,
|
|
@@ -55312,8 +56489,8 @@ Text: ${text.substring(0, 100)}${text.length > 100 ? "..." : ""}`
|
|
|
55312
56489
|
error: `SFX generation failed: ${result.error || "No audio generated"}`
|
|
55313
56490
|
};
|
|
55314
56491
|
}
|
|
55315
|
-
const outputPath =
|
|
55316
|
-
await
|
|
56492
|
+
const outputPath = resolve48(context.workingDirectory, output);
|
|
56493
|
+
await writeFile37(outputPath, result.audioBuffer);
|
|
55317
56494
|
return {
|
|
55318
56495
|
toolCallId: "",
|
|
55319
56496
|
success: true,
|
|
@@ -55361,8 +56538,8 @@ Prompt: ${prompt3}`
|
|
|
55361
56538
|
error: `Music generation failed: ${result.error || "No audio generated"}`
|
|
55362
56539
|
};
|
|
55363
56540
|
}
|
|
55364
|
-
const outputPath =
|
|
55365
|
-
await
|
|
56541
|
+
const outputPath = resolve48(context.workingDirectory, output);
|
|
56542
|
+
await writeFile37(outputPath, result.audioBuffer);
|
|
55366
56543
|
return {
|
|
55367
56544
|
toolCallId: "",
|
|
55368
56545
|
success: true,
|
|
@@ -55414,10 +56591,10 @@ Provider: ElevenLabs${instrumental ? "\nMode: Instrumental" : ""}`
|
|
|
55414
56591
|
}
|
|
55415
56592
|
}
|
|
55416
56593
|
if (finalResult.audioUrl) {
|
|
55417
|
-
const outputPath =
|
|
56594
|
+
const outputPath = resolve48(context.workingDirectory, output);
|
|
55418
56595
|
const response = await fetch(finalResult.audioUrl);
|
|
55419
56596
|
const buffer = Buffer.from(await response.arrayBuffer());
|
|
55420
|
-
await
|
|
56597
|
+
await writeFile37(outputPath, buffer);
|
|
55421
56598
|
} else {
|
|
55422
56599
|
return {
|
|
55423
56600
|
toolCallId: "",
|
|
@@ -55472,8 +56649,8 @@ Provider: Replicate`
|
|
|
55472
56649
|
error: "Failed to generate storyboard"
|
|
55473
56650
|
};
|
|
55474
56651
|
}
|
|
55475
|
-
const outputPath =
|
|
55476
|
-
await
|
|
56652
|
+
const outputPath = resolve48(context.workingDirectory, output);
|
|
56653
|
+
await writeFile37(outputPath, JSON.stringify(result, null, 2), "utf-8");
|
|
55477
56654
|
const sanitizedResult = sanitizeAIResult(result);
|
|
55478
56655
|
const summary = sanitizedResult.map(
|
|
55479
56656
|
(scene, i) => `Scene ${i + 1}: ${scene.description.substring(0, 60)}...`
|
|
@@ -55497,8 +56674,8 @@ ${summary}`
|
|
|
55497
56674
|
generateMotion3 = async (args, context) => {
|
|
55498
56675
|
try {
|
|
55499
56676
|
const { executeMotion: executeMotion2 } = await Promise.resolve().then(() => (init_ai_motion(), ai_motion_exports));
|
|
55500
|
-
const video = args.video ?
|
|
55501
|
-
const output = args.output ?
|
|
56677
|
+
const video = args.video ? resolve48(context.workingDirectory, args.video) : void 0;
|
|
56678
|
+
const output = args.output ? resolve48(context.workingDirectory, args.output) : void 0;
|
|
55502
56679
|
const result = await executeMotion2({
|
|
55503
56680
|
description: args.description,
|
|
55504
56681
|
duration: args.duration,
|
|
@@ -55540,8 +56717,8 @@ ${summary}`
|
|
|
55540
56717
|
});
|
|
55541
56718
|
|
|
55542
56719
|
// src/agent/tools/ai-pipeline.ts
|
|
55543
|
-
import { writeFile as
|
|
55544
|
-
import { resolve as
|
|
56720
|
+
import { writeFile as writeFile38, readFile as readFile42 } from "node:fs/promises";
|
|
56721
|
+
import { resolve as resolve49 } from "node:path";
|
|
55545
56722
|
function getTimestamp2() {
|
|
55546
56723
|
return Date.now().toString();
|
|
55547
56724
|
}
|
|
@@ -55954,7 +57131,7 @@ This tool re-creates videos for failed scenes using image-to-video (if ImgBB key
|
|
|
55954
57131
|
};
|
|
55955
57132
|
scriptToVideoHandler = async (args, context) => {
|
|
55956
57133
|
const script = args.script;
|
|
55957
|
-
const outputDir = args.outputDir ?
|
|
57134
|
+
const outputDir = args.outputDir ? resolve49(context.workingDirectory, args.outputDir) : resolve49(context.workingDirectory, "script-video-output");
|
|
55958
57135
|
try {
|
|
55959
57136
|
const result = await executeScriptToVideo({
|
|
55960
57137
|
script,
|
|
@@ -56026,9 +57203,9 @@ This tool re-creates videos for failed scenes using image-to-video (if ImgBB key
|
|
|
56026
57203
|
}
|
|
56027
57204
|
};
|
|
56028
57205
|
highlightsHandler = async (args, context) => {
|
|
56029
|
-
const media =
|
|
56030
|
-
const output = args.output ?
|
|
56031
|
-
const project = args.project ?
|
|
57206
|
+
const media = resolve49(context.workingDirectory, args.media);
|
|
57207
|
+
const output = args.output ? resolve49(context.workingDirectory, args.output) : void 0;
|
|
57208
|
+
const project = args.project ? resolve49(context.workingDirectory, args.project) : void 0;
|
|
56032
57209
|
try {
|
|
56033
57210
|
const result = await executeHighlights({
|
|
56034
57211
|
media,
|
|
@@ -56090,8 +57267,8 @@ This tool re-creates videos for failed scenes using image-to-video (if ImgBB key
|
|
|
56090
57267
|
}
|
|
56091
57268
|
};
|
|
56092
57269
|
autoShortsHandler = async (args, context) => {
|
|
56093
|
-
const video =
|
|
56094
|
-
const outputDir = args.outputDir ?
|
|
57270
|
+
const video = resolve49(context.workingDirectory, args.video);
|
|
57271
|
+
const outputDir = args.outputDir ? resolve49(context.workingDirectory, args.outputDir) : void 0;
|
|
56095
57272
|
try {
|
|
56096
57273
|
const result = await executeAutoShorts({
|
|
56097
57274
|
video,
|
|
@@ -56155,7 +57332,7 @@ This tool re-creates videos for failed scenes using image-to-video (if ImgBB key
|
|
|
56155
57332
|
geminiVideoHandler = async (args, context) => {
|
|
56156
57333
|
let source = args.source;
|
|
56157
57334
|
if (!source.includes("youtube.com") && !source.includes("youtu.be")) {
|
|
56158
|
-
source =
|
|
57335
|
+
source = resolve49(context.workingDirectory, source);
|
|
56159
57336
|
}
|
|
56160
57337
|
try {
|
|
56161
57338
|
const result = await executeGeminiVideo({
|
|
@@ -56203,7 +57380,7 @@ This tool re-creates videos for failed scenes using image-to-video (if ImgBB key
|
|
|
56203
57380
|
analyzeHandler = async (args, context) => {
|
|
56204
57381
|
let source = args.source;
|
|
56205
57382
|
if (!source.startsWith("http://") && !source.startsWith("https://")) {
|
|
56206
|
-
source =
|
|
57383
|
+
source = resolve49(context.workingDirectory, source);
|
|
56207
57384
|
}
|
|
56208
57385
|
try {
|
|
56209
57386
|
const result = await executeAnalyze({
|
|
@@ -56305,8 +57482,8 @@ This tool re-creates videos for failed scenes using image-to-video (if ImgBB key
|
|
|
56305
57482
|
}
|
|
56306
57483
|
const imageBuffers = [];
|
|
56307
57484
|
for (const imagePath of images) {
|
|
56308
|
-
const absPath =
|
|
56309
|
-
const buffer = await
|
|
57485
|
+
const absPath = resolve49(context.workingDirectory, imagePath);
|
|
57486
|
+
const buffer = await readFile42(absPath);
|
|
56310
57487
|
imageBuffers.push(buffer);
|
|
56311
57488
|
}
|
|
56312
57489
|
let result;
|
|
@@ -56343,14 +57520,14 @@ This tool re-creates videos for failed scenes using image-to-video (if ImgBB key
|
|
|
56343
57520
|
};
|
|
56344
57521
|
}
|
|
56345
57522
|
const img = result.images[0];
|
|
56346
|
-
const outputPath =
|
|
57523
|
+
const outputPath = resolve49(context.workingDirectory, output);
|
|
56347
57524
|
if (img.base64) {
|
|
56348
57525
|
const buffer = Buffer.from(img.base64, "base64");
|
|
56349
|
-
await
|
|
57526
|
+
await writeFile38(outputPath, buffer);
|
|
56350
57527
|
} else if (img.url) {
|
|
56351
57528
|
const resp = await fetch(img.url);
|
|
56352
57529
|
const arrayBuf = await resp.arrayBuffer();
|
|
56353
|
-
await
|
|
57530
|
+
await writeFile38(outputPath, Buffer.from(arrayBuf));
|
|
56354
57531
|
}
|
|
56355
57532
|
return {
|
|
56356
57533
|
toolCallId: "",
|
|
@@ -56417,8 +57594,8 @@ Failed scenes: ${result.failedScenes.join(", ")}`;
|
|
|
56417
57594
|
};
|
|
56418
57595
|
};
|
|
56419
57596
|
animatedCaptionHandler = async (args, context) => {
|
|
56420
|
-
const videoPath =
|
|
56421
|
-
const outputPath =
|
|
57597
|
+
const videoPath = resolve49(context.workingDirectory, args.videoPath);
|
|
57598
|
+
const outputPath = resolve49(context.workingDirectory, args.outputPath);
|
|
56422
57599
|
try {
|
|
56423
57600
|
const result = await executeAnimatedCaption({
|
|
56424
57601
|
videoPath,
|
|
@@ -56463,7 +57640,7 @@ Tier: ${result.tier}`
|
|
|
56463
57640
|
});
|
|
56464
57641
|
|
|
56465
57642
|
// src/agent/tools/ai-editing.ts
|
|
56466
|
-
import { resolve as
|
|
57643
|
+
import { resolve as resolve50 } from "node:path";
|
|
56467
57644
|
function registerEditingTools(registry) {
|
|
56468
57645
|
registry.register(textOverlayDef, textOverlayHandler);
|
|
56469
57646
|
registry.register(reviewDef, reviewHandler);
|
|
@@ -56892,10 +58069,10 @@ var init_ai_editing = __esm({
|
|
|
56892
58069
|
};
|
|
56893
58070
|
};
|
|
56894
58071
|
silenceCutHandler = async (args, context) => {
|
|
56895
|
-
const videoPath =
|
|
58072
|
+
const videoPath = resolve50(context.workingDirectory, args.videoPath);
|
|
56896
58073
|
const ext = videoPath.split(".").pop() || "mp4";
|
|
56897
58074
|
const name = videoPath.replace(/\.[^.]+$/, "");
|
|
56898
|
-
const outputPath = args.outputPath ?
|
|
58075
|
+
const outputPath = args.outputPath ? resolve50(context.workingDirectory, args.outputPath) : `${name}-cut.${ext}`;
|
|
56899
58076
|
try {
|
|
56900
58077
|
const result = await executeSilenceCut({
|
|
56901
58078
|
videoPath,
|
|
@@ -56940,10 +58117,10 @@ var init_ai_editing = __esm({
|
|
|
56940
58117
|
}
|
|
56941
58118
|
};
|
|
56942
58119
|
jumpCutHandler = async (args, context) => {
|
|
56943
|
-
const videoPath =
|
|
58120
|
+
const videoPath = resolve50(context.workingDirectory, args.videoPath);
|
|
56944
58121
|
const ext = videoPath.split(".").pop() || "mp4";
|
|
56945
58122
|
const name = videoPath.replace(/\.[^.]+$/, "");
|
|
56946
|
-
const outputPath = args.outputPath ?
|
|
58123
|
+
const outputPath = args.outputPath ? resolve50(context.workingDirectory, args.outputPath) : `${name}-jumpcut.${ext}`;
|
|
56947
58124
|
try {
|
|
56948
58125
|
const result = await executeJumpCut({
|
|
56949
58126
|
videoPath,
|
|
@@ -56991,10 +58168,10 @@ var init_ai_editing = __esm({
|
|
|
56991
58168
|
}
|
|
56992
58169
|
};
|
|
56993
58170
|
captionHandler = async (args, context) => {
|
|
56994
|
-
const videoPath =
|
|
58171
|
+
const videoPath = resolve50(context.workingDirectory, args.videoPath);
|
|
56995
58172
|
const ext = videoPath.split(".").pop() || "mp4";
|
|
56996
58173
|
const name = videoPath.replace(/\.[^.]+$/, "");
|
|
56997
|
-
const outputPath = args.outputPath ?
|
|
58174
|
+
const outputPath = args.outputPath ? resolve50(context.workingDirectory, args.outputPath) : `${name}-captioned.${ext}`;
|
|
56998
58175
|
try {
|
|
56999
58176
|
const result = await executeCaption({
|
|
57000
58177
|
videoPath,
|
|
@@ -57034,10 +58211,10 @@ var init_ai_editing = __esm({
|
|
|
57034
58211
|
}
|
|
57035
58212
|
};
|
|
57036
58213
|
noiseReduceHandler = async (args, context) => {
|
|
57037
|
-
const inputPath =
|
|
58214
|
+
const inputPath = resolve50(context.workingDirectory, args.inputPath);
|
|
57038
58215
|
const ext = inputPath.split(".").pop() || "mp4";
|
|
57039
58216
|
const name = inputPath.replace(/\.[^.]+$/, "");
|
|
57040
|
-
const outputPath = args.outputPath ?
|
|
58217
|
+
const outputPath = args.outputPath ? resolve50(context.workingDirectory, args.outputPath) : `${name}-denoised.${ext}`;
|
|
57041
58218
|
try {
|
|
57042
58219
|
const result = await executeNoiseReduce({
|
|
57043
58220
|
inputPath,
|
|
@@ -57071,10 +58248,10 @@ var init_ai_editing = __esm({
|
|
|
57071
58248
|
}
|
|
57072
58249
|
};
|
|
57073
58250
|
fadeHandler = async (args, context) => {
|
|
57074
|
-
const videoPath =
|
|
58251
|
+
const videoPath = resolve50(context.workingDirectory, args.videoPath);
|
|
57075
58252
|
const ext = videoPath.split(".").pop() || "mp4";
|
|
57076
58253
|
const name = videoPath.replace(/\.[^.]+$/, "");
|
|
57077
|
-
const outputPath = args.outputPath ?
|
|
58254
|
+
const outputPath = args.outputPath ? resolve50(context.workingDirectory, args.outputPath) : `${name}-faded.${ext}`;
|
|
57078
58255
|
try {
|
|
57079
58256
|
const result = await executeFade({
|
|
57080
58257
|
videoPath,
|
|
@@ -57112,9 +58289,9 @@ var init_ai_editing = __esm({
|
|
|
57112
58289
|
}
|
|
57113
58290
|
};
|
|
57114
58291
|
thumbnailBestFrameHandler = async (args, context) => {
|
|
57115
|
-
const videoPath =
|
|
58292
|
+
const videoPath = resolve50(context.workingDirectory, args.videoPath);
|
|
57116
58293
|
const name = videoPath.replace(/\.[^.]+$/, "");
|
|
57117
|
-
const outputPath = args.outputPath ?
|
|
58294
|
+
const outputPath = args.outputPath ? resolve50(context.workingDirectory, args.outputPath) : `${name}-thumbnail.png`;
|
|
57118
58295
|
try {
|
|
57119
58296
|
const result = await executeThumbnailBestFrame({
|
|
57120
58297
|
videoPath,
|
|
@@ -57149,11 +58326,11 @@ var init_ai_editing = __esm({
|
|
|
57149
58326
|
}
|
|
57150
58327
|
};
|
|
57151
58328
|
translateSrtHandler = async (args, context) => {
|
|
57152
|
-
const srtPath =
|
|
58329
|
+
const srtPath = resolve50(context.workingDirectory, args.srtPath);
|
|
57153
58330
|
const target = args.targetLanguage;
|
|
57154
58331
|
const ext = srtPath.split(".").pop() || "srt";
|
|
57155
58332
|
const name = srtPath.replace(/\.[^.]+$/, "");
|
|
57156
|
-
const outputPath = args.outputPath ?
|
|
58333
|
+
const outputPath = args.outputPath ? resolve50(context.workingDirectory, args.outputPath) : `${name}-${target}.${ext}`;
|
|
57157
58334
|
try {
|
|
57158
58335
|
const result = await executeTranslateSrt({
|
|
57159
58336
|
srtPath,
|
|
@@ -57215,7 +58392,7 @@ var export_exports = {};
|
|
|
57215
58392
|
__export(export_exports, {
|
|
57216
58393
|
registerExportTools: () => registerExportTools
|
|
57217
58394
|
});
|
|
57218
|
-
import { resolve as
|
|
58395
|
+
import { resolve as resolve51, basename as basename26 } from "node:path";
|
|
57219
58396
|
function registerExportTools(registry) {
|
|
57220
58397
|
registry.register(exportVideoDef, exportVideo);
|
|
57221
58398
|
registry.register(exportAudioDef, exportAudio);
|
|
@@ -57310,8 +58487,8 @@ var init_export2 = __esm({
|
|
|
57310
58487
|
const preset = args.preset || "standard";
|
|
57311
58488
|
const overwrite = args.overwrite || false;
|
|
57312
58489
|
const output = args.output || `${basename26(projectPath, ".vibe.json")}.${format}`;
|
|
57313
|
-
const absProjectPath =
|
|
57314
|
-
const absOutputPath =
|
|
58490
|
+
const absProjectPath = resolve51(context.workingDirectory, projectPath);
|
|
58491
|
+
const absOutputPath = resolve51(context.workingDirectory, output);
|
|
57315
58492
|
try {
|
|
57316
58493
|
const result = await runExport(absProjectPath, absOutputPath, {
|
|
57317
58494
|
format,
|
|
@@ -57384,8 +58561,8 @@ var batch_exports = {};
|
|
|
57384
58561
|
__export(batch_exports, {
|
|
57385
58562
|
registerBatchTools: () => registerBatchTools
|
|
57386
58563
|
});
|
|
57387
|
-
import { readFile as
|
|
57388
|
-
import { resolve as
|
|
58564
|
+
import { readFile as readFile43, writeFile as writeFile39, readdir as readdir6 } from "node:fs/promises";
|
|
58565
|
+
import { resolve as resolve52, basename as basename27, extname as extname16, join as join10 } from "node:path";
|
|
57389
58566
|
function detectMediaType5(filePath) {
|
|
57390
58567
|
const ext = extname16(filePath).toLowerCase();
|
|
57391
58568
|
const videoExts = [".mp4", ".mov", ".avi", ".mkv", ".webm", ".m4v"];
|
|
@@ -57546,17 +58723,17 @@ var init_batch = __esm({
|
|
|
57546
58723
|
const filterStr = args.filter;
|
|
57547
58724
|
const imageDuration = args.imageDuration || 5;
|
|
57548
58725
|
try {
|
|
57549
|
-
const filePath =
|
|
57550
|
-
const content = await
|
|
58726
|
+
const filePath = resolve52(context.workingDirectory, projectPath);
|
|
58727
|
+
const content = await readFile43(filePath, "utf-8");
|
|
57551
58728
|
const data = JSON.parse(content);
|
|
57552
58729
|
const project = Project.fromJSON(data);
|
|
57553
|
-
const dirPath =
|
|
58730
|
+
const dirPath = resolve52(context.workingDirectory, directory);
|
|
57554
58731
|
const filterExts = filterStr ? filterStr.split(",").map((e) => e.trim().toLowerCase()) : null;
|
|
57555
58732
|
const mediaFiles = [];
|
|
57556
58733
|
const scanDir = async (dir) => {
|
|
57557
58734
|
const entries = await readdir6(dir, { withFileTypes: true });
|
|
57558
58735
|
for (const entry of entries) {
|
|
57559
|
-
const entryPath =
|
|
58736
|
+
const entryPath = join10(dir, entry.name);
|
|
57560
58737
|
if (entry.isDirectory() && recursive) {
|
|
57561
58738
|
await scanDir(entryPath);
|
|
57562
58739
|
} else if (entry.isFile()) {
|
|
@@ -57597,7 +58774,7 @@ var init_batch = __esm({
|
|
|
57597
58774
|
});
|
|
57598
58775
|
addedSources.push({ id: source.id, name: mediaName, type: mediaType });
|
|
57599
58776
|
}
|
|
57600
|
-
await
|
|
58777
|
+
await writeFile39(filePath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
|
|
57601
58778
|
const output = [
|
|
57602
58779
|
`Imported ${addedSources.length} media files:`,
|
|
57603
58780
|
"",
|
|
@@ -57625,8 +58802,8 @@ var init_batch = __esm({
|
|
|
57625
58802
|
const startTime = args.startTime || 0;
|
|
57626
58803
|
const gap = args.gap || 0;
|
|
57627
58804
|
try {
|
|
57628
|
-
const filePath =
|
|
57629
|
-
const content = await
|
|
58805
|
+
const filePath = resolve52(context.workingDirectory, projectPath);
|
|
58806
|
+
const content = await readFile43(filePath, "utf-8");
|
|
57630
58807
|
const data = JSON.parse(content);
|
|
57631
58808
|
const project = Project.fromJSON(data);
|
|
57632
58809
|
const sourcesToConcat = useAll ? project.getSources() : sourceIds.map((id) => project.getSource(id)).filter(Boolean);
|
|
@@ -57673,7 +58850,7 @@ var init_batch = __esm({
|
|
|
57673
58850
|
});
|
|
57674
58851
|
currentTime += source.duration + gap;
|
|
57675
58852
|
}
|
|
57676
|
-
await
|
|
58853
|
+
await writeFile39(filePath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
|
|
57677
58854
|
const totalDuration = currentTime - gap - startTime;
|
|
57678
58855
|
const output = [
|
|
57679
58856
|
`Created ${createdClips.length} clips (total: ${totalDuration.toFixed(1)}s):`,
|
|
@@ -57702,8 +58879,8 @@ var init_batch = __esm({
|
|
|
57702
58879
|
const duration = args.duration;
|
|
57703
58880
|
const rawParams = args.params || {};
|
|
57704
58881
|
try {
|
|
57705
|
-
const filePath =
|
|
57706
|
-
const content = await
|
|
58882
|
+
const filePath = resolve52(context.workingDirectory, projectPath);
|
|
58883
|
+
const content = await readFile43(filePath, "utf-8");
|
|
57707
58884
|
const data = JSON.parse(content);
|
|
57708
58885
|
const project = Project.fromJSON(data);
|
|
57709
58886
|
const targetClips = useAll ? project.getClips() : clipIds.map((id) => project.getClip(id)).filter(Boolean);
|
|
@@ -57735,7 +58912,7 @@ var init_batch = __esm({
|
|
|
57735
58912
|
appliedEffects.push({ clipId: clip.id, effectId: effect.id });
|
|
57736
58913
|
}
|
|
57737
58914
|
}
|
|
57738
|
-
await
|
|
58915
|
+
await writeFile39(filePath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
|
|
57739
58916
|
return {
|
|
57740
58917
|
toolCallId: "",
|
|
57741
58918
|
success: true,
|
|
@@ -57759,7 +58936,7 @@ __export(scene_exports, {
|
|
|
57759
58936
|
registerSceneTools: () => registerSceneTools,
|
|
57760
58937
|
sceneToolDefinitions: () => sceneToolDefinitions
|
|
57761
58938
|
});
|
|
57762
|
-
import { resolve as
|
|
58939
|
+
import { resolve as resolve53, relative as relative7 } from "node:path";
|
|
57763
58940
|
function summariseLint(result) {
|
|
57764
58941
|
const lines = [
|
|
57765
58942
|
`${result.ok ? "\u2705" : "\u274C"} Lint ${result.ok ? "clean" : "failed"} \u2014 ${result.errorCount} error(s), ${result.warningCount} warning(s), ${result.infoCount} info`
|
|
@@ -57870,7 +59047,7 @@ var init_scene2 = __esm({
|
|
|
57870
59047
|
}
|
|
57871
59048
|
};
|
|
57872
59049
|
sceneInitHandler = async (args, context) => {
|
|
57873
|
-
const dir =
|
|
59050
|
+
const dir = resolve53(context.workingDirectory, args.dir);
|
|
57874
59051
|
try {
|
|
57875
59052
|
const result = await scaffoldSceneProject({
|
|
57876
59053
|
dir,
|
|
@@ -57897,7 +59074,7 @@ var init_scene2 = __esm({
|
|
|
57897
59074
|
}
|
|
57898
59075
|
};
|
|
57899
59076
|
sceneAddHandler = async (args, context) => {
|
|
57900
|
-
const projectDir = args.projectDir ?
|
|
59077
|
+
const projectDir = args.projectDir ? resolve53(context.workingDirectory, args.projectDir) : context.workingDirectory;
|
|
57901
59078
|
const result = await executeSceneAdd({
|
|
57902
59079
|
name: args.name,
|
|
57903
59080
|
preset: args.preset ?? "simple",
|
|
@@ -57934,7 +59111,7 @@ var init_scene2 = __esm({
|
|
|
57934
59111
|
return { toolCallId: "", success: true, output: lines.join("\n") };
|
|
57935
59112
|
};
|
|
57936
59113
|
sceneLintHandler = async (args, context) => {
|
|
57937
|
-
const projectDir = args.projectDir ?
|
|
59114
|
+
const projectDir = args.projectDir ? resolve53(context.workingDirectory, args.projectDir) : context.workingDirectory;
|
|
57938
59115
|
try {
|
|
57939
59116
|
const result = await runProjectLint({
|
|
57940
59117
|
projectDir,
|
|
@@ -57957,7 +59134,7 @@ var init_scene2 = __esm({
|
|
|
57957
59134
|
}
|
|
57958
59135
|
};
|
|
57959
59136
|
sceneRenderHandler = async (args, context) => {
|
|
57960
|
-
const projectDir = args.projectDir ?
|
|
59137
|
+
const projectDir = args.projectDir ? resolve53(context.workingDirectory, args.projectDir) : context.workingDirectory;
|
|
57961
59138
|
const result = await executeSceneRender({
|
|
57962
59139
|
projectDir,
|
|
57963
59140
|
root: args.root,
|
|
@@ -57999,7 +59176,7 @@ init_source();
|
|
|
57999
59176
|
// package.json
|
|
58000
59177
|
var package_default = {
|
|
58001
59178
|
name: "@vibeframe/cli",
|
|
58002
|
-
version: "0.
|
|
59179
|
+
version: "0.59.0",
|
|
58003
59180
|
description: "VibeFrame CLI - natural language video editing from the terminal",
|
|
58004
59181
|
private: false,
|
|
58005
59182
|
license: "MIT",
|
|
@@ -58225,8 +59402,8 @@ projectCommand.command("create").description("Create a new project").argument("<
|
|
|
58225
59402
|
outputPath = resolve2(process.cwd(), options.output);
|
|
58226
59403
|
} else if (name.includes("/")) {
|
|
58227
59404
|
const dirPath = resolve2(process.cwd(), name);
|
|
58228
|
-
const { mkdir:
|
|
58229
|
-
await
|
|
59405
|
+
const { mkdir: mkdir22 } = await import("node:fs/promises");
|
|
59406
|
+
await mkdir22(dirPath, { recursive: true });
|
|
58230
59407
|
outputPath = resolve2(dirPath, "project.vibe.json");
|
|
58231
59408
|
} else {
|
|
58232
59409
|
outputPath = resolve2(process.cwd(), "project.vibe.json");
|
|
@@ -60111,9 +61288,9 @@ function registerViralCommand(ai) {
|
|
|
60111
61288
|
exitWithError(authError("ANTHROPIC_API_KEY", "Anthropic"));
|
|
60112
61289
|
}
|
|
60113
61290
|
let filePath = resolve28(process.cwd(), projectPath);
|
|
60114
|
-
const { statSync } = await import("node:fs");
|
|
61291
|
+
const { statSync: statSync2 } = await import("node:fs");
|
|
60115
61292
|
try {
|
|
60116
|
-
if (
|
|
61293
|
+
if (statSync2(filePath).isDirectory()) {
|
|
60117
61294
|
const candidates = ["project.vibe.json", ".vibe.json"];
|
|
60118
61295
|
let found = false;
|
|
60119
61296
|
for (const candidate of candidates) {
|
|
@@ -66237,20 +67414,20 @@ async function runDiagnostics() {
|
|
|
66237
67414
|
installed = false;
|
|
66238
67415
|
}
|
|
66239
67416
|
} else if (toolName === "chrome") {
|
|
66240
|
-
const { existsSync:
|
|
66241
|
-
const { homedir:
|
|
66242
|
-
const { join:
|
|
67417
|
+
const { existsSync: existsSync30 } = await import("node:fs");
|
|
67418
|
+
const { homedir: homedir5 } = await import("node:os");
|
|
67419
|
+
const { join: join11 } = await import("node:path");
|
|
66243
67420
|
const chromePaths = [
|
|
66244
67421
|
process.env.HYPERFRAMES_CHROME_PATH,
|
|
66245
67422
|
process.env.CHROME_PATH,
|
|
66246
|
-
|
|
67423
|
+
join11(homedir5(), ".cache", "puppeteer", "chrome-headless-shell", "mac_arm-147.0.7727.56", "chrome-headless-shell-mac_arm", "chrome-headless-shell"),
|
|
66247
67424
|
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
|
|
66248
67425
|
"/Applications/Chromium.app/Contents/MacOS/Chromium",
|
|
66249
67426
|
"/usr/bin/google-chrome",
|
|
66250
67427
|
"/usr/bin/chromium",
|
|
66251
67428
|
"/usr/bin/chromium-browser"
|
|
66252
67429
|
];
|
|
66253
|
-
installed = chromePaths.some((p) => p &&
|
|
67430
|
+
installed = chromePaths.some((p) => p && existsSync30(p));
|
|
66254
67431
|
} else {
|
|
66255
67432
|
installed = commandExists(toolName);
|
|
66256
67433
|
}
|
|
@@ -66631,9 +67808,9 @@ init_ora();
|
|
|
66631
67808
|
|
|
66632
67809
|
// src/pipeline/executor.ts
|
|
66633
67810
|
var import_yaml7 = __toESM(require_dist(), 1);
|
|
66634
|
-
import { resolve as
|
|
66635
|
-
import { readFile as
|
|
66636
|
-
import { existsSync as
|
|
67811
|
+
import { resolve as resolve43 } from "node:path";
|
|
67812
|
+
import { readFile as readFile36, writeFile as writeFile32, mkdir as mkdir20 } from "node:fs/promises";
|
|
67813
|
+
import { existsSync as existsSync29 } from "node:fs";
|
|
66637
67814
|
|
|
66638
67815
|
// src/pipeline/resolver.ts
|
|
66639
67816
|
function resolveStepParams(params, completedSteps) {
|
|
@@ -66719,7 +67896,8 @@ var ACTION_TO_COMMAND = {
|
|
|
66719
67896
|
"detect-beats": "detect beats",
|
|
66720
67897
|
"analyze-media": "analyze media",
|
|
66721
67898
|
"analyze-video": "analyze video",
|
|
66722
|
-
"review-video": "analyze review"
|
|
67899
|
+
"review-video": "analyze review",
|
|
67900
|
+
"compose-scenes-with-skills": "compose scenes with skills"
|
|
66723
67901
|
};
|
|
66724
67902
|
function maxCostFor(action) {
|
|
66725
67903
|
const cmd = ACTION_TO_COMMAND[action];
|
|
@@ -66732,9 +67910,9 @@ function registerAction(action, handler) {
|
|
|
66732
67910
|
}
|
|
66733
67911
|
function getOutput(params, outputDir, defaultName) {
|
|
66734
67912
|
if (params.output && typeof params.output === "string") {
|
|
66735
|
-
return
|
|
67913
|
+
return resolve43(outputDir, params.output);
|
|
66736
67914
|
}
|
|
66737
|
-
return
|
|
67915
|
+
return resolve43(outputDir, defaultName);
|
|
66738
67916
|
}
|
|
66739
67917
|
async function ensureActionsRegistered() {
|
|
66740
67918
|
if (Object.keys(ACTION_HANDLERS).length > 0) return;
|
|
@@ -66854,13 +68032,33 @@ async function ensureActionsRegistered() {
|
|
|
66854
68032
|
const r = await executeReview2({ videoPath: params.input, autoApply: params.autoApply, model: params.model });
|
|
66855
68033
|
return { id: "", action: "review-video", success: r.success, output: r.outputPath, data: { feedback: r.feedback }, error: r.error };
|
|
66856
68034
|
});
|
|
68035
|
+
registerAction("compose-scenes-with-skills", async (params, outputDir) => {
|
|
68036
|
+
const { executeComposeScenesWithSkills: executeComposeScenesWithSkills2 } = await Promise.resolve().then(() => (init_compose_scenes_skills(), compose_scenes_skills_exports));
|
|
68037
|
+
const r = await executeComposeScenesWithSkills2(
|
|
68038
|
+
{
|
|
68039
|
+
design: params.design,
|
|
68040
|
+
storyboard: params.storyboard,
|
|
68041
|
+
project: params.project,
|
|
68042
|
+
effort: params.effort
|
|
68043
|
+
},
|
|
68044
|
+
outputDir
|
|
68045
|
+
);
|
|
68046
|
+
return {
|
|
68047
|
+
id: "",
|
|
68048
|
+
action: "compose-scenes-with-skills",
|
|
68049
|
+
success: r.success,
|
|
68050
|
+
output: r.outputPath,
|
|
68051
|
+
data: r.data,
|
|
68052
|
+
error: r.error
|
|
68053
|
+
};
|
|
68054
|
+
});
|
|
66857
68055
|
}
|
|
66858
68056
|
async function loadPipeline(filePath) {
|
|
66859
|
-
const absPath =
|
|
66860
|
-
if (!
|
|
68057
|
+
const absPath = resolve43(process.cwd(), filePath);
|
|
68058
|
+
if (!existsSync29(absPath)) {
|
|
66861
68059
|
throw new Error(`Pipeline file not found: ${absPath}`);
|
|
66862
68060
|
}
|
|
66863
|
-
const content = await
|
|
68061
|
+
const content = await readFile36(absPath, "utf-8");
|
|
66864
68062
|
const manifest = (0, import_yaml7.parse)(content);
|
|
66865
68063
|
if (!manifest.name) throw new Error("Pipeline missing 'name' field");
|
|
66866
68064
|
if (!manifest.steps || !Array.isArray(manifest.steps)) throw new Error("Pipeline missing 'steps' array");
|
|
@@ -66876,15 +68074,15 @@ async function loadPipeline(filePath) {
|
|
|
66876
68074
|
var CHECKPOINT_FILE = ".pipeline-state.yaml";
|
|
66877
68075
|
async function executePipeline(manifest, options = {}) {
|
|
66878
68076
|
await ensureActionsRegistered();
|
|
66879
|
-
const outputDir =
|
|
66880
|
-
await
|
|
68077
|
+
const outputDir = resolve43(process.cwd(), options.outputDir || `${manifest.name}-output`);
|
|
68078
|
+
await mkdir20(outputDir, { recursive: true });
|
|
66881
68079
|
const completedSteps = /* @__PURE__ */ new Map();
|
|
66882
68080
|
const results = [];
|
|
66883
68081
|
const startTime = Date.now();
|
|
66884
68082
|
if (options.resume) {
|
|
66885
|
-
const checkpointPath =
|
|
66886
|
-
if (
|
|
66887
|
-
const checkpointContent = await
|
|
68083
|
+
const checkpointPath = resolve43(outputDir, CHECKPOINT_FILE);
|
|
68084
|
+
if (existsSync29(checkpointPath)) {
|
|
68085
|
+
const checkpointContent = await readFile36(checkpointPath, "utf-8");
|
|
66888
68086
|
const checkpoint = (0, import_yaml7.parse)(checkpointContent);
|
|
66889
68087
|
for (const cs of checkpoint.completedSteps) {
|
|
66890
68088
|
completedSteps.set(cs.id, {
|
|
@@ -67002,8 +68200,8 @@ async function executePipeline(manifest, options = {}) {
|
|
|
67002
68200
|
data: s.data
|
|
67003
68201
|
}))
|
|
67004
68202
|
};
|
|
67005
|
-
await
|
|
67006
|
-
|
|
68203
|
+
await writeFile32(
|
|
68204
|
+
resolve43(outputDir, CHECKPOINT_FILE),
|
|
67007
68205
|
(0, import_yaml7.stringify)(checkpoint, { indent: 2 }),
|
|
67008
68206
|
"utf-8"
|
|
67009
68207
|
);
|
|
@@ -67171,7 +68369,8 @@ Run 'vibe schema run' for structured parameter info.
|
|
|
67171
68369
|
success: s.success,
|
|
67172
68370
|
output: s.output,
|
|
67173
68371
|
duration: s.duration,
|
|
67174
|
-
error: s.error
|
|
68372
|
+
error: s.error,
|
|
68373
|
+
data: s.data
|
|
67175
68374
|
}))
|
|
67176
68375
|
});
|
|
67177
68376
|
} else {
|
|
@@ -67760,14 +68959,14 @@ async function promptConfirm2(toolName, args) {
|
|
|
67760
68959
|
output: process.stdout,
|
|
67761
68960
|
terminal: process.stdin.isTTY ?? false
|
|
67762
68961
|
});
|
|
67763
|
-
return new Promise((
|
|
68962
|
+
return new Promise((resolve54) => {
|
|
67764
68963
|
const argsStr = JSON.stringify(args, null, 2);
|
|
67765
68964
|
console.log();
|
|
67766
68965
|
console.log(source_default.yellow(`Execute ${source_default.bold(toolName)}?`));
|
|
67767
68966
|
console.log(source_default.dim(argsStr));
|
|
67768
68967
|
rl.question(source_default.cyan("(y/n): "), (answer) => {
|
|
67769
68968
|
rl.close();
|
|
67770
|
-
|
|
68969
|
+
resolve54(answer.toLowerCase() === "y" || answer.toLowerCase() === "yes");
|
|
67771
68970
|
});
|
|
67772
68971
|
});
|
|
67773
68972
|
}
|
|
@@ -67856,7 +69055,7 @@ async function startAgent(options = {}) {
|
|
|
67856
69055
|
console.log();
|
|
67857
69056
|
console.log(source_default.dim(" Commands: exit \xB7 reset \xB7 tools \xB7 context"));
|
|
67858
69057
|
console.log();
|
|
67859
|
-
return new Promise((
|
|
69058
|
+
return new Promise((resolve54) => {
|
|
67860
69059
|
if (typeof process.stdin.ref === "function") {
|
|
67861
69060
|
process.stdin.ref();
|
|
67862
69061
|
}
|
|
@@ -67958,7 +69157,7 @@ async function startAgent(options = {}) {
|
|
|
67958
69157
|
clearInterval(keepalive);
|
|
67959
69158
|
console.log();
|
|
67960
69159
|
console.log(source_default.dim("Goodbye!"));
|
|
67961
|
-
|
|
69160
|
+
resolve54();
|
|
67962
69161
|
});
|
|
67963
69162
|
rl.prompt();
|
|
67964
69163
|
});
|
|
@@ -67986,8 +69185,8 @@ init_source();
|
|
|
67986
69185
|
init_config();
|
|
67987
69186
|
init_schema();
|
|
67988
69187
|
init_api_key();
|
|
67989
|
-
import { access as access9, writeFile as
|
|
67990
|
-
import { dirname as
|
|
69188
|
+
import { access as access9, writeFile as writeFile40, mkdir as mkdir21 } from "node:fs/promises";
|
|
69189
|
+
import { dirname as dirname21 } from "node:path";
|
|
67991
69190
|
var BANNER_SHOWN_PATH = CONFIG_PATH.replace(/config\.yaml$/, ".banner-shown");
|
|
67992
69191
|
async function isFirstRun() {
|
|
67993
69192
|
try {
|
|
@@ -68010,8 +69209,8 @@ async function isFirstRun() {
|
|
|
68010
69209
|
}
|
|
68011
69210
|
async function markBannerShown() {
|
|
68012
69211
|
try {
|
|
68013
|
-
await
|
|
68014
|
-
await
|
|
69212
|
+
await mkdir21(dirname21(BANNER_SHOWN_PATH), { recursive: true });
|
|
69213
|
+
await writeFile40(BANNER_SHOWN_PATH, (/* @__PURE__ */ new Date()).toISOString());
|
|
68015
69214
|
} catch {
|
|
68016
69215
|
}
|
|
68017
69216
|
}
|