@kadj-amoah/showrunner 1.1.5 → 1.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +44 -0
- package/dist/cli.js +141 -9
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +11 -0
- package/dist/index.js +8 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,50 @@
|
|
|
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.7] — 2026-05-24
|
|
6
|
+
|
|
7
|
+
Hot-fix the most embarrassing thing about v1.1.6: `showrunner understand --agent` was pointing the agent at the wrong directory.
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- **`understand --agent` now anchors the agent at the actual product codebase, not the Showrunner scaffold.** v1.1.6 spawned `claude` with `cwd = process.cwd()`. The canonical workflow has the user `cd` into their Showrunner scaffold (`cd my-demo`) before running commands, which meant the agent explored a directory containing only template files (the scaffold's stub README, PRD stub, `scripts/`, `segments/`, `output/`) — nothing about the actual product. The agent spent 180s exploring noise and timed out.
|
|
12
|
+
- **Resolution order in v1.1.7** when `--agent` is set:
|
|
13
|
+
1. `--project-dir <path>` flag (explicit per-invocation override)
|
|
14
|
+
2. `project.codebase_root` field in `demo.yaml`, resolved relative to the config's directory
|
|
15
|
+
3. `configDir/..` (the canonical scaffold-inside-product layout) — used when `-c demo.yaml` is given but no explicit anchor exists
|
|
16
|
+
4. `process.cwd()` (only when no `-c` was given)
|
|
17
|
+
- Resolved path is now logged prominently before the agent spawns: `Generating product_model via local claude agent { projectDir: ..., source: ... }`. The `source` field tells you which rule resolved the path.
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- **`--project-dir <path>`** flag on `showrunner understand`. Override the agent's exploration root for a single invocation.
|
|
22
|
+
- **`project.codebase_root`** field in the config schema. Persistent override per project.
|
|
23
|
+
- **`init`'s generated `demo.yaml` now writes `codebase_root: ..` by default**, so newly scaffolded projects get the right behaviour without the user having to know about the field.
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
|
|
27
|
+
- **Claude spawn now passes `--allowedTools "Read,Glob,Grep"`** to be explicit about which tools the agent may use without prompting. v1.1.6 relied on claude's `default` permission mode, which is supposed to auto-approve reads but leaves room for ambiguity around Glob/Grep/Bash. Being explicit short-circuits any related stalling.
|
|
28
|
+
- **Agent spawn timeout dropped from 180s to 90s.** Failure should fail fast; three-minute waits on retest are painful.
|
|
29
|
+
|
|
30
|
+
## [1.1.6] — 2026-05-24
|
|
31
|
+
|
|
32
|
+
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.
|
|
33
|
+
|
|
34
|
+
### Added
|
|
35
|
+
|
|
36
|
+
- **`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.
|
|
37
|
+
- Mutually exclusive with `--interactive`. Errors clearly if `claude` isn't on PATH.
|
|
38
|
+
|
|
39
|
+
### Why this matters
|
|
40
|
+
|
|
41
|
+
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.
|
|
42
|
+
|
|
43
|
+
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.
|
|
44
|
+
|
|
45
|
+
### Updated error messages
|
|
46
|
+
|
|
47
|
+
- When `comprehension.sources` is empty, the error now mentions all three escape hatches: add sources, `--interactive`, or `--agent`.
|
|
48
|
+
|
|
5
49
|
## [1.1.5] — 2026-05-24
|
|
6
50
|
|
|
7
51
|
**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
|
@@ -76,7 +76,14 @@ import "zod";
|
|
|
76
76
|
import { z } from "zod";
|
|
77
77
|
var projectSchema = z.object({
|
|
78
78
|
name: z.string().min(1),
|
|
79
|
-
product_model: z.string().optional()
|
|
79
|
+
product_model: z.string().optional(),
|
|
80
|
+
/**
|
|
81
|
+
* Path to the product's codebase, resolved relative to demo.yaml's directory.
|
|
82
|
+
* Used by `understand --agent` to anchor the agent's exploration.
|
|
83
|
+
* Defaults to `..` because the canonical scaffold layout places the
|
|
84
|
+
* Showrunner project inside or beside the product directory.
|
|
85
|
+
*/
|
|
86
|
+
codebase_root: z.string().optional()
|
|
80
87
|
});
|
|
81
88
|
var comprehensionSourceSchema = z.object({
|
|
82
89
|
type: z.enum(["prd", "readme", "codebase", "openapi", "changelog", "custom"]),
|
|
@@ -6423,6 +6430,11 @@ function demoYamlTemplate(opts) {
|
|
|
6423
6430
|
project:
|
|
6424
6431
|
name: ${opts.name}
|
|
6425
6432
|
# product_model: ./product_model.json # uncomment to skip comprehension
|
|
6433
|
+
# Where the product codebase lives, relative to this demo.yaml. Used by
|
|
6434
|
+
# \`showrunner understand --agent\` to anchor the agent's exploration.
|
|
6435
|
+
# Default \`..\` matches the canonical layout where the Showrunner scaffold
|
|
6436
|
+
# sits inside (or beside) the product directory.
|
|
6437
|
+
codebase_root: ..
|
|
6426
6438
|
|
|
6427
6439
|
comprehension:
|
|
6428
6440
|
mode: documents
|
|
@@ -7587,6 +7599,80 @@ Regenerate the product_model strictly matching the schema.`
|
|
|
7587
7599
|
}
|
|
7588
7600
|
}
|
|
7589
7601
|
|
|
7602
|
+
// src/productModel/generateViaAgent.ts
|
|
7603
|
+
var DEFAULT_MAX_TOKENS5 = 8e3;
|
|
7604
|
+
var AGENT_SYSTEM_PROMPT = [
|
|
7605
|
+
"You are helping Showrunner build a product_model.json for a product demo recording.",
|
|
7606
|
+
"",
|
|
7607
|
+
"You have read-only filesystem tools (Read, Glob, Grep) and can explore the project freely.",
|
|
7608
|
+
"Use them \u2014 do NOT guess from the prompt alone.",
|
|
7609
|
+
"",
|
|
7610
|
+
"What to look for, in order:",
|
|
7611
|
+
" 1. package.json (or pyproject.toml, Cargo.toml, etc.) \u2014 name, description, scripts, deps",
|
|
7612
|
+
" 2. README.md / README.* at the project root \u2014 product description, primary user, features",
|
|
7613
|
+
" 3. docs/PRD.md or similar \u2014 explicit product brief if present",
|
|
7614
|
+
" 4. src/ tree (sample a few files) \u2014 what the product actually DOES, names of routes/pages/components",
|
|
7615
|
+
" 5. .env.example \u2014 hints at integrations and external services",
|
|
7616
|
+
" 6. Any framework config (next.config, vite.config, astro.config, etc.) \u2014 confirms what kind of app this is",
|
|
7617
|
+
"",
|
|
7618
|
+
"When you have enough signal, synthesize a product_model.json matching the requested schema.",
|
|
7619
|
+
"",
|
|
7620
|
+
"Rules:",
|
|
7621
|
+
' - product_name: short, exact product name as branded. Take this from package.json "name" only if it looks human, else from README.',
|
|
7622
|
+
" - tagline: one sentence, 8\u201315 words, plain language. Derived from README/PRD, not invented.",
|
|
7623
|
+
` - primary_user: one short phrase. Inferred from the README's "who is this for" framing if explicit, else from the product's shape.`,
|
|
7624
|
+
" - 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.",
|
|
7625
|
+
" - key_features: 3\u20136 short bullets. No marketing fluff.",
|
|
7626
|
+
" - demo_recommendation.suggested_flows: ids from core_flows, in demo order.",
|
|
7627
|
+
" - demo_recommendation.suggested_duration_seconds: 60\u201390 typical.",
|
|
7628
|
+
" - confidence: 'high' if you found explicit, clear sources; 'medium' if you had to infer; 'low' if the codebase was sparse and you mostly guessed.",
|
|
7629
|
+
" - source: always 'documents' (this path counts as document-driven, just agentically discovered).",
|
|
7630
|
+
" - generated_at: ISO-8601 timestamp.",
|
|
7631
|
+
"",
|
|
7632
|
+
"Output exactly the structured JSON. No prose, no commentary, no markdown fence outside the JSON."
|
|
7633
|
+
].join("\n");
|
|
7634
|
+
async function generateProductModelViaAgent(opts) {
|
|
7635
|
+
const provider = new AgentBridgeLLMProvider({
|
|
7636
|
+
mode: "spawn",
|
|
7637
|
+
command: opts.command ?? "claude",
|
|
7638
|
+
args: opts.args ?? [
|
|
7639
|
+
"-p",
|
|
7640
|
+
"--output-format",
|
|
7641
|
+
"json",
|
|
7642
|
+
// Explicitly allow read-only filesystem tools so claude doesn't stall on
|
|
7643
|
+
// permission ambiguity in headless mode. Comma-separated per claude CLI syntax.
|
|
7644
|
+
"--allowedTools",
|
|
7645
|
+
"Read,Glob,Grep"
|
|
7646
|
+
],
|
|
7647
|
+
// 90s cap — was 180s in v1.1.6, dropped here because failures should fail fast.
|
|
7648
|
+
timeoutMs: opts.timeoutMs ?? 9e4,
|
|
7649
|
+
cwd: opts.projectDir
|
|
7650
|
+
});
|
|
7651
|
+
const userPrompt = [
|
|
7652
|
+
`Project directory: ${opts.projectDir}`,
|
|
7653
|
+
"",
|
|
7654
|
+
"Explore this directory with your filesystem tools, then synthesize a product_model.json."
|
|
7655
|
+
].join("\n");
|
|
7656
|
+
logger.debug("Generating product_model via agent", { projectDir: opts.projectDir });
|
|
7657
|
+
try {
|
|
7658
|
+
return await generateWithRetry(provider, {
|
|
7659
|
+
systemPrompt: AGENT_SYSTEM_PROMPT,
|
|
7660
|
+
userPrompt,
|
|
7661
|
+
schema: productModelSchema,
|
|
7662
|
+
schemaName: "product_model",
|
|
7663
|
+
maxTokens: DEFAULT_MAX_TOKENS5,
|
|
7664
|
+
retryRenderer: (errorText, prevUserPrompt) => `${prevUserPrompt}
|
|
7665
|
+
|
|
7666
|
+
Your previous output failed validation with this error:
|
|
7667
|
+
${errorText}
|
|
7668
|
+
|
|
7669
|
+
Regenerate the product_model strictly matching the schema.`
|
|
7670
|
+
});
|
|
7671
|
+
} catch (err) {
|
|
7672
|
+
throw new ProductModelGenerationError(err instanceof Error ? err.message : String(err));
|
|
7673
|
+
}
|
|
7674
|
+
}
|
|
7675
|
+
|
|
7590
7676
|
// src/productModel/interactive.ts
|
|
7591
7677
|
import { createInterface as createInterface2 } from "readline";
|
|
7592
7678
|
var LineReader = class {
|
|
@@ -7675,10 +7761,15 @@ function clamp(n, lo, hi) {
|
|
|
7675
7761
|
|
|
7676
7762
|
// src/commands/understand.ts
|
|
7677
7763
|
async function understandCommand(opts) {
|
|
7764
|
+
if (opts.interactive && opts.agent) {
|
|
7765
|
+
logger.error("--interactive and --agent are mutually exclusive. Pick one.");
|
|
7766
|
+
process.exit(2);
|
|
7767
|
+
}
|
|
7678
7768
|
let configDir = process.cwd();
|
|
7679
7769
|
let outputRel = opts.output ?? "./product_model.json";
|
|
7680
7770
|
let sources = [];
|
|
7681
7771
|
let llmConfig;
|
|
7772
|
+
let loadedCodebaseRoot;
|
|
7682
7773
|
if (opts.config) {
|
|
7683
7774
|
let loaded;
|
|
7684
7775
|
try {
|
|
@@ -7696,6 +7787,7 @@ async function understandCommand(opts) {
|
|
|
7696
7787
|
}
|
|
7697
7788
|
sources = loaded.config.comprehension.sources.map((s) => ({ path: s.path, type: s.type }));
|
|
7698
7789
|
llmConfig = loaded.config.llm;
|
|
7790
|
+
loadedCodebaseRoot = loaded.config.project.codebase_root;
|
|
7699
7791
|
const envFile = resolve25(loaded.configDir, ".env");
|
|
7700
7792
|
try {
|
|
7701
7793
|
process.loadEnvFile(envFile);
|
|
@@ -7705,16 +7797,31 @@ async function understandCommand(opts) {
|
|
|
7705
7797
|
const outputPath = isAbsolute13(outputRel) ? outputRel : resolve25(configDir, outputRel);
|
|
7706
7798
|
await mkdir15(dirname13(outputPath), { recursive: true });
|
|
7707
7799
|
let productModel;
|
|
7708
|
-
const provider = resolveDefaultLLMProvider({ configDir, llm: llmConfig });
|
|
7709
7800
|
try {
|
|
7710
|
-
if (opts.
|
|
7801
|
+
if (opts.agent) {
|
|
7802
|
+
const projectDir = resolveAgentProjectDir({
|
|
7803
|
+
flagOverride: opts.projectDir,
|
|
7804
|
+
codebaseRoot: opts.config ? loadedCodebaseRoot : void 0,
|
|
7805
|
+
configDir: opts.config ? configDir : void 0
|
|
7806
|
+
});
|
|
7807
|
+
logger.info("Generating product_model via local `claude` agent", {
|
|
7808
|
+
projectDir,
|
|
7809
|
+
source: resolveAgentProjectDirSource({
|
|
7810
|
+
flagOverride: opts.projectDir,
|
|
7811
|
+
codebaseRoot: opts.config ? loadedCodebaseRoot : void 0,
|
|
7812
|
+
configDir: opts.config ? configDir : void 0
|
|
7813
|
+
})
|
|
7814
|
+
});
|
|
7815
|
+
productModel = await generateProductModelViaAgent({ projectDir });
|
|
7816
|
+
} else if (opts.interactive) {
|
|
7711
7817
|
const answers = await runInteractiveQA();
|
|
7712
7818
|
logger.info("Generating product_model from interactive answers");
|
|
7819
|
+
const provider = resolveDefaultLLMProvider({ configDir, llm: llmConfig });
|
|
7713
7820
|
productModel = await generateProductModelFromInteractive({ answers, provider });
|
|
7714
7821
|
} else {
|
|
7715
7822
|
if (sources.length === 0) {
|
|
7716
7823
|
logger.error(
|
|
7717
|
-
"No `comprehension.sources` configured in demo.yaml. Add at least one source (prd, readme, codebase, etc.) or re-run with --
|
|
7824
|
+
"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
7825
|
);
|
|
7719
7826
|
process.exit(2);
|
|
7720
7827
|
}
|
|
@@ -7736,6 +7843,7 @@ async function understandCommand(opts) {
|
|
|
7736
7843
|
logger.info("Generating product_model from documents", {
|
|
7737
7844
|
sources: docs.map((d) => d.path)
|
|
7738
7845
|
});
|
|
7846
|
+
const provider = resolveDefaultLLMProvider({ configDir, llm: llmConfig });
|
|
7739
7847
|
productModel = await generateProductModelFromDocs({ sources: docs, provider });
|
|
7740
7848
|
}
|
|
7741
7849
|
} catch (err) {
|
|
@@ -7748,6 +7856,24 @@ async function understandCommand(opts) {
|
|
|
7748
7856
|
await writeFile15(outputPath, JSON.stringify(productModel, null, 2) + "\n", "utf8");
|
|
7749
7857
|
logger.info("Wrote product_model.json", { path: outputPath });
|
|
7750
7858
|
}
|
|
7859
|
+
function resolveAgentProjectDir(input) {
|
|
7860
|
+
if (input.flagOverride) {
|
|
7861
|
+
return isAbsolute13(input.flagOverride) ? input.flagOverride : resolve25(process.cwd(), input.flagOverride);
|
|
7862
|
+
}
|
|
7863
|
+
if (input.codebaseRoot && input.configDir) {
|
|
7864
|
+
return isAbsolute13(input.codebaseRoot) ? input.codebaseRoot : resolve25(input.configDir, input.codebaseRoot);
|
|
7865
|
+
}
|
|
7866
|
+
if (input.configDir) {
|
|
7867
|
+
return resolve25(input.configDir, "..");
|
|
7868
|
+
}
|
|
7869
|
+
return process.cwd();
|
|
7870
|
+
}
|
|
7871
|
+
function resolveAgentProjectDirSource(input) {
|
|
7872
|
+
if (input.flagOverride) return "--project-dir flag";
|
|
7873
|
+
if (input.codebaseRoot && input.configDir) return "project.codebase_root in demo.yaml";
|
|
7874
|
+
if (input.configDir) return "configDir/.. (default; set project.codebase_root to override)";
|
|
7875
|
+
return "cwd (no -c flag)";
|
|
7876
|
+
}
|
|
7751
7877
|
|
|
7752
7878
|
// src/commands/instrument.ts
|
|
7753
7879
|
import { mkdir as mkdir16, readdir as readdir4, writeFile as writeFile16 } from "fs/promises";
|
|
@@ -7832,7 +7958,7 @@ function pickAttr(node, attr) {
|
|
|
7832
7958
|
|
|
7833
7959
|
// src/instrument/suggest.ts
|
|
7834
7960
|
import { z as z10 } from "zod";
|
|
7835
|
-
var
|
|
7961
|
+
var DEFAULT_MAX_TOKENS6 = 8e3;
|
|
7836
7962
|
var suggestionSchema = z10.object({
|
|
7837
7963
|
suggestions: z10.array(
|
|
7838
7964
|
z10.object({
|
|
@@ -7878,7 +8004,7 @@ For each, return {file, line, original (exact source line), replacement (source
|
|
|
7878
8004
|
userPrompt,
|
|
7879
8005
|
schema: suggestionSchema,
|
|
7880
8006
|
schemaName: "instrument_suggestions",
|
|
7881
|
-
maxTokens:
|
|
8007
|
+
maxTokens: DEFAULT_MAX_TOKENS6
|
|
7882
8008
|
});
|
|
7883
8009
|
return result.suggestions;
|
|
7884
8010
|
} catch (err) {
|
|
@@ -8368,7 +8494,7 @@ async function fileExists10(path) {
|
|
|
8368
8494
|
|
|
8369
8495
|
// src/cli.ts
|
|
8370
8496
|
var program = new Command();
|
|
8371
|
-
program.name("showrunner").description("Automated product demo recording & production tool").version("1.1.
|
|
8497
|
+
program.name("showrunner").description("Automated product demo recording & production tool").version("1.1.7").option("--json", "emit structured JSON logs to stdout").option("--log-level <level>", "log level (debug|info|warn|error)").hook("preAction", (thisCmd) => {
|
|
8372
8498
|
const opts = thisCmd.opts();
|
|
8373
8499
|
if (opts.json) logger.setJson(true);
|
|
8374
8500
|
if (opts.logLevel) logger.setLevel(opts.logLevel);
|
|
@@ -8405,7 +8531,13 @@ program.command("set-target").description("Update demo.yaml's recording.target_u
|
|
|
8405
8531
|
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
8532
|
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
8533
|
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
|
|
8534
|
+
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(
|
|
8535
|
+
"--agent",
|
|
8536
|
+
"delegate to the local `claude` CLI: it explores the project with its read tools and synthesizes the product model. Closes the type=codebase gap in demo.yaml sources."
|
|
8537
|
+
).option(
|
|
8538
|
+
"--project-dir <path>",
|
|
8539
|
+
"directory the --agent run should explore. Overrides project.codebase_root from demo.yaml. Defaults to configDir/.. when -c is given, or cwd otherwise."
|
|
8540
|
+
).option("--output <path>", "output path for product_model.json").action(understandCommand);
|
|
8409
8541
|
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
8542
|
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
8543
|
program.command("preview").description("Preview the generated Playwright script in UI Mode").requiredOption("-c, --config <path>", "path to demo.yaml").action(previewCommand);
|
|
@@ -8430,7 +8562,7 @@ async function printWelcome() {
|
|
|
8430
8562
|
} catch {
|
|
8431
8563
|
}
|
|
8432
8564
|
const browserMissing = await isChromiumMissing();
|
|
8433
|
-
const lines = ["", `Showrunner v1.1.
|
|
8565
|
+
const lines = ["", `Showrunner v1.1.7`, ""];
|
|
8434
8566
|
if (browserMissing) {
|
|
8435
8567
|
lines.push(`Showrunner records using Chromium. You haven't installed it yet.`);
|
|
8436
8568
|
lines.push(``);
|