@kadj-amoah/showrunner 1.1.3 → 1.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1413,10 +1413,10 @@ function extractAndValidate(response, schema) {
1413
1413
  if (response.parsed_output !== void 0 && response.parsed_output !== null) {
1414
1414
  return schema.parse(response.parsed_output);
1415
1415
  }
1416
- const text = response.content.filter((b) => b.type === "text").map((b) => b.text).join("\n").trim();
1416
+ const text2 = response.content.filter((b) => b.type === "text").map((b) => b.text).join("\n").trim();
1417
1417
  throw new LLMProviderError(
1418
- text.length > 0 ? `Anthropic returned no parseable structured output. Raw text:
1419
- ${text.slice(0, 2e3)}` : "Anthropic returned an empty response with no parseable output.",
1418
+ text2.length > 0 ? `Anthropic returned no parseable structured output. Raw text:
1419
+ ${text2.slice(0, 2e3)}` : "Anthropic returned an empty response with no parseable output.",
1420
1420
  "anthropic"
1421
1421
  );
1422
1422
  }
@@ -2351,20 +2351,20 @@ function isOriginsList(value) {
2351
2351
  function buildFormFill(auth) {
2352
2352
  return async (page) => {
2353
2353
  const email = process.env[auth.fields.email.env];
2354
- const password = process.env[auth.fields.password.env];
2354
+ const password2 = process.env[auth.fields.password.env];
2355
2355
  if (!email) {
2356
2356
  throw new AuthError(
2357
2357
  `Form auth: env var ${auth.fields.email.env} is not set`
2358
2358
  );
2359
2359
  }
2360
- if (!password) {
2360
+ if (!password2) {
2361
2361
  throw new AuthError(
2362
2362
  `Form auth: env var ${auth.fields.password.env} is not set`
2363
2363
  );
2364
2364
  }
2365
2365
  await page.goto(auth.login_url);
2366
2366
  await page.fill(auth.fields.email.selector, email);
2367
- await page.fill(auth.fields.password.selector, password);
2367
+ await page.fill(auth.fields.password.selector, password2);
2368
2368
  await page.click(auth.submit_selector);
2369
2369
  try {
2370
2370
  await page.waitForURL(auth.success_url_pattern, { timeout: auth.timeout_ms });
@@ -2719,9 +2719,9 @@ async function recordDemo(opts) {
2719
2719
  const page = await context.newPage();
2720
2720
  if (recording.cursor_highlight) {
2721
2721
  page.on("console", (msg) => {
2722
- const text = msg.text();
2723
- if (text.startsWith("[showrunner-cursor]")) {
2724
- logger.debug(`browser: ${text}`);
2722
+ const text2 = msg.text();
2723
+ if (text2.startsWith("[showrunner-cursor]")) {
2724
+ logger.debug(`browser: ${text2}`);
2725
2725
  }
2726
2726
  });
2727
2727
  }
@@ -4602,8 +4602,8 @@ async function fileExists7(path) {
4602
4602
  return false;
4603
4603
  }
4604
4604
  }
4605
- function escapeFilterText(text) {
4606
- return text.replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/:/g, "\\:").replace(/,/g, "\\,");
4605
+ function escapeFilterText(text2) {
4606
+ return text2.replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/:/g, "\\:").replace(/,/g, "\\,");
4607
4607
  }
4608
4608
  function escapeFilterPath(path) {
4609
4609
  return path.replace(/\\/g, "/").replace(/:/g, "\\\\:");
@@ -4680,14 +4680,14 @@ function chunkAlignmentToCues(alignment, segmentStartSec) {
4680
4680
  let lastBoundaryIdx = -1;
4681
4681
  let i = 0;
4682
4682
  const pushCue = (endIdx) => {
4683
- const text = cueChars.join("").replace(/\s+/g, " ").trim();
4684
- if (text.length === 0) return;
4683
+ const text2 = cueChars.join("").replace(/\s+/g, " ").trim();
4684
+ if (text2.length === 0) return;
4685
4685
  cues.push({
4686
4686
  index: 0,
4687
4687
  // assigned by caller
4688
4688
  startSec: segmentStartSec + cueStart,
4689
4689
  endSec: segmentStartSec + ends[endIdx],
4690
- text
4690
+ text: text2
4691
4691
  });
4692
4692
  };
4693
4693
  while (i < chars.length) {
@@ -5595,8 +5595,294 @@ async function runCommand2(opts) {
5595
5595
  }
5596
5596
 
5597
5597
  // src/commands/init.ts
5598
- import { mkdir as mkdir11, writeFile as writeFile12, access as access2 } from "fs/promises";
5598
+ import { mkdir as mkdir11, writeFile as writeFile12, access as access3 } from "fs/promises";
5599
5599
  import { dirname as dirname10, isAbsolute as isAbsolute8, join as join10, resolve as resolve15 } from "path";
5600
+
5601
+ // src/setup/detect.ts
5602
+ import { spawn as spawn4 } from "child_process";
5603
+ import { access as access2, stat as stat10 } from "fs/promises";
5604
+ async function detectEnvironment() {
5605
+ const [claudeCli, ffmpeg, ffprobe, chromium6] = await Promise.all([
5606
+ isOnPath("claude"),
5607
+ isOnPath("ffmpeg"),
5608
+ isOnPath("ffprobe"),
5609
+ chromiumInstalled()
5610
+ ]);
5611
+ return {
5612
+ claudeCli,
5613
+ ffmpeg,
5614
+ ffprobe,
5615
+ chromium: chromium6,
5616
+ envVars: {
5617
+ anthropic: Boolean(process.env["ANTHROPIC_API_KEY"]),
5618
+ openai: Boolean(process.env["OPENAI_API_KEY"]),
5619
+ elevenlabs: Boolean(process.env["ELEVENLABS_API_KEY"])
5620
+ }
5621
+ };
5622
+ }
5623
+ async function isOnPath(binary) {
5624
+ return new Promise((resolve27) => {
5625
+ const useShell = process.platform === "win32";
5626
+ const child = spawn4(binary, ["--version"], {
5627
+ stdio: ["ignore", "ignore", "ignore"],
5628
+ shell: useShell
5629
+ });
5630
+ let settled = false;
5631
+ const finish = (ok) => {
5632
+ if (settled) return;
5633
+ settled = true;
5634
+ resolve27(ok);
5635
+ };
5636
+ child.on("error", () => finish(false));
5637
+ child.on("exit", (code) => finish(code === 0));
5638
+ setTimeout(() => {
5639
+ if (!settled) {
5640
+ child.kill("SIGKILL");
5641
+ finish(false);
5642
+ }
5643
+ }, 4e3);
5644
+ });
5645
+ }
5646
+ async function chromiumInstalled() {
5647
+ try {
5648
+ const { chromium: chromium6 } = await import("playwright-core");
5649
+ const exec = chromium6.executablePath();
5650
+ await stat10(exec);
5651
+ return true;
5652
+ } catch {
5653
+ return false;
5654
+ }
5655
+ }
5656
+
5657
+ // src/setup/wizard.ts
5658
+ import {
5659
+ intro,
5660
+ outro,
5661
+ text,
5662
+ password,
5663
+ select,
5664
+ confirm,
5665
+ note,
5666
+ spinner,
5667
+ isCancel,
5668
+ cancel
5669
+ } from "@clack/prompts";
5670
+
5671
+ // src/setup/targetProbe.ts
5672
+ import { request as undiciRequest2 } from "undici";
5673
+ var DEFAULT_TIMEOUT_MS = 4e3;
5674
+ async function probeUrl(url, timeoutMs = DEFAULT_TIMEOUT_MS) {
5675
+ const start = Date.now();
5676
+ try {
5677
+ const res = await undiciRequest2(url, {
5678
+ method: "HEAD",
5679
+ bodyTimeout: timeoutMs,
5680
+ headersTimeout: timeoutMs
5681
+ });
5682
+ const elapsedMs = Date.now() - start;
5683
+ return {
5684
+ url,
5685
+ reachable: res.statusCode >= 200 && res.statusCode < 500,
5686
+ statusCode: res.statusCode,
5687
+ elapsedMs
5688
+ };
5689
+ } catch (err) {
5690
+ return {
5691
+ url,
5692
+ reachable: false,
5693
+ reason: err instanceof Error ? err.message : String(err),
5694
+ elapsedMs: Date.now() - start
5695
+ };
5696
+ }
5697
+ }
5698
+
5699
+ // src/setup/wizard.ts
5700
+ var SLUG_RE = /^[a-z0-9](?:[a-z0-9._-]*[a-z0-9])?$/i;
5701
+ async function runWizard(env) {
5702
+ intro("Showrunner setup");
5703
+ note(formatDetection(env), "Detected on this machine");
5704
+ const projectName = await ask(
5705
+ text({
5706
+ message: "What's the project directory name?",
5707
+ placeholder: "showrunner-demo",
5708
+ defaultValue: "showrunner-demo",
5709
+ validate: (v) => {
5710
+ if (!v) return void 0;
5711
+ if (!SLUG_RE.test(v)) {
5712
+ return "Use letters/digits/dash/underscore/dot, starting & ending with alphanumeric.";
5713
+ }
5714
+ return void 0;
5715
+ }
5716
+ })
5717
+ );
5718
+ if (projectName === null) return null;
5719
+ const resolutionPreset = await ask(
5720
+ select({
5721
+ message: "Recording resolution preset?",
5722
+ options: [
5723
+ { value: "low", label: "low (854\xD7480) \u2014 draft / fast iteration" },
5724
+ { value: "standard", label: "standard (1280\xD7720) \u2014 recommended default" },
5725
+ { value: "high", label: "high (1920\xD71080)" },
5726
+ { value: "extreme", label: "extreme (3840\xD72160) \u2014 needs serious RAM" }
5727
+ ],
5728
+ initialValue: "standard"
5729
+ })
5730
+ );
5731
+ if (resolutionPreset === null) return null;
5732
+ if (!RESOLUTION_PRESETS.includes(resolutionPreset)) {
5733
+ cancel("Invalid resolution preset.");
5734
+ return null;
5735
+ }
5736
+ const llmDefault = env.claudeCli ? "agent_bridge" : "anthropic";
5737
+ const llm = await ask(
5738
+ select({
5739
+ message: "Which LLM provider should drive comprehension + script?",
5740
+ options: [
5741
+ {
5742
+ value: "agent_bridge",
5743
+ label: env.claudeCli ? "agent_bridge \u2014 uses your local `claude` CLI (detected). No API key required." : "agent_bridge \u2014 uses a local headless agent CLI. No API key required."
5744
+ },
5745
+ {
5746
+ value: "anthropic",
5747
+ label: env.envVars.anthropic ? "anthropic \u2014 Claude via API (ANTHROPIC_API_KEY already in env)" : "anthropic \u2014 Claude via API (you'll paste an API key in a moment)"
5748
+ },
5749
+ {
5750
+ value: "openai",
5751
+ label: env.envVars.openai ? "openai \u2014 GPT via API (OPENAI_API_KEY already in env)" : "openai \u2014 GPT via API (you'll paste an API key in a moment)"
5752
+ }
5753
+ ],
5754
+ initialValue: llmDefault
5755
+ })
5756
+ );
5757
+ if (llm === null) return null;
5758
+ const collectedKeys = {};
5759
+ if (llm === "anthropic" && !env.envVars.anthropic) {
5760
+ const key = await askKey("ANTHROPIC_API_KEY", "https://console.anthropic.com/settings/keys");
5761
+ if (key === null) return null;
5762
+ if (key.length > 0) collectedKeys["ANTHROPIC_API_KEY"] = key;
5763
+ }
5764
+ if (llm === "openai" && !env.envVars.openai) {
5765
+ const key = await askKey("OPENAI_API_KEY", "https://platform.openai.com/api-keys");
5766
+ if (key === null) return null;
5767
+ if (key.length > 0) collectedKeys["OPENAI_API_KEY"] = key;
5768
+ }
5769
+ const ttsDefault = llm === "agent_bridge" && !env.envVars.openai && !env.envVars.elevenlabs ? "custom" : "elevenlabs";
5770
+ const tts = await ask(
5771
+ select({
5772
+ message: "Which TTS provider for voiceover?",
5773
+ options: [
5774
+ {
5775
+ value: "elevenlabs",
5776
+ label: env.envVars.elevenlabs ? "elevenlabs \u2014 best alignment (ELEVENLABS_API_KEY already in env)" : "elevenlabs \u2014 best alignment (you'll paste an API key)"
5777
+ },
5778
+ {
5779
+ value: "openai",
5780
+ label: env.envVars.openai || llm === "openai" ? "openai \u2014 tts-1-hd (reuses your OpenAI key)" : "openai \u2014 tts-1-hd (you'll paste an API key)"
5781
+ },
5782
+ {
5783
+ value: "custom",
5784
+ label: "custom \u2014 plug in your own TTSProvider module. No API key required here."
5785
+ }
5786
+ ],
5787
+ initialValue: ttsDefault
5788
+ })
5789
+ );
5790
+ if (tts === null) return null;
5791
+ if (tts === "elevenlabs" && !env.envVars.elevenlabs) {
5792
+ const key = await askKey("ELEVENLABS_API_KEY", "https://elevenlabs.io/app/settings/api-keys");
5793
+ if (key === null) return null;
5794
+ if (key.length > 0) collectedKeys["ELEVENLABS_API_KEY"] = key;
5795
+ }
5796
+ if (tts === "openai" && !env.envVars.openai && !collectedKeys["OPENAI_API_KEY"]) {
5797
+ const key = await askKey("OPENAI_API_KEY", "https://platform.openai.com/api-keys");
5798
+ if (key === null) return null;
5799
+ if (key.length > 0) collectedKeys["OPENAI_API_KEY"] = key;
5800
+ }
5801
+ const url = await ask(
5802
+ text({
5803
+ message: "What URL is your product dev server on?",
5804
+ placeholder: "http://localhost:3000",
5805
+ defaultValue: "http://localhost:3000",
5806
+ validate: (v) => {
5807
+ if (!v) return void 0;
5808
+ try {
5809
+ new URL(v);
5810
+ return void 0;
5811
+ } catch {
5812
+ return "Must be a valid URL (e.g. http://localhost:3000).";
5813
+ }
5814
+ }
5815
+ })
5816
+ );
5817
+ if (url === null) return null;
5818
+ const probeSpinner = spinner();
5819
+ probeSpinner.start(`Probing ${url} ...`);
5820
+ const probe = await probeUrl(url);
5821
+ if (probe.reachable) {
5822
+ probeSpinner.stop(`${url} reachable (HTTP ${probe.statusCode}, ${probe.elapsedMs}ms).`);
5823
+ } else {
5824
+ probeSpinner.stop(`${url} not reachable yet.`);
5825
+ note(
5826
+ `That's fine \u2014 you can start your dev server later. When it's up, run:
5827
+
5828
+ showrunner set-target -c demo.yaml --url ${url}
5829
+
5830
+ to re-probe and update the config.`,
5831
+ "Heads up"
5832
+ );
5833
+ }
5834
+ const proceed = await ask(
5835
+ confirm({
5836
+ message: `Scaffold ${projectName}/ with these choices?`,
5837
+ initialValue: true
5838
+ })
5839
+ );
5840
+ if (proceed === null || proceed === false) {
5841
+ cancel("Setup cancelled. Nothing written.");
5842
+ return null;
5843
+ }
5844
+ outro("Scaffolding now...");
5845
+ return {
5846
+ projectName,
5847
+ url,
5848
+ llm,
5849
+ tts,
5850
+ resolutionPreset,
5851
+ collectedKeys
5852
+ };
5853
+ }
5854
+ async function askKey(envVar, dashUrl) {
5855
+ const value = await ask(
5856
+ password({
5857
+ message: `Paste your ${envVar} (or press enter to skip and fill .env later)`
5858
+ })
5859
+ );
5860
+ if (value === null) return null;
5861
+ return typeof value === "string" ? value.trim() : "";
5862
+ }
5863
+ async function ask(promptResult) {
5864
+ const result = await promptResult;
5865
+ if (isCancel(result)) {
5866
+ cancel("Setup cancelled.");
5867
+ return null;
5868
+ }
5869
+ return result;
5870
+ }
5871
+ function formatDetection(env) {
5872
+ const lines = [];
5873
+ lines.push(`claude CLI: ${env.claudeCli ? "found" : "not found"}`);
5874
+ lines.push(`ffmpeg: ${env.ffmpeg ? "found" : "NOT found \u2190 required for muxing"}`);
5875
+ lines.push(`ffprobe: ${env.ffprobe ? "found" : "NOT found \u2190 required for muxing"}`);
5876
+ lines.push(`chromium: ${env.chromium ? "installed" : "NOT installed \u2190 run `showrunner install-browser`"}`);
5877
+ lines.push("");
5878
+ lines.push("env vars:");
5879
+ lines.push(` ANTHROPIC_API_KEY: ${env.envVars.anthropic ? "set" : "unset"}`);
5880
+ lines.push(` OPENAI_API_KEY: ${env.envVars.openai ? "set" : "unset"}`);
5881
+ lines.push(` ELEVENLABS_API_KEY: ${env.envVars.elevenlabs ? "set" : "unset"}`);
5882
+ return lines.join("\n");
5883
+ }
5884
+
5885
+ // src/commands/init.ts
5600
5886
  var LLM_CHOICES = ["anthropic", "openai", "agent_bridge"];
5601
5887
  var TTS_CHOICES = ["elevenlabs", "openai", "custom"];
5602
5888
  var PLACEHOLDER_FILES = [
@@ -5608,22 +5894,52 @@ var PLACEHOLDER_FILES = [
5608
5894
  "output/.gitkeep"
5609
5895
  ];
5610
5896
  async function initCommand(opts) {
5611
- const resolutionPreset = validateChoice(
5612
- opts.resolution,
5613
- [...RESOLUTION_PRESETS],
5614
- "standard",
5615
- "--resolution"
5616
- );
5617
- const resolved = {
5618
- ...opts,
5619
- llm: validateChoice(opts.llmProvider, LLM_CHOICES, "anthropic", "--llm-provider"),
5620
- tts: validateChoice(opts.ttsProvider, TTS_CHOICES, "elevenlabs", "--tts-provider"),
5621
- resolutionPreset,
5622
- resolution: resolvePreset(resolutionPreset)
5623
- };
5624
- const parent = isAbsolute8(opts.dir) ? opts.dir : resolve15(process.cwd(), opts.dir);
5625
- const projectRoot = join10(parent, opts.name);
5626
- if (!opts.force && await pathExists(projectRoot)) {
5897
+ const wantsInteractive = Boolean(process.stdout.isTTY) && !opts.yes;
5898
+ let resolved;
5899
+ let collectedKeys = {};
5900
+ if (wantsInteractive) {
5901
+ const env = await detectEnvironment();
5902
+ const wiz = await runWizard(env);
5903
+ if (!wiz) return;
5904
+ resolved = {
5905
+ name: wiz.projectName,
5906
+ url: wiz.url,
5907
+ dir: opts.dir,
5908
+ force: opts.force,
5909
+ llm: wiz.llm,
5910
+ tts: wiz.tts,
5911
+ resolutionPreset: wiz.resolutionPreset,
5912
+ resolution: resolvePreset(wiz.resolutionPreset)
5913
+ };
5914
+ collectedKeys = wiz.collectedKeys;
5915
+ } else {
5916
+ const resolutionPreset = validateChoice(
5917
+ opts.resolution,
5918
+ [...RESOLUTION_PRESETS],
5919
+ "standard",
5920
+ "--resolution"
5921
+ );
5922
+ resolved = {
5923
+ ...opts,
5924
+ llm: validateChoice(
5925
+ opts.llmProvider,
5926
+ LLM_CHOICES,
5927
+ "anthropic",
5928
+ "--llm-provider"
5929
+ ),
5930
+ tts: validateChoice(
5931
+ opts.ttsProvider,
5932
+ TTS_CHOICES,
5933
+ "elevenlabs",
5934
+ "--tts-provider"
5935
+ ),
5936
+ resolutionPreset,
5937
+ resolution: resolvePreset(resolutionPreset)
5938
+ };
5939
+ }
5940
+ const parent = isAbsolute8(resolved.dir) ? resolved.dir : resolve15(process.cwd(), resolved.dir);
5941
+ const projectRoot = join10(parent, resolved.name);
5942
+ if (!resolved.force && await pathExists(projectRoot)) {
5627
5943
  logger.error(
5628
5944
  `Directory already exists: ${projectRoot}. Pass --force to overwrite, or choose a different --name/--dir.`
5629
5945
  );
@@ -5651,28 +5967,54 @@ async function initCommand(opts) {
5651
5967
  mode: 493
5652
5968
  });
5653
5969
  await writeFile12(join10(projectRoot, "README.md"), readmeTemplate(resolved), "utf8");
5970
+ if (Object.keys(collectedKeys).length > 0) {
5971
+ await writeFile12(join10(projectRoot, ".env"), buildEnvFile(collectedKeys, resolved), "utf8");
5972
+ }
5654
5973
  logger.info(`Showrunner project scaffolded at ${projectRoot} (llm=${resolved.llm}, tts=${resolved.tts})`);
5655
- printNextSteps(opts.name, resolved);
5974
+ printNextSteps(resolved.name, resolved, collectedKeys);
5975
+ }
5976
+ function buildEnvFile(keys, resolved) {
5977
+ const lines = [`# Showrunner secrets \u2014 generated by \`showrunner init\`.`];
5978
+ for (const [k, v] of Object.entries(keys)) {
5979
+ lines.push(`${k}=${v}`);
5980
+ }
5981
+ lines.push("");
5982
+ lines.push(`# Optional: form / setup-script auth.`);
5983
+ lines.push(`DEMO_EMAIL=`);
5984
+ lines.push(`DEMO_PASSWORD=`);
5985
+ lines.push("");
5986
+ lines.push(`# Optional log level: debug | info | warn | error`);
5987
+ lines.push(`SHOWRUNNER_LOG_LEVEL=info`);
5988
+ lines.push("");
5989
+ if (resolved.llm === "openai" || resolved.tts === "openai") {
5990
+ }
5991
+ return lines.join("\n");
5656
5992
  }
5657
- function printNextSteps(projectName, resolved) {
5993
+ function printNextSteps(projectName, resolved, collectedKeys) {
5658
5994
  const envVars = requiredEnvVars(resolved);
5995
+ const keysProvidedByWizard = Object.keys(collectedKeys);
5996
+ const remainingEnvVars = envVars.filter((v) => !keysProvidedByWizard.includes(v));
5659
5997
  const lines = ["", `Next:`, ""];
5660
5998
  let step = 1;
5661
5999
  lines.push(` ${step++}. cd ${projectName}`);
5662
- if (envVars.length > 0) {
5663
- lines.push(` ${step++}. cp .env.example .env`);
6000
+ if (envVars.length === 0) {
6001
+ lines.push(
6002
+ ` ${step++}. (.env not needed \u2014 agent_bridge LLM + ${resolved.tts} TTS don't use API keys.)`
6003
+ );
6004
+ } else if (remainingEnvVars.length === 0) {
6005
+ lines.push(` ${step++}. .env is already populated with the keys you pasted.`);
6006
+ } else {
6007
+ if (keysProvidedByWizard.length === 0) {
6008
+ lines.push(` ${step++}. cp .env.example .env`);
6009
+ }
5664
6010
  lines.push(` ${step++}. Edit .env to fill in:`);
5665
- for (const v of envVars) {
6011
+ for (const v of remainingEnvVars) {
5666
6012
  const dash = PROVIDER_DASHBOARDS[v];
5667
6013
  lines.push(` ${v}${dash ? ` (get it at ${dash})` : ""}`);
5668
6014
  }
5669
6015
  lines.push(
5670
6016
  ` (No keys? Switch llm.default.provider to \`agent_bridge\` in demo.yaml \u2014 uses your local Claude CLI instead.)`
5671
6017
  );
5672
- } else {
5673
- lines.push(
5674
- ` ${step++}. (.env not needed \u2014 agent_bridge LLM + ${resolved.tts} TTS don't use API keys.)`
5675
- );
5676
6018
  }
5677
6019
  lines.push(` ${step++}. Edit docs/PRD.md (the stub explains what each section is for).`);
5678
6020
  lines.push(` ${step++}. showrunner doctor -c demo.yaml`);
@@ -5708,7 +6050,7 @@ function validateChoice(value, allowed, defaultValue, flagName) {
5708
6050
  }
5709
6051
  async function pathExists(p) {
5710
6052
  try {
5711
- await access2(p);
6053
+ await access3(p);
5712
6054
  return true;
5713
6055
  } catch {
5714
6056
  return false;
@@ -6127,8 +6469,8 @@ and \`at_word\` actions degrade to \`at\`).
6127
6469
  }
6128
6470
 
6129
6471
  // src/commands/installBrowser.ts
6130
- import { spawn as spawn4 } from "child_process";
6131
- import { access as access3 } from "fs/promises";
6472
+ import { spawn as spawn5 } from "child_process";
6473
+ import { access as access4 } from "fs/promises";
6132
6474
  import { fileURLToPath } from "url";
6133
6475
  import { dirname as dirname11, join as join11 } from "path";
6134
6476
  var DEFAULT_BROWSER = "chromium";
@@ -6136,7 +6478,7 @@ async function installBrowserCommand(opts) {
6136
6478
  const browser = opts.browser ?? DEFAULT_BROWSER;
6137
6479
  const cli = await resolvePlaywrightCoreCli();
6138
6480
  logger.info(`installing Playwright ${browser} (via bundled playwright-core, no project required)`);
6139
- const child = spawn4(process.execPath, [cli, "install", browser], {
6481
+ const child = spawn5(process.execPath, [cli, "install", browser], {
6140
6482
  stdio: "inherit",
6141
6483
  env: process.env
6142
6484
  });
@@ -6157,7 +6499,7 @@ async function resolvePlaywrightCoreCli() {
6157
6499
  while (dir && dir !== root) {
6158
6500
  const candidate = join11(dir, "node_modules", "playwright-core", "cli.js");
6159
6501
  try {
6160
- await access3(candidate);
6502
+ await access4(candidate);
6161
6503
  return candidate;
6162
6504
  } catch {
6163
6505
  }
@@ -6171,7 +6513,7 @@ async function resolvePlaywrightCoreCli() {
6171
6513
  }
6172
6514
 
6173
6515
  // src/commands/validate.ts
6174
- import { stat as stat10 } from "fs/promises";
6516
+ import { stat as stat11 } from "fs/promises";
6175
6517
  import { isAbsolute as isAbsolute9, resolve as resolve16 } from "path";
6176
6518
  async function validateCommand(opts) {
6177
6519
  try {
@@ -6209,7 +6551,7 @@ async function checkReferencedPaths(config, configDir) {
6209
6551
  if (!relPath) return;
6210
6552
  const abs = isAbsolute9(relPath) ? relPath : resolve16(configDir, relPath);
6211
6553
  try {
6212
- await stat10(abs);
6554
+ await stat11(abs);
6213
6555
  } catch {
6214
6556
  warnings.push(`${label} not found: ${abs}`);
6215
6557
  }
@@ -6260,12 +6602,12 @@ async function printVoCommand(opts) {
6260
6602
  }
6261
6603
  throw err;
6262
6604
  }
6263
- const text = renderVoScript(manifest, { projectName: loaded.config.project.name });
6264
- process.stdout.write(text);
6605
+ const text2 = renderVoScript(manifest, { projectName: loaded.config.project.name });
6606
+ process.stdout.write(text2);
6265
6607
  }
6266
6608
 
6267
6609
  // src/commands/approveVo.ts
6268
- import { rm as rm2, stat as stat11 } from "fs/promises";
6610
+ import { rm as rm2, stat as stat12 } from "fs/promises";
6269
6611
  import { resolve as resolve18 } from "path";
6270
6612
  async function approveVoCommand(opts) {
6271
6613
  let loaded;
@@ -6313,7 +6655,7 @@ async function approveVoCommand(opts) {
6313
6655
  }
6314
6656
  async function pathExists2(p) {
6315
6657
  try {
6316
- await stat11(p);
6658
+ await stat12(p);
6317
6659
  return true;
6318
6660
  } catch {
6319
6661
  return false;
@@ -6531,9 +6873,9 @@ async function launchHeadedSession(opts) {
6531
6873
  const page = await context.newPage();
6532
6874
  if (opts.withCursor) {
6533
6875
  page.on("console", (msg) => {
6534
- const text = msg.text();
6535
- if (text.startsWith("[showrunner-cursor]")) {
6536
- logger.debug(`browser: ${text}`);
6876
+ const text2 = msg.text();
6877
+ if (text2.startsWith("[showrunner-cursor]")) {
6878
+ logger.debug(`browser: ${text2}`);
6537
6879
  }
6538
6880
  });
6539
6881
  }
@@ -6607,7 +6949,7 @@ Then re-run \`showrunner run --config <demo.yaml>\`.
6607
6949
  }
6608
6950
 
6609
6951
  // src/commands/trace.ts
6610
- import { readdir as readdir2, stat as stat12 } from "fs/promises";
6952
+ import { readdir as readdir2, stat as stat13 } from "fs/promises";
6611
6953
  import { isAbsolute as isAbsolute11, join as join13, resolve as resolve22 } from "path";
6612
6954
  async function traceCommand(opts) {
6613
6955
  let loaded;
@@ -6689,7 +7031,7 @@ async function listTraces(dir) {
6689
7031
  }
6690
7032
  async function fileExists8(path) {
6691
7033
  try {
6692
- await stat12(path);
7034
+ await stat13(path);
6693
7035
  return true;
6694
7036
  } catch {
6695
7037
  return false;
@@ -6697,7 +7039,7 @@ async function fileExists8(path) {
6697
7039
  }
6698
7040
 
6699
7041
  // src/commands/preview.ts
6700
- import { stat as stat13 } from "fs/promises";
7042
+ import { stat as stat14 } from "fs/promises";
6701
7043
  import { resolve as resolve23 } from "path";
6702
7044
  async function previewCommand(opts) {
6703
7045
  let loaded;
@@ -6736,7 +7078,7 @@ async function previewCommand(opts) {
6736
7078
  }
6737
7079
  async function fileExists9(path) {
6738
7080
  try {
6739
- await stat13(path);
7081
+ await stat14(path);
6740
7082
  return true;
6741
7083
  } catch {
6742
7084
  return false;
@@ -6875,7 +7217,7 @@ var LineReader = class {
6875
7217
  });
6876
7218
  }
6877
7219
  };
6878
- async function ask(reader, prompt) {
7220
+ async function ask2(reader, prompt) {
6879
7221
  process.stdout.write(prompt);
6880
7222
  return reader.read();
6881
7223
  }
@@ -6886,23 +7228,23 @@ async function runInteractiveQA() {
6886
7228
  process.stdout.write(
6887
7229
  "\nLet's build a product_model interactively. Answer each prompt with a short line.\n\n"
6888
7230
  );
6889
- const productName = (await ask(reader, "1. Product name: ")).trim();
7231
+ const productName = (await ask2(reader, "1. Product name: ")).trim();
6890
7232
  if (!productName) throw new Error("product name is required");
6891
- const tagline = (await ask(reader, "2. One-sentence tagline: ")).trim();
6892
- const primaryUser = (await ask(reader, "3. Who is the primary user? ")).trim();
6893
- const featuresLine = (await ask(reader, "4. Top 3-6 features, comma-separated:\n ")).trim();
7233
+ const tagline = (await ask2(reader, "2. One-sentence tagline: ")).trim();
7234
+ const primaryUser = (await ask2(reader, "3. Who is the primary user? ")).trim();
7235
+ const featuresLine = (await ask2(reader, "4. Top 3-6 features, comma-separated:\n ")).trim();
6894
7236
  const topFeatures = featuresLine.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
6895
- const flowCountStr = (await ask(reader, "5. How many flows would you like to demo (2-4)? ")).trim();
7237
+ const flowCountStr = (await ask2(reader, "5. How many flows would you like to demo (2-4)? ")).trim();
6896
7238
  const flowCount = clamp(parseInt(flowCountStr, 10) || 2, 2, 4);
6897
7239
  const topFlows = [];
6898
7240
  for (let i = 0; i < flowCount; i++) {
6899
- const name = (await ask(reader, ` Flow ${i + 1} name: `)).trim();
6900
- const stepsLine = (await ask(reader, ` Flow ${i + 1} steps, '|' separated:
7241
+ const name = (await ask2(reader, ` Flow ${i + 1} name: `)).trim();
7242
+ const stepsLine = (await ask2(reader, ` Flow ${i + 1} steps, '|' separated:
6901
7243
  `)).trim();
6902
7244
  const steps = stepsLine.split("|").map((s) => s.trim()).filter((s) => s.length > 0);
6903
7245
  topFlows.push({ name, steps });
6904
7246
  }
6905
- const durationStr = (await ask(reader, "6. Target demo duration in seconds (60-120, default 75): ")).trim();
7247
+ const durationStr = (await ask2(reader, "6. Target demo duration in seconds (60-120, default 75): ")).trim();
6906
7248
  const suggestedDurationSeconds = clamp(parseInt(durationStr, 10) || 75, 30, 180);
6907
7249
  return {
6908
7250
  productName,
@@ -7292,7 +7634,7 @@ Apply with: cd ${loaded.configDir} && git apply ${opts.output}
7292
7634
  // src/commands/recordActions.ts
7293
7635
  import { createInterface as createInterface3 } from "readline/promises";
7294
7636
  import { resolve as resolve26 } from "path";
7295
- import { stat as stat14 } from "fs/promises";
7637
+ import { stat as stat15 } from "fs/promises";
7296
7638
 
7297
7639
  // src/recording/captureEvents.ts
7298
7640
  async function installCaptureBinding(context, onEvent) {
@@ -7606,7 +7948,7 @@ async function mergeIntoManifest(opts) {
7606
7948
  }
7607
7949
  async function fileExists10(path) {
7608
7950
  try {
7609
- await stat14(path);
7951
+ await stat15(path);
7610
7952
  return true;
7611
7953
  } catch {
7612
7954
  return false;
@@ -7615,7 +7957,7 @@ async function fileExists10(path) {
7615
7957
 
7616
7958
  // src/cli.ts
7617
7959
  var program = new Command();
7618
- program.name("showrunner").description("Automated product demo recording & production tool").version("1.1.3").option("--json", "emit structured JSON logs to stdout").option("--log-level <level>", "log level (debug|info|warn|error)").hook("preAction", (thisCmd) => {
7960
+ program.name("showrunner").description("Automated product demo recording & production tool").version("1.1.4").option("--json", "emit structured JSON logs to stdout").option("--log-level <level>", "log level (debug|info|warn|error)").hook("preAction", (thisCmd) => {
7619
7961
  const opts = thisCmd.opts();
7620
7962
  if (opts.json) logger.setJson(true);
7621
7963
  if (opts.logLevel) logger.setLevel(opts.logLevel);
@@ -7635,7 +7977,7 @@ program.command("run").description("Run the full Showrunner pipeline").requiredO
7635
7977
  "--resolution <preset>",
7636
7978
  "override resolution for this run: low (854x480), standard (720p), high (1080p), extreme (4K)"
7637
7979
  ).action(runCommand2);
7638
- program.command("init").description("Scaffold a new Showrunner project").option("--name <name>", "project name", "showrunner-demo").option("--url <url>", "target URL of the product to demo", "http://localhost:3000").option("--dir <dir>", "parent directory in which to create the project", process.cwd()).option("--force", "overwrite an existing directory", false).option(
7980
+ program.command("init").description("Scaffold a new Showrunner project (interactive by default; use --yes to skip prompts)").option("--name <name>", "project name", "showrunner-demo").option("--url <url>", "target URL of the product to demo", "http://localhost:3000").option("--dir <dir>", "parent directory in which to create the project", process.cwd()).option("--force", "overwrite an existing directory", false).option("--yes", "skip interactive prompts and use defaults / passed flags", false).option(
7639
7981
  "--llm-provider <name>",
7640
7982
  "LLM provider for comprehension + script + instrument (anthropic | openai | agent_bridge)",
7641
7983
  "anthropic"
@@ -7666,17 +8008,17 @@ program.parseAsync(process.argv).catch((err) => {
7666
8008
  process.exit(1);
7667
8009
  });
7668
8010
  async function printWelcome() {
7669
- const { access: access4 } = await import("fs/promises");
8011
+ const { access: access5 } = await import("fs/promises");
7670
8012
  const { resolve: resolve27 } = await import("path");
7671
8013
  const demoYaml = resolve27(process.cwd(), "demo.yaml");
7672
8014
  let inProject = false;
7673
8015
  try {
7674
- await access4(demoYaml);
8016
+ await access5(demoYaml);
7675
8017
  inProject = true;
7676
8018
  } catch {
7677
8019
  }
7678
8020
  const browserMissing = await isChromiumMissing();
7679
- const lines = ["", `Showrunner v1.1.3`, ""];
8021
+ const lines = ["", `Showrunner v1.1.4`, ""];
7680
8022
  if (browserMissing) {
7681
8023
  lines.push(`Showrunner records using Chromium. You haven't installed it yet.`);
7682
8024
  lines.push(``);
@@ -7704,8 +8046,8 @@ async function isChromiumMissing() {
7704
8046
  try {
7705
8047
  const { chromium: chromium6 } = await import("playwright-core");
7706
8048
  const exec = chromium6.executablePath();
7707
- const { stat: stat15 } = await import("fs/promises");
7708
- await stat15(exec);
8049
+ const { stat: stat16 } = await import("fs/promises");
8050
+ await stat16(exec);
7709
8051
  return false;
7710
8052
  } catch {
7711
8053
  return true;