vidpipe 1.3.6 → 1.3.7
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/cli.js +1357 -269
- package/dist/cli.js.map +1 -1
- package/package.json +2 -1
package/dist/cli.js
CHANGED
|
@@ -316,7 +316,9 @@ function initConfig(cli = {}) {
|
|
|
316
316
|
LATE_PROFILE_ID: cli.lateProfileId || process.env.LATE_PROFILE_ID || "",
|
|
317
317
|
SKIP_SOCIAL_PUBLISH: cli.socialPublish === false,
|
|
318
318
|
GEMINI_API_KEY: process.env.GEMINI_API_KEY || "",
|
|
319
|
-
GEMINI_MODEL: process.env.GEMINI_MODEL || "gemini-2.5-pro"
|
|
319
|
+
GEMINI_MODEL: process.env.GEMINI_MODEL || "gemini-2.5-pro",
|
|
320
|
+
IDEAS_REPO: cli.ideasRepo || process.env.IDEAS_REPO || "htekdev/content-management",
|
|
321
|
+
GITHUB_TOKEN: cli.githubToken || process.env.GITHUB_TOKEN || ""
|
|
320
322
|
};
|
|
321
323
|
return config;
|
|
322
324
|
}
|
|
@@ -402,6 +404,54 @@ var init_configLogger = __esm({
|
|
|
402
404
|
}
|
|
403
405
|
});
|
|
404
406
|
|
|
407
|
+
// src/L0-pure/types/index.ts
|
|
408
|
+
function toLatePlatform(platform) {
|
|
409
|
+
return platform === "x" /* X */ ? "twitter" : platform;
|
|
410
|
+
}
|
|
411
|
+
function fromLatePlatform(latePlatform) {
|
|
412
|
+
const normalized = normalizePlatformString(latePlatform);
|
|
413
|
+
if (normalized === "twitter") {
|
|
414
|
+
return "x" /* X */;
|
|
415
|
+
}
|
|
416
|
+
const platformValues2 = Object.values(Platform);
|
|
417
|
+
if (platformValues2.includes(normalized)) {
|
|
418
|
+
return normalized;
|
|
419
|
+
}
|
|
420
|
+
throw new Error(`Unsupported platform from Late API: ${latePlatform}`);
|
|
421
|
+
}
|
|
422
|
+
function normalizePlatformString(raw) {
|
|
423
|
+
const lower = raw.toLowerCase().trim();
|
|
424
|
+
if (lower === "x" || lower === "x (twitter)" || lower === "x/twitter") {
|
|
425
|
+
return "twitter";
|
|
426
|
+
}
|
|
427
|
+
return lower;
|
|
428
|
+
}
|
|
429
|
+
function isSupportedVideoExtension(ext) {
|
|
430
|
+
return SUPPORTED_VIDEO_EXTENSIONS.includes(ext.toLowerCase());
|
|
431
|
+
}
|
|
432
|
+
var Platform, PLATFORM_CHAR_LIMITS, SUPPORTED_VIDEO_EXTENSIONS;
|
|
433
|
+
var init_types = __esm({
|
|
434
|
+
"src/L0-pure/types/index.ts"() {
|
|
435
|
+
"use strict";
|
|
436
|
+
Platform = /* @__PURE__ */ ((Platform2) => {
|
|
437
|
+
Platform2["TikTok"] = "tiktok";
|
|
438
|
+
Platform2["YouTube"] = "youtube";
|
|
439
|
+
Platform2["Instagram"] = "instagram";
|
|
440
|
+
Platform2["LinkedIn"] = "linkedin";
|
|
441
|
+
Platform2["X"] = "x";
|
|
442
|
+
return Platform2;
|
|
443
|
+
})(Platform || {});
|
|
444
|
+
PLATFORM_CHAR_LIMITS = {
|
|
445
|
+
tiktok: 2200,
|
|
446
|
+
youtube: 5e3,
|
|
447
|
+
instagram: 2200,
|
|
448
|
+
linkedin: 3e3,
|
|
449
|
+
twitter: 280
|
|
450
|
+
};
|
|
451
|
+
SUPPORTED_VIDEO_EXTENSIONS = [".mp4", ".webm"];
|
|
452
|
+
}
|
|
453
|
+
});
|
|
454
|
+
|
|
405
455
|
// src/L0-pure/pricing/pricing.ts
|
|
406
456
|
function calculateTokenCost(model, inputTokens, outputTokens) {
|
|
407
457
|
const pricing = getModelPricing(model);
|
|
@@ -1187,151 +1237,870 @@ var init_modelConfig = __esm({
|
|
|
1187
1237
|
}
|
|
1188
1238
|
});
|
|
1189
1239
|
|
|
1190
|
-
// src/
|
|
1191
|
-
|
|
1192
|
-
|
|
1240
|
+
// src/L2-clients/github/githubClient.ts
|
|
1241
|
+
import { Octokit } from "octokit";
|
|
1242
|
+
function getErrorStatus(error) {
|
|
1243
|
+
if (typeof error === "object" && error !== null && "status" in error && typeof error.status === "number") {
|
|
1244
|
+
return error.status;
|
|
1245
|
+
}
|
|
1246
|
+
return void 0;
|
|
1193
1247
|
}
|
|
1194
1248
|
function getErrorMessage(error) {
|
|
1195
|
-
|
|
1249
|
+
if (error instanceof Error) {
|
|
1250
|
+
return error.message;
|
|
1251
|
+
}
|
|
1252
|
+
if (typeof error === "object" && error !== null && "message" in error && typeof error.message === "string") {
|
|
1253
|
+
return error.message ?? "Unknown GitHub API error";
|
|
1254
|
+
}
|
|
1255
|
+
return String(error);
|
|
1256
|
+
}
|
|
1257
|
+
function normalizeLabels(labels) {
|
|
1258
|
+
return Array.from(new Set(labels.map((label) => label.trim()).filter((label) => label.length > 0)));
|
|
1196
1259
|
}
|
|
1197
|
-
function
|
|
1198
|
-
|
|
1199
|
-
|
|
1260
|
+
function isIssueResponse(value) {
|
|
1261
|
+
return !("pull_request" in value);
|
|
1262
|
+
}
|
|
1263
|
+
function getGitHubClient() {
|
|
1264
|
+
const config2 = getConfig();
|
|
1265
|
+
const nextKey = `${config2.IDEAS_REPO}:${config2.GITHUB_TOKEN}`;
|
|
1266
|
+
if (!clientInstance || clientKey !== nextKey) {
|
|
1267
|
+
clientInstance = new GitHubClient(config2.GITHUB_TOKEN, config2.IDEAS_REPO);
|
|
1268
|
+
clientKey = nextKey;
|
|
1200
1269
|
}
|
|
1201
|
-
return
|
|
1270
|
+
return clientInstance;
|
|
1202
1271
|
}
|
|
1203
|
-
|
|
1204
|
-
|
|
1272
|
+
var DEFAULT_PER_PAGE, GitHubClientError, GitHubClient, clientInstance, clientKey;
|
|
1273
|
+
var init_githubClient = __esm({
|
|
1274
|
+
"src/L2-clients/github/githubClient.ts"() {
|
|
1275
|
+
"use strict";
|
|
1276
|
+
init_environment();
|
|
1277
|
+
init_configLogger();
|
|
1278
|
+
DEFAULT_PER_PAGE = 100;
|
|
1279
|
+
GitHubClientError = class extends Error {
|
|
1280
|
+
constructor(message, status) {
|
|
1281
|
+
super(message);
|
|
1282
|
+
this.status = status;
|
|
1283
|
+
this.name = "GitHubClientError";
|
|
1284
|
+
}
|
|
1285
|
+
};
|
|
1286
|
+
GitHubClient = class {
|
|
1287
|
+
octokit;
|
|
1288
|
+
owner;
|
|
1289
|
+
repo;
|
|
1290
|
+
constructor(token, repoFullName) {
|
|
1291
|
+
const config2 = getConfig();
|
|
1292
|
+
const authToken = token || config2.GITHUB_TOKEN;
|
|
1293
|
+
if (!authToken) {
|
|
1294
|
+
throw new Error("GITHUB_TOKEN is required for GitHub API access");
|
|
1295
|
+
}
|
|
1296
|
+
const fullName = repoFullName || config2.IDEAS_REPO;
|
|
1297
|
+
const [owner, repo] = fullName.split("/").map((part) => part.trim());
|
|
1298
|
+
if (!owner || !repo) {
|
|
1299
|
+
throw new Error(`Invalid IDEAS_REPO format: "${fullName}" \u2014 expected "owner/repo"`);
|
|
1300
|
+
}
|
|
1301
|
+
this.owner = owner;
|
|
1302
|
+
this.repo = repo;
|
|
1303
|
+
this.octokit = new Octokit({ auth: authToken });
|
|
1304
|
+
}
|
|
1305
|
+
async createIssue(input) {
|
|
1306
|
+
logger_default.debug(`[GitHubClient] Creating issue in ${this.owner}/${this.repo}: ${input.title}`);
|
|
1307
|
+
try {
|
|
1308
|
+
const response = await this.octokit.rest.issues.create({
|
|
1309
|
+
owner: this.owner,
|
|
1310
|
+
repo: this.repo,
|
|
1311
|
+
title: input.title,
|
|
1312
|
+
body: input.body,
|
|
1313
|
+
labels: input.labels ? normalizeLabels(input.labels) : void 0
|
|
1314
|
+
});
|
|
1315
|
+
const issue = this.mapIssue(response.data);
|
|
1316
|
+
logger_default.info(`[GitHubClient] Created issue #${issue.number}: ${input.title}`);
|
|
1317
|
+
return issue;
|
|
1318
|
+
} catch (error) {
|
|
1319
|
+
this.logError("create issue", error);
|
|
1320
|
+
throw new GitHubClientError(`Failed to create GitHub issue: ${getErrorMessage(error)}`, getErrorStatus(error));
|
|
1321
|
+
}
|
|
1322
|
+
}
|
|
1323
|
+
async updateIssue(issueNumber, input) {
|
|
1324
|
+
logger_default.debug(`[GitHubClient] Updating issue #${issueNumber} in ${this.owner}/${this.repo}`);
|
|
1325
|
+
try {
|
|
1326
|
+
const response = await this.octokit.rest.issues.update({
|
|
1327
|
+
owner: this.owner,
|
|
1328
|
+
repo: this.repo,
|
|
1329
|
+
issue_number: issueNumber,
|
|
1330
|
+
title: input.title,
|
|
1331
|
+
body: input.body,
|
|
1332
|
+
state: input.state,
|
|
1333
|
+
labels: input.labels ? normalizeLabels(input.labels) : void 0
|
|
1334
|
+
});
|
|
1335
|
+
return this.mapIssue(response.data);
|
|
1336
|
+
} catch (error) {
|
|
1337
|
+
this.logError(`update issue #${issueNumber}`, error);
|
|
1338
|
+
throw new GitHubClientError(
|
|
1339
|
+
`Failed to update GitHub issue #${issueNumber}: ${getErrorMessage(error)}`,
|
|
1340
|
+
getErrorStatus(error)
|
|
1341
|
+
);
|
|
1342
|
+
}
|
|
1343
|
+
}
|
|
1344
|
+
async getIssue(issueNumber) {
|
|
1345
|
+
logger_default.debug(`[GitHubClient] Fetching issue #${issueNumber} from ${this.owner}/${this.repo}`);
|
|
1346
|
+
try {
|
|
1347
|
+
const response = await this.octokit.rest.issues.get({
|
|
1348
|
+
owner: this.owner,
|
|
1349
|
+
repo: this.repo,
|
|
1350
|
+
issue_number: issueNumber
|
|
1351
|
+
});
|
|
1352
|
+
return this.mapIssue(response.data);
|
|
1353
|
+
} catch (error) {
|
|
1354
|
+
this.logError(`get issue #${issueNumber}`, error);
|
|
1355
|
+
throw new GitHubClientError(
|
|
1356
|
+
`Failed to fetch GitHub issue #${issueNumber}: ${getErrorMessage(error)}`,
|
|
1357
|
+
getErrorStatus(error)
|
|
1358
|
+
);
|
|
1359
|
+
}
|
|
1360
|
+
}
|
|
1361
|
+
async listIssues(options = {}) {
|
|
1362
|
+
logger_default.debug(`[GitHubClient] Listing issues for ${this.owner}/${this.repo}`);
|
|
1363
|
+
const issues = [];
|
|
1364
|
+
let page;
|
|
1365
|
+
const maxResults = options.maxResults ?? Number.POSITIVE_INFINITY;
|
|
1366
|
+
try {
|
|
1367
|
+
while (issues.length < maxResults) {
|
|
1368
|
+
const response = await this.octokit.rest.issues.listForRepo({
|
|
1369
|
+
owner: this.owner,
|
|
1370
|
+
repo: this.repo,
|
|
1371
|
+
state: "open",
|
|
1372
|
+
labels: options.labels && options.labels.length > 0 ? normalizeLabels(options.labels).join(",") : void 0,
|
|
1373
|
+
sort: void 0,
|
|
1374
|
+
direction: void 0,
|
|
1375
|
+
per_page: DEFAULT_PER_PAGE,
|
|
1376
|
+
page
|
|
1377
|
+
});
|
|
1378
|
+
const pageItems = response.data.filter(isIssueResponse).map((issue) => this.mapIssue(issue));
|
|
1379
|
+
issues.push(...pageItems);
|
|
1380
|
+
if (pageItems.length < DEFAULT_PER_PAGE) {
|
|
1381
|
+
break;
|
|
1382
|
+
}
|
|
1383
|
+
page = (page ?? 1) + 1;
|
|
1384
|
+
}
|
|
1385
|
+
return issues.slice(0, maxResults);
|
|
1386
|
+
} catch (error) {
|
|
1387
|
+
this.logError("list issues", error);
|
|
1388
|
+
throw new GitHubClientError(`Failed to list GitHub issues: ${getErrorMessage(error)}`, getErrorStatus(error));
|
|
1389
|
+
}
|
|
1390
|
+
}
|
|
1391
|
+
async searchIssues(query, options = {}) {
|
|
1392
|
+
const searchQuery = `repo:${this.owner}/${this.repo} is:issue ${query}`.trim();
|
|
1393
|
+
logger_default.debug(`[GitHubClient] Searching issues in ${this.owner}/${this.repo}: ${query}`);
|
|
1394
|
+
try {
|
|
1395
|
+
const items = await this.octokit.paginate(this.octokit.rest.search.issuesAndPullRequests, {
|
|
1396
|
+
q: searchQuery,
|
|
1397
|
+
per_page: DEFAULT_PER_PAGE
|
|
1398
|
+
});
|
|
1399
|
+
return items.filter(isIssueResponse).map((issue) => this.mapIssue(issue)).slice(0, options.maxResults ?? Number.POSITIVE_INFINITY);
|
|
1400
|
+
} catch (error) {
|
|
1401
|
+
this.logError("search issues", error);
|
|
1402
|
+
throw new GitHubClientError(`Failed to search GitHub issues: ${getErrorMessage(error)}`, getErrorStatus(error));
|
|
1403
|
+
}
|
|
1404
|
+
}
|
|
1405
|
+
async addLabels(issueNumber, labels) {
|
|
1406
|
+
if (labels.length === 0) {
|
|
1407
|
+
return;
|
|
1408
|
+
}
|
|
1409
|
+
logger_default.debug(`[GitHubClient] Adding labels to issue #${issueNumber} in ${this.owner}/${this.repo}`);
|
|
1410
|
+
try {
|
|
1411
|
+
await this.octokit.rest.issues.addLabels({
|
|
1412
|
+
owner: this.owner,
|
|
1413
|
+
repo: this.repo,
|
|
1414
|
+
issue_number: issueNumber,
|
|
1415
|
+
labels
|
|
1416
|
+
});
|
|
1417
|
+
} catch (error) {
|
|
1418
|
+
this.logError(`add labels to issue #${issueNumber}`, error);
|
|
1419
|
+
throw new GitHubClientError(
|
|
1420
|
+
`Failed to add labels to GitHub issue #${issueNumber}: ${getErrorMessage(error)}`,
|
|
1421
|
+
getErrorStatus(error)
|
|
1422
|
+
);
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
async removeLabel(issueNumber, label) {
|
|
1426
|
+
logger_default.debug(`[GitHubClient] Removing label "${label}" from issue #${issueNumber} in ${this.owner}/${this.repo}`);
|
|
1427
|
+
try {
|
|
1428
|
+
await this.octokit.rest.issues.removeLabel({
|
|
1429
|
+
owner: this.owner,
|
|
1430
|
+
repo: this.repo,
|
|
1431
|
+
issue_number: issueNumber,
|
|
1432
|
+
name: label
|
|
1433
|
+
});
|
|
1434
|
+
} catch (error) {
|
|
1435
|
+
if (getErrorStatus(error) === 404) {
|
|
1436
|
+
return;
|
|
1437
|
+
}
|
|
1438
|
+
this.logError(`remove label from issue #${issueNumber}`, error);
|
|
1439
|
+
throw new GitHubClientError(
|
|
1440
|
+
`Failed to remove label from GitHub issue #${issueNumber}: ${getErrorMessage(error)}`,
|
|
1441
|
+
getErrorStatus(error)
|
|
1442
|
+
);
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
async setLabels(issueNumber, labels) {
|
|
1446
|
+
logger_default.debug(`[GitHubClient] Setting labels on issue #${issueNumber} in ${this.owner}/${this.repo}`);
|
|
1447
|
+
try {
|
|
1448
|
+
await this.octokit.rest.issues.setLabels({
|
|
1449
|
+
owner: this.owner,
|
|
1450
|
+
repo: this.repo,
|
|
1451
|
+
issue_number: issueNumber,
|
|
1452
|
+
labels
|
|
1453
|
+
});
|
|
1454
|
+
} catch (error) {
|
|
1455
|
+
this.logError(`set labels on issue #${issueNumber}`, error);
|
|
1456
|
+
throw new GitHubClientError(
|
|
1457
|
+
`Failed to set labels on GitHub issue #${issueNumber}: ${getErrorMessage(error)}`,
|
|
1458
|
+
getErrorStatus(error)
|
|
1459
|
+
);
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1462
|
+
async addComment(issueNumber, body) {
|
|
1463
|
+
logger_default.debug(`[GitHubClient] Adding comment to issue #${issueNumber} in ${this.owner}/${this.repo}`);
|
|
1464
|
+
try {
|
|
1465
|
+
const response = await this.octokit.rest.issues.createComment({
|
|
1466
|
+
owner: this.owner,
|
|
1467
|
+
repo: this.repo,
|
|
1468
|
+
issue_number: issueNumber,
|
|
1469
|
+
body
|
|
1470
|
+
});
|
|
1471
|
+
return this.mapComment(response.data);
|
|
1472
|
+
} catch (error) {
|
|
1473
|
+
this.logError(`add comment to issue #${issueNumber}`, error);
|
|
1474
|
+
throw new GitHubClientError(
|
|
1475
|
+
`Failed to add comment to GitHub issue #${issueNumber}: ${getErrorMessage(error)}`,
|
|
1476
|
+
getErrorStatus(error)
|
|
1477
|
+
);
|
|
1478
|
+
}
|
|
1479
|
+
}
|
|
1480
|
+
async listComments(issueNumber) {
|
|
1481
|
+
logger_default.debug(`[GitHubClient] Listing comments for issue #${issueNumber} in ${this.owner}/${this.repo}`);
|
|
1482
|
+
try {
|
|
1483
|
+
const comments = await this.octokit.paginate(this.octokit.rest.issues.listComments, {
|
|
1484
|
+
owner: this.owner,
|
|
1485
|
+
repo: this.repo,
|
|
1486
|
+
issue_number: issueNumber,
|
|
1487
|
+
per_page: DEFAULT_PER_PAGE
|
|
1488
|
+
});
|
|
1489
|
+
return comments.map((comment) => this.mapComment(comment));
|
|
1490
|
+
} catch (error) {
|
|
1491
|
+
this.logError(`list comments for issue #${issueNumber}`, error);
|
|
1492
|
+
throw new GitHubClientError(
|
|
1493
|
+
`Failed to list comments for GitHub issue #${issueNumber}: ${getErrorMessage(error)}`,
|
|
1494
|
+
getErrorStatus(error)
|
|
1495
|
+
);
|
|
1496
|
+
}
|
|
1497
|
+
}
|
|
1498
|
+
mapIssue(issue) {
|
|
1499
|
+
return {
|
|
1500
|
+
number: issue.number,
|
|
1501
|
+
title: issue.title,
|
|
1502
|
+
body: issue.body ?? "",
|
|
1503
|
+
state: issue.state,
|
|
1504
|
+
labels: issue.labels.map((label) => typeof label === "string" ? label : label.name ?? "").map((label) => label.trim()).filter((label) => label.length > 0),
|
|
1505
|
+
created_at: issue.created_at,
|
|
1506
|
+
updated_at: issue.updated_at,
|
|
1507
|
+
html_url: issue.html_url
|
|
1508
|
+
};
|
|
1509
|
+
}
|
|
1510
|
+
mapComment(comment) {
|
|
1511
|
+
return {
|
|
1512
|
+
id: comment.id,
|
|
1513
|
+
body: comment.body ?? "",
|
|
1514
|
+
created_at: comment.created_at,
|
|
1515
|
+
updated_at: comment.updated_at,
|
|
1516
|
+
html_url: comment.html_url
|
|
1517
|
+
};
|
|
1518
|
+
}
|
|
1519
|
+
logError(action, error) {
|
|
1520
|
+
logger_default.error(`[GitHubClient] Failed to ${action} in ${this.owner}/${this.repo}: ${getErrorMessage(error)}`);
|
|
1521
|
+
}
|
|
1522
|
+
};
|
|
1523
|
+
clientInstance = null;
|
|
1524
|
+
clientKey = "";
|
|
1525
|
+
}
|
|
1526
|
+
});
|
|
1527
|
+
|
|
1528
|
+
// src/L3-services/ideaService/ideaService.ts
|
|
1529
|
+
var ideaService_exports = {};
|
|
1530
|
+
__export(ideaService_exports, {
|
|
1531
|
+
createIdea: () => createIdea,
|
|
1532
|
+
findRelatedIdeas: () => findRelatedIdeas,
|
|
1533
|
+
getIdea: () => getIdea,
|
|
1534
|
+
getPublishHistory: () => getPublishHistory,
|
|
1535
|
+
getReadyIdeas: () => getReadyIdeas,
|
|
1536
|
+
linkVideoToIdea: () => linkVideoToIdea,
|
|
1537
|
+
listIdeas: () => listIdeas,
|
|
1538
|
+
markPublished: () => markPublished,
|
|
1539
|
+
markRecorded: () => markRecorded,
|
|
1540
|
+
recordPublish: () => recordPublish,
|
|
1541
|
+
searchIdeas: () => searchIdeas,
|
|
1542
|
+
updateIdea: () => updateIdea
|
|
1543
|
+
});
|
|
1544
|
+
function getErrorMessage2(error) {
|
|
1545
|
+
return error instanceof Error ? error.message : String(error);
|
|
1205
1546
|
}
|
|
1206
|
-
function
|
|
1207
|
-
return
|
|
1547
|
+
function sanitizeMultilineValue(value) {
|
|
1548
|
+
return (value ?? "").trim();
|
|
1208
1549
|
}
|
|
1209
|
-
function
|
|
1210
|
-
return
|
|
1550
|
+
function normalizeTag(tag) {
|
|
1551
|
+
return tag.trim().toLowerCase().replace(/\s+/g, "-");
|
|
1211
1552
|
}
|
|
1212
|
-
function
|
|
1213
|
-
return
|
|
1553
|
+
function normalizeTags(tags) {
|
|
1554
|
+
return Array.from(new Set(tags.map((tag) => normalizeTag(tag)).filter((tag) => tag.length > 0)));
|
|
1214
1555
|
}
|
|
1215
1556
|
function isPlatform(value) {
|
|
1216
|
-
return
|
|
1557
|
+
return platformValues.has(value);
|
|
1217
1558
|
}
|
|
1218
|
-
function
|
|
1219
|
-
return
|
|
1220
|
-
}
|
|
1221
|
-
function isValidIsoDateString(value) {
|
|
1222
|
-
return typeof value === "string" && !Number.isNaN(new Date(value).getTime());
|
|
1559
|
+
function isIdeaStatus(value) {
|
|
1560
|
+
return ideaStatuses.has(value);
|
|
1223
1561
|
}
|
|
1224
|
-
function
|
|
1225
|
-
return
|
|
1562
|
+
function uniquePlatforms(platforms) {
|
|
1563
|
+
return Array.from(new Set(platforms));
|
|
1226
1564
|
}
|
|
1227
|
-
function
|
|
1228
|
-
|
|
1565
|
+
function classifyIdeaPriority(publishBy, createdAtIso) {
|
|
1566
|
+
const publishByTimestamp = new Date(publishBy).getTime();
|
|
1567
|
+
const createdAtTimestamp = new Date(createdAtIso).getTime();
|
|
1568
|
+
if (Number.isNaN(publishByTimestamp) || Number.isNaN(createdAtTimestamp)) {
|
|
1569
|
+
return "evergreen";
|
|
1570
|
+
}
|
|
1571
|
+
const diffDays = Math.ceil((publishByTimestamp - createdAtTimestamp) / (1e3 * 60 * 60 * 24));
|
|
1572
|
+
if (diffDays <= 7) {
|
|
1573
|
+
return "hot-trend";
|
|
1574
|
+
}
|
|
1575
|
+
if (diffDays <= 14) {
|
|
1576
|
+
return "timely";
|
|
1577
|
+
}
|
|
1578
|
+
return "evergreen";
|
|
1229
1579
|
}
|
|
1230
|
-
|
|
1231
|
-
const
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1580
|
+
function extractLabelsFromIdea(idea, publishBy, createdAtIso) {
|
|
1581
|
+
const labels = [
|
|
1582
|
+
`${STATUS_LABEL_PREFIX}${idea.status}`,
|
|
1583
|
+
...uniquePlatforms(idea.platforms).map((platform) => `${PLATFORM_LABEL_PREFIX}${platform}`),
|
|
1584
|
+
`${PRIORITY_LABEL_PREFIX}${classifyIdeaPriority(publishBy, createdAtIso)}`,
|
|
1585
|
+
...normalizeTags(idea.tags)
|
|
1586
|
+
];
|
|
1587
|
+
return Array.from(new Set(labels));
|
|
1588
|
+
}
|
|
1589
|
+
function parseLabelsToIdea(labels) {
|
|
1590
|
+
let status = "draft";
|
|
1591
|
+
const platforms = [];
|
|
1592
|
+
const tags = [];
|
|
1593
|
+
for (const label of labels) {
|
|
1594
|
+
const normalized = label.trim().toLowerCase();
|
|
1595
|
+
if (!normalized) {
|
|
1596
|
+
continue;
|
|
1597
|
+
}
|
|
1598
|
+
if (normalized.startsWith(STATUS_LABEL_PREFIX)) {
|
|
1599
|
+
const value = normalized.slice(STATUS_LABEL_PREFIX.length);
|
|
1600
|
+
if (isIdeaStatus(value)) {
|
|
1601
|
+
status = value;
|
|
1240
1602
|
}
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1603
|
+
continue;
|
|
1604
|
+
}
|
|
1605
|
+
if (normalized.startsWith(PLATFORM_LABEL_PREFIX)) {
|
|
1606
|
+
const value = normalized.slice(PLATFORM_LABEL_PREFIX.length);
|
|
1607
|
+
if (isPlatform(value)) {
|
|
1608
|
+
platforms.push(value);
|
|
1609
|
+
}
|
|
1610
|
+
continue;
|
|
1611
|
+
}
|
|
1612
|
+
if (normalized.startsWith(PRIORITY_LABEL_PREFIX)) {
|
|
1613
|
+
continue;
|
|
1614
|
+
}
|
|
1615
|
+
tags.push(normalized);
|
|
1616
|
+
}
|
|
1617
|
+
return {
|
|
1618
|
+
status,
|
|
1619
|
+
platforms: uniquePlatforms(platforms),
|
|
1620
|
+
tags: Array.from(new Set(tags))
|
|
1621
|
+
};
|
|
1244
1622
|
}
|
|
1245
|
-
|
|
1246
|
-
const
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1623
|
+
function formatIdeaBody(input) {
|
|
1624
|
+
const sections = [
|
|
1625
|
+
`${MARKDOWN_SECTION_PREFIX}Hook`,
|
|
1626
|
+
sanitizeMultilineValue(input.hook),
|
|
1627
|
+
"",
|
|
1628
|
+
`${MARKDOWN_SECTION_PREFIX}Audience`,
|
|
1629
|
+
sanitizeMultilineValue(input.audience),
|
|
1630
|
+
"",
|
|
1631
|
+
`${MARKDOWN_SECTION_PREFIX}Key Takeaway`,
|
|
1632
|
+
sanitizeMultilineValue(input.keyTakeaway),
|
|
1633
|
+
"",
|
|
1634
|
+
`${MARKDOWN_SECTION_PREFIX}Talking Points`,
|
|
1635
|
+
...input.talkingPoints.map((point) => `- ${sanitizeMultilineValue(point)}`),
|
|
1636
|
+
"",
|
|
1637
|
+
`${MARKDOWN_SECTION_PREFIX}Publish By`,
|
|
1638
|
+
sanitizeMultilineValue(input.publishBy)
|
|
1639
|
+
];
|
|
1640
|
+
const trendContext = sanitizeMultilineValue(input.trendContext);
|
|
1641
|
+
if (trendContext) {
|
|
1642
|
+
sections.push("", `${MARKDOWN_SECTION_PREFIX}Trend Context`, trendContext);
|
|
1643
|
+
}
|
|
1644
|
+
return sections.join("\n").trim();
|
|
1645
|
+
}
|
|
1646
|
+
function parseIdeaBody(body, fallbackPublishBy) {
|
|
1647
|
+
const normalizedBody = body.replace(/\r\n/g, "\n");
|
|
1648
|
+
const sections = /* @__PURE__ */ new Map();
|
|
1649
|
+
let currentSection = null;
|
|
1650
|
+
for (const line of normalizedBody.split("\n")) {
|
|
1651
|
+
if (line.startsWith(MARKDOWN_SECTION_PREFIX)) {
|
|
1652
|
+
currentSection = line.slice(MARKDOWN_SECTION_PREFIX.length).trim();
|
|
1653
|
+
sections.set(currentSection, []);
|
|
1654
|
+
continue;
|
|
1655
|
+
}
|
|
1656
|
+
if (currentSection) {
|
|
1657
|
+
sections.get(currentSection)?.push(line);
|
|
1658
|
+
}
|
|
1251
1659
|
}
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1660
|
+
const getSection = (heading) => (sections.get(heading) ?? []).join("\n").trim();
|
|
1661
|
+
const talkingPointsSection = sections.get("Talking Points") ?? [];
|
|
1662
|
+
const talkingPoints = talkingPointsSection.map((line) => line.trim()).filter((line) => line.startsWith("- ") || line.startsWith("* ")).map((line) => line.slice(2).trim()).filter((line) => line.length > 0);
|
|
1663
|
+
return {
|
|
1664
|
+
hook: getSection("Hook"),
|
|
1665
|
+
audience: getSection("Audience"),
|
|
1666
|
+
keyTakeaway: getSection("Key Takeaway"),
|
|
1667
|
+
talkingPoints,
|
|
1668
|
+
publishBy: getSection("Publish By") || fallbackPublishBy,
|
|
1669
|
+
trendContext: getSection("Trend Context") || void 0
|
|
1670
|
+
};
|
|
1671
|
+
}
|
|
1672
|
+
function formatIdeaComment(data) {
|
|
1673
|
+
return [
|
|
1674
|
+
COMMENT_MARKER,
|
|
1675
|
+
"```json",
|
|
1676
|
+
JSON.stringify(data, null, 2),
|
|
1677
|
+
"```"
|
|
1678
|
+
].join("\n");
|
|
1679
|
+
}
|
|
1680
|
+
function formatPublishRecordComment(record) {
|
|
1681
|
+
return [
|
|
1682
|
+
"Published content recorded for this idea.",
|
|
1683
|
+
"",
|
|
1684
|
+
`- Clip type: ${record.clipType}`,
|
|
1685
|
+
`- Platform: ${record.platform}`,
|
|
1686
|
+
`- Queue item: ${record.queueItemId}`,
|
|
1687
|
+
`- Published at: ${record.publishedAt}`,
|
|
1688
|
+
`- Late post ID: ${record.latePostId}`,
|
|
1689
|
+
`- Late URL: ${record.lateUrl}`,
|
|
1690
|
+
"",
|
|
1691
|
+
formatIdeaComment({ type: "publish-record", record })
|
|
1692
|
+
].join("\n");
|
|
1693
|
+
}
|
|
1694
|
+
function formatVideoLinkComment(videoSlug, linkedAt) {
|
|
1695
|
+
return [
|
|
1696
|
+
"Linked a source video to this idea.",
|
|
1697
|
+
"",
|
|
1698
|
+
`- Video slug: ${videoSlug}`,
|
|
1699
|
+
`- Linked at: ${linkedAt}`,
|
|
1700
|
+
"",
|
|
1701
|
+
formatIdeaComment({ type: "video-link", videoSlug, linkedAt })
|
|
1702
|
+
].join("\n");
|
|
1255
1703
|
}
|
|
1256
|
-
|
|
1257
|
-
const
|
|
1258
|
-
if (
|
|
1704
|
+
function parseIdeaComment(commentBody) {
|
|
1705
|
+
const markerIndex = commentBody.indexOf(COMMENT_MARKER);
|
|
1706
|
+
if (markerIndex === -1) {
|
|
1259
1707
|
return null;
|
|
1260
1708
|
}
|
|
1261
|
-
const
|
|
1262
|
-
|
|
1263
|
-
|
|
1709
|
+
const commentPayload = commentBody.slice(markerIndex + COMMENT_MARKER.length);
|
|
1710
|
+
const fencedJsonMatch = commentPayload.match(/```json\s*([\s\S]*?)\s*```/);
|
|
1711
|
+
const jsonText = fencedJsonMatch?.[1]?.trim() ?? commentPayload.trim();
|
|
1712
|
+
if (!jsonText) {
|
|
1713
|
+
return null;
|
|
1264
1714
|
}
|
|
1265
|
-
|
|
1715
|
+
try {
|
|
1716
|
+
const parsed = JSON.parse(jsonText);
|
|
1717
|
+
if (parsed.type === "video-link" && typeof parsed.videoSlug === "string" && typeof parsed.linkedAt === "string") {
|
|
1718
|
+
return {
|
|
1719
|
+
type: "video-link",
|
|
1720
|
+
videoSlug: parsed.videoSlug,
|
|
1721
|
+
linkedAt: parsed.linkedAt
|
|
1722
|
+
};
|
|
1723
|
+
}
|
|
1724
|
+
if (parsed.type === "publish-record" && parsed.record) {
|
|
1725
|
+
const record = parsed.record;
|
|
1726
|
+
if (typeof record.clipType === "string" && typeof record.platform === "string" && isPlatform(record.platform) && typeof record.queueItemId === "string" && typeof record.publishedAt === "string" && typeof record.latePostId === "string" && typeof record.lateUrl === "string") {
|
|
1727
|
+
return {
|
|
1728
|
+
type: "publish-record",
|
|
1729
|
+
record: {
|
|
1730
|
+
clipType: record.clipType,
|
|
1731
|
+
platform: record.platform,
|
|
1732
|
+
queueItemId: record.queueItemId,
|
|
1733
|
+
publishedAt: record.publishedAt,
|
|
1734
|
+
latePostId: record.latePostId,
|
|
1735
|
+
lateUrl: record.lateUrl
|
|
1736
|
+
}
|
|
1737
|
+
};
|
|
1738
|
+
}
|
|
1739
|
+
}
|
|
1740
|
+
} catch {
|
|
1741
|
+
return null;
|
|
1742
|
+
}
|
|
1743
|
+
return null;
|
|
1266
1744
|
}
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
if (!await fileExists(ideasDir)) {
|
|
1745
|
+
function buildLabelFilters(filters) {
|
|
1746
|
+
if (!filters) {
|
|
1270
1747
|
return [];
|
|
1271
1748
|
}
|
|
1272
|
-
const
|
|
1273
|
-
|
|
1749
|
+
const labels = [];
|
|
1750
|
+
if (filters.status) {
|
|
1751
|
+
labels.push(`${STATUS_LABEL_PREFIX}${filters.status}`);
|
|
1752
|
+
}
|
|
1753
|
+
if (filters.platform) {
|
|
1754
|
+
labels.push(`${PLATFORM_LABEL_PREFIX}${filters.platform}`);
|
|
1755
|
+
}
|
|
1756
|
+
if (filters.tag) {
|
|
1757
|
+
labels.push(normalizeTag(filters.tag));
|
|
1758
|
+
}
|
|
1759
|
+
if (filters.priority) {
|
|
1760
|
+
labels.push(`${PRIORITY_LABEL_PREFIX}${filters.priority}`);
|
|
1761
|
+
}
|
|
1762
|
+
return labels;
|
|
1763
|
+
}
|
|
1764
|
+
function buildLabelsFromIssue(issue, overrides = {}) {
|
|
1765
|
+
const parsedLabels = parseLabelsToIdea(issue.labels);
|
|
1766
|
+
const parsedBody = parseIdeaBody(issue.body, issue.created_at.slice(0, 10));
|
|
1767
|
+
return extractLabelsFromIdea(
|
|
1768
|
+
{
|
|
1769
|
+
status: overrides.status ?? parsedLabels.status,
|
|
1770
|
+
platforms: overrides.platforms ?? parsedLabels.platforms,
|
|
1771
|
+
tags: overrides.tags ?? parsedLabels.tags
|
|
1772
|
+
},
|
|
1773
|
+
parsedBody.publishBy,
|
|
1774
|
+
issue.created_at
|
|
1775
|
+
);
|
|
1776
|
+
}
|
|
1777
|
+
function isNotFoundError(error) {
|
|
1778
|
+
return error instanceof GitHubClientError && error.status === 404;
|
|
1779
|
+
}
|
|
1780
|
+
function mapIssueToIdea(issue, comments) {
|
|
1781
|
+
const config2 = getConfig();
|
|
1782
|
+
const parsedLabels = parseLabelsToIdea(issue.labels);
|
|
1783
|
+
const parsedBody = parseIdeaBody(issue.body, issue.created_at.slice(0, 10));
|
|
1784
|
+
const publishRecords = [];
|
|
1785
|
+
let sourceVideoSlug;
|
|
1786
|
+
for (const comment of comments) {
|
|
1787
|
+
const parsedComment = parseIdeaComment(comment.body);
|
|
1788
|
+
if (!parsedComment) {
|
|
1789
|
+
continue;
|
|
1790
|
+
}
|
|
1791
|
+
if (parsedComment.type === "publish-record") {
|
|
1792
|
+
publishRecords.push(parsedComment.record);
|
|
1793
|
+
continue;
|
|
1794
|
+
}
|
|
1795
|
+
sourceVideoSlug = parsedComment.videoSlug;
|
|
1796
|
+
}
|
|
1797
|
+
return {
|
|
1798
|
+
issueNumber: issue.number,
|
|
1799
|
+
issueUrl: issue.html_url,
|
|
1800
|
+
repoFullName: config2.IDEAS_REPO,
|
|
1801
|
+
id: `idea-${issue.number}`,
|
|
1802
|
+
topic: issue.title,
|
|
1803
|
+
...parsedBody,
|
|
1804
|
+
...parsedLabels,
|
|
1805
|
+
createdAt: issue.created_at,
|
|
1806
|
+
updatedAt: issue.updated_at,
|
|
1807
|
+
sourceVideoSlug,
|
|
1808
|
+
publishedContent: publishRecords.length > 0 ? publishRecords : void 0
|
|
1809
|
+
};
|
|
1810
|
+
}
|
|
1811
|
+
async function createIdea(input) {
|
|
1812
|
+
const client = getGitHubClient();
|
|
1813
|
+
const createdAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1814
|
+
try {
|
|
1815
|
+
const issue = await client.createIssue({
|
|
1816
|
+
title: input.topic,
|
|
1817
|
+
body: formatIdeaBody(input),
|
|
1818
|
+
labels: extractLabelsFromIdea(
|
|
1819
|
+
{
|
|
1820
|
+
status: "draft",
|
|
1821
|
+
platforms: input.platforms,
|
|
1822
|
+
tags: input.tags
|
|
1823
|
+
},
|
|
1824
|
+
input.publishBy,
|
|
1825
|
+
createdAt
|
|
1826
|
+
)
|
|
1827
|
+
});
|
|
1828
|
+
logger_default.info(`[IdeaService] Created idea #${issue.number}: ${input.topic}`);
|
|
1829
|
+
return mapIssueToIdea(issue, []);
|
|
1830
|
+
} catch (error) {
|
|
1831
|
+
const message = getErrorMessage2(error);
|
|
1832
|
+
logger_default.error(`[IdeaService] Failed to create idea "${input.topic}": ${message}`);
|
|
1833
|
+
throw new Error(`Failed to create idea "${input.topic}": ${message}`);
|
|
1834
|
+
}
|
|
1835
|
+
}
|
|
1836
|
+
async function updateIdea(issueNumber, updates) {
|
|
1837
|
+
const client = getGitHubClient();
|
|
1838
|
+
try {
|
|
1839
|
+
const currentIdea = await getIdea(issueNumber);
|
|
1840
|
+
if (!currentIdea) {
|
|
1841
|
+
throw new Error(`Idea #${issueNumber} was not found`);
|
|
1842
|
+
}
|
|
1843
|
+
const nextInput = {
|
|
1844
|
+
topic: updates.topic ?? currentIdea.topic,
|
|
1845
|
+
hook: updates.hook ?? currentIdea.hook,
|
|
1846
|
+
audience: updates.audience ?? currentIdea.audience,
|
|
1847
|
+
keyTakeaway: updates.keyTakeaway ?? currentIdea.keyTakeaway,
|
|
1848
|
+
talkingPoints: updates.talkingPoints ?? currentIdea.talkingPoints,
|
|
1849
|
+
platforms: updates.platforms ?? currentIdea.platforms,
|
|
1850
|
+
tags: updates.tags ?? currentIdea.tags,
|
|
1851
|
+
publishBy: updates.publishBy ?? currentIdea.publishBy,
|
|
1852
|
+
trendContext: updates.trendContext ?? currentIdea.trendContext
|
|
1853
|
+
};
|
|
1854
|
+
const shouldUpdateBody = updates.hook !== void 0 || updates.audience !== void 0 || updates.keyTakeaway !== void 0 || updates.talkingPoints !== void 0 || updates.publishBy !== void 0 || updates.trendContext !== void 0;
|
|
1855
|
+
const shouldUpdateLabels = updates.status !== void 0 || updates.platforms !== void 0 || updates.tags !== void 0 || updates.publishBy !== void 0;
|
|
1856
|
+
const issue = await client.updateIssue(issueNumber, {
|
|
1857
|
+
title: updates.topic,
|
|
1858
|
+
body: shouldUpdateBody ? formatIdeaBody(nextInput) : void 0,
|
|
1859
|
+
labels: shouldUpdateLabels ? extractLabelsFromIdea(
|
|
1860
|
+
{
|
|
1861
|
+
status: updates.status ?? currentIdea.status,
|
|
1862
|
+
platforms: nextInput.platforms,
|
|
1863
|
+
tags: nextInput.tags
|
|
1864
|
+
},
|
|
1865
|
+
nextInput.publishBy,
|
|
1866
|
+
currentIdea.createdAt
|
|
1867
|
+
) : void 0
|
|
1868
|
+
});
|
|
1869
|
+
const comments = await client.listComments(issueNumber);
|
|
1870
|
+
return mapIssueToIdea(issue, comments);
|
|
1871
|
+
} catch (error) {
|
|
1872
|
+
const message = getErrorMessage2(error);
|
|
1873
|
+
logger_default.error(`[IdeaService] Failed to update idea #${issueNumber}: ${message}`);
|
|
1874
|
+
throw new Error(`Failed to update idea #${issueNumber}: ${message}`);
|
|
1875
|
+
}
|
|
1876
|
+
}
|
|
1877
|
+
async function getIdea(issueNumber) {
|
|
1878
|
+
const client = getGitHubClient();
|
|
1879
|
+
try {
|
|
1880
|
+
const [issue, comments] = await Promise.all([
|
|
1881
|
+
client.getIssue(issueNumber),
|
|
1882
|
+
client.listComments(issueNumber)
|
|
1883
|
+
]);
|
|
1884
|
+
return mapIssueToIdea(issue, comments);
|
|
1885
|
+
} catch (error) {
|
|
1886
|
+
if (isNotFoundError(error)) {
|
|
1887
|
+
return null;
|
|
1888
|
+
}
|
|
1889
|
+
const message = getErrorMessage2(error);
|
|
1890
|
+
logger_default.error(`[IdeaService] Failed to get idea #${issueNumber}: ${message}`);
|
|
1891
|
+
throw new Error(`Failed to get idea #${issueNumber}: ${message}`);
|
|
1892
|
+
}
|
|
1893
|
+
}
|
|
1894
|
+
async function listIdeas(filters) {
|
|
1895
|
+
const client = getGitHubClient();
|
|
1896
|
+
try {
|
|
1897
|
+
const issues = await client.listIssues({
|
|
1898
|
+
labels: buildLabelFilters(filters),
|
|
1899
|
+
maxResults: filters?.limit
|
|
1900
|
+
});
|
|
1901
|
+
const ideas = await Promise.all(
|
|
1902
|
+
issues.map(async (issue) => {
|
|
1903
|
+
const comments = await client.listComments(issue.number);
|
|
1904
|
+
return mapIssueToIdea(issue, comments);
|
|
1905
|
+
})
|
|
1906
|
+
);
|
|
1907
|
+
return filters?.limit ? ideas.slice(0, filters.limit) : ideas;
|
|
1908
|
+
} catch (error) {
|
|
1909
|
+
const message = getErrorMessage2(error);
|
|
1910
|
+
logger_default.error(`[IdeaService] Failed to list ideas: ${message}`);
|
|
1911
|
+
throw new Error(`Failed to list ideas: ${message}`);
|
|
1912
|
+
}
|
|
1913
|
+
}
|
|
1914
|
+
async function searchIdeas(query) {
|
|
1915
|
+
const client = getGitHubClient();
|
|
1916
|
+
try {
|
|
1917
|
+
const issues = await client.searchIssues(query);
|
|
1918
|
+
return await Promise.all(
|
|
1919
|
+
issues.map(async (issue) => {
|
|
1920
|
+
const comments = await client.listComments(issue.number);
|
|
1921
|
+
return mapIssueToIdea(issue, comments);
|
|
1922
|
+
})
|
|
1923
|
+
);
|
|
1924
|
+
} catch (error) {
|
|
1925
|
+
const message = getErrorMessage2(error);
|
|
1926
|
+
logger_default.error(`[IdeaService] Failed to search ideas: ${message}`);
|
|
1927
|
+
throw new Error(`Failed to search ideas: ${message}`);
|
|
1928
|
+
}
|
|
1929
|
+
}
|
|
1930
|
+
async function findRelatedIdeas(idea) {
|
|
1931
|
+
const client = getGitHubClient();
|
|
1932
|
+
try {
|
|
1933
|
+
const relatedIssues = /* @__PURE__ */ new Map();
|
|
1934
|
+
for (const tag of normalizeTags(idea.tags)) {
|
|
1935
|
+
const matches = await client.listIssues({ labels: [tag], maxResults: 5 });
|
|
1936
|
+
for (const match of matches) {
|
|
1937
|
+
if (match.number !== idea.issueNumber) {
|
|
1938
|
+
relatedIssues.set(match.number, match);
|
|
1939
|
+
}
|
|
1940
|
+
}
|
|
1941
|
+
}
|
|
1942
|
+
const sortedIssues = Array.from(relatedIssues.values()).sort((left, right) => right.updated_at.localeCompare(left.updated_at)).slice(0, 5);
|
|
1943
|
+
return await Promise.all(
|
|
1944
|
+
sortedIssues.map(async (issue) => {
|
|
1945
|
+
const comments = await client.listComments(issue.number);
|
|
1946
|
+
return mapIssueToIdea(issue, comments);
|
|
1947
|
+
})
|
|
1948
|
+
);
|
|
1949
|
+
} catch (error) {
|
|
1950
|
+
const message = getErrorMessage2(error);
|
|
1951
|
+
logger_default.error(`[IdeaService] Failed to find related ideas for #${idea.issueNumber}: ${message}`);
|
|
1952
|
+
throw new Error(`Failed to find related ideas for #${idea.issueNumber}: ${message}`);
|
|
1953
|
+
}
|
|
1954
|
+
}
|
|
1955
|
+
async function linkVideoToIdea(issueNumber, videoSlug) {
|
|
1956
|
+
const client = getGitHubClient();
|
|
1957
|
+
try {
|
|
1958
|
+
const [issue] = await Promise.all([
|
|
1959
|
+
client.getIssue(issueNumber),
|
|
1960
|
+
client.addComment(issueNumber, formatVideoLinkComment(videoSlug, (/* @__PURE__ */ new Date()).toISOString()))
|
|
1961
|
+
]);
|
|
1962
|
+
await client.updateIssue(issueNumber, {
|
|
1963
|
+
labels: buildLabelsFromIssue(issue, { status: "recorded" })
|
|
1964
|
+
});
|
|
1965
|
+
} catch (error) {
|
|
1966
|
+
const message = getErrorMessage2(error);
|
|
1967
|
+
logger_default.error(`[IdeaService] Failed to link video ${videoSlug} to idea #${issueNumber}: ${message}`);
|
|
1968
|
+
throw new Error(`Failed to link video ${videoSlug} to idea #${issueNumber}: ${message}`);
|
|
1969
|
+
}
|
|
1970
|
+
}
|
|
1971
|
+
async function recordPublish(issueNumber, record) {
|
|
1972
|
+
const client = getGitHubClient();
|
|
1973
|
+
try {
|
|
1974
|
+
const [issue, comments] = await Promise.all([
|
|
1975
|
+
client.getIssue(issueNumber),
|
|
1976
|
+
client.listComments(issueNumber)
|
|
1977
|
+
]);
|
|
1978
|
+
const hasDuplicate = comments.some((comment) => {
|
|
1979
|
+
const parsedComment = parseIdeaComment(comment.body);
|
|
1980
|
+
return parsedComment?.type === "publish-record" && parsedComment.record.queueItemId === record.queueItemId;
|
|
1981
|
+
});
|
|
1982
|
+
if (!hasDuplicate) {
|
|
1983
|
+
await client.addComment(issueNumber, formatPublishRecordComment(record));
|
|
1984
|
+
}
|
|
1985
|
+
if (!issue.labels.includes(`${STATUS_LABEL_PREFIX}published`)) {
|
|
1986
|
+
await client.updateIssue(issueNumber, {
|
|
1987
|
+
labels: buildLabelsFromIssue(issue, { status: "published" })
|
|
1988
|
+
});
|
|
1989
|
+
}
|
|
1990
|
+
} catch (error) {
|
|
1991
|
+
const message = getErrorMessage2(error);
|
|
1992
|
+
logger_default.error(`[IdeaService] Failed to record publish for idea #${issueNumber}: ${message}`);
|
|
1993
|
+
throw new Error(`Failed to record publish for idea #${issueNumber}: ${message}`);
|
|
1994
|
+
}
|
|
1995
|
+
}
|
|
1996
|
+
async function getPublishHistory(issueNumber) {
|
|
1997
|
+
const client = getGitHubClient();
|
|
1998
|
+
try {
|
|
1999
|
+
const comments = await client.listComments(issueNumber);
|
|
2000
|
+
return comments.flatMap((comment) => {
|
|
2001
|
+
const parsedComment = parseIdeaComment(comment.body);
|
|
2002
|
+
return parsedComment?.type === "publish-record" ? [parsedComment.record] : [];
|
|
2003
|
+
});
|
|
2004
|
+
} catch (error) {
|
|
2005
|
+
const message = getErrorMessage2(error);
|
|
2006
|
+
logger_default.error(`[IdeaService] Failed to get publish history for idea #${issueNumber}: ${message}`);
|
|
2007
|
+
throw new Error(`Failed to get publish history for idea #${issueNumber}: ${message}`);
|
|
2008
|
+
}
|
|
1274
2009
|
}
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
2010
|
+
async function getReadyIdeas() {
|
|
2011
|
+
return listIdeas({ status: "ready" });
|
|
2012
|
+
}
|
|
2013
|
+
async function markRecorded(issueNumber, videoSlug) {
|
|
2014
|
+
await linkVideoToIdea(issueNumber, videoSlug);
|
|
2015
|
+
}
|
|
2016
|
+
async function markPublished(issueNumber, record) {
|
|
2017
|
+
await recordPublish(issueNumber, record);
|
|
2018
|
+
}
|
|
2019
|
+
var STATUS_LABEL_PREFIX, PLATFORM_LABEL_PREFIX, PRIORITY_LABEL_PREFIX, COMMENT_MARKER, MARKDOWN_SECTION_PREFIX, platformValues, ideaStatuses;
|
|
2020
|
+
var init_ideaService = __esm({
|
|
2021
|
+
"src/L3-services/ideaService/ideaService.ts"() {
|
|
1278
2022
|
"use strict";
|
|
1279
|
-
|
|
2023
|
+
init_types();
|
|
2024
|
+
init_githubClient();
|
|
2025
|
+
init_environment();
|
|
1280
2026
|
init_configLogger();
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
2027
|
+
STATUS_LABEL_PREFIX = "status:";
|
|
2028
|
+
PLATFORM_LABEL_PREFIX = "platform:";
|
|
2029
|
+
PRIORITY_LABEL_PREFIX = "priority:";
|
|
2030
|
+
COMMENT_MARKER = "<!-- vidpipe:idea-comment -->";
|
|
2031
|
+
MARKDOWN_SECTION_PREFIX = "## ";
|
|
2032
|
+
platformValues = new Set(Object.values(Platform));
|
|
1284
2033
|
ideaStatuses = /* @__PURE__ */ new Set(["draft", "ready", "recorded", "published"]);
|
|
1285
|
-
ideaClipTypes = /* @__PURE__ */ new Set(["video", "short", "medium-clip"]);
|
|
1286
|
-
ideaPlatforms = /* @__PURE__ */ new Set(["tiktok", "youtube", "instagram", "linkedin", "x"]);
|
|
1287
2034
|
}
|
|
1288
2035
|
});
|
|
1289
2036
|
|
|
1290
2037
|
// src/L3-services/ideation/ideaService.ts
|
|
1291
|
-
var
|
|
1292
|
-
__export(
|
|
2038
|
+
var ideaService_exports2 = {};
|
|
2039
|
+
__export(ideaService_exports2, {
|
|
1293
2040
|
getIdeasByIds: () => getIdeasByIds,
|
|
1294
|
-
getReadyIdeas: () =>
|
|
1295
|
-
markPublished: () =>
|
|
1296
|
-
markRecorded: () =>
|
|
2041
|
+
getReadyIdeas: () => getReadyIdeas2,
|
|
2042
|
+
markPublished: () => markPublished2,
|
|
2043
|
+
markRecorded: () => markRecorded2,
|
|
1297
2044
|
matchIdeasToTranscript: () => matchIdeasToTranscript
|
|
1298
2045
|
});
|
|
1299
|
-
|
|
1300
|
-
return
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
2046
|
+
function normalizeIdeaIdentifier(id) {
|
|
2047
|
+
return id.trim();
|
|
2048
|
+
}
|
|
2049
|
+
function buildIdeaLookup(ideas) {
|
|
2050
|
+
const lookup = /* @__PURE__ */ new Map();
|
|
2051
|
+
for (const idea of ideas) {
|
|
2052
|
+
lookup.set(idea.id, idea);
|
|
2053
|
+
lookup.set(String(idea.issueNumber), idea);
|
|
2054
|
+
}
|
|
2055
|
+
return lookup;
|
|
2056
|
+
}
|
|
2057
|
+
async function resolveIdeaByIdentifier(id, ideas) {
|
|
2058
|
+
const normalizedId = normalizeIdeaIdentifier(id);
|
|
2059
|
+
if (!normalizedId) {
|
|
2060
|
+
return null;
|
|
2061
|
+
}
|
|
2062
|
+
const issueNumber = Number.parseInt(normalizedId, 10);
|
|
2063
|
+
if (Number.isInteger(issueNumber)) {
|
|
2064
|
+
const idea = await getIdea(issueNumber);
|
|
2065
|
+
if (idea) {
|
|
1306
2066
|
return idea;
|
|
1307
|
-
}
|
|
1308
|
-
|
|
2067
|
+
}
|
|
2068
|
+
}
|
|
2069
|
+
const availableIdeas = ideas ?? await listIdeas();
|
|
2070
|
+
return buildIdeaLookup(availableIdeas).get(normalizedId) ?? null;
|
|
2071
|
+
}
|
|
2072
|
+
async function getIdeasByIds(ids, _dir) {
|
|
2073
|
+
const ideas = await listIdeas();
|
|
2074
|
+
const lookup = buildIdeaLookup(ideas);
|
|
2075
|
+
return ids.map((id) => {
|
|
2076
|
+
const normalizedId = normalizeIdeaIdentifier(id);
|
|
2077
|
+
const idea = lookup.get(normalizedId);
|
|
2078
|
+
if (!idea) {
|
|
2079
|
+
throw new Error(`Idea not found: ${id}`);
|
|
2080
|
+
}
|
|
2081
|
+
return idea;
|
|
2082
|
+
});
|
|
1309
2083
|
}
|
|
1310
|
-
async function
|
|
1311
|
-
|
|
1312
|
-
return ideas.filter((idea) => idea.status === "ready");
|
|
2084
|
+
async function getReadyIdeas2(_dir) {
|
|
2085
|
+
return getReadyIdeas();
|
|
1313
2086
|
}
|
|
1314
|
-
async function
|
|
1315
|
-
const idea = await
|
|
2087
|
+
async function markRecorded2(id, videoSlug, _dir) {
|
|
2088
|
+
const idea = await resolveIdeaByIdentifier(id);
|
|
1316
2089
|
if (!idea) {
|
|
1317
2090
|
throw new Error(`Idea not found: ${id}`);
|
|
1318
2091
|
}
|
|
1319
|
-
idea.
|
|
1320
|
-
idea.sourceVideoSlug = videoSlug;
|
|
1321
|
-
await writeIdea(idea, dir);
|
|
2092
|
+
await markRecorded(idea.issueNumber, videoSlug);
|
|
1322
2093
|
}
|
|
1323
|
-
async function
|
|
1324
|
-
const idea = await
|
|
2094
|
+
async function markPublished2(id, record, _dir) {
|
|
2095
|
+
const idea = await resolveIdeaByIdentifier(id);
|
|
1325
2096
|
if (!idea) {
|
|
1326
2097
|
throw new Error(`Idea not found: ${id}`);
|
|
1327
2098
|
}
|
|
1328
|
-
|
|
1329
|
-
idea.status = "published";
|
|
1330
|
-
await writeIdea(idea, dir);
|
|
2099
|
+
await markPublished(idea.issueNumber, record);
|
|
1331
2100
|
}
|
|
1332
|
-
async function matchIdeasToTranscript(transcript, ideas,
|
|
2101
|
+
async function matchIdeasToTranscript(transcript, ideas, _dir) {
|
|
1333
2102
|
try {
|
|
1334
|
-
const readyIdeas = (ideas ?? await
|
|
2103
|
+
const readyIdeas = (ideas ?? await getReadyIdeas()).filter((idea) => idea.status === "ready");
|
|
1335
2104
|
if (readyIdeas.length === 0) {
|
|
1336
2105
|
return [];
|
|
1337
2106
|
}
|
|
@@ -1349,9 +2118,7 @@ async function matchIdeasToTranscript(transcript, ideas, dir) {
|
|
|
1349
2118
|
hook: idea.hook,
|
|
1350
2119
|
keyTakeaway: idea.keyTakeaway
|
|
1351
2120
|
}));
|
|
1352
|
-
const knownIdeaIds =
|
|
1353
|
-
ideas ? readyIdeaIds : await listIdeaIds(dir)
|
|
1354
|
-
);
|
|
2121
|
+
const knownIdeaIds = readyIdeaIds;
|
|
1355
2122
|
const session = await provider.createSession({
|
|
1356
2123
|
systemPrompt: MATCH_IDEAS_SYSTEM_PROMPT,
|
|
1357
2124
|
tools: [],
|
|
@@ -1370,7 +2137,7 @@ async function matchIdeasToTranscript(transcript, ideas, dir) {
|
|
|
1370
2137
|
return matchedIdea ? [matchedIdea] : [];
|
|
1371
2138
|
});
|
|
1372
2139
|
}
|
|
1373
|
-
return await getIdeasByIds(matchedIds
|
|
2140
|
+
return await getIdeasByIds(matchedIds);
|
|
1374
2141
|
} finally {
|
|
1375
2142
|
await session.close().catch((error) => {
|
|
1376
2143
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -1403,12 +2170,12 @@ function parseMatchedIdeaIds(rawContent, knownIdeaIds) {
|
|
|
1403
2170
|
return Array.from(new Set(matchedIds.filter((id) => knownIdeaIds.has(id))));
|
|
1404
2171
|
}
|
|
1405
2172
|
var IDEA_MATCH_AGENT_NAME, IDEA_MATCH_LIMIT, TRANSCRIPT_SUMMARY_LIMIT, MATCH_IDEAS_SYSTEM_PROMPT;
|
|
1406
|
-
var
|
|
2173
|
+
var init_ideaService2 = __esm({
|
|
1407
2174
|
"src/L3-services/ideation/ideaService.ts"() {
|
|
1408
2175
|
"use strict";
|
|
1409
2176
|
init_modelConfig();
|
|
1410
|
-
init_ideaStore();
|
|
1411
2177
|
init_configLogger();
|
|
2178
|
+
init_ideaService();
|
|
1412
2179
|
init_providerFactory();
|
|
1413
2180
|
IDEA_MATCH_AGENT_NAME = "IdeaService";
|
|
1414
2181
|
IDEA_MATCH_LIMIT = 3;
|
|
@@ -1440,50 +2207,7 @@ init_environment();
|
|
|
1440
2207
|
init_paths();
|
|
1441
2208
|
init_fileSystem();
|
|
1442
2209
|
init_configLogger();
|
|
1443
|
-
|
|
1444
|
-
// src/L0-pure/types/index.ts
|
|
1445
|
-
var Platform = /* @__PURE__ */ ((Platform2) => {
|
|
1446
|
-
Platform2["TikTok"] = "tiktok";
|
|
1447
|
-
Platform2["YouTube"] = "youtube";
|
|
1448
|
-
Platform2["Instagram"] = "instagram";
|
|
1449
|
-
Platform2["LinkedIn"] = "linkedin";
|
|
1450
|
-
Platform2["X"] = "x";
|
|
1451
|
-
return Platform2;
|
|
1452
|
-
})(Platform || {});
|
|
1453
|
-
var PLATFORM_CHAR_LIMITS = {
|
|
1454
|
-
tiktok: 2200,
|
|
1455
|
-
youtube: 5e3,
|
|
1456
|
-
instagram: 2200,
|
|
1457
|
-
linkedin: 3e3,
|
|
1458
|
-
twitter: 280
|
|
1459
|
-
};
|
|
1460
|
-
function toLatePlatform(platform) {
|
|
1461
|
-
return platform === "x" /* X */ ? "twitter" : platform;
|
|
1462
|
-
}
|
|
1463
|
-
function fromLatePlatform(latePlatform) {
|
|
1464
|
-
const normalized = normalizePlatformString(latePlatform);
|
|
1465
|
-
if (normalized === "twitter") {
|
|
1466
|
-
return "x" /* X */;
|
|
1467
|
-
}
|
|
1468
|
-
const platformValues = Object.values(Platform);
|
|
1469
|
-
if (platformValues.includes(normalized)) {
|
|
1470
|
-
return normalized;
|
|
1471
|
-
}
|
|
1472
|
-
throw new Error(`Unsupported platform from Late API: ${latePlatform}`);
|
|
1473
|
-
}
|
|
1474
|
-
function normalizePlatformString(raw) {
|
|
1475
|
-
const lower = raw.toLowerCase().trim();
|
|
1476
|
-
if (lower === "x" || lower === "x (twitter)" || lower === "x/twitter") {
|
|
1477
|
-
return "twitter";
|
|
1478
|
-
}
|
|
1479
|
-
return lower;
|
|
1480
|
-
}
|
|
1481
|
-
var SUPPORTED_VIDEO_EXTENSIONS = [".mp4", ".webm"];
|
|
1482
|
-
function isSupportedVideoExtension(ext) {
|
|
1483
|
-
return SUPPORTED_VIDEO_EXTENSIONS.includes(ext.toLowerCase());
|
|
1484
|
-
}
|
|
1485
|
-
|
|
1486
|
-
// src/L7-app/fileWatcher.ts
|
|
2210
|
+
init_types();
|
|
1487
2211
|
var FileWatcher = class _FileWatcher extends EventEmitter {
|
|
1488
2212
|
watchFolder;
|
|
1489
2213
|
watcher = null;
|
|
@@ -4449,6 +5173,7 @@ var SocialPostAsset = class extends TextAsset {
|
|
|
4449
5173
|
// src/L5-assets/ShortVideoAsset.ts
|
|
4450
5174
|
init_paths();
|
|
4451
5175
|
init_fileSystem();
|
|
5176
|
+
init_types();
|
|
4452
5177
|
var ShortVideoAsset = class extends VideoAsset {
|
|
4453
5178
|
/** Reference to the source video this short was extracted from */
|
|
4454
5179
|
parent;
|
|
@@ -4581,6 +5306,7 @@ var ShortVideoAsset = class extends VideoAsset {
|
|
|
4581
5306
|
// src/L5-assets/MediumClipAsset.ts
|
|
4582
5307
|
init_paths();
|
|
4583
5308
|
init_fileSystem();
|
|
5309
|
+
init_types();
|
|
4584
5310
|
var MediumClipAsset = class extends VideoAsset {
|
|
4585
5311
|
/** Parent video this clip was extracted from */
|
|
4586
5312
|
parent;
|
|
@@ -6780,6 +7506,7 @@ init_fileSystem();
|
|
|
6780
7506
|
init_paths();
|
|
6781
7507
|
init_configLogger();
|
|
6782
7508
|
init_environment();
|
|
7509
|
+
init_types();
|
|
6783
7510
|
var SYSTEM_PROMPT5 = `You are a viral social-media content strategist.
|
|
6784
7511
|
Given a video transcript and summary you MUST generate one post for each of the 5 platforms listed below.
|
|
6785
7512
|
Each post must match the platform's tone, format, and constraints exactly.
|
|
@@ -7291,8 +8018,11 @@ async function commitAndPush(videoSlug, message) {
|
|
|
7291
8018
|
init_fileSystem();
|
|
7292
8019
|
init_paths();
|
|
7293
8020
|
init_configLogger();
|
|
8021
|
+
init_types();
|
|
8022
|
+
init_types();
|
|
7294
8023
|
|
|
7295
8024
|
// src/L3-services/socialPosting/platformContentStrategy.ts
|
|
8025
|
+
init_types();
|
|
7296
8026
|
var CONTENT_MATRIX = {
|
|
7297
8027
|
["youtube" /* YouTube */]: {
|
|
7298
8028
|
video: { captions: true, variantKey: null },
|
|
@@ -7325,6 +8055,7 @@ function platformAcceptsMedia(platform, clipType) {
|
|
|
7325
8055
|
}
|
|
7326
8056
|
|
|
7327
8057
|
// src/L3-services/postStore/postStore.ts
|
|
8058
|
+
init_types();
|
|
7328
8059
|
init_environment();
|
|
7329
8060
|
init_configLogger();
|
|
7330
8061
|
init_fileSystem();
|
|
@@ -7533,14 +8264,40 @@ async function approveItem(id, publishData) {
|
|
|
7533
8264
|
item.metadata.reviewedAt = now;
|
|
7534
8265
|
if (item.metadata.ideaIds && item.metadata.ideaIds.length > 0) {
|
|
7535
8266
|
try {
|
|
7536
|
-
const { markPublished:
|
|
7537
|
-
|
|
7538
|
-
|
|
8267
|
+
const { getIdea: getIdea2, listIdeas: listIdeas2, markPublished: markPublished3 } = await Promise.resolve().then(() => (init_ideaService(), ideaService_exports));
|
|
8268
|
+
let cachedIdeas;
|
|
8269
|
+
for (const rawIdeaId of item.metadata.ideaIds) {
|
|
8270
|
+
const normalizedIdeaId = String(rawIdeaId).trim();
|
|
8271
|
+
if (!normalizedIdeaId) {
|
|
8272
|
+
continue;
|
|
8273
|
+
}
|
|
8274
|
+
const parsedIssueNumber = Number.parseInt(normalizedIdeaId, 10);
|
|
8275
|
+
let issueNumber;
|
|
8276
|
+
if (Number.isInteger(parsedIssueNumber)) {
|
|
8277
|
+
issueNumber = parsedIssueNumber;
|
|
8278
|
+
} else {
|
|
8279
|
+
if (!cachedIdeas) {
|
|
8280
|
+
const ideas = await listIdeas2();
|
|
8281
|
+
cachedIdeas = new Map(ideas.flatMap((idea2) => [[idea2.id, idea2.issueNumber], [String(idea2.issueNumber), idea2.issueNumber]]));
|
|
8282
|
+
}
|
|
8283
|
+
issueNumber = cachedIdeas.get(normalizedIdeaId);
|
|
8284
|
+
}
|
|
8285
|
+
if (!issueNumber) {
|
|
8286
|
+
logger_default.warn(`Skipping publish record for unknown idea identifier: ${normalizedIdeaId}`);
|
|
8287
|
+
continue;
|
|
8288
|
+
}
|
|
8289
|
+
const idea = await getIdea2(issueNumber);
|
|
8290
|
+
if (!idea) {
|
|
8291
|
+
logger_default.warn(`Skipping publish record for missing idea #${issueNumber}`);
|
|
8292
|
+
continue;
|
|
8293
|
+
}
|
|
8294
|
+
await markPublished3(issueNumber, {
|
|
7539
8295
|
clipType: item.metadata.clipType,
|
|
7540
8296
|
platform: fromLatePlatform(item.metadata.platform),
|
|
7541
8297
|
queueItemId: id,
|
|
7542
8298
|
publishedAt: now,
|
|
7543
|
-
|
|
8299
|
+
latePostId: item.metadata.latePostId ?? "",
|
|
8300
|
+
lateUrl: item.metadata.publishedUrl || (item.metadata.latePostId ? `https://app.late.co/dashboard/post/${item.metadata.latePostId}` : "")
|
|
7544
8301
|
});
|
|
7545
8302
|
}
|
|
7546
8303
|
} catch (err) {
|
|
@@ -8127,6 +8884,7 @@ async function enhanceVideo(videoPath, transcript, video) {
|
|
|
8127
8884
|
// src/L5-assets/MainVideoAsset.ts
|
|
8128
8885
|
init_environment();
|
|
8129
8886
|
init_configLogger();
|
|
8887
|
+
init_types();
|
|
8130
8888
|
var MainVideoAsset = class _MainVideoAsset extends VideoAsset {
|
|
8131
8889
|
sourcePath;
|
|
8132
8890
|
videoDir;
|
|
@@ -9013,7 +9771,7 @@ var MainVideoAsset = class _MainVideoAsset extends VideoAsset {
|
|
|
9013
9771
|
*/
|
|
9014
9772
|
async buildPublishQueueData(shorts, mediumClips, socialPosts, captionedVideoPath) {
|
|
9015
9773
|
const video = await this.toVideoFile();
|
|
9016
|
-
const ideaIds = this._ideas.length > 0 ? this._ideas.map((idea) => idea.
|
|
9774
|
+
const ideaIds = this._ideas.length > 0 ? this._ideas.map((idea) => String(idea.issueNumber)) : void 0;
|
|
9017
9775
|
return buildPublishQueue2(video, shorts, mediumClips, socialPosts, captionedVideoPath, ideaIds);
|
|
9018
9776
|
}
|
|
9019
9777
|
/**
|
|
@@ -10733,17 +11491,26 @@ var ScheduleAgent = class extends BaseAgent {
|
|
|
10733
11491
|
init_fileSystem();
|
|
10734
11492
|
init_environment();
|
|
10735
11493
|
init_modelConfig();
|
|
10736
|
-
init_ideaStore();
|
|
10737
11494
|
init_configLogger();
|
|
11495
|
+
init_ideaService();
|
|
10738
11496
|
init_providerFactory();
|
|
11497
|
+
init_types();
|
|
10739
11498
|
var BASE_SYSTEM_PROMPT = `You are a content strategist for a tech content creator. Your role is to research trending topics, analyze what's working, and generate compelling video ideas grounded in real-world data.
|
|
10740
11499
|
|
|
10741
11500
|
## CRITICAL: Research Before Creating
|
|
10742
11501
|
You MUST research before creating ideas. Do NOT skip the research phase. Ideas generated without research will be generic and stale. The value you provide is grounding ideas in what's ACTUALLY trending right now.
|
|
10743
11502
|
|
|
11503
|
+
## GitHub Issue Workflow
|
|
11504
|
+
Ideas are stored as GitHub Issues in a dedicated repository. Treat the issue tracker as the source of truth:
|
|
11505
|
+
- Use get_past_ideas to inspect the current issue backlog with optional filters.
|
|
11506
|
+
- Use search_ideas for full-text lookups before creating something new.
|
|
11507
|
+
- Use find_related_ideas to cluster overlapping ideas by tags and avoid duplicates.
|
|
11508
|
+
- Use create_idea to create new draft issues.
|
|
11509
|
+
- Use update_idea or organize_ideas when an existing issue should be refined instead of creating a duplicate.
|
|
11510
|
+
|
|
10744
11511
|
## Your Research Process
|
|
10745
11512
|
1. Load the brand context (get_brand_context) to understand the creator's voice, expertise, and content pillars.
|
|
10746
|
-
2. Check existing ideas (get_past_ideas) to avoid duplicates.
|
|
11513
|
+
2. Check existing GitHub issue ideas (get_past_ideas and search_ideas) to avoid duplicates.
|
|
10747
11514
|
3. **RESEARCH PHASE** \u2014 This is the most important step. Use the available MCP tools:
|
|
10748
11515
|
- **web_search_exa**: Search for trending topics, viral content, recent announcements, and hot takes in the creator's niche. Search for specific topics from the creator's content pillars.
|
|
10749
11516
|
- **youtube_search_videos** or **youtube_search**: Find what videos are performing well right now. Look at view counts, recent uploads on trending topics, and gaps in existing content.
|
|
@@ -10769,15 +11536,27 @@ Every idea must:
|
|
|
10769
11536
|
- Written (LinkedIn, X/Twitter): Thought leadership, hot takes, thread-worthy
|
|
10770
11537
|
|
|
10771
11538
|
Generate 3-5 high-quality ideas. Quality over quantity. Every idea must be backed by research.`;
|
|
10772
|
-
var SUPPORTED_PLATFORMS = [
|
|
11539
|
+
var SUPPORTED_PLATFORMS = [
|
|
11540
|
+
"tiktok" /* TikTok */,
|
|
11541
|
+
"youtube" /* YouTube */,
|
|
11542
|
+
"instagram" /* Instagram */,
|
|
11543
|
+
"linkedin" /* LinkedIn */,
|
|
11544
|
+
"x" /* X */
|
|
11545
|
+
];
|
|
11546
|
+
var SUPPORTED_STATUSES = ["draft", "ready", "recorded", "published"];
|
|
11547
|
+
var SUPPORTED_PRIORITIES = ["hot-trend", "timely", "evergreen"];
|
|
10773
11548
|
var MIN_IDEA_COUNT = 3;
|
|
10774
11549
|
var MAX_IDEA_COUNT = 5;
|
|
10775
|
-
|
|
11550
|
+
var DEFAULT_EXISTING_IDEA_LIMIT = 50;
|
|
11551
|
+
function isRecord(value) {
|
|
10776
11552
|
return typeof value === "object" && value !== null;
|
|
10777
11553
|
}
|
|
10778
|
-
function
|
|
11554
|
+
function isStringArray(value) {
|
|
10779
11555
|
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
10780
11556
|
}
|
|
11557
|
+
function hasField(source, field) {
|
|
11558
|
+
return Object.prototype.hasOwnProperty.call(source, field);
|
|
11559
|
+
}
|
|
10781
11560
|
function normalizeCount(count) {
|
|
10782
11561
|
if (typeof count !== "number" || Number.isNaN(count)) {
|
|
10783
11562
|
return MIN_IDEA_COUNT;
|
|
@@ -10790,7 +11569,7 @@ function normalizeSeedTopics(seedTopics) {
|
|
|
10790
11569
|
}
|
|
10791
11570
|
function extractStringArrayField(source, field) {
|
|
10792
11571
|
const value = source[field];
|
|
10793
|
-
return
|
|
11572
|
+
return isStringArray(value) ? value : [];
|
|
10794
11573
|
}
|
|
10795
11574
|
function extractContentPillars(brand) {
|
|
10796
11575
|
const raw = brand.contentPillars;
|
|
@@ -10802,7 +11581,7 @@ function extractContentPillars(brand) {
|
|
|
10802
11581
|
const pillar2 = entry.trim();
|
|
10803
11582
|
return pillar2 ? [{ pillar: pillar2 }] : [];
|
|
10804
11583
|
}
|
|
10805
|
-
if (!
|
|
11584
|
+
if (!isRecord(entry)) {
|
|
10806
11585
|
return [];
|
|
10807
11586
|
}
|
|
10808
11587
|
const pillar = typeof entry.pillar === "string" ? entry.pillar.trim() : "";
|
|
@@ -10811,21 +11590,22 @@ function extractContentPillars(brand) {
|
|
|
10811
11590
|
}
|
|
10812
11591
|
const description = typeof entry.description === "string" ? entry.description.trim() : void 0;
|
|
10813
11592
|
const frequency = typeof entry.frequency === "string" ? entry.frequency.trim() : void 0;
|
|
10814
|
-
const formats =
|
|
11593
|
+
const formats = isStringArray(entry.formats) ? entry.formats.map((format) => format.trim()).filter((format) => format.length > 0) : void 0;
|
|
10815
11594
|
return [{ pillar, description, frequency, formats }];
|
|
10816
11595
|
});
|
|
10817
11596
|
}
|
|
10818
11597
|
function summarizeExistingIdeas(ideas) {
|
|
10819
11598
|
if (ideas.length === 0) {
|
|
10820
|
-
return "No existing ideas found in the
|
|
11599
|
+
return "No existing GitHub issue ideas found in the repository.";
|
|
10821
11600
|
}
|
|
10822
|
-
return ideas.slice(0, 25).map((idea) => `-
|
|
11601
|
+
return ideas.slice(0, 25).map((idea) => `- #${idea.issueNumber}: ${idea.topic} [${idea.status}] (${idea.issueUrl})`).join("\n");
|
|
10823
11602
|
}
|
|
10824
11603
|
function buildPlatformGuidance() {
|
|
10825
11604
|
return [
|
|
10826
11605
|
`Allowed platforms for create_idea: ${SUPPORTED_PLATFORMS.join(", ")}`,
|
|
10827
11606
|
`Create between ${MIN_IDEA_COUNT} and ${MAX_IDEA_COUNT} ideas unless the user explicitly requests fewer within that range.`,
|
|
10828
|
-
"Call create_idea once per idea, then call finalize_ideas exactly once when done."
|
|
11607
|
+
"Call create_idea once per new idea issue, then call finalize_ideas exactly once when done.",
|
|
11608
|
+
"Prefer update_idea or organize_ideas when you discover that a GitHub issue already covers the concept."
|
|
10829
11609
|
].join("\n");
|
|
10830
11610
|
}
|
|
10831
11611
|
function buildBrandPromptSection(brand) {
|
|
@@ -10859,20 +11639,33 @@ function buildBrandPromptSection(brand) {
|
|
|
10859
11639
|
lines.push("Content pillars:");
|
|
10860
11640
|
lines.push(
|
|
10861
11641
|
...contentPillars.map((pillar) => {
|
|
10862
|
-
const details = [
|
|
11642
|
+
const details = [
|
|
11643
|
+
pillar.description,
|
|
11644
|
+
pillar.frequency && `Frequency: ${pillar.frequency}`,
|
|
11645
|
+
pillar.formats?.length ? `Formats: ${pillar.formats.join(", ")}` : void 0
|
|
11646
|
+
].filter((value) => typeof value === "string" && value.length > 0).join(" | ");
|
|
10863
11647
|
return details ? `- ${pillar.pillar}: ${details}` : `- ${pillar.pillar}`;
|
|
10864
11648
|
})
|
|
10865
11649
|
);
|
|
10866
11650
|
}
|
|
10867
11651
|
return lines.join("\n");
|
|
10868
11652
|
}
|
|
10869
|
-
function
|
|
11653
|
+
function buildIdeaRepoPromptSection(ideaRepo) {
|
|
11654
|
+
return [
|
|
11655
|
+
"## GitHub Idea Repository",
|
|
11656
|
+
`Dedicated issue repo: ${ideaRepo}`,
|
|
11657
|
+
"Every idea is a GitHub Issue. The issue tracker is the source of truth for duplicates, lifecycle status, tags, and related concepts."
|
|
11658
|
+
].join("\n");
|
|
11659
|
+
}
|
|
11660
|
+
function buildSystemPrompt3(brand, existingIdeas, seedTopics, count, ideaRepo) {
|
|
10870
11661
|
const promptSections = [
|
|
10871
11662
|
BASE_SYSTEM_PROMPT,
|
|
10872
11663
|
"",
|
|
11664
|
+
buildIdeaRepoPromptSection(ideaRepo),
|
|
11665
|
+
"",
|
|
10873
11666
|
buildBrandPromptSection(brand),
|
|
10874
11667
|
"",
|
|
10875
|
-
"## Existing Idea
|
|
11668
|
+
"## Existing Idea Issues",
|
|
10876
11669
|
summarizeExistingIdeas(existingIdeas),
|
|
10877
11670
|
"",
|
|
10878
11671
|
"## Planning Constraints",
|
|
@@ -10888,7 +11681,7 @@ function buildUserMessage(count, seedTopics, hasMcpServers) {
|
|
|
10888
11681
|
const focusText = seedTopics.length > 0 ? `Focus areas: ${seedTopics.join(", ")}` : "Focus areas: choose the strongest timely opportunities from the creator context and current trends.";
|
|
10889
11682
|
const steps = [
|
|
10890
11683
|
"1. Call get_brand_context to load the creator profile.",
|
|
10891
|
-
"2. Call get_past_ideas to
|
|
11684
|
+
"2. Call get_past_ideas (and search_ideas if needed) to inspect existing GitHub issue ideas before proposing anything new."
|
|
10892
11685
|
];
|
|
10893
11686
|
if (hasMcpServers) {
|
|
10894
11687
|
steps.push(
|
|
@@ -10898,12 +11691,14 @@ function buildUserMessage(count, seedTopics, hasMcpServers) {
|
|
|
10898
11691
|
" - Use perplexity-search to get current analysis on promising topics.",
|
|
10899
11692
|
" Do at least 2-3 research queries. Each idea you create MUST reference specific findings from this research in its trendContext field.",
|
|
10900
11693
|
`4. Call create_idea for each of the ${count} ideas, grounding each in your research findings.`,
|
|
10901
|
-
"5.
|
|
11694
|
+
"5. If you uncover overlap with existing issues, prefer update_idea or organize_ideas over creating duplicates.",
|
|
11695
|
+
"6. Call finalize_ideas when done."
|
|
10902
11696
|
);
|
|
10903
11697
|
} else {
|
|
10904
11698
|
steps.push(
|
|
10905
11699
|
`3. Call create_idea for each of the ${count} ideas.`,
|
|
10906
|
-
"4.
|
|
11700
|
+
"4. If you uncover overlap with existing issues, prefer update_idea or organize_ideas over creating duplicates.",
|
|
11701
|
+
"5. Call finalize_ideas when done."
|
|
10907
11702
|
);
|
|
10908
11703
|
}
|
|
10909
11704
|
return [
|
|
@@ -10920,50 +11715,181 @@ async function loadBrandContext(brandPath) {
|
|
|
10920
11715
|
}
|
|
10921
11716
|
return readJsonFile(brandPath);
|
|
10922
11717
|
}
|
|
10923
|
-
function
|
|
10924
|
-
|
|
10925
|
-
|
|
10926
|
-
|
|
10927
|
-
|
|
11718
|
+
function normalizeRequiredString(value, field) {
|
|
11719
|
+
if (typeof value !== "string") {
|
|
11720
|
+
throw new Error(`Invalid ${field}: expected string`);
|
|
11721
|
+
}
|
|
11722
|
+
const normalized = value.trim();
|
|
11723
|
+
if (!normalized) {
|
|
11724
|
+
throw new Error(`Invalid ${field}: value cannot be empty`);
|
|
10928
11725
|
}
|
|
10929
11726
|
return normalized;
|
|
10930
11727
|
}
|
|
10931
|
-
function
|
|
10932
|
-
|
|
10933
|
-
|
|
10934
|
-
throw new Error(`Idea ID must be kebab-case: ${id}`);
|
|
11728
|
+
function normalizeOptionalString(value, field) {
|
|
11729
|
+
if (value === void 0) {
|
|
11730
|
+
return void 0;
|
|
10935
11731
|
}
|
|
10936
|
-
|
|
11732
|
+
if (typeof value !== "string") {
|
|
11733
|
+
throw new Error(`Invalid ${field}: expected string`);
|
|
11734
|
+
}
|
|
11735
|
+
const normalized = value.trim();
|
|
11736
|
+
return normalized || void 0;
|
|
10937
11737
|
}
|
|
10938
|
-
function
|
|
10939
|
-
|
|
10940
|
-
|
|
10941
|
-
if (args.hook.trim().length > 80) {
|
|
10942
|
-
throw new Error(`Idea hook must be 80 characters or fewer: ${args.id}`);
|
|
11738
|
+
function normalizeStringList(value, field) {
|
|
11739
|
+
if (!isStringArray(value)) {
|
|
11740
|
+
throw new Error(`Invalid ${field}: expected string[]`);
|
|
10943
11741
|
}
|
|
11742
|
+
return value.map((item) => item.trim()).filter((item) => item.length > 0);
|
|
11743
|
+
}
|
|
11744
|
+
function normalizeIssueNumber(value) {
|
|
11745
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value <= 0) {
|
|
11746
|
+
throw new Error("Invalid issueNumber: expected a positive integer");
|
|
11747
|
+
}
|
|
11748
|
+
return value;
|
|
11749
|
+
}
|
|
11750
|
+
function normalizePublishBy(value, field = "publishBy") {
|
|
11751
|
+
const publishBy = normalizeRequiredString(value, field);
|
|
10944
11752
|
if (Number.isNaN(new Date(publishBy).getTime())) {
|
|
10945
|
-
throw new Error(`Invalid
|
|
11753
|
+
throw new Error(`Invalid ${field} date: ${publishBy}`);
|
|
11754
|
+
}
|
|
11755
|
+
return publishBy;
|
|
11756
|
+
}
|
|
11757
|
+
function normalizePlatforms(platforms) {
|
|
11758
|
+
const values = normalizeStringList(platforms, "platforms").map((platform) => platform.toLowerCase());
|
|
11759
|
+
const invalid = values.filter((platform) => !SUPPORTED_PLATFORMS.includes(platform));
|
|
11760
|
+
if (invalid.length > 0) {
|
|
11761
|
+
throw new Error(`Unsupported platforms: ${invalid.join(", ")}`);
|
|
11762
|
+
}
|
|
11763
|
+
return values;
|
|
11764
|
+
}
|
|
11765
|
+
function normalizeStatus(value) {
|
|
11766
|
+
const status = normalizeRequiredString(value, "status").toLowerCase();
|
|
11767
|
+
if (!SUPPORTED_STATUSES.includes(status)) {
|
|
11768
|
+
throw new Error(`Unsupported status: ${status}`);
|
|
11769
|
+
}
|
|
11770
|
+
return status;
|
|
11771
|
+
}
|
|
11772
|
+
function parseCreateIdeaInput(args) {
|
|
11773
|
+
const hook = normalizeRequiredString(args.hook, "hook");
|
|
11774
|
+
if (hook.length > 80) {
|
|
11775
|
+
throw new Error(`Idea hook must be 80 characters or fewer: ${hook}`);
|
|
10946
11776
|
}
|
|
10947
11777
|
return {
|
|
10948
|
-
|
|
10949
|
-
|
|
10950
|
-
|
|
10951
|
-
|
|
10952
|
-
|
|
10953
|
-
talkingPoints: args.talkingPoints.map((point) => point.trim()).filter((point) => point.length > 0),
|
|
11778
|
+
topic: normalizeRequiredString(args.topic, "topic"),
|
|
11779
|
+
hook,
|
|
11780
|
+
audience: normalizeRequiredString(args.audience, "audience"),
|
|
11781
|
+
keyTakeaway: normalizeRequiredString(args.keyTakeaway, "keyTakeaway"),
|
|
11782
|
+
talkingPoints: normalizeStringList(args.talkingPoints, "talkingPoints"),
|
|
10954
11783
|
platforms: normalizePlatforms(args.platforms),
|
|
10955
|
-
|
|
10956
|
-
|
|
10957
|
-
trendContext: args.trendContext
|
|
10958
|
-
|
|
10959
|
-
|
|
10960
|
-
|
|
11784
|
+
tags: normalizeStringList(args.tags, "tags"),
|
|
11785
|
+
publishBy: normalizePublishBy(args.publishBy),
|
|
11786
|
+
trendContext: normalizeOptionalString(args.trendContext, "trendContext")
|
|
11787
|
+
};
|
|
11788
|
+
}
|
|
11789
|
+
function parseIdeaFilters(args) {
|
|
11790
|
+
const filters = {};
|
|
11791
|
+
if (hasField(args, "status") && args.status !== void 0) {
|
|
11792
|
+
filters.status = normalizeStatus(args.status);
|
|
11793
|
+
}
|
|
11794
|
+
if (hasField(args, "platform") && args.platform !== void 0) {
|
|
11795
|
+
filters.platform = normalizePlatforms([args.platform].flat())[0];
|
|
11796
|
+
}
|
|
11797
|
+
if (hasField(args, "tag") && args.tag !== void 0) {
|
|
11798
|
+
filters.tag = normalizeRequiredString(args.tag, "tag");
|
|
11799
|
+
}
|
|
11800
|
+
if (hasField(args, "priority") && args.priority !== void 0) {
|
|
11801
|
+
const priority = normalizeRequiredString(args.priority, "priority").toLowerCase();
|
|
11802
|
+
if (!SUPPORTED_PRIORITIES.includes(priority)) {
|
|
11803
|
+
throw new Error(`Unsupported priority: ${priority}`);
|
|
11804
|
+
}
|
|
11805
|
+
filters.priority = priority;
|
|
11806
|
+
}
|
|
11807
|
+
if (hasField(args, "limit") && args.limit !== void 0) {
|
|
11808
|
+
if (typeof args.limit !== "number" || !Number.isInteger(args.limit) || args.limit <= 0) {
|
|
11809
|
+
throw new Error("Invalid limit: expected a positive integer");
|
|
11810
|
+
}
|
|
11811
|
+
filters.limit = args.limit;
|
|
11812
|
+
}
|
|
11813
|
+
return filters;
|
|
11814
|
+
}
|
|
11815
|
+
function extractIdeaUpdates(source) {
|
|
11816
|
+
const updates = {};
|
|
11817
|
+
if (hasField(source, "topic")) {
|
|
11818
|
+
updates.topic = normalizeRequiredString(source.topic, "updates.topic");
|
|
11819
|
+
}
|
|
11820
|
+
if (hasField(source, "hook")) {
|
|
11821
|
+
const hook = normalizeRequiredString(source.hook, "updates.hook");
|
|
11822
|
+
if (hook.length > 80) {
|
|
11823
|
+
throw new Error(`Idea hook must be 80 characters or fewer: ${hook}`);
|
|
11824
|
+
}
|
|
11825
|
+
updates.hook = hook;
|
|
11826
|
+
}
|
|
11827
|
+
if (hasField(source, "audience")) {
|
|
11828
|
+
updates.audience = normalizeRequiredString(source.audience, "updates.audience");
|
|
11829
|
+
}
|
|
11830
|
+
if (hasField(source, "keyTakeaway")) {
|
|
11831
|
+
updates.keyTakeaway = normalizeRequiredString(source.keyTakeaway, "updates.keyTakeaway");
|
|
11832
|
+
}
|
|
11833
|
+
if (hasField(source, "talkingPoints")) {
|
|
11834
|
+
updates.talkingPoints = normalizeStringList(source.talkingPoints, "updates.talkingPoints");
|
|
11835
|
+
}
|
|
11836
|
+
if (hasField(source, "platforms")) {
|
|
11837
|
+
updates.platforms = normalizePlatforms(source.platforms);
|
|
11838
|
+
}
|
|
11839
|
+
if (hasField(source, "tags")) {
|
|
11840
|
+
updates.tags = normalizeStringList(source.tags, "updates.tags");
|
|
11841
|
+
}
|
|
11842
|
+
if (hasField(source, "publishBy")) {
|
|
11843
|
+
updates.publishBy = normalizePublishBy(source.publishBy, "updates.publishBy");
|
|
11844
|
+
}
|
|
11845
|
+
if (hasField(source, "trendContext")) {
|
|
11846
|
+
updates.trendContext = normalizeOptionalString(source.trendContext, "updates.trendContext");
|
|
11847
|
+
}
|
|
11848
|
+
if (hasField(source, "status")) {
|
|
11849
|
+
updates.status = normalizeStatus(source.status);
|
|
11850
|
+
}
|
|
11851
|
+
return updates;
|
|
11852
|
+
}
|
|
11853
|
+
function parseUpdateIdeaArgs(args) {
|
|
11854
|
+
if (!isRecord(args.updates)) {
|
|
11855
|
+
throw new Error("Invalid update_idea arguments: updates must be an object");
|
|
11856
|
+
}
|
|
11857
|
+
return {
|
|
11858
|
+
issueNumber: normalizeIssueNumber(args.issueNumber),
|
|
11859
|
+
updates: extractIdeaUpdates(args.updates)
|
|
10961
11860
|
};
|
|
10962
11861
|
}
|
|
11862
|
+
function parseOrganizeIdeasArgs(args) {
|
|
11863
|
+
const { items } = args;
|
|
11864
|
+
if (!Array.isArray(items)) {
|
|
11865
|
+
throw new Error("Invalid organize_ideas arguments: items must be an array");
|
|
11866
|
+
}
|
|
11867
|
+
return items.map((item, index) => {
|
|
11868
|
+
if (!isRecord(item)) {
|
|
11869
|
+
throw new Error(`Invalid organize_ideas item at index ${index}`);
|
|
11870
|
+
}
|
|
11871
|
+
if (item.updates !== void 0 && !isRecord(item.updates)) {
|
|
11872
|
+
throw new Error(`Invalid organize_ideas item at index ${index}: updates must be an object`);
|
|
11873
|
+
}
|
|
11874
|
+
return {
|
|
11875
|
+
issueNumber: normalizeIssueNumber(item.issueNumber),
|
|
11876
|
+
updates: item.updates ? extractIdeaUpdates(item.updates) : void 0,
|
|
11877
|
+
includeRelated: item.includeRelated === void 0 ? true : Boolean(item.includeRelated)
|
|
11878
|
+
};
|
|
11879
|
+
});
|
|
11880
|
+
}
|
|
11881
|
+
function summarizeLinkedIssues(ideas) {
|
|
11882
|
+
return ideas.map((idea) => ({
|
|
11883
|
+
issueNumber: idea.issueNumber,
|
|
11884
|
+
issueUrl: idea.issueUrl,
|
|
11885
|
+
topic: idea.topic,
|
|
11886
|
+
tags: idea.tags
|
|
11887
|
+
}));
|
|
11888
|
+
}
|
|
10963
11889
|
var IdeationAgent = class extends BaseAgent {
|
|
10964
11890
|
brandContext;
|
|
10965
11891
|
existingIdeas;
|
|
10966
|
-
|
|
11892
|
+
ideaRepo;
|
|
10967
11893
|
targetCount;
|
|
10968
11894
|
generatedIdeas = [];
|
|
10969
11895
|
finalized = false;
|
|
@@ -10971,7 +11897,7 @@ var IdeationAgent = class extends BaseAgent {
|
|
|
10971
11897
|
super("IdeationAgent", systemPrompt, getProvider2(), model ?? getModelForAgent("IdeationAgent"));
|
|
10972
11898
|
this.brandContext = context.brandContext;
|
|
10973
11899
|
this.existingIdeas = [...context.existingIdeas];
|
|
10974
|
-
this.
|
|
11900
|
+
this.ideaRepo = context.ideaRepo;
|
|
10975
11901
|
this.targetCount = context.targetCount;
|
|
10976
11902
|
}
|
|
10977
11903
|
resetForRetry() {
|
|
@@ -11022,20 +11948,25 @@ var IdeationAgent = class extends BaseAgent {
|
|
|
11022
11948
|
},
|
|
11023
11949
|
{
|
|
11024
11950
|
name: "get_past_ideas",
|
|
11025
|
-
description: "
|
|
11951
|
+
description: "List GitHub-backed ideas with optional status, platform, tag, priority, or limit filters.",
|
|
11026
11952
|
parameters: {
|
|
11027
11953
|
type: "object",
|
|
11028
|
-
properties: {
|
|
11954
|
+
properties: {
|
|
11955
|
+
status: { type: "string", enum: [...SUPPORTED_STATUSES] },
|
|
11956
|
+
platform: { type: "string", enum: [...SUPPORTED_PLATFORMS] },
|
|
11957
|
+
tag: { type: "string" },
|
|
11958
|
+
priority: { type: "string", enum: [...SUPPORTED_PRIORITIES] },
|
|
11959
|
+
limit: { type: "integer", minimum: 1 }
|
|
11960
|
+
}
|
|
11029
11961
|
},
|
|
11030
11962
|
handler: async (args) => this.handleToolCall("get_past_ideas", args)
|
|
11031
11963
|
},
|
|
11032
11964
|
{
|
|
11033
11965
|
name: "create_idea",
|
|
11034
|
-
description:
|
|
11966
|
+
description: `Create a new draft GitHub Issue in ${this.ideaRepo} using the full idea schema.`,
|
|
11035
11967
|
parameters: {
|
|
11036
11968
|
type: "object",
|
|
11037
11969
|
properties: {
|
|
11038
|
-
id: { type: "string", description: "Kebab-case idea identifier" },
|
|
11039
11970
|
topic: { type: "string", description: "Main topic or title" },
|
|
11040
11971
|
hook: { type: "string", description: "Attention-grabbing hook (80 chars max)" },
|
|
11041
11972
|
audience: { type: "string", description: "Target audience" },
|
|
@@ -11067,10 +11998,98 @@ var IdeationAgent = class extends BaseAgent {
|
|
|
11067
11998
|
description: "Why this idea is timely right now"
|
|
11068
11999
|
}
|
|
11069
12000
|
},
|
|
11070
|
-
required: ["
|
|
12001
|
+
required: ["topic", "hook", "audience", "keyTakeaway", "talkingPoints", "platforms", "tags", "publishBy"]
|
|
11071
12002
|
},
|
|
11072
12003
|
handler: async (args) => this.handleToolCall("create_idea", args)
|
|
11073
12004
|
},
|
|
12005
|
+
{
|
|
12006
|
+
name: "search_ideas",
|
|
12007
|
+
description: "Search GitHub-backed ideas with full-text search across issue content.",
|
|
12008
|
+
parameters: {
|
|
12009
|
+
type: "object",
|
|
12010
|
+
properties: {
|
|
12011
|
+
query: { type: "string", description: "Full-text search query" }
|
|
12012
|
+
},
|
|
12013
|
+
required: ["query"]
|
|
12014
|
+
},
|
|
12015
|
+
handler: async (args) => this.handleToolCall("search_ideas", args)
|
|
12016
|
+
},
|
|
12017
|
+
{
|
|
12018
|
+
name: "find_related_ideas",
|
|
12019
|
+
description: "Find related ideas for an issue by looking up the issue and matching similar tagged GitHub ideas.",
|
|
12020
|
+
parameters: {
|
|
12021
|
+
type: "object",
|
|
12022
|
+
properties: {
|
|
12023
|
+
issueNumber: { type: "integer", description: "GitHub issue number for the idea" }
|
|
12024
|
+
},
|
|
12025
|
+
required: ["issueNumber"]
|
|
12026
|
+
},
|
|
12027
|
+
handler: async (args) => this.handleToolCall("find_related_ideas", args)
|
|
12028
|
+
},
|
|
12029
|
+
{
|
|
12030
|
+
name: "update_idea",
|
|
12031
|
+
description: "Update an existing GitHub idea issue: refine copy, adjust labels, or change lifecycle status.",
|
|
12032
|
+
parameters: {
|
|
12033
|
+
type: "object",
|
|
12034
|
+
properties: {
|
|
12035
|
+
issueNumber: { type: "integer", description: "GitHub issue number for the idea" },
|
|
12036
|
+
updates: {
|
|
12037
|
+
type: "object",
|
|
12038
|
+
properties: {
|
|
12039
|
+
topic: { type: "string" },
|
|
12040
|
+
hook: { type: "string" },
|
|
12041
|
+
audience: { type: "string" },
|
|
12042
|
+
keyTakeaway: { type: "string" },
|
|
12043
|
+
talkingPoints: { type: "array", items: { type: "string" } },
|
|
12044
|
+
platforms: { type: "array", items: { type: "string", enum: [...SUPPORTED_PLATFORMS] } },
|
|
12045
|
+
tags: { type: "array", items: { type: "string" } },
|
|
12046
|
+
publishBy: { type: "string" },
|
|
12047
|
+
trendContext: { type: "string" },
|
|
12048
|
+
status: { type: "string", enum: [...SUPPORTED_STATUSES] }
|
|
12049
|
+
}
|
|
12050
|
+
}
|
|
12051
|
+
},
|
|
12052
|
+
required: ["issueNumber", "updates"]
|
|
12053
|
+
},
|
|
12054
|
+
handler: async (args) => this.handleToolCall("update_idea", args)
|
|
12055
|
+
},
|
|
12056
|
+
{
|
|
12057
|
+
name: "organize_ideas",
|
|
12058
|
+
description: "Batch update GitHub idea issue labels/statuses and return related issue links for clustering.",
|
|
12059
|
+
parameters: {
|
|
12060
|
+
type: "object",
|
|
12061
|
+
properties: {
|
|
12062
|
+
items: {
|
|
12063
|
+
type: "array",
|
|
12064
|
+
items: {
|
|
12065
|
+
type: "object",
|
|
12066
|
+
properties: {
|
|
12067
|
+
issueNumber: { type: "integer" },
|
|
12068
|
+
updates: {
|
|
12069
|
+
type: "object",
|
|
12070
|
+
properties: {
|
|
12071
|
+
topic: { type: "string" },
|
|
12072
|
+
hook: { type: "string" },
|
|
12073
|
+
audience: { type: "string" },
|
|
12074
|
+
keyTakeaway: { type: "string" },
|
|
12075
|
+
talkingPoints: { type: "array", items: { type: "string" } },
|
|
12076
|
+
platforms: { type: "array", items: { type: "string", enum: [...SUPPORTED_PLATFORMS] } },
|
|
12077
|
+
tags: { type: "array", items: { type: "string" } },
|
|
12078
|
+
publishBy: { type: "string" },
|
|
12079
|
+
trendContext: { type: "string" },
|
|
12080
|
+
status: { type: "string", enum: [...SUPPORTED_STATUSES] }
|
|
12081
|
+
}
|
|
12082
|
+
},
|
|
12083
|
+
includeRelated: { type: "boolean" }
|
|
12084
|
+
},
|
|
12085
|
+
required: ["issueNumber"]
|
|
12086
|
+
}
|
|
12087
|
+
}
|
|
12088
|
+
},
|
|
12089
|
+
required: ["items"]
|
|
12090
|
+
},
|
|
12091
|
+
handler: async (args) => this.handleToolCall("organize_ideas", args)
|
|
12092
|
+
},
|
|
11074
12093
|
{
|
|
11075
12094
|
name: "finalize_ideas",
|
|
11076
12095
|
description: "Signal that idea generation is complete.",
|
|
@@ -11086,16 +12105,18 @@ var IdeationAgent = class extends BaseAgent {
|
|
|
11086
12105
|
switch (toolName) {
|
|
11087
12106
|
case "get_brand_context":
|
|
11088
12107
|
return this.brandContext ?? await Promise.resolve(getBrandConfig());
|
|
11089
|
-
case "get_past_ideas":
|
|
11090
|
-
|
|
11091
|
-
return ideas.map((idea) => ({
|
|
11092
|
-
id: idea.id,
|
|
11093
|
-
topic: idea.topic,
|
|
11094
|
-
status: idea.status
|
|
11095
|
-
}));
|
|
11096
|
-
}
|
|
12108
|
+
case "get_past_ideas":
|
|
12109
|
+
return await listIdeas(parseIdeaFilters(args));
|
|
11097
12110
|
case "create_idea":
|
|
11098
|
-
return this.handleCreateIdea(args);
|
|
12111
|
+
return await this.handleCreateIdea(args);
|
|
12112
|
+
case "search_ideas":
|
|
12113
|
+
return await searchIdeas(normalizeRequiredString(args.query, "query"));
|
|
12114
|
+
case "find_related_ideas":
|
|
12115
|
+
return await this.handleFindRelatedIdeas(args);
|
|
12116
|
+
case "update_idea":
|
|
12117
|
+
return await this.handleUpdateIdea(args);
|
|
12118
|
+
case "organize_ideas":
|
|
12119
|
+
return await this.handleOrganizeIdeas(args);
|
|
11099
12120
|
case "finalize_ideas":
|
|
11100
12121
|
this.finalized = true;
|
|
11101
12122
|
return { success: true, count: this.generatedIdeas.length };
|
|
@@ -11107,43 +12128,70 @@ var IdeationAgent = class extends BaseAgent {
|
|
|
11107
12128
|
if (this.generatedIdeas.length >= this.targetCount) {
|
|
11108
12129
|
throw new Error(`Target idea count already reached (${this.targetCount})`);
|
|
11109
12130
|
}
|
|
11110
|
-
const
|
|
11111
|
-
const
|
|
11112
|
-
const duplicateTopic = this.findDuplicateTopic(idea.topic);
|
|
12131
|
+
const input = parseCreateIdeaInput(args);
|
|
12132
|
+
const duplicateTopic = this.findDuplicateTopic(input.topic);
|
|
11113
12133
|
if (duplicateTopic) {
|
|
11114
12134
|
throw new Error(`Duplicate idea topic detected: ${duplicateTopic}`);
|
|
11115
12135
|
}
|
|
11116
|
-
const
|
|
11117
|
-
|
|
11118
|
-
|
|
11119
|
-
}
|
|
11120
|
-
await writeIdea(idea, this.ideasDir);
|
|
11121
|
-
this.generatedIdeas.push(idea);
|
|
11122
|
-
logger_default.info(`[IdeationAgent] Created idea ${idea.id}: ${idea.topic}`);
|
|
12136
|
+
const idea = await createIdea(input);
|
|
12137
|
+
this.upsertIdea(this.existingIdeas, idea);
|
|
12138
|
+
this.upsertIdea(this.generatedIdeas, idea);
|
|
12139
|
+
logger_default.info(`[IdeationAgent] Created GitHub idea #${idea.issueNumber}: ${idea.topic}`);
|
|
11123
12140
|
return { success: true, idea };
|
|
11124
12141
|
}
|
|
11125
|
-
|
|
11126
|
-
const
|
|
11127
|
-
|
|
11128
|
-
|
|
12142
|
+
async handleFindRelatedIdeas(args) {
|
|
12143
|
+
const issueNumber = normalizeIssueNumber(args.issueNumber);
|
|
12144
|
+
const idea = await getIdea(issueNumber);
|
|
12145
|
+
if (!idea) {
|
|
12146
|
+
throw new Error(`Idea #${issueNumber} was not found in ${this.ideaRepo}`);
|
|
11129
12147
|
}
|
|
12148
|
+
return await findRelatedIdeas(idea);
|
|
12149
|
+
}
|
|
12150
|
+
async handleUpdateIdea(args) {
|
|
12151
|
+
const { issueNumber, updates } = parseUpdateIdeaArgs(args);
|
|
12152
|
+
const idea = await updateIdea(issueNumber, updates);
|
|
12153
|
+
this.upsertIdea(this.existingIdeas, idea);
|
|
12154
|
+
this.syncGeneratedIdea(idea);
|
|
12155
|
+
logger_default.info(`[IdeationAgent] Updated GitHub idea #${idea.issueNumber}: ${idea.topic}`);
|
|
12156
|
+
return { success: true, idea };
|
|
12157
|
+
}
|
|
12158
|
+
async handleOrganizeIdeas(args) {
|
|
12159
|
+
const items = parseOrganizeIdeasArgs(args);
|
|
12160
|
+
const organizedItems = await Promise.all(
|
|
12161
|
+
items.map(async (item) => {
|
|
12162
|
+
const currentIdea = await getIdea(item.issueNumber);
|
|
12163
|
+
if (!currentIdea) {
|
|
12164
|
+
throw new Error(`Idea #${item.issueNumber} was not found in ${this.ideaRepo}`);
|
|
12165
|
+
}
|
|
12166
|
+
const nextIdea = item.updates ? await updateIdea(item.issueNumber, item.updates) : currentIdea;
|
|
12167
|
+
this.upsertIdea(this.existingIdeas, nextIdea);
|
|
12168
|
+
this.syncGeneratedIdea(nextIdea);
|
|
12169
|
+
const relatedIdeas = item.includeRelated ? await findRelatedIdeas(nextIdea) : [];
|
|
12170
|
+
return {
|
|
12171
|
+
issueNumber: nextIdea.issueNumber,
|
|
12172
|
+
idea: nextIdea,
|
|
12173
|
+
linkedIssues: summarizeLinkedIssues(relatedIdeas)
|
|
12174
|
+
};
|
|
12175
|
+
})
|
|
12176
|
+
);
|
|
11130
12177
|
return {
|
|
11131
|
-
|
|
11132
|
-
|
|
11133
|
-
hook,
|
|
11134
|
-
audience,
|
|
11135
|
-
keyTakeaway,
|
|
11136
|
-
talkingPoints,
|
|
11137
|
-
platforms,
|
|
11138
|
-
tags,
|
|
11139
|
-
publishBy,
|
|
11140
|
-
trendContext
|
|
12178
|
+
success: true,
|
|
12179
|
+
items: organizedItems
|
|
11141
12180
|
};
|
|
11142
12181
|
}
|
|
11143
|
-
|
|
11144
|
-
const
|
|
11145
|
-
|
|
11146
|
-
|
|
12182
|
+
upsertIdea(collection, nextIdea) {
|
|
12183
|
+
const existingIndex = collection.findIndex((idea) => idea.issueNumber === nextIdea.issueNumber);
|
|
12184
|
+
if (existingIndex === -1) {
|
|
12185
|
+
collection.push(nextIdea);
|
|
12186
|
+
return;
|
|
12187
|
+
}
|
|
12188
|
+
collection.splice(existingIndex, 1, nextIdea);
|
|
12189
|
+
}
|
|
12190
|
+
syncGeneratedIdea(nextIdea) {
|
|
12191
|
+
const existingIndex = this.generatedIdeas.findIndex((idea) => idea.issueNumber === nextIdea.issueNumber);
|
|
12192
|
+
if (existingIndex !== -1) {
|
|
12193
|
+
this.generatedIdeas.splice(existingIndex, 1, nextIdea);
|
|
12194
|
+
}
|
|
11147
12195
|
}
|
|
11148
12196
|
findDuplicateTopic(topic) {
|
|
11149
12197
|
const normalizedTopic = topic.trim().toLowerCase();
|
|
@@ -11166,12 +12214,12 @@ async function generateIdeas(options = {}) {
|
|
|
11166
12214
|
config2.BRAND_PATH = options.brandPath;
|
|
11167
12215
|
}
|
|
11168
12216
|
const brandContext = await loadBrandContext(options.brandPath);
|
|
11169
|
-
const existingIdeas = await
|
|
11170
|
-
const systemPrompt = buildSystemPrompt3(brandContext, existingIdeas, seedTopics, count);
|
|
12217
|
+
const existingIdeas = await listIdeas({ limit: DEFAULT_EXISTING_IDEA_LIMIT });
|
|
12218
|
+
const systemPrompt = buildSystemPrompt3(brandContext, existingIdeas, seedTopics, count, config2.IDEAS_REPO);
|
|
11171
12219
|
const agent = new IdeationAgent(systemPrompt, {
|
|
11172
12220
|
brandContext,
|
|
11173
12221
|
existingIdeas,
|
|
11174
|
-
|
|
12222
|
+
ideaRepo: config2.IDEAS_REPO,
|
|
11175
12223
|
targetCount: count
|
|
11176
12224
|
});
|
|
11177
12225
|
try {
|
|
@@ -11217,6 +12265,7 @@ function createScheduleAgent(...args) {
|
|
|
11217
12265
|
}
|
|
11218
12266
|
|
|
11219
12267
|
// src/L6-pipeline/pipeline.ts
|
|
12268
|
+
init_types();
|
|
11220
12269
|
async function runStage(stageName, fn, stageResults) {
|
|
11221
12270
|
const start = Date.now();
|
|
11222
12271
|
try {
|
|
@@ -12104,7 +13153,7 @@ Type \x1B[33mexit\x1B[0m or \x1B[33mquit\x1B[0m to leave. Press Ctrl+C to stop.
|
|
|
12104
13153
|
|
|
12105
13154
|
// src/L7-app/commands/ideate.ts
|
|
12106
13155
|
init_environment();
|
|
12107
|
-
|
|
13156
|
+
init_ideaService();
|
|
12108
13157
|
|
|
12109
13158
|
// src/L6-pipeline/ideation.ts
|
|
12110
13159
|
function generateIdeas3(...args) {
|
|
@@ -12115,8 +13164,8 @@ function generateIdeas3(...args) {
|
|
|
12115
13164
|
async function runIdeate(options = {}) {
|
|
12116
13165
|
initConfig();
|
|
12117
13166
|
if (options.list) {
|
|
12118
|
-
const ideas2 = await
|
|
12119
|
-
const filtered = options.status ? ideas2.filter((
|
|
13167
|
+
const ideas2 = await listIdeas();
|
|
13168
|
+
const filtered = options.status ? ideas2.filter((idea) => idea.status === options.status) : ideas2;
|
|
12120
13169
|
if (filtered.length === 0) {
|
|
12121
13170
|
console.log("No ideas found.");
|
|
12122
13171
|
if (options.status) {
|
|
@@ -12166,9 +13215,9 @@ ${filtered.length} idea(s) total`);
|
|
|
12166
13215
|
console.log(` Status: ${idea.status}`);
|
|
12167
13216
|
console.log("");
|
|
12168
13217
|
}
|
|
12169
|
-
console.log("Ideas saved to
|
|
13218
|
+
console.log("Ideas saved to the GitHub-backed idea service.");
|
|
12170
13219
|
console.log("Use `vidpipe ideate --list` to view all ideas.");
|
|
12171
|
-
console.log("Use `vidpipe process video.mp4 --ideas <
|
|
13220
|
+
console.log("Use `vidpipe process video.mp4 --ideas <issueNumber1>,<issueNumber2>` to link ideas to a recording.");
|
|
12172
13221
|
}
|
|
12173
13222
|
|
|
12174
13223
|
// src/L1-infra/http/http.ts
|
|
@@ -12179,14 +13228,16 @@ import { Router } from "express";
|
|
|
12179
13228
|
init_paths();
|
|
12180
13229
|
|
|
12181
13230
|
// src/L7-app/review/routes.ts
|
|
12182
|
-
|
|
13231
|
+
init_ideaService2();
|
|
13232
|
+
init_types();
|
|
12183
13233
|
init_configLogger();
|
|
12184
13234
|
|
|
12185
13235
|
// src/L7-app/review/approvalQueue.ts
|
|
12186
13236
|
init_fileSystem();
|
|
12187
|
-
|
|
13237
|
+
init_ideaService2();
|
|
12188
13238
|
|
|
12189
13239
|
// src/L3-services/socialPosting/accountMapping.ts
|
|
13240
|
+
init_types();
|
|
12190
13241
|
init_configLogger();
|
|
12191
13242
|
init_fileSystem();
|
|
12192
13243
|
init_paths();
|
|
@@ -12290,6 +13341,7 @@ async function getAccountId(platform) {
|
|
|
12290
13341
|
}
|
|
12291
13342
|
|
|
12292
13343
|
// src/L7-app/review/approvalQueue.ts
|
|
13344
|
+
init_types();
|
|
12293
13345
|
init_configLogger();
|
|
12294
13346
|
var queue = [];
|
|
12295
13347
|
var processing = false;
|
|
@@ -12329,20 +13381,32 @@ async function processApprovalBatch(itemIds) {
|
|
|
12329
13381
|
itemIds.map(async (id) => ({ id, item: await getItem(id) }))
|
|
12330
13382
|
);
|
|
12331
13383
|
const itemMap = new Map(loadedItems.map(({ id, item }) => [id, item]));
|
|
12332
|
-
const
|
|
12333
|
-
|
|
12334
|
-
|
|
12335
|
-
|
|
13384
|
+
const allIdeaIds = /* @__PURE__ */ new Set();
|
|
13385
|
+
for (const { item } of loadedItems) {
|
|
13386
|
+
if (item?.metadata.ideaIds?.length) {
|
|
13387
|
+
for (const ideaId of item.metadata.ideaIds) {
|
|
13388
|
+
allIdeaIds.add(ideaId);
|
|
12336
13389
|
}
|
|
12337
|
-
|
|
12338
|
-
|
|
12339
|
-
|
|
12340
|
-
|
|
12341
|
-
|
|
12342
|
-
|
|
13390
|
+
}
|
|
13391
|
+
}
|
|
13392
|
+
let ideaMap = /* @__PURE__ */ new Map();
|
|
13393
|
+
if (allIdeaIds.size > 0) {
|
|
13394
|
+
try {
|
|
13395
|
+
const allIdeas = await getIdeasByIds([...allIdeaIds]);
|
|
13396
|
+
for (const idea of allIdeas) {
|
|
13397
|
+
ideaMap.set(idea.id, idea);
|
|
13398
|
+
ideaMap.set(String(idea.issueNumber), idea);
|
|
12343
13399
|
}
|
|
12344
|
-
}
|
|
12345
|
-
|
|
13400
|
+
} catch {
|
|
13401
|
+
}
|
|
13402
|
+
}
|
|
13403
|
+
const enriched = loadedItems.map(({ id, item }) => {
|
|
13404
|
+
if (!item?.metadata.ideaIds?.length) {
|
|
13405
|
+
return { id, publishBy: null, hasIdeas: false };
|
|
13406
|
+
}
|
|
13407
|
+
const dates = item.metadata.ideaIds.map((ideaId) => ideaMap.get(ideaId)?.publishBy).filter((publishBy) => Boolean(publishBy)).sort();
|
|
13408
|
+
return { id, publishBy: dates[0] ?? null, hasIdeas: true };
|
|
13409
|
+
});
|
|
12346
13410
|
const now = Date.now();
|
|
12347
13411
|
const sevenDays = 7 * 24 * 60 * 60 * 1e3;
|
|
12348
13412
|
enriched.sort((a, b) => {
|
|
@@ -12480,7 +13544,31 @@ async function enrichQueueItem(item) {
|
|
|
12480
13544
|
};
|
|
12481
13545
|
}
|
|
12482
13546
|
async function enrichQueueItems(items) {
|
|
12483
|
-
|
|
13547
|
+
const allIdeaIds = /* @__PURE__ */ new Set();
|
|
13548
|
+
for (const item of items) {
|
|
13549
|
+
if (item.metadata.ideaIds?.length) {
|
|
13550
|
+
for (const ideaId of item.metadata.ideaIds) {
|
|
13551
|
+
allIdeaIds.add(ideaId);
|
|
13552
|
+
}
|
|
13553
|
+
}
|
|
13554
|
+
}
|
|
13555
|
+
let publishByMap = /* @__PURE__ */ new Map();
|
|
13556
|
+
if (allIdeaIds.size > 0) {
|
|
13557
|
+
try {
|
|
13558
|
+
const ideas = await getIdeasByIds([...allIdeaIds]);
|
|
13559
|
+
for (const idea of ideas) {
|
|
13560
|
+
publishByMap.set(idea.id, idea.publishBy);
|
|
13561
|
+
publishByMap.set(String(idea.issueNumber), idea.publishBy);
|
|
13562
|
+
}
|
|
13563
|
+
} catch {
|
|
13564
|
+
}
|
|
13565
|
+
}
|
|
13566
|
+
return items.map((item) => {
|
|
13567
|
+
if (!item.metadata.ideaIds?.length) return { ...item };
|
|
13568
|
+
const dates = item.metadata.ideaIds.map((id) => publishByMap.get(id)).filter((publishBy) => Boolean(publishBy)).sort();
|
|
13569
|
+
const ideaPublishBy = dates[0];
|
|
13570
|
+
return ideaPublishBy ? { ...item, ideaPublishBy } : { ...item };
|
|
13571
|
+
});
|
|
12484
13572
|
}
|
|
12485
13573
|
async function enrichGroupedQueueItems(groups) {
|
|
12486
13574
|
return Promise.all(groups.map(async (group) => ({
|
|
@@ -12806,7 +13894,7 @@ var defaultCmd = program.command("process", { isDefault: true }).argument("[vide
|
|
|
12806
13894
|
logger_default.info(`Output dir: ${config2.OUTPUT_DIR}`);
|
|
12807
13895
|
let ideas;
|
|
12808
13896
|
if (opts.ideas) {
|
|
12809
|
-
const { getIdeasByIds: getIdeasByIds2 } = await Promise.resolve().then(() => (
|
|
13897
|
+
const { getIdeasByIds: getIdeasByIds2 } = await Promise.resolve().then(() => (init_ideaService2(), ideaService_exports2));
|
|
12810
13898
|
const ideaIds = opts.ideas.split(",").map((id) => id.trim()).filter(Boolean);
|
|
12811
13899
|
try {
|
|
12812
13900
|
ideas = await getIdeasByIds2(ideaIds);
|
|
@@ -12823,10 +13911,10 @@ var defaultCmd = program.command("process", { isDefault: true }).argument("[vide
|
|
|
12823
13911
|
await processVideoSafe(resolvedPath, ideas);
|
|
12824
13912
|
if (ideas && ideas.length > 0) {
|
|
12825
13913
|
try {
|
|
12826
|
-
const { markRecorded:
|
|
13914
|
+
const { markRecorded: markRecorded3 } = await Promise.resolve().then(() => (init_ideaService(), ideaService_exports));
|
|
12827
13915
|
const slug = resolvedPath.replace(/\\/g, "/").split("/").pop()?.replace(/\.(mp4|mov|webm|avi|mkv)$/i, "") || "";
|
|
12828
13916
|
for (const idea of ideas) {
|
|
12829
|
-
await
|
|
13917
|
+
await markRecorded3(idea.issueNumber, slug);
|
|
12830
13918
|
}
|
|
12831
13919
|
logger_default.info(`Marked ${ideas.length} idea(s) as recorded`);
|
|
12832
13920
|
} catch (err) {
|