@koda-sl/baker-cli 0.139.0-dev.da48e17cb → 0.139.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -15
- package/dist/{chunk-TXNOPO6P.js → chunk-TAGQCU36.js} +4 -4
- package/dist/chunk-TAGQCU36.js.map +1 -0
- package/dist/cli.js +214 -1213
- package/dist/cli.js.map +1 -1
- package/dist/engine/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-TXNOPO6P.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -34,7 +34,7 @@ import {
|
|
|
34
34
|
toModelSafeImage,
|
|
35
35
|
ulid,
|
|
36
36
|
validateCanvasDeep
|
|
37
|
-
} from "./chunk-
|
|
37
|
+
} from "./chunk-TAGQCU36.js";
|
|
38
38
|
import {
|
|
39
39
|
csvOrJson,
|
|
40
40
|
daysAgoIso,
|
|
@@ -73,7 +73,7 @@ import {
|
|
|
73
73
|
} from "./chunk-RK67WL4O.js";
|
|
74
74
|
|
|
75
75
|
// src/cli.ts
|
|
76
|
-
import { defineCommand as
|
|
76
|
+
import { defineCommand as defineCommand174, runMain } from "citty";
|
|
77
77
|
|
|
78
78
|
// src/commands/actions/index.ts
|
|
79
79
|
import { defineCommand as defineCommand18 } from "citty";
|
|
@@ -3602,9 +3602,9 @@ var unlinkCommand = defineCommand16({
|
|
|
3602
3602
|
import { defineCommand as defineCommand17 } from "citty";
|
|
3603
3603
|
registerSchema({
|
|
3604
3604
|
command: "actions.update",
|
|
3605
|
-
description: "Stage an update on an action (name, description, tags, and/or priority) \u2014 no claim needed; edit any action you're not working on. Applies on publish. Blocked only if another chat currently has
|
|
3605
|
+
description: "Stage an update on an action (name, description, tags, and/or priority) \u2014 no claim needed; edit any action you're not working on. Accepts either a real action ID or a tempId from `baker actions create` in THIS chat (edits the still-staged task in place \u2014 use this to flesh out a task you just created instead of removing and recreating it). Applies on publish. Blocked only if another chat currently has a real action claimed. --tags REPLACES the tag set; pass --tags '' to clear. --priority accepts urgent|high|medium|low or 'none' to clear back to unset (== normal).",
|
|
3606
3606
|
args: {
|
|
3607
|
-
id: { type: "string", description: "Action ID", required: true },
|
|
3607
|
+
id: { type: "string", description: "Action ID or tempId (temp_*) of a task staged in this chat", required: true },
|
|
3608
3608
|
name: { type: "string", description: "New name", required: false },
|
|
3609
3609
|
description: { type: "string", description: "New description", required: false },
|
|
3610
3610
|
tags: {
|
|
@@ -3622,11 +3622,11 @@ registerSchema({
|
|
|
3622
3622
|
var updateCommand = defineCommand17({
|
|
3623
3623
|
meta: {
|
|
3624
3624
|
name: "update",
|
|
3625
|
-
description: 'Stage an update on an action (no claim needed). Example: baker actions update <id> --
|
|
3625
|
+
description: 'Stage an update on an action, or edit a task still staged in this chat by its tempId (no claim needed). Example: baker actions update <id-or-tempId> --description "\u2026"'
|
|
3626
3626
|
},
|
|
3627
3627
|
args: {
|
|
3628
|
-
id: { type: "positional", description: "Action ID", required: false },
|
|
3629
|
-
"action-id": { type: "string", description: "Action ID", required: false },
|
|
3628
|
+
id: { type: "positional", description: "Action ID or tempId (temp_*)", required: false },
|
|
3629
|
+
"action-id": { type: "string", description: "Action ID or tempId (temp_*)", required: false },
|
|
3630
3630
|
name: { type: "string", description: "New name", required: false },
|
|
3631
3631
|
description: { type: "string", description: "New description", required: false },
|
|
3632
3632
|
tags: { type: "string", description: "Comma-separated tag slugs \u2014 REPLACES existing ('' clears)", required: false },
|
|
@@ -3638,7 +3638,9 @@ var updateCommand = defineCommand17({
|
|
|
3638
3638
|
if (!id) {
|
|
3639
3639
|
failValidation("Action ID is required.");
|
|
3640
3640
|
}
|
|
3641
|
-
|
|
3641
|
+
if (!isTempId(id)) {
|
|
3642
|
+
validateConvexId(id);
|
|
3643
|
+
}
|
|
3642
3644
|
const tags = parseTagList(args.tags);
|
|
3643
3645
|
const priority = parsePriority(args.priority, { allowClear: true });
|
|
3644
3646
|
if (args.name === void 0 && args.description === void 0 && tags === void 0 && priority === void 0) {
|
|
@@ -5455,11 +5457,11 @@ function microsFlag(value, flag) {
|
|
|
5455
5457
|
if (value === void 0 || value === null || value === "") {
|
|
5456
5458
|
return void 0;
|
|
5457
5459
|
}
|
|
5458
|
-
const
|
|
5459
|
-
if (Number.isNaN(
|
|
5460
|
+
const num = Number(value);
|
|
5461
|
+
if (Number.isNaN(num) || num <= 0) {
|
|
5460
5462
|
failWriteValidation(`${flag} must be a positive amount (major currency units, e.g. 50 or 50.00)`);
|
|
5461
5463
|
}
|
|
5462
|
-
return toMicros(
|
|
5464
|
+
return toMicros(num);
|
|
5463
5465
|
}
|
|
5464
5466
|
function listFlag(value) {
|
|
5465
5467
|
if (typeof value !== "string" || value.length === 0) {
|
|
@@ -5487,11 +5489,11 @@ function rawTextEntries(value) {
|
|
|
5487
5489
|
const values = Array.isArray(value) ? value : typeof value === "string" ? [value] : [];
|
|
5488
5490
|
return values.filter((v) => typeof v === "string").flatMap((v) => v.split(",")).map((v) => v.trim()).filter(Boolean);
|
|
5489
5491
|
}
|
|
5490
|
-
function rawFileEntries(
|
|
5491
|
-
if (typeof
|
|
5492
|
+
function rawFileEntries(path23) {
|
|
5493
|
+
if (typeof path23 !== "string" || path23.length === 0) {
|
|
5492
5494
|
return [];
|
|
5493
5495
|
}
|
|
5494
|
-
return readFileSync2(
|
|
5496
|
+
return readFileSync2(path23, "utf8").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
|
|
5495
5497
|
}
|
|
5496
5498
|
function keywordEntries(args) {
|
|
5497
5499
|
const defaultMatch = typeof args["match-type"] === "string" ? args["match-type"].toUpperCase() : void 0;
|
|
@@ -5514,19 +5516,19 @@ function keywordEntries(args) {
|
|
|
5514
5516
|
}
|
|
5515
5517
|
return entries;
|
|
5516
5518
|
}
|
|
5517
|
-
function loadJsonFileArg(
|
|
5518
|
-
if (typeof
|
|
5519
|
+
function loadJsonFileArg(path23) {
|
|
5520
|
+
if (typeof path23 !== "string" || path23.length === 0) {
|
|
5519
5521
|
return {};
|
|
5520
5522
|
}
|
|
5521
5523
|
try {
|
|
5522
|
-
const parsed = JSON.parse(readFileSync2(
|
|
5524
|
+
const parsed = JSON.parse(readFileSync2(path23, "utf8"));
|
|
5523
5525
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
5524
|
-
failWriteValidation(`${
|
|
5526
|
+
failWriteValidation(`${path23} must contain a JSON object`);
|
|
5525
5527
|
}
|
|
5526
5528
|
return parsed;
|
|
5527
5529
|
} catch (err) {
|
|
5528
5530
|
if (err instanceof SyntaxError) {
|
|
5529
|
-
failWriteValidation(`${
|
|
5531
|
+
failWriteValidation(`${path23} is not valid JSON: ${err.message}`);
|
|
5530
5532
|
}
|
|
5531
5533
|
throw err;
|
|
5532
5534
|
}
|
|
@@ -5637,10 +5639,10 @@ async function stageUpdate(kind, customerId, target, payload) {
|
|
|
5637
5639
|
async function stageTarget(kind, customerId, target) {
|
|
5638
5640
|
await stageGoogleOp({ kind, customerId, target });
|
|
5639
5641
|
}
|
|
5640
|
-
async function draftAction(
|
|
5642
|
+
async function draftAction(path23, body) {
|
|
5641
5643
|
try {
|
|
5642
5644
|
const chatId = requireChatId();
|
|
5643
|
-
const response = await apiPost(
|
|
5645
|
+
const response = await apiPost(path23, { chatId, ...body });
|
|
5644
5646
|
writeJsonEnvelope(response);
|
|
5645
5647
|
} catch (err) {
|
|
5646
5648
|
handleGoogleError(err);
|
|
@@ -9203,19 +9205,19 @@ function failWriteValidation2(message) {
|
|
|
9203
9205
|
writeJsonEnvelope({ ok: false, error: { code: "VALIDATION_ERROR", message } });
|
|
9204
9206
|
process.exit(1);
|
|
9205
9207
|
}
|
|
9206
|
-
function loadJsonFileArg2(
|
|
9207
|
-
if (typeof
|
|
9208
|
+
function loadJsonFileArg2(path23) {
|
|
9209
|
+
if (typeof path23 !== "string" || path23.length === 0) {
|
|
9208
9210
|
return {};
|
|
9209
9211
|
}
|
|
9210
9212
|
try {
|
|
9211
|
-
const parsed = JSON.parse(readFileSync6(
|
|
9213
|
+
const parsed = JSON.parse(readFileSync6(path23, "utf8"));
|
|
9212
9214
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
9213
|
-
failWriteValidation2(`${
|
|
9215
|
+
failWriteValidation2(`${path23} must contain a JSON object`);
|
|
9214
9216
|
}
|
|
9215
9217
|
return parsed;
|
|
9216
9218
|
} catch (err) {
|
|
9217
9219
|
if (err instanceof SyntaxError) {
|
|
9218
|
-
failWriteValidation2(`${
|
|
9220
|
+
failWriteValidation2(`${path23} is not valid JSON: ${err.message}`);
|
|
9219
9221
|
}
|
|
9220
9222
|
throw err;
|
|
9221
9223
|
}
|
|
@@ -9300,15 +9302,15 @@ function parseLocaleFlag(value) {
|
|
|
9300
9302
|
}
|
|
9301
9303
|
return { language: match[1], country: match[2].toUpperCase() };
|
|
9302
9304
|
}
|
|
9303
|
-
function loadTargetingFileArg(
|
|
9304
|
-
if (typeof
|
|
9305
|
+
function loadTargetingFileArg(path23) {
|
|
9306
|
+
if (typeof path23 !== "string" || path23.length === 0) {
|
|
9305
9307
|
return void 0;
|
|
9306
9308
|
}
|
|
9307
|
-
const parsed = loadJsonFileArg2(
|
|
9309
|
+
const parsed = loadJsonFileArg2(path23);
|
|
9308
9310
|
const criteria = parsed.targetingCriteria ?? parsed;
|
|
9309
9311
|
if (!criteria.include) {
|
|
9310
9312
|
failWriteValidation2(
|
|
9311
|
-
`${
|
|
9313
|
+
`${path23} must contain targeting criteria with an "include" block (see baker schema ads.linkedin.campaigns.create)`
|
|
9312
9314
|
);
|
|
9313
9315
|
}
|
|
9314
9316
|
return criteria;
|
|
@@ -9343,14 +9345,14 @@ function parseCsvLine(line) {
|
|
|
9343
9345
|
cells.push(current);
|
|
9344
9346
|
return cells.map((cell) => cell.trim());
|
|
9345
9347
|
}
|
|
9346
|
-
function parseListFileArg(
|
|
9347
|
-
if (typeof
|
|
9348
|
+
function parseListFileArg(path23, maxRows) {
|
|
9349
|
+
if (typeof path23 !== "string" || path23.length === 0) {
|
|
9348
9350
|
return void 0;
|
|
9349
9351
|
}
|
|
9350
|
-
const raw = readFileSync6(
|
|
9352
|
+
const raw = readFileSync6(path23, "utf8");
|
|
9351
9353
|
const lines = raw.split(/\r?\n/).filter((line) => line.trim().length > 0);
|
|
9352
9354
|
if (lines.length < 2) {
|
|
9353
|
-
failWriteValidation2(`${
|
|
9355
|
+
failWriteValidation2(`${path23} needs a header row and at least one data row`);
|
|
9354
9356
|
}
|
|
9355
9357
|
const columns = parseCsvLine(lines[0]).map((column) => column.trim());
|
|
9356
9358
|
const rows = [];
|
|
@@ -9369,7 +9371,7 @@ function parseListFileArg(path27, maxRows) {
|
|
|
9369
9371
|
}
|
|
9370
9372
|
}
|
|
9371
9373
|
if (rows.length > maxRows) {
|
|
9372
|
-
failWriteValidation2(`${
|
|
9374
|
+
failWriteValidation2(`${path23} has ${rows.length} rows \u2014 the inline limit is ${maxRows}. Split the list.`);
|
|
9373
9375
|
}
|
|
9374
9376
|
return { columns, rows };
|
|
9375
9377
|
}
|
|
@@ -11363,6 +11365,11 @@ var OPTIMIZATION_GOALS = [
|
|
|
11363
11365
|
"CONVERSATIONS",
|
|
11364
11366
|
"DERIVED_EVENTS"
|
|
11365
11367
|
];
|
|
11368
|
+
var OPTIMIZATION_GOALS_REQUIRING_PAGE = [
|
|
11369
|
+
"LEAD_GENERATION",
|
|
11370
|
+
"QUALITY_LEAD",
|
|
11371
|
+
"PAGE_LIKES"
|
|
11372
|
+
];
|
|
11366
11373
|
var DESTINATION_TYPES = [
|
|
11367
11374
|
"WEBSITE",
|
|
11368
11375
|
"APP",
|
|
@@ -11452,11 +11459,11 @@ var updateStatusSchema = z14.enum(UPDATE_STATUSES);
|
|
|
11452
11459
|
function currencyMinimums2(currencyCode) {
|
|
11453
11460
|
return CURRENCY_MINIMUMS2[currencyCode] ?? DEFAULT_CURRENCY_MINIMUM2;
|
|
11454
11461
|
}
|
|
11455
|
-
function validateDailyBudgetFloor(money, ctx,
|
|
11462
|
+
function validateDailyBudgetFloor(money, ctx, path23) {
|
|
11456
11463
|
if (money?.currencyCode) {
|
|
11457
11464
|
const min = currencyMinimums2(money.currencyCode).dailyBudgetMin;
|
|
11458
11465
|
if (Number(money.amount) < min) {
|
|
11459
|
-
ctx.addIssue({ code: "custom", path:
|
|
11466
|
+
ctx.addIssue({ code: "custom", path: path23, message: `below the ${min} ${money.currencyCode} daily minimum` });
|
|
11460
11467
|
}
|
|
11461
11468
|
}
|
|
11462
11469
|
}
|
|
@@ -11580,8 +11587,18 @@ function validateAdSetBudgetAndBid(p, ctx) {
|
|
|
11580
11587
|
ctx.addIssue({ code: "custom", path: ["end_time"], message: "end_time must be after start_time" });
|
|
11581
11588
|
}
|
|
11582
11589
|
}
|
|
11590
|
+
function validateAdSetPromotedObject(p, ctx) {
|
|
11591
|
+
if (p.optimization_goal && OPTIMIZATION_GOALS_REQUIRING_PAGE.includes(p.optimization_goal) && !p.promoted_object?.page_id) {
|
|
11592
|
+
ctx.addIssue({
|
|
11593
|
+
code: "custom",
|
|
11594
|
+
path: ["promoted_object", "page_id"],
|
|
11595
|
+
message: `optimization_goal ${p.optimization_goal} needs promoted_object.page_id \u2014 pass --page-id with the Facebook Page the leads/likes belong to`
|
|
11596
|
+
});
|
|
11597
|
+
}
|
|
11598
|
+
}
|
|
11583
11599
|
var adSetCreateSchema = z14.object(adSetFields).superRefine((p, ctx) => {
|
|
11584
11600
|
validateAdSetBudgetAndBid(p, ctx);
|
|
11601
|
+
validateAdSetPromotedObject(p, ctx);
|
|
11585
11602
|
});
|
|
11586
11603
|
var adSetUpdateSchema = z14.object({
|
|
11587
11604
|
name: adSetFields.name.optional(),
|
|
@@ -11602,6 +11619,9 @@ var adSetUpdateSchema = z14.object({
|
|
|
11602
11619
|
ctx.addIssue({ code: "custom", message: "update needs at least one field" });
|
|
11603
11620
|
}
|
|
11604
11621
|
validateAdSetBudgetAndBid(p, ctx);
|
|
11622
|
+
if (p.optimization_goal) {
|
|
11623
|
+
validateAdSetPromotedObject(p, ctx);
|
|
11624
|
+
}
|
|
11605
11625
|
});
|
|
11606
11626
|
var messageSchema = z14.string().min(1).max(META_LIMITS.creative.messageHardMax);
|
|
11607
11627
|
var headlineSchema2 = z14.string().min(1).max(META_LIMITS.creative.headlineMax);
|
|
@@ -11944,19 +11964,19 @@ function failWriteValidation3(message) {
|
|
|
11944
11964
|
writeJsonEnvelope({ ok: false, error: { code: "VALIDATION_ERROR", message } });
|
|
11945
11965
|
process.exit(1);
|
|
11946
11966
|
}
|
|
11947
|
-
function loadJsonFileArg3(
|
|
11948
|
-
if (typeof
|
|
11967
|
+
function loadJsonFileArg3(path23) {
|
|
11968
|
+
if (typeof path23 !== "string" || path23.length === 0) {
|
|
11949
11969
|
return {};
|
|
11950
11970
|
}
|
|
11951
11971
|
try {
|
|
11952
|
-
const parsed = JSON.parse(readFileSync8(
|
|
11972
|
+
const parsed = JSON.parse(readFileSync8(path23, "utf8"));
|
|
11953
11973
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
11954
|
-
failWriteValidation3(`${
|
|
11974
|
+
failWriteValidation3(`${path23} must contain a JSON object`);
|
|
11955
11975
|
}
|
|
11956
11976
|
return parsed;
|
|
11957
11977
|
} catch (err) {
|
|
11958
11978
|
if (err instanceof SyntaxError) {
|
|
11959
|
-
failWriteValidation3(`${
|
|
11979
|
+
failWriteValidation3(`${path23} is not valid JSON: ${err.message}`);
|
|
11960
11980
|
}
|
|
11961
11981
|
throw err;
|
|
11962
11982
|
}
|
|
@@ -12189,7 +12209,10 @@ var adSetWriteArgs = {
|
|
|
12189
12209
|
end: { type: "string", description: "End time" },
|
|
12190
12210
|
"targeting-file": { type: "string", description: "JSON file with the full Meta targeting spec" },
|
|
12191
12211
|
"promoted-object-file": { type: "string", description: "JSON file with the promoted_object" },
|
|
12192
|
-
"page-id": {
|
|
12212
|
+
"page-id": {
|
|
12213
|
+
type: "string",
|
|
12214
|
+
description: "promoted_object.page_id shortcut (required for LEAD_GENERATION / QUALITY_LEAD / PAGE_LIKES goals)"
|
|
12215
|
+
},
|
|
12193
12216
|
"pixel-id": { type: "string", description: "promoted_object.pixel_id shortcut" },
|
|
12194
12217
|
"custom-event-type": { type: "string", description: "promoted_object.custom_event_type (PURCHASE|LEAD|\u2026)" },
|
|
12195
12218
|
file: { type: "string", description: "JSON file with the full payload; flags override" }
|
|
@@ -12199,6 +12222,7 @@ var adSetsCreateCommand = defineCommand54({
|
|
|
12199
12222
|
name: "create",
|
|
12200
12223
|
description: `Stage a new ad set. ${STAGED_NOTE2}
|
|
12201
12224
|
Chain to a campaign with --campaign (real id or meta_temp_* ref).
|
|
12225
|
+
Lead-form goals (LEAD_GENERATION, QUALITY_LEAD) and PAGE_LIKES need --page-id (the Facebook Page the leads/likes belong to).
|
|
12202
12226
|
Example: baker ads meta adsets create --campaign meta_temp_ab12 --name "US 25-54" --optimization-goal LINK_CLICKS --billing-event IMPRESSIONS --daily-budget 50 --targeting-file t.json`
|
|
12203
12227
|
},
|
|
12204
12228
|
args: { campaign: { type: "string", description: "Parent campaign id or meta_temp_* ref" }, ...adSetWriteArgs },
|
|
@@ -12212,6 +12236,7 @@ var adSetsUpdateCommand = defineCommand54({
|
|
|
12212
12236
|
meta: {
|
|
12213
12237
|
name: "update",
|
|
12214
12238
|
description: `Stage changes to an ad set. ${STAGED_NOTE2}
|
|
12239
|
+
Switching --optimization-goal to a lead-form goal (LEAD_GENERATION, QUALITY_LEAD) or PAGE_LIKES needs --page-id in the same change.
|
|
12215
12240
|
Example: baker ads meta adsets update 6123 --daily-budget 80`
|
|
12216
12241
|
},
|
|
12217
12242
|
args: { id: { type: "positional", description: "Ad set id or meta_temp_* ref", required: true }, ...adSetWriteArgs },
|
|
@@ -16123,13 +16148,13 @@ function referenceRank(type) {
|
|
|
16123
16148
|
function orderSlotsByIdentity(slots) {
|
|
16124
16149
|
return [...slots].sort((a, b) => referenceRank(a.type) - referenceRank(b.type));
|
|
16125
16150
|
}
|
|
16126
|
-
function buildFramePrompt(edge, sceneIndex, framePrompt,
|
|
16151
|
+
function buildFramePrompt(edge, sceneIndex, framePrompt, present, hasAnchor, mode, sceneStyle, imageModel) {
|
|
16127
16152
|
const profile = imageModel ? imageProfileFor(imageModel) : void 0;
|
|
16128
16153
|
const exclusionsLast = profile?.constraintPlacement === "last";
|
|
16129
16154
|
const photoreal = profile ? profile.photorealCue : true;
|
|
16130
16155
|
const EDGE = edge.toUpperCase();
|
|
16131
16156
|
const legend = [
|
|
16132
|
-
...
|
|
16157
|
+
...present.map((s) => `- ${s.label} \u2014 ${roleForSlot(s)}`),
|
|
16133
16158
|
...hasAnchor ? [
|
|
16134
16159
|
"- ORIGINAL_FRAME \u2014 use ONLY for composition: framing, camera angle, shot size, subject placement, pose, and proportions. IGNORE its text, logo, brand name, colors, AND the identity of every person/animal/object in it \u2014 those come from the labeled reference images above, never from this frame. It is a DIFFERENT brand's footage with DIFFERENT actors, here ONLY to anchor where things sit and how the shot is framed (e.g. a profile/side angle stays a profile/side angle), never who they are or what palette to use."
|
|
16135
16160
|
] : []
|
|
@@ -16187,7 +16212,7 @@ function buildFramePrompt(edge, sceneIndex, framePrompt, present2, hasAnchor, mo
|
|
|
16187
16212
|
// RECAST is the whole point of a transform: the dropped el_* images define who/
|
|
16188
16213
|
// what is on screen, NOT the source footage and NOT the prose. Without this, the
|
|
16189
16214
|
// model reproduces the original ad's people (a proven failure mode).
|
|
16190
|
-
...
|
|
16215
|
+
...present.length > 0 ? [
|
|
16191
16216
|
"IDENTITY & AESTHETIC \u2014 RECAST (this is a transform, not a copy):",
|
|
16192
16217
|
"Identity comes from the reference image, never from the source footage or this prose. Render every",
|
|
16193
16218
|
"person, animal, product, and set to MATCH its labeled reference image above \u2014 that image is the ONLY",
|
|
@@ -16242,17 +16267,17 @@ function ingestFrameRef(url, edge, ctx, nodes) {
|
|
|
16242
16267
|
ctx.ingestCache?.set(url, ref);
|
|
16243
16268
|
return ref;
|
|
16244
16269
|
}
|
|
16245
|
-
function buildFrameRef(edge, url, framePrompt,
|
|
16270
|
+
function buildFrameRef(edge, url, framePrompt, present, ctx, nodes) {
|
|
16246
16271
|
const tag = ctx.tag ?? "";
|
|
16247
16272
|
if (ctx.reuse && url) return ingestFrameRef(url, edge, ctx, nodes);
|
|
16248
|
-
const castSlots =
|
|
16273
|
+
const castSlots = present.filter((s) => {
|
|
16249
16274
|
const t = s.type.toLowerCase();
|
|
16250
16275
|
return t === "person" || t === "animal";
|
|
16251
16276
|
});
|
|
16252
16277
|
const useOriginalAnchor = Boolean(url) && castSlots.length === 0;
|
|
16253
16278
|
const hasOriginal = useOriginalAnchor;
|
|
16254
16279
|
const originalRef = useOriginalAnchor && url ? ingestFrameRef(url, edge, ctx, nodes) : void 0;
|
|
16255
|
-
const presentOrdered = orderSlotsByIdentity(
|
|
16280
|
+
const presentOrdered = orderSlotsByIdentity(present);
|
|
16256
16281
|
const reference = [...presentOrdered.map((s) => s.ref), ...originalRef ? [originalRef] : []];
|
|
16257
16282
|
const imageProfile = imageProfileFor(ctx.imageModel);
|
|
16258
16283
|
const genParams = {
|
|
@@ -16388,13 +16413,13 @@ function spokenTextParts(scene, nativeLine, loc, profile) {
|
|
|
16388
16413
|
if (transcript) parts.push(`Transcript: ${loc(transcript)}`);
|
|
16389
16414
|
return parts;
|
|
16390
16415
|
}
|
|
16391
|
-
function buildSeedancePrompt(scene, sceneIndex,
|
|
16416
|
+
function buildSeedancePrompt(scene, sceneIndex, present, mode, audio, nativeLine, nativeLang, uiRouted, videoModel) {
|
|
16392
16417
|
const profile = (videoModel ? clipProfileFor(videoModel) : void 0) ?? SEEDANCE_PROFILE;
|
|
16393
16418
|
const loc = (s) => nativeLine ? localizeNumeralsForNative(s, nativeLang) : s;
|
|
16394
16419
|
const routed = uiRouted ?? (compositeRegionsOf(scene) !== null && isUiOnlyComposite(compositeRegionsOf(scene) ?? []));
|
|
16395
16420
|
const plate = routed ? compositePlateRegion(scene) ?? {} : null;
|
|
16396
16421
|
const parts = visualBriefParts(scene, sceneIndex, plate);
|
|
16397
|
-
const refs = plate ?
|
|
16422
|
+
const refs = plate ? present.filter((s) => !UI_SURFACE_RE.test(s.description ?? "")) : present;
|
|
16398
16423
|
if (refs.length > 0) {
|
|
16399
16424
|
parts.push(
|
|
16400
16425
|
`Keep these consistent with their references: ${refs.map((s) => `${s.label} (${s.description ?? s.type})`).join("; ")}`
|
|
@@ -16433,15 +16458,15 @@ function sceneOutTransition(scene, isLast) {
|
|
|
16433
16458
|
const xfade = type ? XFADE_BY_TYPE[type] : void 0;
|
|
16434
16459
|
return xfade ? { xfade, dur: TRANSITION_DEFAULT_S } : null;
|
|
16435
16460
|
}
|
|
16436
|
-
function sceneShootMode(scene,
|
|
16461
|
+
function sceneShootMode(scene, present, nativeTurn, cameraOn, casts) {
|
|
16437
16462
|
const talking = Boolean(nativeTurn) || cameraOn && (scene.dialogue ?? []).some(
|
|
16438
16463
|
(d) => d.line?.trim() && isOnCameraSpeaker(d.speaker ?? "voiceover", casts, cameraOn)
|
|
16439
16464
|
);
|
|
16440
16465
|
return deriveShootMode({
|
|
16441
16466
|
explicit: scene.shoot_mode,
|
|
16442
16467
|
talking,
|
|
16443
|
-
hasPerson:
|
|
16444
|
-
hasProduct:
|
|
16468
|
+
hasPerson: present.some((s) => s.type.toLowerCase() === "person" || s.type.toLowerCase() === "animal"),
|
|
16469
|
+
hasProduct: present.some((s) => s.type.toLowerCase() === "product")
|
|
16445
16470
|
});
|
|
16446
16471
|
}
|
|
16447
16472
|
function emitSceneNativeAudio(i, nativeTurn, ambientBroll, lengths, clock, nodes, voTracks, nativeSegments, clipRef) {
|
|
@@ -16485,12 +16510,12 @@ function sameSpeakerTrackEntries(segments) {
|
|
|
16485
16510
|
return sorted.map((s, k) => {
|
|
16486
16511
|
const next = sorted[k + 1];
|
|
16487
16512
|
const naturalLen = s.end_s - s.start_s;
|
|
16488
|
-
const
|
|
16489
|
-
const capped =
|
|
16513
|
+
const cap2 = next ? Math.min(s.end_s, next.start_s) - s.start_s : naturalLen;
|
|
16514
|
+
const capped = cap2 < naturalLen - 1e-3;
|
|
16490
16515
|
return {
|
|
16491
16516
|
slot: `seg${k}`,
|
|
16492
16517
|
start_s: s.start_s,
|
|
16493
|
-
...capped ? { duration_s: Math.round(Math.max(0.1,
|
|
16518
|
+
...capped ? { duration_s: Math.round(Math.max(0.1, cap2) * 1e3) / 1e3 } : {}
|
|
16494
16519
|
};
|
|
16495
16520
|
});
|
|
16496
16521
|
}
|
|
@@ -16529,7 +16554,7 @@ function buildPerSpeakerVoiceConversion(segments, totalMs, nodes) {
|
|
|
16529
16554
|
}
|
|
16530
16555
|
return tracks;
|
|
16531
16556
|
}
|
|
16532
|
-
function emitSceneClip(i, scene,
|
|
16557
|
+
function emitSceneClip(i, scene, present, mode, nativeTurn, ambientBroll, frames, lengths, out, opts, nodes, tag = "", defer = false) {
|
|
16533
16558
|
const profile = clipProfileFor(opts.videoModel) ?? SEEDANCE_PROFILE;
|
|
16534
16559
|
const clipParams = {
|
|
16535
16560
|
model: opts.videoModel,
|
|
@@ -16539,7 +16564,7 @@ function emitSceneClip(i, scene, present2, mode, nativeTurn, ambientBroll, frame
|
|
|
16539
16564
|
prompt: buildSeedancePrompt(
|
|
16540
16565
|
scene,
|
|
16541
16566
|
i,
|
|
16542
|
-
|
|
16567
|
+
present,
|
|
16543
16568
|
mode,
|
|
16544
16569
|
Boolean(nativeTurn) || ambientBroll,
|
|
16545
16570
|
nativeTurn?.text,
|
|
@@ -16638,9 +16663,9 @@ function uiRoutedRuns(input) {
|
|
|
16638
16663
|
flushRun();
|
|
16639
16664
|
return runs;
|
|
16640
16665
|
}
|
|
16641
|
-
function sceneIsFullScreenUi(scene,
|
|
16666
|
+
function sceneIsFullScreenUi(scene, present) {
|
|
16642
16667
|
if (scene.narrative_role?.trim() === "cta") return false;
|
|
16643
|
-
const hasCast =
|
|
16668
|
+
const hasCast = present.some((s) => {
|
|
16644
16669
|
const t = s.type.toLowerCase();
|
|
16645
16670
|
return t === "person" || t === "animal";
|
|
16646
16671
|
});
|
|
@@ -16675,14 +16700,14 @@ function presenterIndexOf(regions, hasNative) {
|
|
|
16675
16700
|
if (flagged >= 0) return flagged;
|
|
16676
16701
|
return hasNative ? 0 : -1;
|
|
16677
16702
|
}
|
|
16678
|
-
function slotsForRegion(
|
|
16679
|
-
return
|
|
16703
|
+
function slotsForRegion(present, isPresenter) {
|
|
16704
|
+
return present.filter((s) => {
|
|
16680
16705
|
const t = s.type.toLowerCase();
|
|
16681
16706
|
const person = t === "person" || t === "animal";
|
|
16682
16707
|
return isPresenter ? person : !person;
|
|
16683
16708
|
});
|
|
16684
16709
|
}
|
|
16685
|
-
function buildCompositeScene(layout, regions, comp, scene, i,
|
|
16710
|
+
function buildCompositeScene(layout, regions, comp, scene, i, present, mode, nativeTurn, lengths, out, opts, nodes) {
|
|
16686
16711
|
const dims = canvasDims(opts.outAr);
|
|
16687
16712
|
const presIdx = presenterIndexOf(regions, Boolean(nativeTurn));
|
|
16688
16713
|
const regionRefs = [];
|
|
@@ -16690,7 +16715,7 @@ function buildCompositeScene(layout, regions, comp, scene, i, present2, mode, na
|
|
|
16690
16715
|
regions.forEach((region, r) => {
|
|
16691
16716
|
const isPresenter = r === presIdx;
|
|
16692
16717
|
const tag = `_r${r}`;
|
|
16693
|
-
const regionSlots = slotsForRegion(
|
|
16718
|
+
const regionSlots = slotsForRegion(present, isPresenter);
|
|
16694
16719
|
const ctx = {
|
|
16695
16720
|
sceneIndex: i,
|
|
16696
16721
|
genAr: opts.genAr,
|
|
@@ -16790,14 +16815,14 @@ function sceneTiming(scene, isLast, nativeTurn, durationSet = SEEDANCE_DURATIONS
|
|
|
16790
16815
|
const genDur = ceilToDurations(durationSet, Math.max(trimTarget, speech));
|
|
16791
16816
|
return { dur, out, trimTarget, genDur, speech };
|
|
16792
16817
|
}
|
|
16793
|
-
function emitCompositeScene(composite, scene, i,
|
|
16818
|
+
function emitCompositeScene(composite, scene, i, present, mode, nativeTurn, lengths, out, opts, clock, nodes, voTracks, nativeSegments, clips) {
|
|
16794
16819
|
const built = buildCompositeScene(
|
|
16795
16820
|
composite.layout,
|
|
16796
16821
|
composite.regions,
|
|
16797
16822
|
composite.comp,
|
|
16798
16823
|
scene,
|
|
16799
16824
|
i,
|
|
16800
|
-
|
|
16825
|
+
present,
|
|
16801
16826
|
mode,
|
|
16802
16827
|
nativeTurn,
|
|
16803
16828
|
{ dur: lengths.dur, trimTarget: lengths.trimTarget, genDur: lengths.genDur },
|
|
@@ -16902,9 +16927,9 @@ function emitGraphicScene(i, scene, lengths, out, surfaceIngests, nodes, clips)
|
|
|
16902
16927
|
clips.push({ ref: `$ref:${refId}.asset`, scene_s: lengths.dur, out, still: { fit: "pad" } });
|
|
16903
16928
|
}
|
|
16904
16929
|
var BRAND_CARD_RE = /\b(?:solid|plain|flat|brand|logo|wordmark|end[- ]?card|cta card|title card|colou?r background|background colou?r)\b/i;
|
|
16905
|
-
function sceneIsBrandCard(scene,
|
|
16930
|
+
function sceneIsBrandCard(scene, present, isCta) {
|
|
16906
16931
|
if (!isCta) return false;
|
|
16907
|
-
const hasCast =
|
|
16932
|
+
const hasCast = present.some((s) => {
|
|
16908
16933
|
const t = s.type.toLowerCase();
|
|
16909
16934
|
return t === "person" || t === "animal";
|
|
16910
16935
|
});
|
|
@@ -17240,7 +17265,7 @@ function emitPhraseClip(phrase, voiceNode, env, nodes, out) {
|
|
|
17240
17265
|
const anchor = phrase.shownScenes[0];
|
|
17241
17266
|
const anchorScene = env.blueprint.scenes[anchor];
|
|
17242
17267
|
if (!anchorScene) return;
|
|
17243
|
-
const
|
|
17268
|
+
const present = slotsForScene(env.slots, anchor);
|
|
17244
17269
|
const nativeTurn = {
|
|
17245
17270
|
sceneIndex: anchor,
|
|
17246
17271
|
speaker: phrase.speaker,
|
|
@@ -17250,7 +17275,7 @@ function emitPhraseClip(phrase, voiceNode, env, nodes, out) {
|
|
|
17250
17275
|
voiceNode,
|
|
17251
17276
|
native: true
|
|
17252
17277
|
};
|
|
17253
|
-
const mode = sceneShootMode(anchorScene,
|
|
17278
|
+
const mode = sceneShootMode(anchorScene, present, nativeTurn, env.cameraOn, env.casts);
|
|
17254
17279
|
const ctx = {
|
|
17255
17280
|
sceneIndex: anchor,
|
|
17256
17281
|
genAr: env.genAr,
|
|
@@ -17293,7 +17318,7 @@ function emitPhraseClip(phrase, voiceNode, env, nodes, out) {
|
|
|
17293
17318
|
prompt: buildSeedancePrompt(
|
|
17294
17319
|
anchorScene,
|
|
17295
17320
|
anchor,
|
|
17296
|
-
|
|
17321
|
+
present,
|
|
17297
17322
|
mode,
|
|
17298
17323
|
true,
|
|
17299
17324
|
phrase.text,
|
|
@@ -17410,7 +17435,7 @@ function emitPhraseTts(phrase, voiceNode, idx, used, clock, nodes, out, language
|
|
|
17410
17435
|
});
|
|
17411
17436
|
}
|
|
17412
17437
|
function emitCompositeInTimeline(composite, scene, i, isLast, env, canonical, ensureVoiceNode, usedVoIds, nodes, out) {
|
|
17413
|
-
const
|
|
17438
|
+
const present = slotsForScene(env.slots, i);
|
|
17414
17439
|
const onCam = (scene.dialogue ?? []).filter(
|
|
17415
17440
|
(l) => Boolean(l.line?.trim()) && isOnCameraSpeaker(l.speaker ?? "voiceover", env.casts, env.cameraOn)
|
|
17416
17441
|
);
|
|
@@ -17431,13 +17456,13 @@ function emitCompositeInTimeline(composite, scene, i, isLast, env, canonical, en
|
|
|
17431
17456
|
speech_words: wordCount(text)
|
|
17432
17457
|
});
|
|
17433
17458
|
}
|
|
17434
|
-
const mode = sceneShootMode(scene,
|
|
17459
|
+
const mode = sceneShootMode(scene, present, nativeTurn, env.cameraOn, env.casts);
|
|
17435
17460
|
const lengths = sceneTiming(scene, isLast, nativeTurn, clipProfileFor(env.opts.videoModel)?.durationSet);
|
|
17436
17461
|
emitCompositeScene(
|
|
17437
17462
|
composite,
|
|
17438
17463
|
scene,
|
|
17439
17464
|
i,
|
|
17440
|
-
|
|
17465
|
+
present,
|
|
17441
17466
|
mode,
|
|
17442
17467
|
nativeTurn,
|
|
17443
17468
|
lengths,
|
|
@@ -17532,8 +17557,8 @@ function brollKeyframes(scene, i, env, ctx, lengths, prevEndFrame, nodes) {
|
|
|
17532
17557
|
return { first, last, sharesPrevFrame };
|
|
17533
17558
|
}
|
|
17534
17559
|
function emitBrollScene(scene, i, isLast, env, nodes, out, prevEndFrame) {
|
|
17535
|
-
const
|
|
17536
|
-
const mode = sceneShootMode(scene,
|
|
17560
|
+
const present = slotsForScene(env.slots, i);
|
|
17561
|
+
const mode = sceneShootMode(scene, present, void 0, env.cameraOn, env.casts);
|
|
17537
17562
|
const ambientBroll = Boolean(env.opts.ambient) && mode !== "ugc_selfie";
|
|
17538
17563
|
const lengths = sceneTiming(scene, isLast, void 0, clipProfileFor(env.opts.videoModel)?.durationSet);
|
|
17539
17564
|
const ctx = {
|
|
@@ -17550,13 +17575,13 @@ function emitBrollScene(scene, i, isLast, env, nodes, out, prevEndFrame) {
|
|
|
17550
17575
|
out.routes.set(i, "graphic_plate");
|
|
17551
17576
|
return void 0;
|
|
17552
17577
|
}
|
|
17553
|
-
if (!env.reuse && sceneIsFullScreenUi(scene,
|
|
17578
|
+
if (!env.reuse && sceneIsFullScreenUi(scene, present)) {
|
|
17554
17579
|
emitScreenScene(i, scene, lengths, lengths.out, env.surfaceIngests, nodes, out.clips);
|
|
17555
17580
|
out.routes.set(i, "screen_still");
|
|
17556
17581
|
return void 0;
|
|
17557
17582
|
}
|
|
17558
17583
|
const isCta = scene.narrative_role?.trim() === "cta" || isLast;
|
|
17559
|
-
if (!env.reuse && sceneIsBrandCard(scene,
|
|
17584
|
+
if (!env.reuse && sceneIsBrandCard(scene, present, isCta)) {
|
|
17560
17585
|
emitBrandCardScene(i, lengths, lengths.out, env.outAr, brandPlateColor(env.blueprint), nodes, out.clips);
|
|
17561
17586
|
out.routes.set(i, "brand_card");
|
|
17562
17587
|
return void 0;
|
|
@@ -17571,7 +17596,7 @@ function emitBrollScene(scene, i, isLast, env, nodes, out, prevEndFrame) {
|
|
|
17571
17596
|
const clip = emitSceneClip(
|
|
17572
17597
|
i,
|
|
17573
17598
|
scene,
|
|
17574
|
-
|
|
17599
|
+
present,
|
|
17575
17600
|
mode,
|
|
17576
17601
|
void 0,
|
|
17577
17602
|
ambientBroll,
|
|
@@ -18333,7 +18358,7 @@ function sceneSpokenText(scene) {
|
|
|
18333
18358
|
return (scene.dialogue ?? []).map((d) => d.line?.trim()).filter((l) => Boolean(l)).join(" ") || null;
|
|
18334
18359
|
}
|
|
18335
18360
|
function buildMotionBoard(blueprint) {
|
|
18336
|
-
const
|
|
18361
|
+
const round4 = (n) => Math.round(n * 100) / 100;
|
|
18337
18362
|
let cursor = 0;
|
|
18338
18363
|
return blueprint.scenes.map((scene, i) => {
|
|
18339
18364
|
const start_s = scene.start_s ?? cursor;
|
|
@@ -18345,15 +18370,15 @@ function buildMotionBoard(blueprint) {
|
|
|
18345
18370
|
const graphics = [
|
|
18346
18371
|
...(overlays.success ? overlays.data : []).filter((ov) => ov.text?.trim()).map((ov) => ({
|
|
18347
18372
|
kind: "text",
|
|
18348
|
-
at_s:
|
|
18349
|
-
dur_s:
|
|
18373
|
+
at_s: round4(ov.appears_at_s ?? start_s),
|
|
18374
|
+
dur_s: round4(ov.duration_s ?? 2.5),
|
|
18350
18375
|
position: ov.position ?? "bottom_center",
|
|
18351
18376
|
text: ov.text?.trim()
|
|
18352
18377
|
})),
|
|
18353
18378
|
...(floats.success ? floats.data : []).map((fe) => ({
|
|
18354
18379
|
kind: "graphic",
|
|
18355
|
-
at_s:
|
|
18356
|
-
dur_s:
|
|
18380
|
+
at_s: round4(fe.appears_at_s ?? start_s),
|
|
18381
|
+
dur_s: round4(fe.duration_s ?? 2.5),
|
|
18357
18382
|
position: fe.position ?? "bottom_center",
|
|
18358
18383
|
label: fe.brand_name || fe.what_it_represents || fe.description || fe.kind || "element"
|
|
18359
18384
|
}))
|
|
@@ -18361,7 +18386,7 @@ function buildMotionBoard(blueprint) {
|
|
|
18361
18386
|
return {
|
|
18362
18387
|
scene: i,
|
|
18363
18388
|
role: resolveSceneRole(scene, i, blueprint.scenes.length),
|
|
18364
|
-
window_s: [
|
|
18389
|
+
window_s: [round4(start_s), round4(end_s)],
|
|
18365
18390
|
// A continuation b-roll scene shares the previous scene's end frame as its start
|
|
18366
18391
|
// (no own `s<i>_start` node), so point the storyboard at that shared keyframe.
|
|
18367
18392
|
storyboard_frames: [scene.continues_previous && i > 0 ? `s${i - 1}_end` : `s${i}_start`],
|
|
@@ -19026,10 +19051,10 @@ function runDirsToPrune(entries, keep, currentRunId) {
|
|
|
19026
19051
|
return runs.slice(0, Math.max(0, runs.length - keep));
|
|
19027
19052
|
}
|
|
19028
19053
|
async function pruneOldRuns(outputsDir, keep, currentRunId, log) {
|
|
19029
|
-
const { readdir:
|
|
19054
|
+
const { readdir: readdir7 } = await import("fs/promises");
|
|
19030
19055
|
let entries;
|
|
19031
19056
|
try {
|
|
19032
|
-
entries = await
|
|
19057
|
+
entries = await readdir7(outputsDir);
|
|
19033
19058
|
} catch {
|
|
19034
19059
|
return;
|
|
19035
19060
|
}
|
|
@@ -20801,10 +20826,10 @@ function resolveShippedCanvasDir(name, startDir, exists = existsSync3, maxDepth
|
|
|
20801
20826
|
import { appendFile, readFile as readFile13 } from "fs/promises";
|
|
20802
20827
|
import path19 from "path";
|
|
20803
20828
|
function missingGitignoreEntries(existing, entries) {
|
|
20804
|
-
const
|
|
20829
|
+
const present = new Set(
|
|
20805
20830
|
existing.split("\n").map((l) => l.trim().replace(/\/+$/, "")).filter((l) => l.length > 0 && !l.startsWith("#"))
|
|
20806
20831
|
);
|
|
20807
|
-
return entries.filter((e) => !
|
|
20832
|
+
return entries.filter((e) => !present.has(e.trim().replace(/\/+$/, "")));
|
|
20808
20833
|
}
|
|
20809
20834
|
async function ensureGitignore(dir, entries) {
|
|
20810
20835
|
const file = path19.join(dir, ".gitignore");
|
|
@@ -22076,12 +22101,12 @@ function listFlowSlugs() {
|
|
|
22076
22101
|
return readdirSync2(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory() && !entry.name.startsWith("_") && entry.name !== ".gitkeep").map((entry) => entry.name).sort();
|
|
22077
22102
|
}
|
|
22078
22103
|
function readFlowTree(slug) {
|
|
22079
|
-
const
|
|
22080
|
-
if (!existsSync4(
|
|
22104
|
+
const path23 = join3(flowsDir(), slug, "_data.json");
|
|
22105
|
+
if (!existsSync4(path23)) {
|
|
22081
22106
|
failLocal(`No form "${slug}". Run "baker flows list" to see the forms in this workspace.`);
|
|
22082
22107
|
}
|
|
22083
22108
|
try {
|
|
22084
|
-
return JSON.parse(readFileSync9(
|
|
22109
|
+
return JSON.parse(readFileSync9(path23, "utf-8"));
|
|
22085
22110
|
} catch (error) {
|
|
22086
22111
|
failLocal(`Could not read form "${slug}": ${error instanceof Error ? error.message : String(error)}`);
|
|
22087
22112
|
}
|
|
@@ -23237,9 +23262,9 @@ async function readImageBuffer(pathOrUrl) {
|
|
|
23237
23262
|
}
|
|
23238
23263
|
return readFile18(pathOrUrl);
|
|
23239
23264
|
}
|
|
23240
|
-
async function isDirectory(
|
|
23265
|
+
async function isDirectory(path23) {
|
|
23241
23266
|
try {
|
|
23242
|
-
const s = await stat4(
|
|
23267
|
+
const s = await stat4(path23);
|
|
23243
23268
|
return s.isDirectory();
|
|
23244
23269
|
} catch {
|
|
23245
23270
|
return false;
|
|
@@ -24939,7 +24964,7 @@ function emitError3(err) {
|
|
|
24939
24964
|
process.exit(1);
|
|
24940
24965
|
}
|
|
24941
24966
|
function coerceRawArgs(args) {
|
|
24942
|
-
const
|
|
24967
|
+
const num = (v) => {
|
|
24943
24968
|
if (typeof v === "number") return v;
|
|
24944
24969
|
if (typeof v === "string" && v.length > 0) {
|
|
24945
24970
|
const n = Number(v);
|
|
@@ -24954,8 +24979,8 @@ function coerceRawArgs(args) {
|
|
|
24954
24979
|
color: str(args.color),
|
|
24955
24980
|
"remove-bg": bool(args["remove-bg"]),
|
|
24956
24981
|
"shrink-to-content": bool(args["shrink-to-content"]),
|
|
24957
|
-
height:
|
|
24958
|
-
width:
|
|
24982
|
+
height: num(args.height),
|
|
24983
|
+
width: num(args.width),
|
|
24959
24984
|
size: str(args.size),
|
|
24960
24985
|
fit: str(args.fit),
|
|
24961
24986
|
output: str(args.output),
|
|
@@ -25802,1031 +25827,8 @@ Paid transforms (run on the Convex backend, cost-tracked):
|
|
|
25802
25827
|
}
|
|
25803
25828
|
});
|
|
25804
25829
|
|
|
25805
|
-
// src/commands/landing/index.ts
|
|
25806
|
-
import { defineCommand as defineCommand134 } from "citty";
|
|
25807
|
-
|
|
25808
|
-
// src/commands/landing/critique.ts
|
|
25809
|
-
import { stat as stat6 } from "fs/promises";
|
|
25810
|
-
import path26 from "path";
|
|
25811
|
-
import { defineCommand as defineCommand133 } from "citty";
|
|
25812
|
-
|
|
25813
|
-
// src/engine/landing/lib/brand-tokens.ts
|
|
25814
|
-
import { readFile as readFile20 } from "fs/promises";
|
|
25815
|
-
import path23 from "path";
|
|
25816
|
-
|
|
25817
|
-
// src/engine/landing/lib/color.ts
|
|
25818
|
-
var NEUTRAL_COLOR_KEYWORDS = /* @__PURE__ */ new Set([
|
|
25819
|
-
"transparent",
|
|
25820
|
-
"currentcolor",
|
|
25821
|
-
"black",
|
|
25822
|
-
"white",
|
|
25823
|
-
"gray",
|
|
25824
|
-
"grey",
|
|
25825
|
-
"silver",
|
|
25826
|
-
"dimgray",
|
|
25827
|
-
"dimgrey",
|
|
25828
|
-
"darkgray",
|
|
25829
|
-
"darkgrey",
|
|
25830
|
-
"lightgray",
|
|
25831
|
-
"lightgrey",
|
|
25832
|
-
"gainsboro",
|
|
25833
|
-
"whitesmoke"
|
|
25834
|
-
]);
|
|
25835
|
-
function hexChannels(color) {
|
|
25836
|
-
const long = color.match(/^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})(?:[0-9a-f]{2})?$/i);
|
|
25837
|
-
if (long)
|
|
25838
|
-
return [
|
|
25839
|
-
Number.parseInt(long[1] ?? "0", 16),
|
|
25840
|
-
Number.parseInt(long[2] ?? "0", 16),
|
|
25841
|
-
Number.parseInt(long[3] ?? "0", 16)
|
|
25842
|
-
];
|
|
25843
|
-
const short = color.match(/^#([0-9a-f])([0-9a-f])([0-9a-f])(?:[0-9a-f])?$/i);
|
|
25844
|
-
if (short) {
|
|
25845
|
-
const dup = (s) => Number.parseInt(`${s ?? "0"}${s ?? "0"}`, 16);
|
|
25846
|
-
return [dup(short[1]), dup(short[2]), dup(short[3])];
|
|
25847
|
-
}
|
|
25848
|
-
return null;
|
|
25849
|
-
}
|
|
25850
|
-
function parseColor2(raw) {
|
|
25851
|
-
const c = String(raw || "").trim().toLowerCase();
|
|
25852
|
-
if (!c || c === "transparent") return null;
|
|
25853
|
-
const rgb = c.match(/rgba?\(\s*([\d.]+)[\s,]+([\d.]+)[\s,]+([\d.]+)(?:[\s,/]+([\d.]+%?))?\s*\)/i);
|
|
25854
|
-
if (rgb) {
|
|
25855
|
-
const alpha = rgb[4];
|
|
25856
|
-
const a = alpha === void 0 ? 1 : alpha.endsWith("%") ? Number.parseFloat(alpha) / 100 : Number.parseFloat(alpha);
|
|
25857
|
-
return { r: Number(rgb[1]), g: Number(rgb[2]), b: Number(rgb[3]), a };
|
|
25858
|
-
}
|
|
25859
|
-
const hex = hexChannels(c);
|
|
25860
|
-
if (hex) return { r: hex[0], g: hex[1], b: hex[2], a: 1 };
|
|
25861
|
-
return null;
|
|
25862
|
-
}
|
|
25863
|
-
function isNeutralAuthoredColor(rawColor) {
|
|
25864
|
-
const c = String(rawColor || "").trim().toLowerCase();
|
|
25865
|
-
if (!c) return false;
|
|
25866
|
-
if (NEUTRAL_COLOR_KEYWORDS.has(c)) return true;
|
|
25867
|
-
if (/^rgba?\(/i.test(c)) {
|
|
25868
|
-
const channels2 = c.match(/^rgba?\(\s*([\d.]+)[\s,]+([\d.]+)[\s,]+([\d.]+)/i);
|
|
25869
|
-
if (channels2) {
|
|
25870
|
-
const values = [1, 2, 3].map((i) => Number(channels2[i]));
|
|
25871
|
-
return Math.max(...values) - Math.min(...values) < 30;
|
|
25872
|
-
}
|
|
25873
|
-
return false;
|
|
25874
|
-
}
|
|
25875
|
-
const oklch = c.match(/oklch\(\s*[\d.]+%?\s+([\d.-]+)/i);
|
|
25876
|
-
if (oklch) return Number.parseFloat(oklch[1] ?? "0") < 0.02;
|
|
25877
|
-
const lch = c.match(/lch\(\s*[\d.]+%?\s+([\d.-]+)/i);
|
|
25878
|
-
if (lch) return Number.parseFloat(lch[1] ?? "0") < 3;
|
|
25879
|
-
const hsl = c.match(/hsla?\(\s*[\d.-]+\s*,?\s*([\d.]+)%/i);
|
|
25880
|
-
if (hsl) return Number.parseFloat(hsl[1] ?? "0") < 10;
|
|
25881
|
-
const channels = hexChannels(c);
|
|
25882
|
-
if (channels) return Math.max(...channels) - Math.min(...channels) < 30;
|
|
25883
|
-
return false;
|
|
25884
|
-
}
|
|
25885
|
-
function hasChroma(c, threshold = 30) {
|
|
25886
|
-
if (!c) return false;
|
|
25887
|
-
return Math.max(c.r, c.g, c.b) - Math.min(c.r, c.g, c.b) >= threshold;
|
|
25888
|
-
}
|
|
25889
|
-
function getHue(c) {
|
|
25890
|
-
if (!c) return 0;
|
|
25891
|
-
const r = c.r / 255;
|
|
25892
|
-
const g = c.g / 255;
|
|
25893
|
-
const b = c.b / 255;
|
|
25894
|
-
const max = Math.max(r, g, b);
|
|
25895
|
-
const min = Math.min(r, g, b);
|
|
25896
|
-
if (max === min) return 0;
|
|
25897
|
-
const d = max - min;
|
|
25898
|
-
let h;
|
|
25899
|
-
if (max === r) h = ((g - b) / d + (g < b ? 6 : 0)) / 6;
|
|
25900
|
-
else if (max === g) h = ((b - r) / d + 2) / 6;
|
|
25901
|
-
else h = ((r - g) / d + 4) / 6;
|
|
25902
|
-
return Math.round(h * 360);
|
|
25903
|
-
}
|
|
25904
|
-
function isAiPurpleHue(hue) {
|
|
25905
|
-
return hue >= 260 && hue <= 310;
|
|
25906
|
-
}
|
|
25907
|
-
function isCreamColor(c) {
|
|
25908
|
-
if (Math.min(c.r, c.g, c.b) < 209) return false;
|
|
25909
|
-
if (!(c.r >= c.g && c.g >= c.b)) return false;
|
|
25910
|
-
const warmth = c.r - c.b;
|
|
25911
|
-
return warmth >= 6 && warmth <= 48;
|
|
25912
|
-
}
|
|
25913
|
-
|
|
25914
|
-
// src/engine/landing/lib/brand-tokens.ts
|
|
25915
|
-
var EMPTY = { fonts: /* @__PURE__ */ new Set(), colors: [], hasTokens: false };
|
|
25916
|
-
function parseBrandTokens(globalCss, brandMd) {
|
|
25917
|
-
const fonts = /* @__PURE__ */ new Set();
|
|
25918
|
-
const colors = [];
|
|
25919
|
-
parseGlobalCss(globalCss, fonts, colors);
|
|
25920
|
-
parseBrandMd(brandMd, fonts, colors);
|
|
25921
|
-
return { fonts, colors, hasTokens: fonts.size > 0 || colors.length > 0 };
|
|
25922
|
-
}
|
|
25923
|
-
function parseGlobalCss(globalCss, fonts, colors) {
|
|
25924
|
-
for (const m of globalCss.matchAll(/--font-[\w-]*\s*:\s*([^;]+);/gi)) {
|
|
25925
|
-
const first = (m[1] ?? "").split(",")[0]?.trim().replace(/^['"]|['"]$/g, "").toLowerCase();
|
|
25926
|
-
if (first) fonts.add(first);
|
|
25927
|
-
}
|
|
25928
|
-
for (const m of globalCss.matchAll(
|
|
25929
|
-
/--color-[\w-]*\s*:\s*(#[0-9a-f]{3,8}\b|rgba?\([^)]+\)|oklch\([^)]+\)|hsla?\([^)]+\))/gi
|
|
25930
|
-
)) {
|
|
25931
|
-
const c = parseColor2(m[1] ?? "");
|
|
25932
|
-
if (c) colors.push(c);
|
|
25933
|
-
}
|
|
25934
|
-
}
|
|
25935
|
-
function parseBrandMd(brandMd, fonts, colors) {
|
|
25936
|
-
for (const m of brandMd.matchAll(/#[0-9a-f]{6}\b/gi)) {
|
|
25937
|
-
const c = parseColor2(m[0]);
|
|
25938
|
-
if (c) colors.push(c);
|
|
25939
|
-
}
|
|
25940
|
-
for (const line of brandMd.split("\n")) {
|
|
25941
|
-
if (!/\b(?:font|typeface|family)\b/i.test(line)) continue;
|
|
25942
|
-
const named = line.match(/["'`]([A-Za-z][\w ]{1,30})["'`]/g);
|
|
25943
|
-
if (named) for (const n of named) fonts.add(n.slice(1, -1).trim().toLowerCase());
|
|
25944
|
-
}
|
|
25945
|
-
}
|
|
25946
|
-
async function loadBrandTokens(projectRoot) {
|
|
25947
|
-
const globalCss = await safeRead(path23.join(projectRoot, "src", "styles", "global.css"));
|
|
25948
|
-
const brandMd = await safeRead(path23.join(projectRoot, "src", "brand", "BRAND.md"));
|
|
25949
|
-
if (!globalCss && !brandMd) return EMPTY;
|
|
25950
|
-
return parseBrandTokens(globalCss, brandMd);
|
|
25951
|
-
}
|
|
25952
|
-
async function safeRead(file) {
|
|
25953
|
-
try {
|
|
25954
|
-
return await readFile20(file, "utf8");
|
|
25955
|
-
} catch {
|
|
25956
|
-
return "";
|
|
25957
|
-
}
|
|
25958
|
-
}
|
|
25959
|
-
function brandCommitsFont(brand, snippet) {
|
|
25960
|
-
if (brand.fonts.size === 0) return false;
|
|
25961
|
-
const lower = snippet.toLowerCase();
|
|
25962
|
-
for (const f of brand.fonts) if (f.length >= 3 && lower.includes(f)) return true;
|
|
25963
|
-
return false;
|
|
25964
|
-
}
|
|
25965
|
-
function brandCommitsAiHue(brand) {
|
|
25966
|
-
return brand.colors.some((c) => hasChroma(c, 40) && isAiPurpleHue(getHue(c)));
|
|
25967
|
-
}
|
|
25968
|
-
function brandCommitsCream(brand) {
|
|
25969
|
-
return brand.colors.some((c) => isCreamColor(c));
|
|
25970
|
-
}
|
|
25971
|
-
|
|
25972
|
-
// src/engine/landing/lib/constants.ts
|
|
25973
|
-
var OVERUSED_FONTS = /* @__PURE__ */ new Set([
|
|
25974
|
-
// Older monoculture (still ubiquitous):
|
|
25975
|
-
"inter",
|
|
25976
|
-
"roboto",
|
|
25977
|
-
"open sans",
|
|
25978
|
-
"lato",
|
|
25979
|
-
"montserrat",
|
|
25980
|
-
"arial",
|
|
25981
|
-
"helvetica",
|
|
25982
|
-
// Newer monoculture (the Anthropic-skill / Vercel / GitHub default wave):
|
|
25983
|
-
"fraunces",
|
|
25984
|
-
"instrument sans",
|
|
25985
|
-
"instrument serif",
|
|
25986
|
-
"geist",
|
|
25987
|
-
"geist sans",
|
|
25988
|
-
"geist mono",
|
|
25989
|
-
"mona sans",
|
|
25990
|
-
"plus jakarta sans",
|
|
25991
|
-
"space grotesk",
|
|
25992
|
-
"space mono",
|
|
25993
|
-
"recoleta",
|
|
25994
|
-
"dm sans",
|
|
25995
|
-
"dm serif display",
|
|
25996
|
-
"dm serif text",
|
|
25997
|
-
"outfit",
|
|
25998
|
-
"syne",
|
|
25999
|
-
"cormorant",
|
|
26000
|
-
"playfair display",
|
|
26001
|
-
"lora",
|
|
26002
|
-
"crimson",
|
|
26003
|
-
"newsreader",
|
|
26004
|
-
"ibm plex",
|
|
26005
|
-
"ibm plex sans",
|
|
26006
|
-
"ibm plex serif"
|
|
26007
|
-
]);
|
|
26008
|
-
var GENERIC_FONTS = /* @__PURE__ */ new Set([
|
|
26009
|
-
"serif",
|
|
26010
|
-
"sans-serif",
|
|
26011
|
-
"monospace",
|
|
26012
|
-
"cursive",
|
|
26013
|
-
"fantasy",
|
|
26014
|
-
"system-ui",
|
|
26015
|
-
"ui-serif",
|
|
26016
|
-
"ui-sans-serif",
|
|
26017
|
-
"ui-monospace",
|
|
26018
|
-
"ui-rounded",
|
|
26019
|
-
"-apple-system",
|
|
26020
|
-
"blinkmacsystemfont",
|
|
26021
|
-
"segoe ui",
|
|
26022
|
-
"inherit",
|
|
26023
|
-
"initial",
|
|
26024
|
-
"unset",
|
|
26025
|
-
"revert"
|
|
26026
|
-
]);
|
|
26027
|
-
var EM_DASH_FLOOR = 8;
|
|
26028
|
-
var EM_DASH_CHARS_PER_DASH = 500;
|
|
26029
|
-
var TAILWIND_TEXT_PX = {
|
|
26030
|
-
"text-xs": 12,
|
|
26031
|
-
"text-sm": 14,
|
|
26032
|
-
"text-base": 16,
|
|
26033
|
-
"text-lg": 18,
|
|
26034
|
-
"text-xl": 20,
|
|
26035
|
-
"text-2xl": 24,
|
|
26036
|
-
"text-3xl": 30,
|
|
26037
|
-
"text-4xl": 36,
|
|
26038
|
-
"text-5xl": 48,
|
|
26039
|
-
"text-6xl": 60,
|
|
26040
|
-
"text-7xl": 72,
|
|
26041
|
-
"text-8xl": 96,
|
|
26042
|
-
"text-9xl": 128
|
|
26043
|
-
};
|
|
26044
|
-
var BUZZWORDS = [
|
|
26045
|
-
"streamline your",
|
|
26046
|
-
"empower your",
|
|
26047
|
-
"supercharge your",
|
|
26048
|
-
"unleash your",
|
|
26049
|
-
"unleash the power",
|
|
26050
|
-
"leverage the power",
|
|
26051
|
-
"built for the modern",
|
|
26052
|
-
"trusted by leading",
|
|
26053
|
-
"trusted by the world",
|
|
26054
|
-
"best-in-class",
|
|
26055
|
-
"industry-leading",
|
|
26056
|
-
"world-class",
|
|
26057
|
-
"enterprise-grade",
|
|
26058
|
-
"next-generation",
|
|
26059
|
-
"cutting-edge",
|
|
26060
|
-
"transform your business",
|
|
26061
|
-
"revolutionize",
|
|
26062
|
-
"game-changer",
|
|
26063
|
-
"game changing",
|
|
26064
|
-
"mission-critical",
|
|
26065
|
-
"best of breed",
|
|
26066
|
-
"future-proof",
|
|
26067
|
-
"future proof",
|
|
26068
|
-
"seamless experience",
|
|
26069
|
-
"seamlessly integrate",
|
|
26070
|
-
"drive engagement",
|
|
26071
|
-
"drive growth",
|
|
26072
|
-
"drive results",
|
|
26073
|
-
"harness the power"
|
|
26074
|
-
];
|
|
26075
|
-
var AI_PURPLE_HEXES = /* @__PURE__ */ new Set([
|
|
26076
|
-
"#7c3aed",
|
|
26077
|
-
"#8b5cf6",
|
|
26078
|
-
"#a855f7",
|
|
26079
|
-
"#9333ea",
|
|
26080
|
-
"#7e22ce",
|
|
26081
|
-
"#6d28d9",
|
|
26082
|
-
"#6366f1",
|
|
26083
|
-
"#764ba2",
|
|
26084
|
-
"#667eea"
|
|
26085
|
-
]);
|
|
26086
|
-
var CREAM_TAILWIND_BG = /* @__PURE__ */ new Set([
|
|
26087
|
-
"bg-amber-50",
|
|
26088
|
-
"bg-amber-100",
|
|
26089
|
-
"bg-orange-50",
|
|
26090
|
-
"bg-orange-100",
|
|
26091
|
-
"bg-yellow-50",
|
|
26092
|
-
"bg-stone-50",
|
|
26093
|
-
"bg-stone-100",
|
|
26094
|
-
"bg-stone-200"
|
|
26095
|
-
]);
|
|
26096
|
-
var RULE_META = {
|
|
26097
|
-
"gradient-text": {
|
|
26098
|
-
family: "color",
|
|
26099
|
-
severity: "block",
|
|
26100
|
-
note: "Gradient text is a top AI tell. Emphasis comes from weight or size, not a clipped gradient fill."
|
|
26101
|
-
},
|
|
26102
|
-
"broken-image": {
|
|
26103
|
-
family: "integrity",
|
|
26104
|
-
severity: "block",
|
|
26105
|
-
note: "An <img> with an empty or placeholder src ships a broken image. Source a real asset via `baker images`."
|
|
26106
|
-
},
|
|
26107
|
-
"side-tab": {
|
|
26108
|
-
family: "borders_depth",
|
|
26109
|
-
severity: "warn",
|
|
26110
|
-
note: "A thick colored border on one edge is the 'side-tab' AI tell. Use full borders, a background, or spacing instead."
|
|
26111
|
-
},
|
|
26112
|
-
"border-accent-on-rounded": {
|
|
26113
|
-
family: "borders_depth",
|
|
26114
|
-
severity: "warn",
|
|
26115
|
-
note: "A colored top/bottom accent bar on a rounded card is an AI tell. Let the card's own surface carry the accent."
|
|
26116
|
-
},
|
|
26117
|
-
"overused-font": {
|
|
26118
|
-
family: "typography",
|
|
26119
|
-
severity: "warn",
|
|
26120
|
-
brandAware: "font",
|
|
26121
|
-
note: "This face is a training-data default. Pick a face with a point of view \u2014 unless BRAND.md commits it."
|
|
26122
|
-
},
|
|
26123
|
-
"single-font": {
|
|
26124
|
-
family: "typography",
|
|
26125
|
-
severity: "advisory",
|
|
26126
|
-
note: "Only one face across the page flattens hierarchy. A display/body pair usually reads richer."
|
|
26127
|
-
},
|
|
26128
|
-
"flat-type-hierarchy": {
|
|
26129
|
-
family: "typography",
|
|
26130
|
-
severity: "warn",
|
|
26131
|
-
note: "Type steps are too close (<2\xD7 span). Widen the scale so the primary, secondary, and body are obvious at a glance."
|
|
26132
|
-
},
|
|
26133
|
-
"ai-color-palette": {
|
|
26134
|
-
family: "color",
|
|
26135
|
-
severity: "warn",
|
|
26136
|
-
brandAware: "color",
|
|
26137
|
-
note: "Purple/indigo-on-heading or purple\u2192cyan gradients are the AI palette cluster. Commit to the brand's own hues."
|
|
26138
|
-
},
|
|
26139
|
-
"cream-palette": {
|
|
26140
|
-
family: "color",
|
|
26141
|
-
severity: "warn",
|
|
26142
|
-
brandAware: "color",
|
|
26143
|
-
note: "Cream/parchment ground is the 'measured rendition' AI default. Rework from the brand's saturated materials."
|
|
26144
|
-
},
|
|
26145
|
-
"gray-on-color": {
|
|
26146
|
-
family: "color",
|
|
26147
|
-
severity: "warn",
|
|
26148
|
-
note: "Gray text on a colored surface reads muddy. Tint secondary text from the surface hue, never neutral gray."
|
|
26149
|
-
},
|
|
26150
|
-
"bounce-easing": {
|
|
26151
|
-
family: "motion",
|
|
26152
|
-
severity: "warn",
|
|
26153
|
-
note: "Bounce/elastic easing feels dated. Use natural deceleration (e.g. cubic-bezier(0.16, 1, 0.3, 1))."
|
|
26154
|
-
},
|
|
26155
|
-
"layout-transition": {
|
|
26156
|
-
family: "motion",
|
|
26157
|
-
severity: "advisory",
|
|
26158
|
-
note: "Transitioning width/height/padding/margin janks. Animate transform/opacity, or clip-path/max-height with care."
|
|
26159
|
-
},
|
|
26160
|
-
"monotonous-spacing": {
|
|
26161
|
-
family: "spacing",
|
|
26162
|
-
severity: "warn",
|
|
26163
|
-
note: "One spacing value dominates. Create rhythm \u2014 tight groups, generous separation, more space above a heading than below."
|
|
26164
|
-
},
|
|
26165
|
-
"marketing-buzzword": {
|
|
26166
|
-
family: "copy",
|
|
26167
|
-
severity: "warn",
|
|
26168
|
-
note: "SaaS filler ('supercharge your\u2026') is generic. Name names, use numbers, say the specific thing only this product proves."
|
|
26169
|
-
},
|
|
26170
|
-
"aphoristic-cadence": {
|
|
26171
|
-
family: "copy",
|
|
26172
|
-
severity: "warn",
|
|
26173
|
-
note: "Manufactured-contrast cadence ('Not an X. A Y.') is an AI copy tell. Vary sentence shape; make a real claim."
|
|
26174
|
-
},
|
|
26175
|
-
"em-dash-overuse": {
|
|
26176
|
-
family: "copy",
|
|
26177
|
-
severity: "warn",
|
|
26178
|
-
note: "Dense em-dashes are an AI cadence tell (and brand copy bans them). Recast with commas, colons, or full stops."
|
|
26179
|
-
},
|
|
26180
|
-
"dark-glow": {
|
|
26181
|
-
family: "borders_depth",
|
|
26182
|
-
severity: "warn",
|
|
26183
|
-
note: "A zero-offset colored glow is decoration, not depth. Shadows carry an offset and a soft blur."
|
|
26184
|
-
},
|
|
26185
|
-
marquee: {
|
|
26186
|
-
family: "motion",
|
|
26187
|
-
severity: "warn",
|
|
26188
|
-
note: "Auto-scrolling marquees are a dated tell. Let the visitor control the pace, or drop the motion."
|
|
26189
|
-
}
|
|
26190
|
-
};
|
|
26191
|
-
var SEVERITY_WEIGHT = {
|
|
26192
|
-
block: 0.5,
|
|
26193
|
-
warn: 0.2,
|
|
26194
|
-
advisory: 0.05
|
|
26195
|
-
};
|
|
26196
|
-
|
|
26197
|
-
// src/engine/landing/lib/rules.ts
|
|
26198
|
-
var cap2 = (m, i) => m[i] ?? "";
|
|
26199
|
-
var num = (m, i) => Number(m[i] ?? 0);
|
|
26200
|
-
var hasRounded = (line) => /\brounded(?:-\w+)?\b/.test(line) || /border-radius/i.test(line);
|
|
26201
|
-
var isSafeElement = (line) => /<(?:blockquote|nav[\s>]|pre[\s>]|code[\s>]|a\s|input[\s>]|span[\s>])/i.test(line);
|
|
26202
|
-
function isNeutralBorderColor(str) {
|
|
26203
|
-
const m = str.match(/solid\s+((?:rgba?|hsla?|oklch|oklab|lab|lch|hwb|color)\([^)]*\)|#[0-9a-f]{3,8}\b|[a-z]+)/i);
|
|
26204
|
-
if (!m) return false;
|
|
26205
|
-
return isNeutralAuthoredColor(m[1] ?? "");
|
|
26206
|
-
}
|
|
26207
|
-
var LINE_MATCHERS = [
|
|
26208
|
-
// ── Side-tab: a thick colored border on one edge ──────────────────────────
|
|
26209
|
-
{
|
|
26210
|
-
id: "side-tab",
|
|
26211
|
-
regex: /\bborder-[lrse]-(\d+)\b/g,
|
|
26212
|
-
test: (m, line) => hasRounded(line) ? num(m, 1) >= 2 : num(m, 1) >= 4,
|
|
26213
|
-
fmt: (m) => cap2(m, 0)
|
|
26214
|
-
},
|
|
26215
|
-
{
|
|
26216
|
-
id: "side-tab",
|
|
26217
|
-
regex: /border-(?:left|right)\s*:\s*(\d+)px\s+solid[^;]*/gi,
|
|
26218
|
-
test: (m, line) => {
|
|
26219
|
-
if (isSafeElement(line)) return false;
|
|
26220
|
-
if (isNeutralBorderColor(cap2(m, 0))) return false;
|
|
26221
|
-
return hasRounded(line) ? num(m, 1) >= 2 : num(m, 1) >= 3;
|
|
26222
|
-
},
|
|
26223
|
-
fmt: (m) => cap2(m, 0).replace(/\s*;?\s*$/, "")
|
|
26224
|
-
},
|
|
26225
|
-
{
|
|
26226
|
-
id: "side-tab",
|
|
26227
|
-
regex: /border-(?:left|right)-width\s*:\s*(\d+)px/gi,
|
|
26228
|
-
test: (m, line) => !isSafeElement(line) && num(m, 1) >= 3,
|
|
26229
|
-
fmt: (m) => cap2(m, 0)
|
|
26230
|
-
},
|
|
26231
|
-
{
|
|
26232
|
-
id: "side-tab",
|
|
26233
|
-
regex: /border-inline-(?:start|end)(?:-width)?\s*:\s*(\d+)px/gi,
|
|
26234
|
-
test: (m, line) => !isSafeElement(line) && num(m, 1) >= 3,
|
|
26235
|
-
fmt: (m) => cap2(m, 0)
|
|
26236
|
-
},
|
|
26237
|
-
// ── Border accent on a rounded card (top/bottom colored bar) ──────────────
|
|
26238
|
-
{
|
|
26239
|
-
id: "border-accent-on-rounded",
|
|
26240
|
-
regex: /\bborder-[tb]-(\d+)\b/g,
|
|
26241
|
-
test: (m, line) => hasRounded(line) && num(m, 1) >= 1,
|
|
26242
|
-
fmt: (m) => cap2(m, 0)
|
|
26243
|
-
},
|
|
26244
|
-
{
|
|
26245
|
-
id: "border-accent-on-rounded",
|
|
26246
|
-
regex: /border-(?:top|bottom)\s*:\s*(\d+)px\s+solid/gi,
|
|
26247
|
-
test: (m, line) => num(m, 1) >= 3 && hasRounded(line),
|
|
26248
|
-
fmt: (m) => cap2(m, 0)
|
|
26249
|
-
},
|
|
26250
|
-
// ── Gradient text ─────────────────────────────────────────────────────────
|
|
26251
|
-
{
|
|
26252
|
-
id: "gradient-text",
|
|
26253
|
-
regex: /background-clip\s*:\s*text|-webkit-background-clip\s*:\s*text/gi,
|
|
26254
|
-
test: (_m, line) => /gradient/i.test(line),
|
|
26255
|
-
fmt: () => "background-clip: text + gradient"
|
|
26256
|
-
},
|
|
26257
|
-
{
|
|
26258
|
-
id: "gradient-text",
|
|
26259
|
-
regex: /\bbg-clip-text\b/g,
|
|
26260
|
-
test: (_m, line) => /\bbg-gradient-to-|\bbg-\[linear-gradient/i.test(line),
|
|
26261
|
-
fmt: () => "bg-clip-text + gradient"
|
|
26262
|
-
},
|
|
26263
|
-
// ── Gray text on a colored background (Tailwind) ──────────────────────────
|
|
26264
|
-
{
|
|
26265
|
-
id: "gray-on-color",
|
|
26266
|
-
regex: /\btext-(?:gray|slate|zinc|neutral|stone)-(\d+)\b/g,
|
|
26267
|
-
test: (_m, line) => /\bbg-(?:red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-\d+\b/.test(
|
|
26268
|
-
line
|
|
26269
|
-
),
|
|
26270
|
-
fmt: (m, line) => {
|
|
26271
|
-
const bg = line.match(
|
|
26272
|
-
/\bbg-(?:red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-\d+\b/
|
|
26273
|
-
);
|
|
26274
|
-
return `${cap2(m, 0)} on ${bg?.[0] || "?"}`;
|
|
26275
|
-
}
|
|
26276
|
-
},
|
|
26277
|
-
// ── AI color palette (Tailwind purple/indigo + banned hero hexes) ─────────
|
|
26278
|
-
{
|
|
26279
|
-
id: "ai-color-palette",
|
|
26280
|
-
regex: /\btext-(?:purple|violet|indigo)-(\d+)\b/g,
|
|
26281
|
-
test: (_m, line) => /\btext-(?:[2-9]xl)\b|<h[1-3]/i.test(line),
|
|
26282
|
-
fmt: (m) => `${cap2(m, 0)} on heading`
|
|
26283
|
-
},
|
|
26284
|
-
{
|
|
26285
|
-
id: "ai-color-palette",
|
|
26286
|
-
regex: /\bfrom-(?:purple|violet|indigo)-(\d+)\b/g,
|
|
26287
|
-
test: (_m, line) => /\bto-(?:purple|violet|indigo|blue|cyan|pink|fuchsia)-\d+\b/.test(line),
|
|
26288
|
-
fmt: (m) => `${cap2(m, 0)} gradient`
|
|
26289
|
-
},
|
|
26290
|
-
{
|
|
26291
|
-
id: "ai-color-palette",
|
|
26292
|
-
regex: /#[0-9a-f]{6}\b/gi,
|
|
26293
|
-
test: (m) => AI_PURPLE_HEXES.has(cap2(m, 0).toLowerCase()),
|
|
26294
|
-
fmt: (m) => `${cap2(m, 0)} (AI-cluster purple)`
|
|
26295
|
-
},
|
|
26296
|
-
// ── Cream/parchment ground (Tailwind + authored background hex) ───────────
|
|
26297
|
-
{
|
|
26298
|
-
id: "cream-palette",
|
|
26299
|
-
regex: /\bbg-(?:amber|orange|yellow|stone)-(?:50|100|200)\b/g,
|
|
26300
|
-
test: (m) => CREAM_TAILWIND_BG.has(cap2(m, 0)),
|
|
26301
|
-
fmt: (m) => `${cap2(m, 0)} (cream ground)`
|
|
26302
|
-
},
|
|
26303
|
-
{
|
|
26304
|
-
id: "cream-palette",
|
|
26305
|
-
regex: /background(?:-color)?\s*:\s*(#[0-9a-f]{6}\b|rgba?\([^)]+\))/gi,
|
|
26306
|
-
test: (m) => {
|
|
26307
|
-
const c = parseColor2(cap2(m, 1));
|
|
26308
|
-
return c !== null && isCreamColor(c);
|
|
26309
|
-
},
|
|
26310
|
-
fmt: (m) => `${cap2(m, 1)} (cream ground)`
|
|
26311
|
-
},
|
|
26312
|
-
// ── Bounce / elastic easing ───────────────────────────────────────────────
|
|
26313
|
-
{
|
|
26314
|
-
id: "bounce-easing",
|
|
26315
|
-
regex: /\banimate-bounce\b/g,
|
|
26316
|
-
test: () => true,
|
|
26317
|
-
fmt: () => "animate-bounce (Tailwind)"
|
|
26318
|
-
},
|
|
26319
|
-
{
|
|
26320
|
-
id: "bounce-easing",
|
|
26321
|
-
regex: /animation(?:-name)?\s*:\s*([^;{}]*(?:bounce|elastic|wobble|jiggle|spring)[^;{}]*)/gi,
|
|
26322
|
-
test: () => true,
|
|
26323
|
-
fmt: (m) => `animation: ${cap2(m, 1).trim()}`
|
|
26324
|
-
},
|
|
26325
|
-
{
|
|
26326
|
-
id: "bounce-easing",
|
|
26327
|
-
regex: /cubic-bezier\(\s*([\d.-]+)\s*,\s*([\d.-]+)\s*,\s*([\d.-]+)\s*,\s*([\d.-]+)\s*\)/g,
|
|
26328
|
-
test: (m) => {
|
|
26329
|
-
const y1 = Number.parseFloat(cap2(m, 2));
|
|
26330
|
-
const y2 = Number.parseFloat(cap2(m, 4));
|
|
26331
|
-
return y1 < -0.1 || y1 > 1.1 || y2 < -0.1 || y2 > 1.1;
|
|
26332
|
-
},
|
|
26333
|
-
fmt: (m) => `cubic-bezier(${cap2(m, 1)}, ${cap2(m, 2)}, ${cap2(m, 3)}, ${cap2(m, 4)})`
|
|
26334
|
-
},
|
|
26335
|
-
// ── Transitioning layout properties (jank) ────────────────────────────────
|
|
26336
|
-
{
|
|
26337
|
-
id: "layout-transition",
|
|
26338
|
-
regex: /transition(?:-property)?\s*:\s*([^;{}]+)/gi,
|
|
26339
|
-
test: (m) => {
|
|
26340
|
-
const val = cap2(m, 1).toLowerCase();
|
|
26341
|
-
if (/\ball\b/.test(val)) return false;
|
|
26342
|
-
return /\b(?:(?:max|min)-)?(?:width|height)\b|\bpadding\b|\bmargin\b/.test(val);
|
|
26343
|
-
},
|
|
26344
|
-
fmt: (m) => {
|
|
26345
|
-
const found = cap2(m, 1).match(
|
|
26346
|
-
/\b(?:(?:max|min)-)?(?:width|height)\b|\bpadding(?:-(?:top|right|bottom|left))?\b|\bmargin(?:-(?:top|right|bottom|left))?\b/gi
|
|
26347
|
-
);
|
|
26348
|
-
return `transition: ${found ? found.join(", ") : cap2(m, 1).trim()}`;
|
|
26349
|
-
}
|
|
26350
|
-
},
|
|
26351
|
-
// ── Broken image ──────────────────────────────────────────────────────────
|
|
26352
|
-
{
|
|
26353
|
-
id: "broken-image",
|
|
26354
|
-
regex: /<img\b[^>]*?\bsrc\s*=\s*(?:""|''|"\s+"|'\s+'|"#"|'#')/gi,
|
|
26355
|
-
test: () => true,
|
|
26356
|
-
fmt: (m) => cap2(m, 0).slice(0, 100)
|
|
26357
|
-
},
|
|
26358
|
-
{
|
|
26359
|
-
id: "broken-image",
|
|
26360
|
-
regex: /<img\b(?:(?!\bsrc\s*=)[^>])*>/gi,
|
|
26361
|
-
test: (m) => !/\bsrc\s*=/i.test(cap2(m, 0)),
|
|
26362
|
-
fmt: (m) => cap2(m, 0).slice(0, 100)
|
|
26363
|
-
}
|
|
26364
|
-
];
|
|
26365
|
-
function stripHtmlToText(html) {
|
|
26366
|
-
return html.replace(/<script\b[^>]*>[\s\S]*?<\/script>/gi, " ").replace(/<style\b[^>]*>[\s\S]*?<\/style>/gi, " ").replace(/<!--[\s\S]*?-->/g, " ").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ");
|
|
26367
|
-
}
|
|
26368
|
-
var ANALYZERS = [
|
|
26369
|
-
// Overused primary font (font-family declarations + Google Fonts URLs).
|
|
26370
|
-
(text, file) => {
|
|
26371
|
-
const out = [];
|
|
26372
|
-
const seen = /* @__PURE__ */ new Set();
|
|
26373
|
-
for (const m of text.matchAll(/font-family\s*:\s*([^;}{]+)/gi)) {
|
|
26374
|
-
const first = firstFamily(cap2(m, 1));
|
|
26375
|
-
if (first && !GENERIC_FONTS.has(first) && OVERUSED_FONTS.has(first) && !seen.has(first)) {
|
|
26376
|
-
seen.add(first);
|
|
26377
|
-
out.push({ id: "overused-font", snippet: `font-family: ${first}`, file, line: lineOf(text, m.index ?? 0) });
|
|
26378
|
-
}
|
|
26379
|
-
}
|
|
26380
|
-
for (const m of text.matchAll(/fonts\.googleapis\.com\/css2?\?[^"'\s)<>]*/gi)) {
|
|
26381
|
-
for (const fam of googleFontFamilies(cap2(m, 0))) {
|
|
26382
|
-
if (OVERUSED_FONTS.has(fam) && !seen.has(fam)) {
|
|
26383
|
-
seen.add(fam);
|
|
26384
|
-
out.push({ id: "overused-font", snippet: `Google Fonts: ${fam}`, file, line: lineOf(text, m.index ?? 0) });
|
|
26385
|
-
}
|
|
26386
|
-
}
|
|
26387
|
-
}
|
|
26388
|
-
return out;
|
|
26389
|
-
},
|
|
26390
|
-
// Single font across a substantial page.
|
|
26391
|
-
(text, file) => {
|
|
26392
|
-
const fonts = /* @__PURE__ */ new Set();
|
|
26393
|
-
for (const m of text.matchAll(/font-family\s*:\s*([^;}{]+)/gi)) {
|
|
26394
|
-
for (const f of cap2(m, 1).split(",").map(
|
|
26395
|
-
(x) => x.trim().replace(/^['"]|['"]$/g, "").toLowerCase()
|
|
26396
|
-
)) {
|
|
26397
|
-
if (f && !GENERIC_FONTS.has(f)) fonts.add(f);
|
|
26398
|
-
}
|
|
26399
|
-
}
|
|
26400
|
-
for (const f of allGoogleFontFamilies(text)) fonts.add(f);
|
|
26401
|
-
if (fonts.size !== 1 || text.split("\n").length < 20) return [];
|
|
26402
|
-
const only = [...fonts][0] ?? "";
|
|
26403
|
-
return [{ id: "single-font", snippet: `only font used is ${only}`, file }];
|
|
26404
|
-
},
|
|
26405
|
-
// Flat type hierarchy (all sizes span <2×).
|
|
26406
|
-
(text, file) => {
|
|
26407
|
-
const sizes = /* @__PURE__ */ new Set();
|
|
26408
|
-
const REM = 16;
|
|
26409
|
-
for (const m of text.matchAll(/font-size\s*:\s*([\d.]+)(px|rem|em)\b/gi)) {
|
|
26410
|
-
const px = cap2(m, 2) === "px" ? num(m, 1) : num(m, 1) * REM;
|
|
26411
|
-
if (px > 0 && px < 200) sizes.add(Math.round(px * 10) / 10);
|
|
26412
|
-
}
|
|
26413
|
-
for (const [cls, px] of Object.entries(TAILWIND_TEXT_PX)) {
|
|
26414
|
-
if (new RegExp(`\\b${cls}\\b`).test(text)) sizes.add(px);
|
|
26415
|
-
}
|
|
26416
|
-
if (sizes.size < 3) return [];
|
|
26417
|
-
const sorted = [...sizes].sort((a, b) => a - b);
|
|
26418
|
-
const min = sorted[0] ?? 1;
|
|
26419
|
-
const max = sorted[sorted.length - 1] ?? 1;
|
|
26420
|
-
const ratio = max / min;
|
|
26421
|
-
if (ratio >= 2) return [];
|
|
26422
|
-
return [
|
|
26423
|
-
{
|
|
26424
|
-
id: "flat-type-hierarchy",
|
|
26425
|
-
snippet: `sizes ${sorted.map((s) => `${s}px`).join(", ")} (ratio ${ratio.toFixed(1)}:1)`,
|
|
26426
|
-
file
|
|
26427
|
-
}
|
|
26428
|
-
];
|
|
26429
|
-
},
|
|
26430
|
-
// Monotonous spacing (one value dominates).
|
|
26431
|
-
(text, file) => {
|
|
26432
|
-
const vals = [];
|
|
26433
|
-
for (const m of text.matchAll(/(?:padding|margin)(?:-(?:top|right|bottom|left))?\s*:\s*(\d+)px/gi)) {
|
|
26434
|
-
const v = num(m, 1);
|
|
26435
|
-
if (v > 0 && v < 200) vals.push(v);
|
|
26436
|
-
}
|
|
26437
|
-
for (const m of text.matchAll(/(?:padding|margin)(?:-(?:top|right|bottom|left))?\s*:\s*([\d.]+)rem/gi)) {
|
|
26438
|
-
const v = Math.round(Number.parseFloat(cap2(m, 1)) * 16);
|
|
26439
|
-
if (v > 0 && v < 200) vals.push(v);
|
|
26440
|
-
}
|
|
26441
|
-
for (const m of text.matchAll(/\b(?:p|px|py|pt|pb|pl|pr|m|mx|my|mt|mb|ml|mr|gap)-(\d+)\b/g))
|
|
26442
|
-
vals.push(num(m, 1) * 4);
|
|
26443
|
-
const rounded = vals.map((v) => Math.round(v / 4) * 4);
|
|
26444
|
-
if (rounded.length < 10) return [];
|
|
26445
|
-
const counts = {};
|
|
26446
|
-
for (const v of rounded) counts[v] = (counts[v] || 0) + 1;
|
|
26447
|
-
const maxCount = Math.max(...Object.values(counts));
|
|
26448
|
-
const pct = maxCount / rounded.length;
|
|
26449
|
-
const unique = [...new Set(rounded)].filter((v) => v > 0);
|
|
26450
|
-
if (pct <= 0.6 || unique.length > 3) return [];
|
|
26451
|
-
const dominant = Object.entries(counts).sort((a, b) => b[1] - a[1])[0]?.[0] ?? "?";
|
|
26452
|
-
return [
|
|
26453
|
-
{
|
|
26454
|
-
id: "monotonous-spacing",
|
|
26455
|
-
snippet: `~${dominant}px used ${maxCount}/${rounded.length} times (${Math.round(pct * 100)}%)`,
|
|
26456
|
-
file
|
|
26457
|
-
}
|
|
26458
|
-
];
|
|
26459
|
-
},
|
|
26460
|
-
// Em-dash overuse (density-gated).
|
|
26461
|
-
(text, file) => {
|
|
26462
|
-
const body = stripHtmlToText(text).replace(/—|�*8212;|�*2014;/gi, "\u2014");
|
|
26463
|
-
const count = (body.match(/[—]|--(?=\S)/g) ?? []).length;
|
|
26464
|
-
if (count < EM_DASH_FLOOR) return [];
|
|
26465
|
-
if (body.length > count * EM_DASH_CHARS_PER_DASH) return [];
|
|
26466
|
-
return [{ id: "em-dash-overuse", snippet: `${count} em-dashes in body copy`, file }];
|
|
26467
|
-
},
|
|
26468
|
-
// Marketing buzzwords.
|
|
26469
|
-
(text, file) => {
|
|
26470
|
-
const body = stripHtmlToText(text);
|
|
26471
|
-
const lower = body.toLowerCase();
|
|
26472
|
-
let count = 0;
|
|
26473
|
-
let firstSample = "";
|
|
26474
|
-
for (const phrase of BUZZWORDS) {
|
|
26475
|
-
let from = 0;
|
|
26476
|
-
for (; ; ) {
|
|
26477
|
-
const idx = lower.indexOf(phrase, from);
|
|
26478
|
-
if (idx === -1) break;
|
|
26479
|
-
count++;
|
|
26480
|
-
if (!firstSample) {
|
|
26481
|
-
firstSample = body.slice(Math.max(0, idx - 12), Math.min(body.length, idx + phrase.length + 12)).trim();
|
|
26482
|
-
}
|
|
26483
|
-
from = idx + phrase.length;
|
|
26484
|
-
}
|
|
26485
|
-
}
|
|
26486
|
-
if (count === 0) return [];
|
|
26487
|
-
return [
|
|
26488
|
-
{
|
|
26489
|
-
id: "marketing-buzzword",
|
|
26490
|
-
snippet: `${count} buzzword phrase${count === 1 ? "" : "s"}: "${firstSample}"`,
|
|
26491
|
-
file
|
|
26492
|
-
}
|
|
26493
|
-
];
|
|
26494
|
-
},
|
|
26495
|
-
// Aphoristic cadence (manufactured contrast + short rebuttal).
|
|
26496
|
-
(text, file) => {
|
|
26497
|
-
const body = stripHtmlToText(text);
|
|
26498
|
-
let count = 0;
|
|
26499
|
-
let firstSample = "";
|
|
26500
|
-
for (const m of body.matchAll(/\bNot an? [a-z][^.!?]{1,40}[.!]\s+[A-Z][^.!?]{1,60}[.!]/g)) {
|
|
26501
|
-
count++;
|
|
26502
|
-
if (!firstSample) firstSample = cap2(m, 0).trim().slice(0, 80);
|
|
26503
|
-
}
|
|
26504
|
-
for (const m of body.matchAll(/\b[A-Z][^.!?]{4,80}[.!]\s+(No|Just)\s+[a-z][^.!?]{2,60}[.!]/g)) {
|
|
26505
|
-
count++;
|
|
26506
|
-
if (!firstSample) firstSample = cap2(m, 0).trim().slice(0, 80);
|
|
26507
|
-
}
|
|
26508
|
-
if (count < 3) return [];
|
|
26509
|
-
return [{ id: "aphoristic-cadence", snippet: `${count} aphoristic constructions: "${firstSample}"`, file }];
|
|
26510
|
-
},
|
|
26511
|
-
// Dark glow: a zero-offset chromatic shadow used as decoration.
|
|
26512
|
-
(text, file) => {
|
|
26513
|
-
const out = [];
|
|
26514
|
-
for (const m of text.matchAll(/(?:box|text)-shadow\s*:\s*([^;{}]+)/gi)) {
|
|
26515
|
-
if (shadowIsGlow(cap2(m, 1))) {
|
|
26516
|
-
out.push({
|
|
26517
|
-
id: "dark-glow",
|
|
26518
|
-
snippet: `shadow: ${cap2(m, 1).trim().slice(0, 60)}`,
|
|
26519
|
-
file,
|
|
26520
|
-
line: lineOf(text, m.index ?? 0)
|
|
26521
|
-
});
|
|
26522
|
-
}
|
|
26523
|
-
}
|
|
26524
|
-
return out;
|
|
26525
|
-
},
|
|
26526
|
-
// Marquee (<marquee> element).
|
|
26527
|
-
(text, file) => /<marquee\b/i.test(text) ? [{ id: "marquee", snippet: "<marquee> element", file }] : []
|
|
26528
|
-
];
|
|
26529
|
-
function shadowIsGlow(value) {
|
|
26530
|
-
const first = (value.split(/,(?![^(]*\))/)[0] ?? "").trim();
|
|
26531
|
-
const colorMatch = first.match(/#[0-9a-f]{3,8}\b|rgba?\([^)]+\)|hsla?\([^)]+\)/i);
|
|
26532
|
-
if (!colorMatch) return false;
|
|
26533
|
-
const lengthsPart = first.replace(colorMatch[0], " ").replace(/\binset\b/gi, " ");
|
|
26534
|
-
const offsets = lengthsPart.match(/-?\d*\.?\d+(?:px|rem|em)?/g);
|
|
26535
|
-
if (!offsets || offsets.length < 3) return false;
|
|
26536
|
-
const x = Number.parseFloat(offsets[0] ?? "0");
|
|
26537
|
-
const y = Number.parseFloat(offsets[1] ?? "0");
|
|
26538
|
-
const blur = Number.parseFloat(offsets[2] ?? "0");
|
|
26539
|
-
if (!(x === 0 && y === 0 && blur > 4)) return false;
|
|
26540
|
-
const c = parseColor2(colorMatch[0]);
|
|
26541
|
-
return c !== null && hasChroma(c, 30);
|
|
26542
|
-
}
|
|
26543
|
-
function firstFamily(decl) {
|
|
26544
|
-
return (decl.split(",")[0] ?? "").trim().replace(/^['"]|['"]$/g, "").toLowerCase();
|
|
26545
|
-
}
|
|
26546
|
-
function googleFontFamilies(url) {
|
|
26547
|
-
const q = url.indexOf("?");
|
|
26548
|
-
if (q === -1) return [];
|
|
26549
|
-
const params = new URLSearchParams(url.slice(q + 1).replace(/&/g, "&"));
|
|
26550
|
-
const out = [];
|
|
26551
|
-
for (const value of params.getAll("family")) {
|
|
26552
|
-
for (const part of value.split("|")) out.push((part.split(":")[0] ?? "").trim().toLowerCase());
|
|
26553
|
-
}
|
|
26554
|
-
return out.filter(Boolean);
|
|
26555
|
-
}
|
|
26556
|
-
function allGoogleFontFamilies(text) {
|
|
26557
|
-
const out = [];
|
|
26558
|
-
for (const m of text.matchAll(/fonts\.googleapis\.com\/css2?\?[^"'\s)<>]*/gi))
|
|
26559
|
-
out.push(...googleFontFamilies(cap2(m, 0)));
|
|
26560
|
-
return out;
|
|
26561
|
-
}
|
|
26562
|
-
function lineOf(text, index) {
|
|
26563
|
-
let line = 1;
|
|
26564
|
-
for (let i = 0; i < index && i < text.length; i++) if (text[i] === "\n") line++;
|
|
26565
|
-
return line;
|
|
26566
|
-
}
|
|
26567
|
-
function detectSource(source) {
|
|
26568
|
-
const { path: file, text } = source;
|
|
26569
|
-
const findings = [];
|
|
26570
|
-
const lines = text.split("\n");
|
|
26571
|
-
for (const matcher of LINE_MATCHERS) {
|
|
26572
|
-
for (let i = 0; i < lines.length; i++) {
|
|
26573
|
-
const line = lines[i] ?? "";
|
|
26574
|
-
for (const m of line.matchAll(matcher.regex)) {
|
|
26575
|
-
if (matcher.test(m, line)) findings.push({ id: matcher.id, snippet: matcher.fmt(m, line), file, line: i + 1 });
|
|
26576
|
-
}
|
|
26577
|
-
}
|
|
26578
|
-
}
|
|
26579
|
-
for (const analyzer of ANALYZERS) findings.push(...analyzer(text, file));
|
|
26580
|
-
return dedupe(findings);
|
|
26581
|
-
}
|
|
26582
|
-
function dedupe(findings) {
|
|
26583
|
-
const out = [];
|
|
26584
|
-
for (const f of findings) {
|
|
26585
|
-
const dupe = out.some(
|
|
26586
|
-
(d) => d.id === f.id && d.snippet === f.snippet && Math.abs((d.line ?? 0) - (f.line ?? 0)) <= 2
|
|
26587
|
-
);
|
|
26588
|
-
if (!dupe) out.push(f);
|
|
26589
|
-
}
|
|
26590
|
-
return out;
|
|
26591
|
-
}
|
|
26592
|
-
|
|
26593
|
-
// src/engine/landing/lib/critique.ts
|
|
26594
|
-
var FAMILIES = ["typography", "color", "borders_depth", "motion", "spacing", "copy", "integrity"];
|
|
26595
|
-
function round4(n) {
|
|
26596
|
-
return Math.round(n * 100) / 100;
|
|
26597
|
-
}
|
|
26598
|
-
function clamp012(n) {
|
|
26599
|
-
return Math.max(0, Math.min(1, n));
|
|
26600
|
-
}
|
|
26601
|
-
function applyBrand(id, snippet, severity, brand) {
|
|
26602
|
-
const meta = RULE_META[id];
|
|
26603
|
-
if (!meta?.brandAware || !brand.hasTokens) return { severity, brandDrift: false };
|
|
26604
|
-
const matched = meta.brandAware === "font" && brandCommitsFont(brand, snippet) || meta.brandAware === "color" && id === "ai-color-palette" && brandCommitsAiHue(brand) || meta.brandAware === "color" && id === "cream-palette" && brandCommitsCream(brand);
|
|
26605
|
-
return matched ? { severity: "advisory", brandDrift: true } : { severity, brandDrift: false };
|
|
26606
|
-
}
|
|
26607
|
-
function critiqueLanding(input) {
|
|
26608
|
-
const sources = Array.isArray(input.sources) ? input.sources : [];
|
|
26609
|
-
const findings = [];
|
|
26610
|
-
for (const source of sources) {
|
|
26611
|
-
for (const raw of detectSource(source)) {
|
|
26612
|
-
const meta = RULE_META[raw.id];
|
|
26613
|
-
if (!meta) continue;
|
|
26614
|
-
const { severity, brandDrift } = applyBrand(raw.id, raw.snippet, meta.severity, input.brand);
|
|
26615
|
-
findings.push({
|
|
26616
|
-
id: raw.id,
|
|
26617
|
-
severity,
|
|
26618
|
-
family: meta.family,
|
|
26619
|
-
snippet: raw.snippet,
|
|
26620
|
-
file: raw.file,
|
|
26621
|
-
line: raw.line,
|
|
26622
|
-
note: meta.note,
|
|
26623
|
-
...brandDrift ? { brandDrift: true } : {}
|
|
26624
|
-
});
|
|
26625
|
-
}
|
|
26626
|
-
}
|
|
26627
|
-
const dimensions = FAMILIES.map((family) => {
|
|
26628
|
-
const fam = findings.filter((f) => f.family === family);
|
|
26629
|
-
const penalty = fam.reduce((s, f) => s + SEVERITY_WEIGHT[f.severity], 0);
|
|
26630
|
-
const score = round4(clamp012(1 - penalty));
|
|
26631
|
-
const note = fam.length === 0 ? "clean" : `${fam.length} finding(s): ${describeCounts(fam)}`;
|
|
26632
|
-
return { dimension: family, score, note };
|
|
26633
|
-
});
|
|
26634
|
-
const overall = round4(dimensions.reduce((s, d) => s + d.score, 0) / dimensions.length);
|
|
26635
|
-
const counts = {
|
|
26636
|
-
block: findings.filter((f) => f.severity === "block").length,
|
|
26637
|
-
warn: findings.filter((f) => f.severity === "warn").length,
|
|
26638
|
-
advisory: findings.filter((f) => f.severity === "advisory").length
|
|
26639
|
-
};
|
|
26640
|
-
return { advisory: true, overall, dimensions, findings, counts };
|
|
26641
|
-
}
|
|
26642
|
-
function describeCounts(findings) {
|
|
26643
|
-
const b = findings.filter((f) => f.severity === "block").length;
|
|
26644
|
-
const w = findings.filter((f) => f.severity === "warn").length;
|
|
26645
|
-
const a = findings.filter((f) => f.severity === "advisory").length;
|
|
26646
|
-
return [b ? `${b} block` : "", w ? `${w} warn` : "", a ? `${a} advisory` : ""].filter(Boolean).join(", ");
|
|
26647
|
-
}
|
|
26648
|
-
|
|
26649
|
-
// src/commands/landing/snapshot.ts
|
|
26650
|
-
import { mkdir as mkdir7, writeFile as writeFile10 } from "fs/promises";
|
|
26651
|
-
import path24 from "path";
|
|
26652
|
-
var CRITIC_VERSION = "1";
|
|
26653
|
-
function critiqueCacheDir(projectRoot) {
|
|
26654
|
-
return path24.join(projectRoot, ".cache", "landing-critique");
|
|
26655
|
-
}
|
|
26656
|
-
function snapshotPath(projectRoot, slug) {
|
|
26657
|
-
return path24.join(critiqueCacheDir(projectRoot), `${slug}.json`);
|
|
26658
|
-
}
|
|
26659
|
-
async function writeCritiqueSnapshot(projectRoot, snapshot) {
|
|
26660
|
-
await mkdir7(critiqueCacheDir(projectRoot), { recursive: true });
|
|
26661
|
-
await writeFile10(snapshotPath(projectRoot, snapshot.slug), `${JSON.stringify(snapshot, null, 2)}
|
|
26662
|
-
`, "utf8");
|
|
26663
|
-
}
|
|
26664
|
-
|
|
26665
|
-
// src/commands/landing/source-version.ts
|
|
26666
|
-
import { readdir as readdir7, readFile as readFile21, stat as stat5 } from "fs/promises";
|
|
26667
|
-
import path25 from "path";
|
|
26668
|
-
async function landingSourceRelPaths(landingDir) {
|
|
26669
|
-
const rel = [];
|
|
26670
|
-
if (await isFile(path25.join(landingDir, "index.astro"))) rel.push("index.astro");
|
|
26671
|
-
const componentsDir = path25.join(landingDir, "_components");
|
|
26672
|
-
for (const abs of await walkAstro(componentsDir)) {
|
|
26673
|
-
rel.push(path25.relative(landingDir, abs).split(path25.sep).join("/"));
|
|
26674
|
-
}
|
|
26675
|
-
return rel.sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
|
|
26676
|
-
}
|
|
26677
|
-
async function readLandingSources(landingDir) {
|
|
26678
|
-
const rel = await landingSourceRelPaths(landingDir);
|
|
26679
|
-
const out = [];
|
|
26680
|
-
for (const r of rel) out.push({ path: r, text: await readFile21(path25.join(landingDir, r), "utf8") });
|
|
26681
|
-
return out;
|
|
26682
|
-
}
|
|
26683
|
-
async function computeLandingSourceSha(landingDir) {
|
|
26684
|
-
const rel = await landingSourceRelPaths(landingDir);
|
|
26685
|
-
const parts = [];
|
|
26686
|
-
for (const r of rel) {
|
|
26687
|
-
let bytes;
|
|
26688
|
-
try {
|
|
26689
|
-
bytes = await readFile21(path25.join(landingDir, r));
|
|
26690
|
-
} catch {
|
|
26691
|
-
bytes = Buffer.alloc(0);
|
|
26692
|
-
}
|
|
26693
|
-
parts.push(Buffer.from(`${r}\0${bytes.length}\0`), bytes);
|
|
26694
|
-
}
|
|
26695
|
-
return sha256Hex(Buffer.concat(parts));
|
|
26696
|
-
}
|
|
26697
|
-
async function isFile(p) {
|
|
26698
|
-
try {
|
|
26699
|
-
return (await stat5(p)).isFile();
|
|
26700
|
-
} catch {
|
|
26701
|
-
return false;
|
|
26702
|
-
}
|
|
26703
|
-
}
|
|
26704
|
-
async function walkAstro(dir) {
|
|
26705
|
-
let entries;
|
|
26706
|
-
try {
|
|
26707
|
-
entries = await readdir7(dir, { withFileTypes: true });
|
|
26708
|
-
} catch {
|
|
26709
|
-
return [];
|
|
26710
|
-
}
|
|
26711
|
-
const out = [];
|
|
26712
|
-
for (const entry of entries) {
|
|
26713
|
-
const abs = path25.join(dir, entry.name);
|
|
26714
|
-
if (entry.isDirectory()) out.push(...await walkAstro(abs));
|
|
26715
|
-
else if (entry.isFile() && entry.name.endsWith(".astro")) out.push(abs);
|
|
26716
|
-
}
|
|
26717
|
-
return out;
|
|
26718
|
-
}
|
|
26719
|
-
|
|
26720
|
-
// src/commands/landing/critique.ts
|
|
26721
|
-
registerSchema({
|
|
26722
|
-
command: "landing.critique",
|
|
26723
|
-
description: "Deterministic design-quality critic for a landing page (ADVISORY \u2014 never blocks, always exits 0). Flags the known AI 'slop' tells (gradient text, overused fonts, side-tab borders, cream palettes, buzzword copy, broken images\u2026) tiered block/warn/advisory, and records the critique the publish quality gate requires.",
|
|
26724
|
-
args: {
|
|
26725
|
-
slug: { type: "string", description: "Landing slug (folder under src/pages/)", required: true },
|
|
26726
|
-
full: {
|
|
26727
|
-
type: "boolean",
|
|
26728
|
-
description: "Also print advisory-tier findings (default: block + warn only)",
|
|
26729
|
-
required: false
|
|
26730
|
-
}
|
|
26731
|
-
}
|
|
26732
|
-
});
|
|
26733
|
-
var critiqueCommand2 = defineCommand133({
|
|
26734
|
-
meta: {
|
|
26735
|
-
name: "critique",
|
|
26736
|
-
description: "Deterministic design-quality critic for a landing (ADVISORY \u2014 never blocks). Flags the known AI design tells (gradient text, overused fonts, side-tab borders, cream palettes, buzzword copy, broken images) tiered block/warn/advisory, respecting the client's BRAND.md as the allowlist. Also records the critique that `baker publish` requires \u2014 run it before finishing a landing."
|
|
26737
|
-
},
|
|
26738
|
-
args: {
|
|
26739
|
-
slug: { type: "positional", required: true, description: "Landing slug (folder under src/pages/)" },
|
|
26740
|
-
full: { type: "boolean", description: "Also print advisory-tier findings", default: false }
|
|
26741
|
-
},
|
|
26742
|
-
async run({ args }) {
|
|
26743
|
-
const slug = String(args.slug);
|
|
26744
|
-
const projectRoot = process.cwd();
|
|
26745
|
-
const landingDir = path26.resolve(projectRoot, "src", "pages", slug);
|
|
26746
|
-
if (!await isDir(landingDir)) {
|
|
26747
|
-
process.stderr.write(
|
|
26748
|
-
`${JSON.stringify(
|
|
26749
|
-
{ ok: false, error: { code: "not-found", message: `No landing at src/pages/${slug}/` } },
|
|
26750
|
-
null,
|
|
26751
|
-
2
|
|
26752
|
-
)}
|
|
26753
|
-
`
|
|
26754
|
-
);
|
|
26755
|
-
process.exit(2);
|
|
26756
|
-
return;
|
|
26757
|
-
}
|
|
26758
|
-
const [sources, brand, sourceSha] = await Promise.all([
|
|
26759
|
-
readLandingSources(landingDir),
|
|
26760
|
-
loadBrandTokens(projectRoot),
|
|
26761
|
-
computeLandingSourceSha(landingDir)
|
|
26762
|
-
]);
|
|
26763
|
-
const report = critiqueLanding({ slug, sources, brand });
|
|
26764
|
-
try {
|
|
26765
|
-
await writeCritiqueSnapshot(projectRoot, {
|
|
26766
|
-
slug,
|
|
26767
|
-
sourceSha,
|
|
26768
|
-
criticVersion: CRITIC_VERSION,
|
|
26769
|
-
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
26770
|
-
blockCount: report.counts.block,
|
|
26771
|
-
warnCount: report.counts.warn
|
|
26772
|
-
});
|
|
26773
|
-
} catch {
|
|
26774
|
-
}
|
|
26775
|
-
const shown = report.findings.filter((f) => args.full ? true : f.severity !== "advisory");
|
|
26776
|
-
process.stdout.write(
|
|
26777
|
-
`${JSON.stringify(
|
|
26778
|
-
{
|
|
26779
|
-
ok: true,
|
|
26780
|
-
advisory: true,
|
|
26781
|
-
slug,
|
|
26782
|
-
overall: report.overall,
|
|
26783
|
-
counts: report.counts,
|
|
26784
|
-
dimensions: report.dimensions,
|
|
26785
|
-
findings: shown.map(present),
|
|
26786
|
-
hint: report.counts.block > 0 ? "Block-tier tells are held at publish \u2014 fix them. Warn/advisory are guidance. Scores are 0\u20131 (higher is better)." : "No block-tier tells. Warn/advisory findings are guidance, not gates. Re-run after edits so the publish gate stays fresh."
|
|
26787
|
-
},
|
|
26788
|
-
null,
|
|
26789
|
-
2
|
|
26790
|
-
)}
|
|
26791
|
-
`
|
|
26792
|
-
);
|
|
26793
|
-
}
|
|
26794
|
-
});
|
|
26795
|
-
function present(f) {
|
|
26796
|
-
return {
|
|
26797
|
-
id: f.id,
|
|
26798
|
-
severity: f.severity,
|
|
26799
|
-
family: f.family,
|
|
26800
|
-
where: f.line ? `${f.file}:${f.line}` : f.file,
|
|
26801
|
-
snippet: f.snippet,
|
|
26802
|
-
note: f.note,
|
|
26803
|
-
...f.brandDrift ? { brandDrift: true } : {}
|
|
26804
|
-
};
|
|
26805
|
-
}
|
|
26806
|
-
async function isDir(p) {
|
|
26807
|
-
try {
|
|
26808
|
-
return (await stat6(p)).isDirectory();
|
|
26809
|
-
} catch {
|
|
26810
|
-
return false;
|
|
26811
|
-
}
|
|
26812
|
-
}
|
|
26813
|
-
|
|
26814
|
-
// src/commands/landing/index.ts
|
|
26815
|
-
var landingCommand = defineCommand134({
|
|
26816
|
-
meta: {
|
|
26817
|
-
name: "landing",
|
|
26818
|
-
description: `Design-quality tools for landing pages (src/pages/<slug>/).
|
|
26819
|
-
|
|
26820
|
-
Subcommands:
|
|
26821
|
-
baker landing critique <slug> \u2014 deterministic design-quality critic (advisory): flags the known AI 'slop' tells (gradient text, overused fonts, side-tab borders, cream palettes, buzzword copy, broken images) tiered block/warn/advisory, respecting the client's BRAND.md. Records the critique the publish quality gate requires \u2014 run it before finishing a landing.`
|
|
26822
|
-
},
|
|
26823
|
-
subCommands: {
|
|
26824
|
-
critique: critiqueCommand2
|
|
26825
|
-
}
|
|
26826
|
-
});
|
|
26827
|
-
|
|
26828
25830
|
// src/commands/mcp/index.ts
|
|
26829
|
-
import { defineCommand as
|
|
25831
|
+
import { defineCommand as defineCommand133 } from "citty";
|
|
26830
25832
|
var SCOPES = ["user", "user_org", "company", "org"];
|
|
26831
25833
|
function parseScope(raw) {
|
|
26832
25834
|
const scope = raw === void 0 ? "company" : String(raw);
|
|
@@ -26865,7 +25867,7 @@ registerSchema({
|
|
|
26865
25867
|
description: "List the custom MCP servers this company's chats see (org + company + your own user scope).",
|
|
26866
25868
|
args: {}
|
|
26867
25869
|
});
|
|
26868
|
-
var listCommand8 =
|
|
25870
|
+
var listCommand8 = defineCommand133({
|
|
26869
25871
|
meta: { name: "list", description: "List custom MCP servers visible to this company's chats." },
|
|
26870
25872
|
run: async () => {
|
|
26871
25873
|
try {
|
|
@@ -26890,7 +25892,7 @@ registerSchema({
|
|
|
26890
25892
|
header: { type: "string", description: 'Auth header "Key: Value" (repeatable)', required: false }
|
|
26891
25893
|
}
|
|
26892
25894
|
});
|
|
26893
|
-
var addCommand =
|
|
25895
|
+
var addCommand = defineCommand133({
|
|
26894
25896
|
meta: {
|
|
26895
25897
|
name: "add",
|
|
26896
25898
|
description: `Register a custom MCP server. Tools appear as mcp__<name>__* on the NEXT message.
|
|
@@ -26931,7 +25933,7 @@ registerSchema({
|
|
|
26931
25933
|
description: "Remove a company custom MCP server by name.",
|
|
26932
25934
|
args: { name: { type: "string", description: "Server name to remove", required: true } }
|
|
26933
25935
|
});
|
|
26934
|
-
var removeCommand4 =
|
|
25936
|
+
var removeCommand4 = defineCommand133({
|
|
26935
25937
|
meta: {
|
|
26936
25938
|
name: "remove",
|
|
26937
25939
|
description: `Remove a company custom MCP server by name.
|
|
@@ -26953,7 +25955,7 @@ Example:
|
|
|
26953
25955
|
}
|
|
26954
25956
|
}
|
|
26955
25957
|
});
|
|
26956
|
-
var mcpCommand =
|
|
25958
|
+
var mcpCommand = defineCommand133({
|
|
26957
25959
|
meta: {
|
|
26958
25960
|
name: "mcp",
|
|
26959
25961
|
description: `Custom MCP servers for this company \u2014 point the agent at any HTTPS MCP endpoint.
|
|
@@ -26977,10 +25979,10 @@ Examples:
|
|
|
26977
25979
|
});
|
|
26978
25980
|
|
|
26979
25981
|
// src/commands/research/index.ts
|
|
26980
|
-
import { defineCommand as
|
|
25982
|
+
import { defineCommand as defineCommand144 } from "citty";
|
|
26981
25983
|
|
|
26982
25984
|
// src/commands/research/advertisers.ts
|
|
26983
|
-
import { defineCommand as
|
|
25985
|
+
import { defineCommand as defineCommand134 } from "citty";
|
|
26984
25986
|
|
|
26985
25987
|
// src/commands/research/output.ts
|
|
26986
25988
|
var RESEARCH_DATA_NOTE = "Estimates based on third-party SERP data \u2014 not exact figures. Use for directional insights, not precise measurement.";
|
|
@@ -27093,7 +26095,7 @@ var FIELDS3 = {
|
|
|
27093
26095
|
etv: "Estimated traffic value (USD)",
|
|
27094
26096
|
visibility: "SERP visibility score (0-1)"
|
|
27095
26097
|
};
|
|
27096
|
-
var advertisersCommand =
|
|
26098
|
+
var advertisersCommand = defineCommand134({
|
|
27097
26099
|
meta: {
|
|
27098
26100
|
name: "advertisers",
|
|
27099
26101
|
description: `Find domains competing for a keyword in Google SERPs.
|
|
@@ -27140,7 +26142,7 @@ Examples:
|
|
|
27140
26142
|
});
|
|
27141
26143
|
|
|
27142
26144
|
// src/commands/research/autocomplete.ts
|
|
27143
|
-
import { defineCommand as
|
|
26145
|
+
import { defineCommand as defineCommand135 } from "citty";
|
|
27144
26146
|
registerSchema({
|
|
27145
26147
|
command: "research.autocomplete",
|
|
27146
26148
|
description: "Get Google Autocomplete suggestions for a seed keyword. Useful for keyword expansion and discovering what people actually search for. IMPORTANT: If --location and --language are omitted, defaults to United States (us) and English (en).",
|
|
@@ -27163,7 +26165,7 @@ registerSchema({
|
|
|
27163
26165
|
var FIELDS4 = {
|
|
27164
26166
|
suggestion: "Autocomplete suggestion from Google"
|
|
27165
26167
|
};
|
|
27166
|
-
var autocompleteCommand =
|
|
26168
|
+
var autocompleteCommand = defineCommand135({
|
|
27167
26169
|
meta: {
|
|
27168
26170
|
name: "autocomplete",
|
|
27169
26171
|
description: `Get Google Autocomplete suggestions for keyword expansion.
|
|
@@ -27209,7 +26211,7 @@ Examples:
|
|
|
27209
26211
|
});
|
|
27210
26212
|
|
|
27211
26213
|
// src/commands/research/countries.ts
|
|
27212
|
-
import { defineCommand as
|
|
26214
|
+
import { defineCommand as defineCommand136 } from "citty";
|
|
27213
26215
|
registerSchema({
|
|
27214
26216
|
command: "research.countries",
|
|
27215
26217
|
description: "List all supported country codes for --location flag in research commands.",
|
|
@@ -27266,7 +26268,7 @@ var FIELDS5 = {
|
|
|
27266
26268
|
code: "Country code to pass as --location",
|
|
27267
26269
|
name: "Country name"
|
|
27268
26270
|
};
|
|
27269
|
-
var countriesCommand =
|
|
26271
|
+
var countriesCommand = defineCommand136({
|
|
27270
26272
|
meta: {
|
|
27271
26273
|
name: "countries",
|
|
27272
26274
|
description: "List all supported country codes for --location flag."
|
|
@@ -27277,7 +26279,7 @@ var countriesCommand = defineCommand138({
|
|
|
27277
26279
|
});
|
|
27278
26280
|
|
|
27279
26281
|
// src/commands/research/intent.ts
|
|
27280
|
-
import { defineCommand as
|
|
26282
|
+
import { defineCommand as defineCommand137 } from "citty";
|
|
27281
26283
|
registerSchema({
|
|
27282
26284
|
command: "research.intent",
|
|
27283
26285
|
description: "Classify Google Search intent for keywords. Determines if someone searching is looking to buy, research, or navigate. IMPORTANT: If --language is omitted, defaults to English (en). The response includes a query_context object showing which language was used.",
|
|
@@ -27300,7 +26302,7 @@ var FIELDS6 = {
|
|
|
27300
26302
|
intent: "Primary Google Search intent: informational, navigational, commercial, transactional",
|
|
27301
26303
|
probability: "Confidence score 0.0-1.0"
|
|
27302
26304
|
};
|
|
27303
|
-
var intentCommand =
|
|
26305
|
+
var intentCommand = defineCommand137({
|
|
27304
26306
|
meta: {
|
|
27305
26307
|
name: "intent",
|
|
27306
26308
|
description: `Classify Google Search intent for keywords. Returns intent type and confidence.
|
|
@@ -27348,7 +26350,7 @@ Examples:
|
|
|
27348
26350
|
});
|
|
27349
26351
|
|
|
27350
26352
|
// src/commands/research/keyword-gap.ts
|
|
27351
|
-
import { defineCommand as
|
|
26353
|
+
import { defineCommand as defineCommand138 } from "citty";
|
|
27352
26354
|
registerSchema({
|
|
27353
26355
|
command: "research.keyword-gap",
|
|
27354
26356
|
description: "Find keywords a competitor ranks for (organic or paid) that you don't. Discovers expansion opportunities. IMPORTANT: If --location and --language are omitted, defaults to United States (us) and English (en). The response includes a query_context object showing which location/language were used.",
|
|
@@ -27377,7 +26379,7 @@ var FIELDS7 = {
|
|
|
27377
26379
|
cpc: "Cost per click USD",
|
|
27378
26380
|
their_position: "Competitor's ranking position"
|
|
27379
26381
|
};
|
|
27380
|
-
var keywordGapCommand =
|
|
26382
|
+
var keywordGapCommand = defineCommand138({
|
|
27381
26383
|
meta: {
|
|
27382
26384
|
name: "keyword-gap",
|
|
27383
26385
|
description: `Find keywords a competitor has that you don't. Supports pagination via --offset.
|
|
@@ -27451,7 +26453,7 @@ Examples:
|
|
|
27451
26453
|
});
|
|
27452
26454
|
|
|
27453
26455
|
// src/commands/research/keywords-for-site.ts
|
|
27454
|
-
import { defineCommand as
|
|
26456
|
+
import { defineCommand as defineCommand139 } from "citty";
|
|
27455
26457
|
registerSchema({
|
|
27456
26458
|
command: "research.keywords-for-site",
|
|
27457
26459
|
description: "Get keywords a competitor targets in Google. Use --type paid to see only paid keywords, --type organic for organic only. IMPORTANT: If --location and --language are omitted, defaults to United States (us) and English (en). The response includes a query_context object showing which location/language were used.",
|
|
@@ -27484,7 +26486,7 @@ var FIELDS8 = {
|
|
|
27484
26486
|
competition: "LOW, MEDIUM, or HIGH",
|
|
27485
26487
|
competition_index: "Competition score 0-100"
|
|
27486
26488
|
};
|
|
27487
|
-
var keywordsForSiteCommand =
|
|
26489
|
+
var keywordsForSiteCommand = defineCommand139({
|
|
27488
26490
|
meta: {
|
|
27489
26491
|
name: "keywords-for-site",
|
|
27490
26492
|
description: `Get keywords a competitor targets in Google. Use --type to filter paid/organic.
|
|
@@ -27537,7 +26539,7 @@ Examples:
|
|
|
27537
26539
|
});
|
|
27538
26540
|
|
|
27539
26541
|
// src/commands/research/languages.ts
|
|
27540
|
-
import { defineCommand as
|
|
26542
|
+
import { defineCommand as defineCommand140 } from "citty";
|
|
27541
26543
|
registerSchema({
|
|
27542
26544
|
command: "research.languages",
|
|
27543
26545
|
description: "List all supported language codes for --language flag in research commands.",
|
|
@@ -27567,7 +26569,7 @@ var FIELDS9 = {
|
|
|
27567
26569
|
code: "Language code to pass as --language",
|
|
27568
26570
|
name: "Language name (also accepted by --language)"
|
|
27569
26571
|
};
|
|
27570
|
-
var languagesCommand2 =
|
|
26572
|
+
var languagesCommand2 = defineCommand140({
|
|
27571
26573
|
meta: {
|
|
27572
26574
|
name: "languages",
|
|
27573
26575
|
description: "List all supported language codes for --language flag."
|
|
@@ -27578,7 +26580,7 @@ var languagesCommand2 = defineCommand142({
|
|
|
27578
26580
|
});
|
|
27579
26581
|
|
|
27580
26582
|
// src/commands/research/lighthouse.ts
|
|
27581
|
-
import { defineCommand as
|
|
26583
|
+
import { defineCommand as defineCommand141 } from "citty";
|
|
27582
26584
|
registerSchema({
|
|
27583
26585
|
command: "research.lighthouse",
|
|
27584
26586
|
description: "Landing page performance audit. Returns metrics that affect Google Ads Quality Score and CPC.",
|
|
@@ -27597,7 +26599,7 @@ var FIELDS10 = {
|
|
|
27597
26599
|
speed_index_ms: "Speed Index in ms (good: < 3400)",
|
|
27598
26600
|
interactive_ms: "Time to Interactive in ms (good: < 3800)"
|
|
27599
26601
|
};
|
|
27600
|
-
var lighthouseCommand =
|
|
26602
|
+
var lighthouseCommand = defineCommand141({
|
|
27601
26603
|
meta: {
|
|
27602
26604
|
name: "lighthouse",
|
|
27603
26605
|
description: `Landing page performance audit. Metrics affecting Google Ads Quality Score.
|
|
@@ -27635,7 +26637,7 @@ Examples:
|
|
|
27635
26637
|
});
|
|
27636
26638
|
|
|
27637
26639
|
// src/commands/research/relevant-pages.ts
|
|
27638
|
-
import { defineCommand as
|
|
26640
|
+
import { defineCommand as defineCommand142 } from "citty";
|
|
27639
26641
|
registerSchema({
|
|
27640
26642
|
command: "research.relevant-pages",
|
|
27641
26643
|
description: "Get the top pages of a competitor domain with organic traffic and ranking data. Shows which pages drive the most traffic. IMPORTANT: If --location and --language are omitted, defaults to United States (us) and English (en).",
|
|
@@ -27661,7 +26663,7 @@ var FIELDS11 = {
|
|
|
27661
26663
|
keywords: "Total organic keywords the page ranks for",
|
|
27662
26664
|
top_10: "Keywords in positions 1-10"
|
|
27663
26665
|
};
|
|
27664
|
-
var relevantPagesCommand =
|
|
26666
|
+
var relevantPagesCommand = defineCommand142({
|
|
27665
26667
|
meta: {
|
|
27666
26668
|
name: "relevant-pages",
|
|
27667
26669
|
description: `Get the top pages of a competitor domain with traffic data.
|
|
@@ -27707,7 +26709,7 @@ Examples:
|
|
|
27707
26709
|
});
|
|
27708
26710
|
|
|
27709
26711
|
// src/commands/research/web.ts
|
|
27710
|
-
import { defineCommand as
|
|
26712
|
+
import { defineCommand as defineCommand143 } from "citty";
|
|
27711
26713
|
registerSchema({
|
|
27712
26714
|
command: "research.web",
|
|
27713
26715
|
description: "Search the web with AI to answer marketing questions \u2014 competitors, ICP, pricing, pain points, market trends. Three depth levels: medium (quick, default), high (thorough), xhigh (exhaustive deep research).",
|
|
@@ -27758,7 +26760,7 @@ async function runDeepResearch(question) {
|
|
|
27758
26760
|
}
|
|
27759
26761
|
throw new Error("Deep research timed out");
|
|
27760
26762
|
}
|
|
27761
|
-
var webCommand =
|
|
26763
|
+
var webCommand = defineCommand143({
|
|
27762
26764
|
meta: {
|
|
27763
26765
|
name: "web",
|
|
27764
26766
|
description: `Search the web with AI to answer any open-ended marketing question. Uses live internet data via Google Search.
|
|
@@ -27818,7 +26820,7 @@ Examples:
|
|
|
27818
26820
|
});
|
|
27819
26821
|
|
|
27820
26822
|
// src/commands/research/index.ts
|
|
27821
|
-
var researchCommand =
|
|
26823
|
+
var researchCommand = defineCommand144({
|
|
27822
26824
|
meta: {
|
|
27823
26825
|
name: "research",
|
|
27824
26826
|
description: `Competitive intelligence and AI-powered research commands.
|
|
@@ -27858,10 +26860,10 @@ Examples:
|
|
|
27858
26860
|
});
|
|
27859
26861
|
|
|
27860
26862
|
// src/commands/scheduled-actions/index.ts
|
|
27861
|
-
import { defineCommand as
|
|
26863
|
+
import { defineCommand as defineCommand151 } from "citty";
|
|
27862
26864
|
|
|
27863
26865
|
// src/commands/scheduled-actions/create.ts
|
|
27864
|
-
import { defineCommand as
|
|
26866
|
+
import { defineCommand as defineCommand145 } from "citty";
|
|
27865
26867
|
|
|
27866
26868
|
// src/commands/scheduled-actions/shared.ts
|
|
27867
26869
|
var TEMP_SCHEDULED_ACTION_PREFIX = "temp_sched_";
|
|
@@ -27976,7 +26978,7 @@ registerSchema({
|
|
|
27976
26978
|
prompt: { type: "string", description: "Additional prompt instructions for the spawned agent", required: false }
|
|
27977
26979
|
}
|
|
27978
26980
|
});
|
|
27979
|
-
var createCommand2 =
|
|
26981
|
+
var createCommand2 = defineCommand145({
|
|
27980
26982
|
meta: {
|
|
27981
26983
|
name: "create",
|
|
27982
26984
|
description: 'Stage a scheduled action. Example: baker scheduled-actions create --name "Weekly report" --description "..." --cron "0 9 * * MON"'
|
|
@@ -28025,7 +27027,7 @@ var createCommand2 = defineCommand147({
|
|
|
28025
27027
|
});
|
|
28026
27028
|
|
|
28027
27029
|
// src/commands/scheduled-actions/delete.ts
|
|
28028
|
-
import { defineCommand as
|
|
27030
|
+
import { defineCommand as defineCommand146 } from "citty";
|
|
28029
27031
|
registerSchema({
|
|
28030
27032
|
command: "scheduled-actions.delete",
|
|
28031
27033
|
description: "Stage deletion of a published scheduled action or cancellation of a temp_sched_* draft creation.",
|
|
@@ -28033,7 +27035,7 @@ registerSchema({
|
|
|
28033
27035
|
id: { type: "string", description: "Published scheduled action ID or temp_sched_* draft ID", required: true }
|
|
28034
27036
|
}
|
|
28035
27037
|
});
|
|
28036
|
-
var deleteCommand2 =
|
|
27038
|
+
var deleteCommand2 = defineCommand146({
|
|
28037
27039
|
meta: {
|
|
28038
27040
|
name: "delete",
|
|
28039
27041
|
description: "Stage scheduled action deletion. Example: baker scheduled-actions delete <id-or-temp_sched_id>"
|
|
@@ -28062,7 +27064,7 @@ var deleteCommand2 = defineCommand148({
|
|
|
28062
27064
|
});
|
|
28063
27065
|
|
|
28064
27066
|
// src/commands/scheduled-actions/get.ts
|
|
28065
|
-
import { defineCommand as
|
|
27067
|
+
import { defineCommand as defineCommand147 } from "citty";
|
|
28066
27068
|
registerSchema({
|
|
28067
27069
|
command: "scheduled-actions.get",
|
|
28068
27070
|
description: "Get a published scheduled action or a temp_sched_* draft-created scheduled action.",
|
|
@@ -28070,7 +27072,7 @@ registerSchema({
|
|
|
28070
27072
|
id: { type: "string", description: "Published scheduled action ID or temp_sched_* draft ID", required: true }
|
|
28071
27073
|
}
|
|
28072
27074
|
});
|
|
28073
|
-
var getCommand3 =
|
|
27075
|
+
var getCommand3 = defineCommand147({
|
|
28074
27076
|
meta: {
|
|
28075
27077
|
name: "get",
|
|
28076
27078
|
description: "Get a scheduled action. Example: baker scheduled-actions get <id-or-temp_sched_id>"
|
|
@@ -28107,13 +27109,13 @@ var getCommand3 = defineCommand149({
|
|
|
28107
27109
|
});
|
|
28108
27110
|
|
|
28109
27111
|
// src/commands/scheduled-actions/list.ts
|
|
28110
|
-
import { defineCommand as
|
|
27112
|
+
import { defineCommand as defineCommand148 } from "citty";
|
|
28111
27113
|
registerSchema({
|
|
28112
27114
|
command: "scheduled-actions.list",
|
|
28113
27115
|
description: "List published scheduled actions. Includes draft state when BAKER_CHAT_ID is set.",
|
|
28114
27116
|
args: {}
|
|
28115
27117
|
});
|
|
28116
|
-
var listCommand9 =
|
|
27118
|
+
var listCommand9 = defineCommand148({
|
|
28117
27119
|
meta: {
|
|
28118
27120
|
name: "list",
|
|
28119
27121
|
description: "List scheduled actions. Includes staged draft ops when BAKER_CHAT_ID is set."
|
|
@@ -28134,7 +27136,7 @@ var listCommand9 = defineCommand150({
|
|
|
28134
27136
|
});
|
|
28135
27137
|
|
|
28136
27138
|
// src/commands/scheduled-actions/trigger.ts
|
|
28137
|
-
import { defineCommand as
|
|
27139
|
+
import { defineCommand as defineCommand149 } from "citty";
|
|
28138
27140
|
registerSchema({
|
|
28139
27141
|
command: "scheduled-actions.trigger",
|
|
28140
27142
|
description: "Immediately trigger a published scheduled action. Does not require BAKER_CHAT_ID and rejects temp_sched_* IDs.",
|
|
@@ -28142,7 +27144,7 @@ registerSchema({
|
|
|
28142
27144
|
id: { type: "string", description: "Published scheduled action ID", required: true }
|
|
28143
27145
|
}
|
|
28144
27146
|
});
|
|
28145
|
-
var triggerCommand =
|
|
27147
|
+
var triggerCommand = defineCommand149({
|
|
28146
27148
|
meta: {
|
|
28147
27149
|
name: "trigger",
|
|
28148
27150
|
description: "Immediately trigger a published scheduled action. Example: baker scheduled-actions trigger <id>"
|
|
@@ -28179,7 +27181,7 @@ var triggerCommand = defineCommand151({
|
|
|
28179
27181
|
});
|
|
28180
27182
|
|
|
28181
27183
|
// src/commands/scheduled-actions/update.ts
|
|
28182
|
-
import { defineCommand as
|
|
27184
|
+
import { defineCommand as defineCommand150 } from "citty";
|
|
28183
27185
|
registerSchema({
|
|
28184
27186
|
command: "scheduled-actions.update",
|
|
28185
27187
|
description: "Stage an update to a published scheduled action or temp_sched_* draft-created scheduled action.",
|
|
@@ -28204,7 +27206,7 @@ registerSchema({
|
|
|
28204
27206
|
prompt: { type: "string", description: "Replacement additional spawned-agent instructions", required: false }
|
|
28205
27207
|
}
|
|
28206
27208
|
});
|
|
28207
|
-
var updateCommand2 =
|
|
27209
|
+
var updateCommand2 = defineCommand150({
|
|
28208
27210
|
meta: {
|
|
28209
27211
|
name: "update",
|
|
28210
27212
|
description: "Stage a scheduled action update. Example: baker scheduled-actions update <id> --enabled false"
|
|
@@ -28275,7 +27277,7 @@ var updateCommand2 = defineCommand152({
|
|
|
28275
27277
|
});
|
|
28276
27278
|
|
|
28277
27279
|
// src/commands/scheduled-actions/index.ts
|
|
28278
|
-
var scheduledActionsCommand =
|
|
27280
|
+
var scheduledActionsCommand = defineCommand151({
|
|
28279
27281
|
meta: {
|
|
28280
27282
|
name: "scheduled-actions",
|
|
28281
27283
|
description: `Manage Scheduled Actions. Subcommands: list, get, create, update, delete, trigger.
|
|
@@ -28301,8 +27303,8 @@ Examples:
|
|
|
28301
27303
|
});
|
|
28302
27304
|
|
|
28303
27305
|
// src/commands/schema.ts
|
|
28304
|
-
import { defineCommand as
|
|
28305
|
-
var schemaCommand =
|
|
27306
|
+
import { defineCommand as defineCommand152 } from "citty";
|
|
27307
|
+
var schemaCommand = defineCommand152({
|
|
28306
27308
|
meta: {
|
|
28307
27309
|
name: "schema",
|
|
28308
27310
|
description: "Inspect command argument schemas (for AI agent introspection). Lists all commands if no argument given. Example: baker schema images.search"
|
|
@@ -28338,7 +27340,7 @@ var schemaCommand = defineCommand154({
|
|
|
28338
27340
|
});
|
|
28339
27341
|
|
|
28340
27342
|
// src/commands/tags/index.ts
|
|
28341
|
-
import { defineCommand as
|
|
27343
|
+
import { defineCommand as defineCommand153 } from "citty";
|
|
28342
27344
|
|
|
28343
27345
|
// src/commands/tags/shared.ts
|
|
28344
27346
|
function failApi3(err) {
|
|
@@ -28404,7 +27406,7 @@ async function listTags(json) {
|
|
|
28404
27406
|
failApi3(err);
|
|
28405
27407
|
}
|
|
28406
27408
|
}
|
|
28407
|
-
var listCommand10 =
|
|
27409
|
+
var listCommand10 = defineCommand153({
|
|
28408
27410
|
meta: {
|
|
28409
27411
|
name: "list",
|
|
28410
27412
|
description: "Effective tags for this chat (production + staged), with each tag's full readable config (secrets excluded) \u2014 reuse a stored value to pre-fill a change rather than asking the user. Refs printed here are what flow side-effect tagIds should use. Example: baker tags list"
|
|
@@ -28423,7 +27425,7 @@ async function listDraft3() {
|
|
|
28423
27425
|
failApi3(err);
|
|
28424
27426
|
}
|
|
28425
27427
|
}
|
|
28426
|
-
var draftCommand3 =
|
|
27428
|
+
var draftCommand3 = defineCommand153({
|
|
28427
27429
|
meta: {
|
|
28428
27430
|
name: "draft",
|
|
28429
27431
|
description: "Review the tag changes staged in this chat (read-only). Staged changes were approved via request_tag_input and apply when the chat is published; to amend or drop one, propose a follow-up change through the same tool (a delete on a tag_temp_* ref drops the staged create)."
|
|
@@ -28432,7 +27434,7 @@ var draftCommand3 = defineCommand155({
|
|
|
28432
27434
|
await listDraft3();
|
|
28433
27435
|
}
|
|
28434
27436
|
});
|
|
28435
|
-
var tagsCommand3 =
|
|
27437
|
+
var tagsCommand3 = defineCommand153({
|
|
28436
27438
|
meta: {
|
|
28437
27439
|
name: "tags",
|
|
28438
27440
|
description: `Read the client's marketing/analytics tags (Meta pixel, GA4, Google Ads, GTM, Clarity, Hotjar, \u2026) \u2014 production tags plus the changes staged in this chat.
|
|
@@ -28457,10 +27459,10 @@ Examples:
|
|
|
28457
27459
|
});
|
|
28458
27460
|
|
|
28459
27461
|
// src/commands/testimonials/index.ts
|
|
28460
|
-
import { defineCommand as
|
|
27462
|
+
import { defineCommand as defineCommand157 } from "citty";
|
|
28461
27463
|
|
|
28462
27464
|
// src/commands/testimonials/get.ts
|
|
28463
|
-
import { defineCommand as
|
|
27465
|
+
import { defineCommand as defineCommand154 } from "citty";
|
|
28464
27466
|
registerSchema({
|
|
28465
27467
|
command: "testimonials.get",
|
|
28466
27468
|
description: "Get a single testimonial by ID",
|
|
@@ -28468,7 +27470,7 @@ registerSchema({
|
|
|
28468
27470
|
id: { type: "string", description: "Testimonial ID", required: true }
|
|
28469
27471
|
}
|
|
28470
27472
|
});
|
|
28471
|
-
var getCommand4 =
|
|
27473
|
+
var getCommand4 = defineCommand154({
|
|
28472
27474
|
meta: { name: "get", description: "Get a single testimonial by ID. Example: baker testimonials get j571abc123" },
|
|
28473
27475
|
args: {
|
|
28474
27476
|
id: { type: "positional", description: "Testimonial ID", required: false },
|
|
@@ -28505,7 +27507,7 @@ var getCommand4 = defineCommand156({
|
|
|
28505
27507
|
});
|
|
28506
27508
|
|
|
28507
27509
|
// src/commands/testimonials/list.ts
|
|
28508
|
-
import { defineCommand as
|
|
27510
|
+
import { defineCommand as defineCommand155 } from "citty";
|
|
28509
27511
|
registerSchema({
|
|
28510
27512
|
command: "testimonials.list",
|
|
28511
27513
|
description: "List testimonials with optional filters.",
|
|
@@ -28535,7 +27537,7 @@ registerSchema({
|
|
|
28535
27537
|
limit: { type: "number", description: "Max results (default 50)", required: false, default: 50 }
|
|
28536
27538
|
}
|
|
28537
27539
|
});
|
|
28538
|
-
var listCommand11 =
|
|
27540
|
+
var listCommand11 = defineCommand155({
|
|
28539
27541
|
meta: {
|
|
28540
27542
|
name: "list",
|
|
28541
27543
|
description: "List testimonials with optional filters. Example: baker testimonials list --source google --sentiment positive"
|
|
@@ -28584,7 +27586,7 @@ var listCommand11 = defineCommand157({
|
|
|
28584
27586
|
});
|
|
28585
27587
|
|
|
28586
27588
|
// src/commands/testimonials/search.ts
|
|
28587
|
-
import { defineCommand as
|
|
27589
|
+
import { defineCommand as defineCommand156 } from "citty";
|
|
28588
27590
|
function languageBiasHint(results, requestedLanguage) {
|
|
28589
27591
|
if (requestedLanguage) {
|
|
28590
27592
|
return null;
|
|
@@ -28662,7 +27664,7 @@ function buildSearchRequest(query, args) {
|
|
|
28662
27664
|
}
|
|
28663
27665
|
return body;
|
|
28664
27666
|
}
|
|
28665
|
-
var searchCommand2 =
|
|
27667
|
+
var searchCommand2 = defineCommand156({
|
|
28666
27668
|
meta: {
|
|
28667
27669
|
name: "search",
|
|
28668
27670
|
description: "Semantic search testimonials by text query. Uses hybrid BM25 + vector + reranking. Example: baker testimonials search 'great service' --rating-min 4"
|
|
@@ -28718,7 +27720,7 @@ var searchCommand2 = defineCommand158({
|
|
|
28718
27720
|
var tagsCommand4 = makeTagsCommand("testimonials", "testimonial", "/api/testimonials/tags");
|
|
28719
27721
|
|
|
28720
27722
|
// src/commands/testimonials/index.ts
|
|
28721
|
-
var testimonialsCommand =
|
|
27723
|
+
var testimonialsCommand = defineCommand157({
|
|
28722
27724
|
meta: {
|
|
28723
27725
|
name: "testimonials",
|
|
28724
27726
|
description: `Find and browse testimonials in Baker. Subcommands: search, get, list, tags.
|
|
@@ -28739,10 +27741,10 @@ Examples:
|
|
|
28739
27741
|
});
|
|
28740
27742
|
|
|
28741
27743
|
// src/commands/videos/index.ts
|
|
28742
|
-
import { defineCommand as
|
|
27744
|
+
import { defineCommand as defineCommand162 } from "citty";
|
|
28743
27745
|
|
|
28744
27746
|
// src/commands/videos/delete.ts
|
|
28745
|
-
import { defineCommand as
|
|
27747
|
+
import { defineCommand as defineCommand158 } from "citty";
|
|
28746
27748
|
registerSchema({
|
|
28747
27749
|
command: "videos.delete",
|
|
28748
27750
|
description: "Delete a video by ID",
|
|
@@ -28756,7 +27758,7 @@ registerSchema({
|
|
|
28756
27758
|
}
|
|
28757
27759
|
}
|
|
28758
27760
|
});
|
|
28759
|
-
var deleteCommand3 =
|
|
27761
|
+
var deleteCommand3 = defineCommand158({
|
|
28760
27762
|
meta: {
|
|
28761
27763
|
name: "delete",
|
|
28762
27764
|
description: "Delete a video by ID. Use --dry-run to preview. Example: baker videos delete j571abc123 --dry-run"
|
|
@@ -28797,7 +27799,7 @@ var deleteCommand3 = defineCommand160({
|
|
|
28797
27799
|
});
|
|
28798
27800
|
|
|
28799
27801
|
// src/commands/videos/get.ts
|
|
28800
|
-
import { defineCommand as
|
|
27802
|
+
import { defineCommand as defineCommand159 } from "citty";
|
|
28801
27803
|
registerSchema({
|
|
28802
27804
|
command: "videos.get",
|
|
28803
27805
|
description: "Get a single video by ID",
|
|
@@ -28805,7 +27807,7 @@ registerSchema({
|
|
|
28805
27807
|
id: { type: "string", description: "Video ID", required: true }
|
|
28806
27808
|
}
|
|
28807
27809
|
});
|
|
28808
|
-
var getCommand5 =
|
|
27810
|
+
var getCommand5 = defineCommand159({
|
|
28809
27811
|
meta: { name: "get", description: "Get a single video by ID. Example: baker videos get j571abc123" },
|
|
28810
27812
|
args: {
|
|
28811
27813
|
id: { type: "positional", description: "Video ID", required: false },
|
|
@@ -28842,7 +27844,7 @@ var getCommand5 = defineCommand161({
|
|
|
28842
27844
|
});
|
|
28843
27845
|
|
|
28844
27846
|
// src/commands/videos/search.ts
|
|
28845
|
-
import { defineCommand as
|
|
27847
|
+
import { defineCommand as defineCommand160 } from "citty";
|
|
28846
27848
|
registerSchema({
|
|
28847
27849
|
command: "videos.search",
|
|
28848
27850
|
description: "Search videos by text query. Only returns ready videos.",
|
|
@@ -28852,7 +27854,7 @@ registerSchema({
|
|
|
28852
27854
|
tags: { type: "string", description: "Comma-separated tags to filter by", required: false }
|
|
28853
27855
|
}
|
|
28854
27856
|
});
|
|
28855
|
-
var searchCommand3 =
|
|
27857
|
+
var searchCommand3 = defineCommand160({
|
|
28856
27858
|
meta: {
|
|
28857
27859
|
name: "search",
|
|
28858
27860
|
description: "Semantic search videos by text query. Uses hybrid BM25 + vector + reranking. Example: baker videos search 'product demo' --tags tutorial"
|
|
@@ -28902,9 +27904,9 @@ var searchCommand3 = defineCommand162({
|
|
|
28902
27904
|
var tagsCommand5 = makeTagsCommand("videos", "video", "/api/videos/tags");
|
|
28903
27905
|
|
|
28904
27906
|
// src/commands/videos/upload.ts
|
|
28905
|
-
import { readFile as
|
|
27907
|
+
import { readFile as readFile20, stat as stat5 } from "fs/promises";
|
|
28906
27908
|
import { extname as extname3 } from "path";
|
|
28907
|
-
import { defineCommand as
|
|
27909
|
+
import { defineCommand as defineCommand161 } from "citty";
|
|
28908
27910
|
var MIME_MAP = {
|
|
28909
27911
|
".mp4": "video/mp4",
|
|
28910
27912
|
".mov": "video/quicktime",
|
|
@@ -28938,7 +27940,7 @@ function detectContentType(filePath) {
|
|
|
28938
27940
|
}
|
|
28939
27941
|
return mime;
|
|
28940
27942
|
}
|
|
28941
|
-
var uploadCommand2 =
|
|
27943
|
+
var uploadCommand2 = defineCommand161({
|
|
28942
27944
|
meta: {
|
|
28943
27945
|
name: "upload",
|
|
28944
27946
|
description: "Upload a video file to Baker via Mux direct upload. Auto-detects content type. Example: baker videos upload ./demo.mp4"
|
|
@@ -28957,7 +27959,7 @@ var uploadCommand2 = defineCommand163({
|
|
|
28957
27959
|
}
|
|
28958
27960
|
const contentType = args["content-type"] || detectContentType(filePath);
|
|
28959
27961
|
if (args["dry-run"]) {
|
|
28960
|
-
const fileStats = await
|
|
27962
|
+
const fileStats = await stat5(filePath);
|
|
28961
27963
|
writeJson({
|
|
28962
27964
|
ok: true,
|
|
28963
27965
|
dryRun: true,
|
|
@@ -28967,7 +27969,7 @@ var uploadCommand2 = defineCommand163({
|
|
|
28967
27969
|
return;
|
|
28968
27970
|
}
|
|
28969
27971
|
const { uploadUrl, videoId } = await apiPost("/api/videos/upload", {});
|
|
28970
|
-
const fileBuffer = await
|
|
27972
|
+
const fileBuffer = await readFile20(filePath);
|
|
28971
27973
|
const uploadResponse = await fetch(uploadUrl, {
|
|
28972
27974
|
method: "PUT",
|
|
28973
27975
|
headers: { "Content-Type": contentType },
|
|
@@ -28992,7 +27994,7 @@ var uploadCommand2 = defineCommand163({
|
|
|
28992
27994
|
});
|
|
28993
27995
|
|
|
28994
27996
|
// src/commands/videos/index.ts
|
|
28995
|
-
var videosCommand =
|
|
27997
|
+
var videosCommand = defineCommand162({
|
|
28996
27998
|
meta: {
|
|
28997
27999
|
name: "videos",
|
|
28998
28000
|
description: `Find and manage videos in Baker. Subcommands: search, get, upload, delete, tags.
|
|
@@ -29015,10 +28017,10 @@ Examples:
|
|
|
29015
28017
|
});
|
|
29016
28018
|
|
|
29017
28019
|
// src/commands/winning-ads/index.ts
|
|
29018
|
-
import { defineCommand as
|
|
28020
|
+
import { defineCommand as defineCommand173 } from "citty";
|
|
29019
28021
|
|
|
29020
28022
|
// src/commands/winning-ads/advertisers.ts
|
|
29021
|
-
import { defineCommand as
|
|
28023
|
+
import { defineCommand as defineCommand163 } from "citty";
|
|
29022
28024
|
|
|
29023
28025
|
// src/commands/winning-ads/shared.ts
|
|
29024
28026
|
function splitList(value) {
|
|
@@ -29071,7 +28073,7 @@ function advertiserNormalizer(record, full) {
|
|
|
29071
28073
|
last_synced_at: record.last_synced_at ?? null
|
|
29072
28074
|
};
|
|
29073
28075
|
}
|
|
29074
|
-
var advertisersCommand2 =
|
|
28076
|
+
var advertisersCommand2 = defineCommand163({
|
|
29075
28077
|
meta: {
|
|
29076
28078
|
name: "advertisers",
|
|
29077
28079
|
description: 'List corpus advertisers by name or domain. Find your own advertiser for --exclude-advertiser, or a competitor for --advertiser-id / winners. Example: baker winning-ads advertisers "Deel" --output md'
|
|
@@ -29129,7 +28131,7 @@ var advertisersCommand2 = defineCommand165({
|
|
|
29129
28131
|
});
|
|
29130
28132
|
|
|
29131
28133
|
// src/commands/winning-ads/brief.ts
|
|
29132
|
-
import { defineCommand as
|
|
28134
|
+
import { defineCommand as defineCommand164 } from "citty";
|
|
29133
28135
|
registerSchema({
|
|
29134
28136
|
command: "winning-ads.brief",
|
|
29135
28137
|
description: "Generate a creative brief grounded in strategically-similar winning ads. Optionally describe the target creative with --dna (JSON) and steer with --notes.",
|
|
@@ -29175,7 +28177,7 @@ function parseDna(raw) {
|
|
|
29175
28177
|
}
|
|
29176
28178
|
return parsed;
|
|
29177
28179
|
}
|
|
29178
|
-
var briefCommand =
|
|
28180
|
+
var briefCommand = defineCommand164({
|
|
29179
28181
|
meta: {
|
|
29180
28182
|
name: "brief",
|
|
29181
28183
|
description: `Generate a creative brief from winning references. Example: baker winning-ads brief --dna '{"angle":"cost savings"}' --notes "B2B, LinkedIn video" --k 8`
|
|
@@ -29211,7 +28213,7 @@ var briefCommand = defineCommand166({
|
|
|
29211
28213
|
});
|
|
29212
28214
|
|
|
29213
28215
|
// src/commands/winning-ads/feed.ts
|
|
29214
|
-
import { defineCommand as
|
|
28216
|
+
import { defineCommand as defineCommand165 } from "citty";
|
|
29215
28217
|
function buildFeedParams(input) {
|
|
29216
28218
|
const params = {};
|
|
29217
28219
|
const advertiser = splitList(input.advertiser);
|
|
@@ -29263,7 +28265,7 @@ registerSchema({
|
|
|
29263
28265
|
format: { type: "string", description: "Comma-separated formats to include (e.g. static,video)", required: false }
|
|
29264
28266
|
}
|
|
29265
28267
|
});
|
|
29266
|
-
var feedCommand =
|
|
28268
|
+
var feedCommand = defineCommand165({
|
|
29267
28269
|
meta: {
|
|
29268
28270
|
name: "feed",
|
|
29269
28271
|
description: "Winners across every brand you follow (browse, then trim per advertiser). Example: baker winning-ads feed --per-advertiser 5 --output md"
|
|
@@ -29348,7 +28350,7 @@ var feedCommand = defineCommand167({
|
|
|
29348
28350
|
});
|
|
29349
28351
|
|
|
29350
28352
|
// src/commands/winning-ads/follow.ts
|
|
29351
|
-
import { defineCommand as
|
|
28353
|
+
import { defineCommand as defineCommand166 } from "citty";
|
|
29352
28354
|
var PLATFORMS = ["meta", "linkedin"];
|
|
29353
28355
|
registerSchema({
|
|
29354
28356
|
command: "winning-ads.follow",
|
|
@@ -29363,7 +28365,7 @@ registerSchema({
|
|
|
29363
28365
|
label: { type: "string", description: "Optional display label (defaults to the resolved name)", required: false }
|
|
29364
28366
|
}
|
|
29365
28367
|
});
|
|
29366
|
-
var followCommand =
|
|
28368
|
+
var followCommand = defineCommand166({
|
|
29367
28369
|
meta: {
|
|
29368
28370
|
name: "follow",
|
|
29369
28371
|
description: 'Follow a brand to track ALL its ads \u2014 every platform and country. --platform is how we read your input, not a limit. A domain tracks both Meta + LinkedIn. Example: baker winning-ads follow "deel.com" --platform meta'
|
|
@@ -29410,7 +28412,7 @@ var followCommand = defineCommand168({
|
|
|
29410
28412
|
});
|
|
29411
28413
|
|
|
29412
28414
|
// src/commands/winning-ads/following.ts
|
|
29413
|
-
import { defineCommand as
|
|
28415
|
+
import { defineCommand as defineCommand167 } from "citty";
|
|
29414
28416
|
registerSchema({
|
|
29415
28417
|
command: "winning-ads.following",
|
|
29416
28418
|
description: "List the brands you follow in your ad-dna library, with each one's status (ready vs still adding) and cached ad counts.",
|
|
@@ -29443,7 +28445,7 @@ function followingNormalizer(record, full) {
|
|
|
29443
28445
|
platforms: Array.isArray(record.platforms) ? record.platforms : []
|
|
29444
28446
|
};
|
|
29445
28447
|
}
|
|
29446
|
-
var followingCommand =
|
|
28448
|
+
var followingCommand = defineCommand167({
|
|
29447
28449
|
meta: {
|
|
29448
28450
|
name: "following",
|
|
29449
28451
|
description: "List brands you follow, with status (ready / adding\u2026) and cached counts. Example: baker winning-ads following --output md"
|
|
@@ -29478,7 +28480,7 @@ var followingCommand = defineCommand169({
|
|
|
29478
28480
|
});
|
|
29479
28481
|
|
|
29480
28482
|
// src/commands/winning-ads/patterns.ts
|
|
29481
|
-
import { defineCommand as
|
|
28483
|
+
import { defineCommand as defineCommand168 } from "citty";
|
|
29482
28484
|
registerSchema({
|
|
29483
28485
|
command: "winning-ads.patterns",
|
|
29484
28486
|
description: "Mine what separates two cohorts of ads: pass a comma-list of winning ad ids (--winners) and a comma-list of weaker ad ids (--duds). Returns the discriminating DNA fields.",
|
|
@@ -29517,7 +28519,7 @@ function discriminatorRow(record) {
|
|
|
29517
28519
|
top_values_duds: Array.isArray(record.top_values_b) ? record.top_values_b.join(", ") : ""
|
|
29518
28520
|
};
|
|
29519
28521
|
}
|
|
29520
|
-
var patternsCommand =
|
|
28522
|
+
var patternsCommand = defineCommand168({
|
|
29521
28523
|
meta: {
|
|
29522
28524
|
name: "patterns",
|
|
29523
28525
|
description: "Discover what separates winning ads from weak ones. Example: baker winning-ads patterns --winners a_1,a_2,a_3 --duds a_9,a_8 --output md"
|
|
@@ -29573,7 +28575,7 @@ var patternsCommand = defineCommand170({
|
|
|
29573
28575
|
});
|
|
29574
28576
|
|
|
29575
28577
|
// src/commands/winning-ads/search.ts
|
|
29576
|
-
import { defineCommand as
|
|
28578
|
+
import { defineCommand as defineCommand169 } from "citty";
|
|
29577
28579
|
registerSchema({
|
|
29578
28580
|
command: "winning-ads.search",
|
|
29579
28581
|
description: "Search the ad-dna corpus of scored winning ads. Returns a lean shortlist (advertiser, summary, scores, media_url) to pick a reference to reproduce.",
|
|
@@ -29681,7 +28683,7 @@ function buildSearchBody(args) {
|
|
|
29681
28683
|
}
|
|
29682
28684
|
return body;
|
|
29683
28685
|
}
|
|
29684
|
-
var searchCommand4 =
|
|
28686
|
+
var searchCommand4 = defineCommand169({
|
|
29685
28687
|
meta: {
|
|
29686
28688
|
name: "search",
|
|
29687
28689
|
description: "Search winning reference ads. Example: baker winning-ads search 'B2B SaaS before/after AI automation' --platform meta --format static --winner-category winner --exclude-advertiser adv_123 --output md"
|
|
@@ -29796,7 +28798,7 @@ var searchCommand4 = defineCommand171({
|
|
|
29796
28798
|
});
|
|
29797
28799
|
|
|
29798
28800
|
// src/commands/winning-ads/seeds.ts
|
|
29799
|
-
import { defineCommand as
|
|
28801
|
+
import { defineCommand as defineCommand170 } from "citty";
|
|
29800
28802
|
function leanRow(r) {
|
|
29801
28803
|
return {
|
|
29802
28804
|
key: r.key,
|
|
@@ -29824,7 +28826,7 @@ function makeSeedCommand(opts) {
|
|
|
29824
28826
|
limit: { type: "number", description: "Max keys 1-100 (default 20)", required: false, default: 20 }
|
|
29825
28827
|
}
|
|
29826
28828
|
});
|
|
29827
|
-
return
|
|
28829
|
+
return defineCommand170({
|
|
29828
28830
|
meta: { name: opts.name, description: opts.description },
|
|
29829
28831
|
args: {
|
|
29830
28832
|
platform: { type: "string", description: "Single platform to segment on", required: false },
|
|
@@ -29873,7 +28875,7 @@ var formatsCommand = makeSeedCommand({
|
|
|
29873
28875
|
});
|
|
29874
28876
|
|
|
29875
28877
|
// src/commands/winning-ads/unfollow.ts
|
|
29876
|
-
import { defineCommand as
|
|
28878
|
+
import { defineCommand as defineCommand171 } from "citty";
|
|
29877
28879
|
registerSchema({
|
|
29878
28880
|
command: "winning-ads.unfollow",
|
|
29879
28881
|
description: "Stop following a brand \u2014 removes it from your ad-dna library by advertiser id.",
|
|
@@ -29881,7 +28883,7 @@ registerSchema({
|
|
|
29881
28883
|
advertiser: { type: "string", description: "Advertiser id to unfollow", required: true }
|
|
29882
28884
|
}
|
|
29883
28885
|
});
|
|
29884
|
-
var unfollowCommand =
|
|
28886
|
+
var unfollowCommand = defineCommand171({
|
|
29885
28887
|
meta: {
|
|
29886
28888
|
name: "unfollow",
|
|
29887
28889
|
description: "Stop following a brand by advertiser id. Example: baker winning-ads unfollow adv_123"
|
|
@@ -29902,7 +28904,7 @@ var unfollowCommand = defineCommand173({
|
|
|
29902
28904
|
});
|
|
29903
28905
|
|
|
29904
28906
|
// src/commands/winning-ads/winners.ts
|
|
29905
|
-
import { defineCommand as
|
|
28907
|
+
import { defineCommand as defineCommand172 } from "citty";
|
|
29906
28908
|
registerSchema({
|
|
29907
28909
|
command: "winning-ads.winners",
|
|
29908
28910
|
description: "Top winning ads for one advertiser id (from `advertisers` or `following`). Returns lean winner cards; add --full for DNA + longevity.",
|
|
@@ -29912,7 +28914,7 @@ registerSchema({
|
|
|
29912
28914
|
platform: { type: "string", description: "Filter to a single platform: meta|linkedin", required: false }
|
|
29913
28915
|
}
|
|
29914
28916
|
});
|
|
29915
|
-
var winnersCommand =
|
|
28917
|
+
var winnersCommand = defineCommand172({
|
|
29916
28918
|
meta: {
|
|
29917
28919
|
name: "winners",
|
|
29918
28920
|
description: "Top winning ads for a specific advertiser id. Example: baker winning-ads winners adv_123 --top 15 --output md"
|
|
@@ -29962,7 +28964,7 @@ var winnersCommand = defineCommand174({
|
|
|
29962
28964
|
});
|
|
29963
28965
|
|
|
29964
28966
|
// src/commands/winning-ads/index.ts
|
|
29965
|
-
var winningAdsCommand =
|
|
28967
|
+
var winningAdsCommand = defineCommand173({
|
|
29966
28968
|
meta: {
|
|
29967
28969
|
name: "winning-ads",
|
|
29968
28970
|
description: `Search the ad-dna corpus of scored "winning" ads for reference creatives to reproduce, and manage the brands your library tracks. Proxied through the Baker backend (BAKER_API_KEY) \u2014 no separate token needed.
|
|
@@ -30027,7 +29029,7 @@ function getCliVersion() {
|
|
|
30027
29029
|
}
|
|
30028
29030
|
|
|
30029
29031
|
// src/cli.ts
|
|
30030
|
-
var main =
|
|
29032
|
+
var main = defineCommand174({
|
|
30031
29033
|
meta: {
|
|
30032
29034
|
name: "baker",
|
|
30033
29035
|
version: getCliVersion(),
|
|
@@ -30049,7 +29051,6 @@ Introspection: Run 'baker schema <command>' to inspect argument schemas.`
|
|
|
30049
29051
|
creatives: creativesCommand3,
|
|
30050
29052
|
flows: flowsCommand,
|
|
30051
29053
|
images: imagesCommand,
|
|
30052
|
-
landing: landingCommand,
|
|
30053
29054
|
videos: videosCommand,
|
|
30054
29055
|
testimonials: testimonialsCommand,
|
|
30055
29056
|
canvas: canvasCommand,
|