@koda-sl/baker-cli 0.239.0 → 0.240.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -5601,7 +5601,7 @@ baker landing inspiration page <source id> # a whole page as a section sequ
5601
5601
  baker landing inspiration sequences "b2b saas pricing page" --scope all # what follows what, across many pages
5602
5602
  baker landing inspiration add https://linear.app --note "client likes this density"
5603
5603
  baker landing inspiration favorites # what this company has saved
5604
- baker landing inspiration scrape <url> --out <dir> # capture any page now, synchronously
5604
+ baker landing inspiration scrape <url> --out .baker/<dir> # capture any page now, synchronously
5605
5605
  ```
5606
5606
 
5607
5607
  - **Hybrid search over three signals** — keywords, meaning, and *appearance* (the screenshot is embedded, so a query like "dark developer hero with a terminal" can match a section whose text never says "terminal"). Filters: `--type --composition --register --interaction --motion --media --device --theme --max-rank --min-craft --min-fidelity --domain --similar-to --scope --limit`.
@@ -5618,6 +5618,7 @@ baker landing inspiration scrape <url> --out <dir> # capture any page now, syn
5618
5618
  - **Filming is the expensive pass, and it runs three sections at a time.** Every moving section is filmed in its own fresh page load — the only way to catch an entrance animation before it fires — so a page with ten moving sections pays for ten full loads. Measured on one heavy page, filming was 74% of the capture. The takes are independent, so they overlap; a page that cannot be captured inside the library's budget now says so instead of timing out silently.
5619
5619
  - **A page we can't open says why.** A dead certificate, an address that doesn't resolve, a site that won't answer or one that is simply too slow each produce their own plain-language reason rather than a browser error.
5620
5620
  - **Inspiration, never a clipboard.** Both `code` and `scrape` record what was consulted, and `critique`'s `originality` family blocks a publish that reuses a reference's copy verbatim.
5621
+ - **A capture has to land in `.baker/`, and `scrape` refuses anywhere else in a workspace.** `--out` is free-form and the directory is the agent's choice, but a capture is tens of MB of lossless PNG whose individual files clear every size gate — written to a tracked directory it becomes a permanent part of the client's repo, force-cloned into every later session. `.baker/` is gitignored precisely so a capture costs nothing. The refusal is scoped to the workspace: outside a git work tree — a local run against `/tmp` — any path is allowed, because there is no history to bloat.
5621
5622
 
5622
5623
  ### `baker landing critique`
5623
5624
 
package/dist/cli.js CHANGED
@@ -9763,11 +9763,11 @@ function rawTextEntries(value) {
9763
9763
  const values = Array.isArray(value) ? value : typeof value === "string" ? [value] : [];
9764
9764
  return values.filter((v) => typeof v === "string").flatMap((v) => v.split(",")).map((v) => v.trim()).filter(Boolean);
9765
9765
  }
9766
- function rawFileEntries(path39) {
9767
- if (typeof path39 !== "string" || path39.length === 0) {
9766
+ function rawFileEntries(path40) {
9767
+ if (typeof path40 !== "string" || path40.length === 0) {
9768
9768
  return [];
9769
9769
  }
9770
- return readFileSync2(path39, "utf8").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
9770
+ return readFileSync2(path40, "utf8").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
9771
9771
  }
9772
9772
  function keywordEntries(args) {
9773
9773
  const defaultMatch = typeof args["match-type"] === "string" ? args["match-type"].toUpperCase() : void 0;
@@ -9790,19 +9790,19 @@ function keywordEntries(args) {
9790
9790
  }
9791
9791
  return entries;
9792
9792
  }
9793
- function loadJsonFileArg(path39) {
9794
- if (typeof path39 !== "string" || path39.length === 0) {
9793
+ function loadJsonFileArg(path40) {
9794
+ if (typeof path40 !== "string" || path40.length === 0) {
9795
9795
  return {};
9796
9796
  }
9797
9797
  try {
9798
- const parsed = JSON.parse(readFileSync2(path39, "utf8"));
9798
+ const parsed = JSON.parse(readFileSync2(path40, "utf8"));
9799
9799
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
9800
- failWriteValidation(`${path39} must contain a JSON object`);
9800
+ failWriteValidation(`${path40} must contain a JSON object`);
9801
9801
  }
9802
9802
  return parsed;
9803
9803
  } catch (err) {
9804
9804
  if (err instanceof SyntaxError) {
9805
- failWriteValidation(`${path39} is not valid JSON: ${err.message}`);
9805
+ failWriteValidation(`${path40} is not valid JSON: ${err.message}`);
9806
9806
  }
9807
9807
  throw err;
9808
9808
  }
@@ -9932,10 +9932,10 @@ async function stageUpdate(kind, customerId, target, payload, hints) {
9932
9932
  async function stageTarget(kind, customerId, target, hints) {
9933
9933
  await stageGoogleOp({ kind, customerId, target }, hints);
9934
9934
  }
9935
- async function draftAction(path39, body, chat) {
9935
+ async function draftAction(path40, body, chat) {
9936
9936
  try {
9937
9937
  const chatId = resolveChatId(chat);
9938
- const response = await apiPost(path39, { chatId, ...body });
9938
+ const response = await apiPost(path40, { chatId, ...body });
9939
9939
  writeJsonEnvelope(response);
9940
9940
  } catch (err) {
9941
9941
  handleGoogleError(err);
@@ -14869,19 +14869,19 @@ function failWriteValidation2(message) {
14869
14869
  writeJsonEnvelope({ ok: false, error: { code: "VALIDATION_ERROR", message } });
14870
14870
  process.exit(1);
14871
14871
  }
14872
- function loadJsonFileArg2(path39) {
14873
- if (typeof path39 !== "string" || path39.length === 0) {
14872
+ function loadJsonFileArg2(path40) {
14873
+ if (typeof path40 !== "string" || path40.length === 0) {
14874
14874
  return {};
14875
14875
  }
14876
14876
  try {
14877
- const parsed = JSON.parse(readFileSync4(path39, "utf8"));
14877
+ const parsed = JSON.parse(readFileSync4(path40, "utf8"));
14878
14878
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
14879
- failWriteValidation2(`${path39} must contain a JSON object`);
14879
+ failWriteValidation2(`${path40} must contain a JSON object`);
14880
14880
  }
14881
14881
  return parsed;
14882
14882
  } catch (err) {
14883
14883
  if (err instanceof SyntaxError) {
14884
- failWriteValidation2(`${path39} is not valid JSON: ${err.message}`);
14884
+ failWriteValidation2(`${path40} is not valid JSON: ${err.message}`);
14885
14885
  }
14886
14886
  throw err;
14887
14887
  }
@@ -14966,15 +14966,15 @@ function parseLocaleFlag(value) {
14966
14966
  }
14967
14967
  return { language: match[1], country: match[2].toUpperCase() };
14968
14968
  }
14969
- function loadTargetingFileArg(path39) {
14970
- if (typeof path39 !== "string" || path39.length === 0) {
14969
+ function loadTargetingFileArg(path40) {
14970
+ if (typeof path40 !== "string" || path40.length === 0) {
14971
14971
  return void 0;
14972
14972
  }
14973
- const parsed = loadJsonFileArg2(path39);
14973
+ const parsed = loadJsonFileArg2(path40);
14974
14974
  const criteria = parsed.targetingCriteria ?? parsed;
14975
14975
  if (!criteria.include) {
14976
14976
  failWriteValidation2(
14977
- `${path39} must contain targeting criteria with an "include" block (see baker schema ads.linkedin.campaigns.create)`
14977
+ `${path40} must contain targeting criteria with an "include" block (see baker schema ads.linkedin.campaigns.create)`
14978
14978
  );
14979
14979
  }
14980
14980
  return criteria;
@@ -15009,14 +15009,14 @@ function parseCsvLine(line) {
15009
15009
  cells.push(current);
15010
15010
  return cells.map((cell2) => cell2.trim());
15011
15011
  }
15012
- function parseListFileArg(path39, maxRows) {
15013
- if (typeof path39 !== "string" || path39.length === 0) {
15012
+ function parseListFileArg(path40, maxRows) {
15013
+ if (typeof path40 !== "string" || path40.length === 0) {
15014
15014
  return void 0;
15015
15015
  }
15016
- const raw = readFileSync4(path39, "utf8");
15016
+ const raw = readFileSync4(path40, "utf8");
15017
15017
  const lines = raw.split(/\r?\n/).filter((line) => line.trim().length > 0);
15018
15018
  if (lines.length < 2) {
15019
- failWriteValidation2(`${path39} needs a header row and at least one data row`);
15019
+ failWriteValidation2(`${path40} needs a header row and at least one data row`);
15020
15020
  }
15021
15021
  const columns = parseCsvLine(lines[0]).map((column) => column.trim());
15022
15022
  const rows = [];
@@ -15035,7 +15035,7 @@ function parseListFileArg(path39, maxRows) {
15035
15035
  }
15036
15036
  }
15037
15037
  if (rows.length > maxRows) {
15038
- failWriteValidation2(`${path39} has ${rows.length} rows \u2014 the inline limit is ${maxRows}. Split the list.`);
15038
+ failWriteValidation2(`${path40} has ${rows.length} rows \u2014 the inline limit is ${maxRows}. Split the list.`);
15039
15039
  }
15040
15040
  return { columns, rows };
15041
15041
  }
@@ -15131,11 +15131,11 @@ function readPositionals(args) {
15131
15131
  function splitIdList(raw) {
15132
15132
  return raw.split(",").map((id) => id.trim()).filter(Boolean);
15133
15133
  }
15134
- function idsFileEntries(path39) {
15135
- if (typeof path39 !== "string" || path39.length === 0) {
15134
+ function idsFileEntries(path40) {
15135
+ if (typeof path40 !== "string" || path40.length === 0) {
15136
15136
  return [];
15137
15137
  }
15138
- return readFileSync4(path39, "utf8").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#")).flatMap(splitIdList);
15138
+ return readFileSync4(path40, "utf8").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#")).flatMap(splitIdList);
15139
15139
  }
15140
15140
  function requireTargets(args, entity) {
15141
15141
  const positionals = readPositionals(args);
@@ -17756,9 +17756,9 @@ function compactRow(row) {
17756
17756
  ...destination.postUrn ? { postUrn: destination.postUrn } : {}
17757
17757
  };
17758
17758
  }
17759
- function readPath(row, path39) {
17759
+ function readPath(row, path40) {
17760
17760
  let current = row;
17761
- for (const segment of path39.split(".")) {
17761
+ for (const segment of path40.split(".")) {
17762
17762
  const record = asRecord2(current);
17763
17763
  if (!record) return void 0;
17764
17764
  current = record[segment];
@@ -17768,10 +17768,10 @@ function readPath(row, path39) {
17768
17768
  function projectFields(rows, paths) {
17769
17769
  return rows.map((row) => {
17770
17770
  const projected = {};
17771
- for (const path39 of paths) {
17772
- const value = readPath(row, path39);
17771
+ for (const path40 of paths) {
17772
+ const value = readPath(row, path40);
17773
17773
  if (value !== void 0) {
17774
- projected[path39] = value;
17774
+ projected[path40] = value;
17775
17775
  }
17776
17776
  }
17777
17777
  return projected;
@@ -19071,11 +19071,11 @@ var updateStatusSchema = z25.enum(UPDATE_STATUSES);
19071
19071
  function currencyMinimums2(currencyCode) {
19072
19072
  return CURRENCY_MINIMUMS2[currencyCode] ?? DEFAULT_CURRENCY_MINIMUM2;
19073
19073
  }
19074
- function validateDailyBudgetFloor(money, ctx, path39) {
19074
+ function validateDailyBudgetFloor(money, ctx, path40) {
19075
19075
  if (money?.currencyCode) {
19076
19076
  const min = currencyMinimums2(money.currencyCode).dailyBudgetMin;
19077
19077
  if (Number(money.amount) < min) {
19078
- ctx.addIssue({ code: "custom", path: path39, message: `below the ${min} ${money.currencyCode} daily minimum` });
19078
+ ctx.addIssue({ code: "custom", path: path40, message: `below the ${min} ${money.currencyCode} daily minimum` });
19079
19079
  }
19080
19080
  }
19081
19081
  }
@@ -19737,19 +19737,19 @@ function failWriteValidation3(message) {
19737
19737
  writeJsonEnvelope({ ok: false, error: { code: "VALIDATION_ERROR", message } });
19738
19738
  process.exit(1);
19739
19739
  }
19740
- function loadJsonFileArg3(path39) {
19741
- if (typeof path39 !== "string" || path39.length === 0) {
19740
+ function loadJsonFileArg3(path40) {
19741
+ if (typeof path40 !== "string" || path40.length === 0) {
19742
19742
  return {};
19743
19743
  }
19744
19744
  try {
19745
- const parsed = JSON.parse(readFileSync8(path39, "utf8"));
19745
+ const parsed = JSON.parse(readFileSync8(path40, "utf8"));
19746
19746
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
19747
- failWriteValidation3(`${path39} must contain a JSON object`);
19747
+ failWriteValidation3(`${path40} must contain a JSON object`);
19748
19748
  }
19749
19749
  return parsed;
19750
19750
  } catch (err) {
19751
19751
  if (err instanceof SyntaxError) {
19752
- failWriteValidation3(`${path39} is not valid JSON: ${err.message}`);
19752
+ failWriteValidation3(`${path40} is not valid JSON: ${err.message}`);
19753
19753
  }
19754
19754
  throw err;
19755
19755
  }
@@ -24327,11 +24327,11 @@ function unwrap(response) {
24327
24327
  }
24328
24328
  return response.data;
24329
24329
  }
24330
- async function readAvatars(path39, params) {
24331
- return unwrap(await apiGet(path39, params));
24330
+ async function readAvatars(path40, params) {
24331
+ return unwrap(await apiGet(path40, params));
24332
24332
  }
24333
- async function writeAvatars(path39, body) {
24334
- return unwrap(await apiPost(path39, body));
24333
+ async function writeAvatars(path40, body) {
24334
+ return unwrap(await apiPost(path40, body));
24335
24335
  }
24336
24336
 
24337
24337
  // src/commands/avatars/create.ts
@@ -25165,12 +25165,12 @@ function missingFontFiles(urls, available) {
25165
25165
  function planFontAdoption(sources, families) {
25166
25166
  const wanted = new Map(families.map((family) => [normalizeFamily(family), family]));
25167
25167
  const byFamily = /* @__PURE__ */ new Map();
25168
- for (const { path: path39, source } of sources) {
25169
- const dir = posix.dirname(path39);
25168
+ for (const { path: path40, source } of sources) {
25169
+ const dir = posix.dirname(path40);
25170
25170
  for (const face of declaredFontFaces(source)) {
25171
25171
  if (!wanted.has(face.family)) continue;
25172
25172
  const perFile = byFamily.get(face.family) ?? /* @__PURE__ */ new Map();
25173
- perFile.set(path39, [...perFile.get(path39) ?? [], rebaseFontFaceSrc(face.block, dir)]);
25173
+ perFile.set(path40, [...perFile.get(path40) ?? [], rebaseFontFaceSrc(face.block, dir)]);
25174
25174
  byFamily.set(face.family, perFile);
25175
25175
  }
25176
25176
  }
@@ -33365,12 +33365,12 @@ function collectSideEffects(tree) {
33365
33365
  );
33366
33366
  }
33367
33367
  function readFlowTree(slug) {
33368
- const path39 = join3(flowsDir(), slug, "_data.json");
33369
- if (!existsSync5(path39)) {
33368
+ const path40 = join3(flowsDir(), slug, "_data.json");
33369
+ if (!existsSync5(path40)) {
33370
33370
  failLocal(`No form "${slug}". Run "baker flows list" to see the forms in this workspace.`);
33371
33371
  }
33372
33372
  try {
33373
- return JSON.parse(readFileSync9(path39, "utf-8"));
33373
+ return JSON.parse(readFileSync9(path40, "utf-8"));
33374
33374
  } catch (error) {
33375
33375
  failLocal(`Could not read form "${slug}": ${error instanceof Error ? error.message : String(error)}`);
33376
33376
  }
@@ -33762,10 +33762,10 @@ function parseValueExpression(raw) {
33762
33762
  return parts.map(parsePart);
33763
33763
  }
33764
33764
  function trackingFieldIds() {
33765
- const path39 = join4(flowsDir(), "..", "tracking.ts");
33766
- if (!existsSync6(path39)) return null;
33765
+ const path40 = join4(flowsDir(), "..", "tracking.ts");
33766
+ if (!existsSync6(path40)) return null;
33767
33767
  try {
33768
- const source = readFileSync10(path39, "utf-8");
33768
+ const source = readFileSync10(path40, "utf-8");
33769
33769
  const block2 = source.match(/TRACKING_FIELD_IDS\s*=\s*\[([\s\S]*?)\]\s*as const/)?.[1];
33770
33770
  if (!block2) return null;
33771
33771
  const ids = [...block2.matchAll(/"(tracking\.[a-z0-9_]+)"/g)].map((match) => match[1]);
@@ -34317,13 +34317,13 @@ function specsFromFile(parsed) {
34317
34317
  return `${destField}${type}=${entry?.value ?? ""}`;
34318
34318
  });
34319
34319
  }
34320
- function readSpecFile(path39) {
34320
+ function readSpecFile(path40) {
34321
34321
  let raw;
34322
34322
  try {
34323
- raw = path39 === "-" ? readFileSync11(0, "utf-8") : readFileSync11(path39, "utf-8");
34323
+ raw = path40 === "-" ? readFileSync11(0, "utf-8") : readFileSync11(path40, "utf-8");
34324
34324
  } catch (error) {
34325
34325
  refuse(
34326
- `Could not read ${path39 === "-" ? "the mapping from stdin" : `"${path39}"`}: ${error instanceof Error ? error.message : String(error)}`
34326
+ `Could not read ${path40 === "-" ? "the mapping from stdin" : `"${path40}"`}: ${error instanceof Error ? error.message : String(error)}`
34327
34327
  );
34328
34328
  }
34329
34329
  let parsed;
@@ -34331,7 +34331,7 @@ function readSpecFile(path39) {
34331
34331
  parsed = JSON.parse(raw);
34332
34332
  } catch (error) {
34333
34333
  refuse(
34334
- `${path39 === "-" ? "stdin" : `"${path39}"`} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`,
34334
+ `${path40 === "-" ? "stdin" : `"${path40}"`} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`,
34335
34335
  'Expected { "map": { "<destField>": "<value>", \u2026 } }'
34336
34336
  );
34337
34337
  }
@@ -34585,18 +34585,18 @@ var ARRAY_FIELDS = [
34585
34585
  "tagIds"
34586
34586
  ];
34587
34587
  var ARRAY_OWNERS = ["", "body"];
34588
- function dropUnsetOptionals(sideEffect, path39) {
34588
+ function dropUnsetOptionals(sideEffect, path40) {
34589
34589
  return OPTIONAL_STRINGS.flatMap((key) => {
34590
34590
  if (!(key in sideEffect) || sideEffect[key] !== null && sideEffect[key] !== "") return [];
34591
34591
  delete sideEffect[key];
34592
- return [{ path: path39, change: `dropped \`${key}\` (an optional string is absent, never null)` }];
34592
+ return [{ path: path40, change: `dropped \`${key}\` (an optional string is absent, never null)` }];
34593
34593
  });
34594
34594
  }
34595
- function fillNulledArrays(target, prefix, path39) {
34595
+ function fillNulledArrays(target, prefix, path40) {
34596
34596
  return ARRAY_FIELDS.flatMap((key) => {
34597
34597
  if (!(key in target) || target[key] !== null) return [];
34598
34598
  target[key] = [];
34599
- return [{ path: path39, change: `\`${prefix}${key}: null\` \u2192 \`[]\`` }];
34599
+ return [{ path: path40, change: `\`${prefix}${key}: null\` \u2192 \`[]\`` }];
34600
34600
  });
34601
34601
  }
34602
34602
  function sideEffectsOf(node) {
@@ -34606,13 +34606,13 @@ function sideEffectsOf(node) {
34606
34606
  );
34607
34607
  }
34608
34608
  function normalizeSideEffect(sideEffect, where) {
34609
- const path39 = `${where} \u2192 ${String(sideEffect.id ?? "side effect")}`;
34609
+ const path40 = `${where} \u2192 ${String(sideEffect.id ?? "side effect")}`;
34610
34610
  const arrays = ARRAY_OWNERS.flatMap((owner) => {
34611
34611
  const target = owner ? sideEffect[owner] : sideEffect;
34612
34612
  if (!target || typeof target !== "object") return [];
34613
- return fillNulledArrays(target, owner ? `${owner}.` : "", path39);
34613
+ return fillNulledArrays(target, owner ? `${owner}.` : "", path40);
34614
34614
  });
34615
- return [...dropUnsetOptionals(sideEffect, path39), ...arrays];
34615
+ return [...dropUnsetOptionals(sideEffect, path40), ...arrays];
34616
34616
  }
34617
34617
  function normalizeFlowTree(tree) {
34618
34618
  const changes = [];
@@ -35150,10 +35150,10 @@ async function stageOps(ops) {
35150
35150
  handleError2(err);
35151
35151
  }
35152
35152
  }
35153
- async function draftAction2(path39, body, chat) {
35153
+ async function draftAction2(path40, body, chat) {
35154
35154
  const chatId = resolveChatId(chat);
35155
35155
  try {
35156
- const data = await apiPost(path39, { chatId, ...body });
35156
+ const data = await apiPost(path40, { chatId, ...body });
35157
35157
  writeJsonEnvelope({ ok: true, data });
35158
35158
  return data;
35159
35159
  } catch (err) {
@@ -37585,9 +37585,9 @@ async function readImageBuffer(pathOrUrl) {
37585
37585
  }
37586
37586
  return readFile20(pathOrUrl);
37587
37587
  }
37588
- async function isDirectory(path39) {
37588
+ async function isDirectory(path40) {
37589
37589
  try {
37590
- const s = await stat4(path39);
37590
+ const s = await stat4(path40);
37591
37591
  return s.isDirectory();
37592
37592
  } catch {
37593
37593
  return false;
@@ -37889,13 +37889,13 @@ function resolveDownloadPath({ baseName, extension, out, outIsDirectory: outIsDi
37889
37889
  }
37890
37890
  function disambiguate(paths) {
37891
37891
  const taken = /* @__PURE__ */ new Set();
37892
- return paths.map((path39) => {
37893
- if (!taken.has(path39)) {
37894
- taken.add(path39);
37895
- return path39;
37892
+ return paths.map((path40) => {
37893
+ if (!taken.has(path40)) {
37894
+ taken.add(path40);
37895
+ return path40;
37896
37896
  }
37897
- const ext = extname3(path39);
37898
- const stem = path39.slice(0, path39.length - ext.length);
37897
+ const ext = extname3(path40);
37898
+ const stem = path40.slice(0, path40.length - ext.length);
37899
37899
  let n = 2;
37900
37900
  while (taken.has(`${stem}-${n}${ext}`)) n += 1;
37901
37901
  const unique = `${stem}-${n}${ext}`;
@@ -38022,10 +38022,10 @@ async function runDownloads(plan) {
38022
38022
  const paths = disambiguate(fetched.map((item) => item.path));
38023
38023
  const downloaded = [];
38024
38024
  for (const [index, item] of fetched.entries()) {
38025
- const path39 = paths[index] ?? item.path;
38025
+ const path40 = paths[index] ?? item.path;
38026
38026
  try {
38027
- await atomicWrite(path39, item.buffer);
38028
- downloaded.push({ input: item.input, output: path39, bytes: item.buffer.length, contentType: item.contentType });
38027
+ await atomicWrite(path40, item.buffer);
38028
+ downloaded.push({ input: item.input, output: path40, bytes: item.buffer.length, contentType: item.contentType });
38029
38029
  } catch (err) {
38030
38030
  failed.push({ input: item.input, error: failureMessage(err, "Write failed") });
38031
38031
  }
@@ -42706,7 +42706,7 @@ var pageCommand2 = defineCommand160({
42706
42706
 
42707
42707
  // src/commands/landing/inspiration/scrape.ts
42708
42708
  import { readFile as readFile23 } from "fs/promises";
42709
- import path33 from "path";
42709
+ import path34 from "path";
42710
42710
  import { defineCommand as defineCommand161 } from "citty";
42711
42711
 
42712
42712
  // src/engine/landing/lib/capturedReferences.ts
@@ -44773,6 +44773,44 @@ async function scrapeLanding(options) {
44773
44773
  }
44774
44774
  }
44775
44775
 
44776
+ // src/commands/landing/inspiration/captureOut.ts
44777
+ import { existsSync as existsSync9 } from "fs";
44778
+ import path33 from "path";
44779
+ var SCRATCH_DIR = ".baker";
44780
+ function isWithin(parent, target) {
44781
+ const relative = path33.relative(parent, target);
44782
+ return relative === "" || !relative.startsWith("..") && !path33.isAbsolute(relative);
44783
+ }
44784
+ function findRepoRoot(from) {
44785
+ let dir = path33.resolve(from);
44786
+ for (; ; ) {
44787
+ if (existsSync9(path33.join(dir, ".git"))) return dir;
44788
+ const parent = path33.dirname(dir);
44789
+ if (parent === dir) return null;
44790
+ dir = parent;
44791
+ }
44792
+ }
44793
+ function checkCaptureOut(out, options) {
44794
+ const { cwd, repoRoot } = options;
44795
+ const resolved = path33.resolve(cwd, out);
44796
+ if (repoRoot === null || !isWithin(repoRoot, resolved)) return { ok: true };
44797
+ const scratch = path33.join(repoRoot, SCRATCH_DIR);
44798
+ if (isWithin(scratch, resolved)) return { ok: true };
44799
+ const suggestion = path33.posix.join(SCRATCH_DIR, "teardowns", path33.basename(resolved) || "capture");
44800
+ return {
44801
+ ok: false,
44802
+ error: {
44803
+ code: "CAPTURE_OUT_NOT_SCRATCH",
44804
+ message: `A capture has to be written to ${SCRATCH_DIR}/ inside this workspace. It is reference material for this turn, and anywhere else it becomes a permanent part of the client's files.`,
44805
+ fix: {
44806
+ action: "retry_with_changes",
44807
+ explanation: `Re-run with --out ${suggestion}.`
44808
+ },
44809
+ retryable: false
44810
+ }
44811
+ };
44812
+ }
44813
+
44776
44814
  // src/commands/landing/inspiration/scrape.ts
44777
44815
  var RETRYABLE_FAILURES = /* @__PURE__ */ new Set([
44778
44816
  "SITE_TOO_SLOW",
@@ -44787,7 +44825,7 @@ async function recordCapture(manifest, outDir) {
44787
44825
  for (const section of manifest.sections) {
44788
44826
  if (!section.bundle) continue;
44789
44827
  try {
44790
- const markup = await readFile23(path33.join(outDir, section.bundle), "utf8");
44828
+ const markup = await readFile23(path34.join(outDir, section.bundle), "utf8");
44791
44829
  const copyStrings = capturedCopyStrings(markup);
44792
44830
  if (copyStrings.length === 0) continue;
44793
44831
  references.push({
@@ -44833,7 +44871,7 @@ registerSchema({
44833
44871
  description: "Start here when the user points at a specific page and wants it now: capture one landing page into a directory of section screenshots, standalone HTML bundles, a whole-page reproduction and motion filmstrips. Returns when the capture is done, unlike `add`. Read the screenshots. Also records every captured section for the originality check.",
44834
44872
  args: {
44835
44873
  url: { type: "string", description: "Page to capture", required: true },
44836
- out: { type: "string", description: "Output directory", required: true },
44874
+ out: { type: "string", description: "Output directory under .baker/", required: true },
44837
44875
  mobile: { type: "boolean", description: "Phone-viewport pass. `--no-mobile` to skip", required: false },
44838
44876
  code: {
44839
44877
  type: "boolean",
@@ -44855,7 +44893,7 @@ var scrapeCommand = defineCommand161({
44855
44893
  },
44856
44894
  args: {
44857
44895
  url: { type: "positional", description: "Page to capture", required: true },
44858
- out: { type: "string", description: "Output directory", required: true },
44896
+ out: { type: "string", description: "Output directory, under .baker/", required: true },
44859
44897
  mobile: {
44860
44898
  type: "boolean",
44861
44899
  description: "Phone-viewport pass (--no-mobile to skip)",
@@ -44872,6 +44910,14 @@ var scrapeCommand = defineCommand161({
44872
44910
  report: { type: "boolean", description: "Write report.html (--no-report to skip)", required: false, default: true }
44873
44911
  },
44874
44912
  run: async ({ args }) => {
44913
+ const location2 = checkCaptureOut(args.out, {
44914
+ cwd: process.cwd(),
44915
+ repoRoot: findRepoRoot(process.cwd())
44916
+ });
44917
+ if (!location2.ok) {
44918
+ writeJson({ ok: false, error: location2.error });
44919
+ process.exit(1);
44920
+ }
44875
44921
  try {
44876
44922
  const manifest = await scrapeLanding({
44877
44923
  url: args.url,
@@ -44949,12 +44995,12 @@ var scrapeCommand = defineCommand161({
44949
44995
  });
44950
44996
 
44951
44997
  // src/commands/landing/inspiration/search.ts
44952
- import path35 from "path";
44998
+ import path36 from "path";
44953
44999
  import { defineCommand as defineCommand162 } from "citty";
44954
45000
 
44955
45001
  // src/commands/landing/inspiration/shot.ts
44956
45002
  import { mkdir as mkdir12, writeFile as writeFile17 } from "fs/promises";
44957
- import path34 from "path";
45003
+ import path35 from "path";
44958
45004
  import sharp6 from "sharp";
44959
45005
  var READABLE_SHOT = {
44960
45006
  maxWidth: 1440,
@@ -44980,9 +45026,9 @@ async function downloadReadableShot(url, file) {
44980
45026
  const response = await fetch(url);
44981
45027
  if (!response.ok) return null;
44982
45028
  const shot = await toReadableShot(Buffer.from(await response.arrayBuffer()));
44983
- await mkdir12(path34.dirname(file), { recursive: true });
45029
+ await mkdir12(path35.dirname(file), { recursive: true });
44984
45030
  await writeFile17(file, shot);
44985
- return path34.relative(process.cwd(), file);
45031
+ return path35.relative(process.cwd(), file);
44986
45032
  } catch {
44987
45033
  return null;
44988
45034
  }
@@ -45074,13 +45120,13 @@ function buildSearchBody(args) {
45074
45120
  return body;
45075
45121
  }
45076
45122
  async function downloadShots(results) {
45077
- const dir = path35.join(process.cwd(), ".baker", "inspiration");
45123
+ const dir = path36.join(process.cwd(), ".baker", "inspiration");
45078
45124
  const saved = /* @__PURE__ */ new Map();
45079
45125
  await Promise.all(
45080
45126
  results.map(async (result) => {
45081
45127
  const file = await downloadReadableShot(
45082
45128
  result.desktopShotUrl,
45083
- path35.join(dir, `${result.id}.${READABLE_SHOT.extension}`)
45129
+ path36.join(dir, `${result.id}.${READABLE_SHOT.extension}`)
45084
45130
  );
45085
45131
  if (file) saved.set(result.id, file);
45086
45132
  })
@@ -45308,7 +45354,7 @@ var sequencesCommand = defineCommand163({
45308
45354
  });
45309
45355
 
45310
45356
  // src/commands/landing/inspiration/view.ts
45311
- import path36 from "path";
45357
+ import path37 from "path";
45312
45358
  import { defineCommand as defineCommand164 } from "citty";
45313
45359
  registerSchema({
45314
45360
  command: "landing.inspiration.view",
@@ -45341,12 +45387,12 @@ var viewCommand2 = defineCommand164({
45341
45387
  const id = args.id;
45342
45388
  const data = await apiGet("/api/landing-inspiration/section", { id });
45343
45389
  const section = data.section;
45344
- const dir = path36.join(process.cwd(), ".baker", "inspiration", id);
45390
+ const dir = path37.join(process.cwd(), ".baker", "inspiration", id);
45345
45391
  const ext = READABLE_SHOT.extension;
45346
45392
  const [desktop, mobile, filmstrip] = await Promise.all([
45347
- downloadReadableShot(section.desktopShotUrl, path36.join(dir, `desktop.${ext}`)),
45348
- downloadReadableShot(section.mobileShotUrl, path36.join(dir, `mobile.${ext}`)),
45349
- downloadReadableShot(section.motionFilmstripUrl, path36.join(dir, `motion-filmstrip.${ext}`))
45393
+ downloadReadableShot(section.desktopShotUrl, path37.join(dir, `desktop.${ext}`)),
45394
+ downloadReadableShot(section.mobileShotUrl, path37.join(dir, `mobile.${ext}`)),
45395
+ downloadReadableShot(section.motionFilmstripUrl, path37.join(dir, `motion-filmstrip.${ext}`))
45350
45396
  ]);
45351
45397
  const full = args.full;
45352
45398
  const hints = [INSPIRATION_HINTS.structureNotCopy, INSPIRATION_HINTS.adapt];
@@ -47196,7 +47242,7 @@ var listCommand15 = defineCommand183({
47196
47242
 
47197
47243
  // src/commands/scheduled-actions/templates.ts
47198
47244
  import { readFile as readFile24 } from "fs/promises";
47199
- import path37 from "path";
47245
+ import path38 from "path";
47200
47246
  import { defineCommand as defineCommand184 } from "citty";
47201
47247
  registerSchema({
47202
47248
  command: "scheduled-actions.templates",
@@ -47299,7 +47345,7 @@ Full guide: __tooling__/docs/tools/baker/scheduled-actions.md`
47299
47345
  }
47300
47346
  if (save.length > 0) {
47301
47347
  const briefFile = flag("brief-file");
47302
- const brief = briefFile.length > 0 ? await readFile24(path37.resolve(briefFile), "utf8") : flag("brief");
47348
+ const brief = briefFile.length > 0 ? await readFile24(path38.resolve(briefFile), "utf8") : flag("brief");
47303
47349
  if (brief.trim().length === 0) {
47304
47350
  failValidation4("--brief-file (preferred) or --brief is required: the brief is the recipe.");
47305
47351
  }
@@ -47770,7 +47816,7 @@ function parseImageRefs(spec) {
47770
47816
  }
47771
47817
  var defaultDeps = {
47772
47818
  ingest: (url) => apiPost("/api/images/ingest", { url, source: "uploaded" }),
47773
- upload: (path39) => uploadLocalImage({ file: path39, contentType: detectImageContentType(path39), source: "uploaded" })
47819
+ upload: (path40) => uploadLocalImage({ file: path40, contentType: detectImageContentType(path40), source: "uploaded" })
47774
47820
  };
47775
47821
  async function resolveLibraryImageIds(spec, limit, deps = defaultDeps) {
47776
47822
  const refs = parseImageRefs(spec);
@@ -47790,10 +47836,10 @@ async function resolveLibraryImageIds(spec, limit, deps = defaultDeps) {
47790
47836
  }
47791
47837
  return { imageIds, added };
47792
47838
  }
47793
- function uploadFailure(path39) {
47839
+ function uploadFailure(path40) {
47794
47840
  return (error) => {
47795
47841
  if (error instanceof ApiError) throw error;
47796
- throw new ApiError("VALIDATION_ERROR", `Could not read "${path39}" as an image.`);
47842
+ throw new ApiError("VALIDATION_ERROR", `Could not read "${path40}" as an image.`);
47797
47843
  };
47798
47844
  }
47799
47845
 
@@ -48855,10 +48901,10 @@ async function stageOp4(op) {
48855
48901
  handleError5(err);
48856
48902
  }
48857
48903
  }
48858
- async function draftAction3(path39, body, chat) {
48904
+ async function draftAction3(path40, body, chat) {
48859
48905
  const chatId = resolveChatId(chat);
48860
48906
  try {
48861
- const data = await apiPost(path39, { chatId, ...body });
48907
+ const data = await apiPost(path40, { chatId, ...body });
48862
48908
  writeJsonEnvelope({ ok: true, data });
48863
48909
  return data;
48864
48910
  } catch (err) {
@@ -49965,7 +50011,7 @@ var groupCommand2 = defineCommand209({
49965
50011
  // src/commands/videos/ingest.ts
49966
50012
  import { mkdtemp as mkdtemp2, rm as rm7, stat as stat7 } from "fs/promises";
49967
50013
  import { tmpdir as tmpdir3 } from "os";
49968
- import path38 from "path";
50014
+ import path39 from "path";
49969
50015
  import { defineCommand as defineCommand210 } from "citty";
49970
50016
 
49971
50017
  // src/lib/streamUpload.ts
@@ -50316,7 +50362,7 @@ function ingestUrl(args) {
50316
50362
  }
50317
50363
  async function downloadThenIngest(args, country) {
50318
50364
  const vimeoCookie = captureVimeoCookie();
50319
- const workDir = await mkdtemp2(path38.join(tmpdir3(), "videos-ingest-"));
50365
+ const workDir = await mkdtemp2(path39.join(tmpdir3(), "videos-ingest-"));
50320
50366
  try {
50321
50367
  const probe = await probeYtDlp({ url: args.url, country, vimeoCookie, cookieDir: workDir });
50322
50368
  if (isAudioOnly(probe.info)) {
@@ -51936,7 +51982,7 @@ function unknownFlagEnvelope(unknown, commandPath, suggestion) {
51936
51982
  };
51937
51983
  }
51938
51984
  function commandPathOf(root, argv) {
51939
- const path39 = [];
51985
+ const path40 = [];
51940
51986
  let command = root;
51941
51987
  for (const token of argv) {
51942
51988
  if (token === "--" || token.startsWith("-")) {
@@ -51947,10 +51993,10 @@ function commandPathOf(root, argv) {
51947
51993
  if (next === void 0 || typeof next !== "object") {
51948
51994
  break;
51949
51995
  }
51950
- path39.push(token);
51996
+ path40.push(token);
51951
51997
  command = next;
51952
51998
  }
51953
- return path39.join(" ");
51999
+ return path40.join(" ");
51954
52000
  }
51955
52001
  function refuseUnknownFlags(root, argv) {
51956
52002
  const unknown = findUnknownFlags(root, argv);