@kadj-amoah/showrunner 1.1.5 → 1.1.6

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/CHANGELOG.md CHANGED
@@ -2,6 +2,25 @@
2
2
 
3
3
  All notable changes to Showrunner are documented here. Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); the project tracks loose semver — minor bumps for new capability, patch for fixes.
4
4
 
5
+ ## [1.1.6] — 2026-05-24
6
+
7
+ Closes a long-latent gap: `showrunner understand` now has an `--agent` mode that delegates codebase exploration to the local `claude` CLI rather than relying on `comprehension.sources` to be set up correctly.
8
+
9
+ ### Added
10
+
11
+ - **`showrunner understand --agent`** — when set, Showrunner skips the `comprehension.sources` reading path entirely and hands the project off to the local `claude` agent (spawned via `claude -p --output-format json` from `process.cwd()`). The agent uses its native Read/Glob/Grep tools to explore the repository, identify the product, and synthesize a `product_model.json` matching the existing schema. Validated against `productModelSchema` like every other path; retries on schema-violation just like the doc-driven path.
12
+ - Mutually exclusive with `--interactive`. Errors clearly if `claude` isn't on PATH.
13
+
14
+ ### Why this matters
15
+
16
+ Until v1.1.5, the `comprehension.sources` config accepted `type: codebase` with `include`/`exclude` globs, but the implementation (`src/commands/understand.ts:71-88`) called `readFile()` directly on `src.path` for every source type, treating directories as files (which silently fail with `EISDIR`) and ignoring the glob fields entirely. `--agent` sidesteps this: instead of teaching Showrunner to walk codebases itself (which would mean micromatch, fast-glob, binary filters, file caps, vendored-dir defaults), it lets the agent — which already has those capabilities — do the discovery.
17
+
18
+ The `codebase` source type in `comprehension.sources` is **still non-functional** when passed to `understand` without `--agent` (treated as single file → silently skipped). Wiring real codebase support to the document path remains a candidate for a future release, but `--agent` is the recommended path now.
19
+
20
+ ### Updated error messages
21
+
22
+ - When `comprehension.sources` is empty, the error now mentions all three escape hatches: add sources, `--interactive`, or `--agent`.
23
+
5
24
  ## [1.1.5] — 2026-05-24
6
25
 
7
26
  **Phase B + Phase C of the interactive-setup overhaul shipped together.** When the wizard's URL probe fails, it now tries progressively smarter strategies before falling back to a warning.
package/dist/cli.js CHANGED
@@ -7587,6 +7587,71 @@ Regenerate the product_model strictly matching the schema.`
7587
7587
  }
7588
7588
  }
7589
7589
 
7590
+ // src/productModel/generateViaAgent.ts
7591
+ var DEFAULT_MAX_TOKENS5 = 8e3;
7592
+ var AGENT_SYSTEM_PROMPT = [
7593
+ "You are helping Showrunner build a product_model.json for a product demo recording.",
7594
+ "",
7595
+ "You have read-only filesystem tools (Read, Glob, Grep) and can explore the project freely.",
7596
+ "Use them \u2014 do NOT guess from the prompt alone.",
7597
+ "",
7598
+ "What to look for, in order:",
7599
+ " 1. package.json (or pyproject.toml, Cargo.toml, etc.) \u2014 name, description, scripts, deps",
7600
+ " 2. README.md / README.* at the project root \u2014 product description, primary user, features",
7601
+ " 3. docs/PRD.md or similar \u2014 explicit product brief if present",
7602
+ " 4. src/ tree (sample a few files) \u2014 what the product actually DOES, names of routes/pages/components",
7603
+ " 5. .env.example \u2014 hints at integrations and external services",
7604
+ " 6. Any framework config (next.config, vite.config, astro.config, etc.) \u2014 confirms what kind of app this is",
7605
+ "",
7606
+ "When you have enough signal, synthesize a product_model.json matching the requested schema.",
7607
+ "",
7608
+ "Rules:",
7609
+ ' - product_name: short, exact product name as branded. Take this from package.json "name" only if it looks human, else from README.',
7610
+ " - tagline: one sentence, 8\u201315 words, plain language. Derived from README/PRD, not invented.",
7611
+ ` - primary_user: one short phrase. Inferred from the README's "who is this for" framing if explicit, else from the product's shape.`,
7612
+ " - core_flows: 2\u20134 user flows. Each has id (kebab-case), name, 3\u20136 imperative steps from the user's POV, optional entry_url. Ground these in the actual routes/pages the codebase exposes.",
7613
+ " - key_features: 3\u20136 short bullets. No marketing fluff.",
7614
+ " - demo_recommendation.suggested_flows: ids from core_flows, in demo order.",
7615
+ " - demo_recommendation.suggested_duration_seconds: 60\u201390 typical.",
7616
+ " - confidence: 'high' if you found explicit, clear sources; 'medium' if you had to infer; 'low' if the codebase was sparse and you mostly guessed.",
7617
+ " - source: always 'documents' (this path counts as document-driven, just agentically discovered).",
7618
+ " - generated_at: ISO-8601 timestamp.",
7619
+ "",
7620
+ "Output exactly the structured JSON. No prose, no commentary, no markdown fence outside the JSON."
7621
+ ].join("\n");
7622
+ async function generateProductModelViaAgent(opts) {
7623
+ const provider = new AgentBridgeLLMProvider({
7624
+ mode: "spawn",
7625
+ command: opts.command ?? "claude",
7626
+ args: opts.args ?? ["-p", "--output-format", "json"],
7627
+ timeoutMs: opts.timeoutMs ?? 18e4,
7628
+ cwd: opts.projectDir
7629
+ });
7630
+ const userPrompt = [
7631
+ `Project directory: ${opts.projectDir}`,
7632
+ "",
7633
+ "Explore this directory with your filesystem tools, then synthesize a product_model.json."
7634
+ ].join("\n");
7635
+ logger.debug("Generating product_model via agent", { projectDir: opts.projectDir });
7636
+ try {
7637
+ return await generateWithRetry(provider, {
7638
+ systemPrompt: AGENT_SYSTEM_PROMPT,
7639
+ userPrompt,
7640
+ schema: productModelSchema,
7641
+ schemaName: "product_model",
7642
+ maxTokens: DEFAULT_MAX_TOKENS5,
7643
+ retryRenderer: (errorText, prevUserPrompt) => `${prevUserPrompt}
7644
+
7645
+ Your previous output failed validation with this error:
7646
+ ${errorText}
7647
+
7648
+ Regenerate the product_model strictly matching the schema.`
7649
+ });
7650
+ } catch (err) {
7651
+ throw new ProductModelGenerationError(err instanceof Error ? err.message : String(err));
7652
+ }
7653
+ }
7654
+
7590
7655
  // src/productModel/interactive.ts
7591
7656
  import { createInterface as createInterface2 } from "readline";
7592
7657
  var LineReader = class {
@@ -7675,6 +7740,10 @@ function clamp(n, lo, hi) {
7675
7740
 
7676
7741
  // src/commands/understand.ts
7677
7742
  async function understandCommand(opts) {
7743
+ if (opts.interactive && opts.agent) {
7744
+ logger.error("--interactive and --agent are mutually exclusive. Pick one.");
7745
+ process.exit(2);
7746
+ }
7678
7747
  let configDir = process.cwd();
7679
7748
  let outputRel = opts.output ?? "./product_model.json";
7680
7749
  let sources = [];
@@ -7705,16 +7774,20 @@ async function understandCommand(opts) {
7705
7774
  const outputPath = isAbsolute13(outputRel) ? outputRel : resolve25(configDir, outputRel);
7706
7775
  await mkdir15(dirname13(outputPath), { recursive: true });
7707
7776
  let productModel;
7708
- const provider = resolveDefaultLLMProvider({ configDir, llm: llmConfig });
7709
7777
  try {
7710
- if (opts.interactive) {
7778
+ if (opts.agent) {
7779
+ const projectDir = process.cwd();
7780
+ logger.info("Generating product_model via local `claude` agent", { projectDir });
7781
+ productModel = await generateProductModelViaAgent({ projectDir });
7782
+ } else if (opts.interactive) {
7711
7783
  const answers = await runInteractiveQA();
7712
7784
  logger.info("Generating product_model from interactive answers");
7785
+ const provider = resolveDefaultLLMProvider({ configDir, llm: llmConfig });
7713
7786
  productModel = await generateProductModelFromInteractive({ answers, provider });
7714
7787
  } else {
7715
7788
  if (sources.length === 0) {
7716
7789
  logger.error(
7717
- "No `comprehension.sources` configured in demo.yaml. Add at least one source (prd, readme, codebase, etc.) or re-run with --interactive."
7790
+ "No `comprehension.sources` configured in demo.yaml. Add at least one source (prd, readme, codebase, etc.), re-run with --interactive (Q&A), or re-run with --agent (the local `claude` CLI explores your repo)."
7718
7791
  );
7719
7792
  process.exit(2);
7720
7793
  }
@@ -7736,6 +7809,7 @@ async function understandCommand(opts) {
7736
7809
  logger.info("Generating product_model from documents", {
7737
7810
  sources: docs.map((d) => d.path)
7738
7811
  });
7812
+ const provider = resolveDefaultLLMProvider({ configDir, llm: llmConfig });
7739
7813
  productModel = await generateProductModelFromDocs({ sources: docs, provider });
7740
7814
  }
7741
7815
  } catch (err) {
@@ -7832,7 +7906,7 @@ function pickAttr(node, attr) {
7832
7906
 
7833
7907
  // src/instrument/suggest.ts
7834
7908
  import { z as z10 } from "zod";
7835
- var DEFAULT_MAX_TOKENS5 = 8e3;
7909
+ var DEFAULT_MAX_TOKENS6 = 8e3;
7836
7910
  var suggestionSchema = z10.object({
7837
7911
  suggestions: z10.array(
7838
7912
  z10.object({
@@ -7878,7 +7952,7 @@ For each, return {file, line, original (exact source line), replacement (source
7878
7952
  userPrompt,
7879
7953
  schema: suggestionSchema,
7880
7954
  schemaName: "instrument_suggestions",
7881
- maxTokens: DEFAULT_MAX_TOKENS5
7955
+ maxTokens: DEFAULT_MAX_TOKENS6
7882
7956
  });
7883
7957
  return result.suggestions;
7884
7958
  } catch (err) {
@@ -8368,7 +8442,7 @@ async function fileExists10(path) {
8368
8442
 
8369
8443
  // src/cli.ts
8370
8444
  var program = new Command();
8371
- program.name("showrunner").description("Automated product demo recording & production tool").version("1.1.5").option("--json", "emit structured JSON logs to stdout").option("--log-level <level>", "log level (debug|info|warn|error)").hook("preAction", (thisCmd) => {
8445
+ program.name("showrunner").description("Automated product demo recording & production tool").version("1.1.6").option("--json", "emit structured JSON logs to stdout").option("--log-level <level>", "log level (debug|info|warn|error)").hook("preAction", (thisCmd) => {
8372
8446
  const opts = thisCmd.opts();
8373
8447
  if (opts.json) logger.setJson(true);
8374
8448
  if (opts.logLevel) logger.setLevel(opts.logLevel);
@@ -8405,7 +8479,10 @@ program.command("set-target").description("Update demo.yaml's recording.target_u
8405
8479
  program.command("install-browser").description("Install the Playwright browser binary (chromium by default) \u2014 wraps playwright-core install").option("--browser <name>", "browser to install: chromium | firefox | webkit", "chromium").action(installBrowserCommand);
8406
8480
  program.command("doctor").description("Run preflight checks on the current config + environment").requiredOption("-c, --config <path>", "path to demo.yaml").option("--json", "emit results as JSON instead of human-readable rows").action(doctorCommand);
8407
8481
  program.command("validate").description("Validate a demo.yaml config file").requiredOption("-c, --config <path>", "path to demo.yaml").option("--strict", "exit nonzero on any warning (e.g. missing provider env var)").action(validateCommand);
8408
- program.command("understand").description("Build product_model.json from documents or interactive Q&A").option("-c, --config <path>", "path to demo.yaml").option("--interactive", "use interactive Q&A mode").option("--output <path>", "output path for product_model.json").action(understandCommand);
8482
+ program.command("understand").description("Build product_model.json from documents, interactive Q&A, or agent-driven repo exploration").option("-c, --config <path>", "path to demo.yaml").option("--interactive", "use interactive Q&A mode (5 prompts, no LLM)").option(
8483
+ "--agent",
8484
+ "delegate to the local `claude` CLI: it explores the current directory with its read tools and synthesizes the product model. Closes the type=codebase gap in demo.yaml sources."
8485
+ ).option("--output <path>", "output path for product_model.json").action(understandCommand);
8409
8486
  program.command("instrument").description("Suggest data-testid attributes for a codebase").requiredOption("-c, --config <path>", "path to demo.yaml").requiredOption("--output <path>", "unified diff output path").option("--glob <pattern>", "override comprehension.sources with an ad-hoc glob (relative to configDir)").action(instrumentCommand);
8410
8487
  program.command("record-actions").description("Author manifest actions by demonstrating them in a live browser").requiredOption("-c, --config <path>", "path to demo.yaml").option("--segment <id>", "replace actions for an existing segment id").option("--output <path>", "manifest output path (default ./scripts/manifest.json)").action(recordActionsCommand);
8411
8488
  program.command("preview").description("Preview the generated Playwright script in UI Mode").requiredOption("-c, --config <path>", "path to demo.yaml").action(previewCommand);
@@ -8430,7 +8507,7 @@ async function printWelcome() {
8430
8507
  } catch {
8431
8508
  }
8432
8509
  const browserMissing = await isChromiumMissing();
8433
- const lines = ["", `Showrunner v1.1.5`, ""];
8510
+ const lines = ["", `Showrunner v1.1.6`, ""];
8434
8511
  if (browserMissing) {
8435
8512
  lines.push(`Showrunner records using Chromium. You haven't installed it yet.`);
8436
8513
  lines.push(``);