@kadj-amoah/showrunner 1.1.6 → 1.1.8
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 +187 -186
- package/README.md +213 -191
- package/dist/cli.js +149 -19
- 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 +85 -85
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"]),
|
|
@@ -5928,6 +5935,29 @@ function sleep3(ms) {
|
|
|
5928
5935
|
var SLUG_RE = /^[a-z0-9](?:[a-z0-9._-]*[a-z0-9])?$/i;
|
|
5929
5936
|
async function runWizard(env) {
|
|
5930
5937
|
intro("Showrunner setup");
|
|
5938
|
+
note(
|
|
5939
|
+
[
|
|
5940
|
+
`Showrunner will scaffold a new project inside:`,
|
|
5941
|
+
``,
|
|
5942
|
+
` ${process.cwd()}`,
|
|
5943
|
+
``,
|
|
5944
|
+
`That directory should be the root of the product you want to demo (the dir`,
|
|
5945
|
+
`where your package.json / pyproject.toml / etc. lives). If you're somewhere`,
|
|
5946
|
+
`else, cancel with Ctrl+C, \`cd\` to your product's root, and re-run`,
|
|
5947
|
+
`\`showrunner init\` from there.`
|
|
5948
|
+
].join("\n"),
|
|
5949
|
+
"Heads up \u2014 confirm your starting directory"
|
|
5950
|
+
);
|
|
5951
|
+
const proceedFromCwd = await ask(
|
|
5952
|
+
confirm({
|
|
5953
|
+
message: "Continue scaffolding from this directory?",
|
|
5954
|
+
initialValue: true
|
|
5955
|
+
})
|
|
5956
|
+
);
|
|
5957
|
+
if (proceedFromCwd === null || proceedFromCwd === false) {
|
|
5958
|
+
cancel("Setup cancelled. Re-run `showrunner init` from your product root.");
|
|
5959
|
+
return null;
|
|
5960
|
+
}
|
|
5931
5961
|
note(formatDetection(env), "Detected on this machine");
|
|
5932
5962
|
const projectName = await ask(
|
|
5933
5963
|
text({
|
|
@@ -6423,6 +6453,11 @@ function demoYamlTemplate(opts) {
|
|
|
6423
6453
|
project:
|
|
6424
6454
|
name: ${opts.name}
|
|
6425
6455
|
# product_model: ./product_model.json # uncomment to skip comprehension
|
|
6456
|
+
# Where the product codebase lives, relative to this demo.yaml. Used by
|
|
6457
|
+
# \`showrunner understand --agent\` to anchor the agent's exploration.
|
|
6458
|
+
# Default \`..\` matches the canonical layout where the Showrunner scaffold
|
|
6459
|
+
# sits inside (or beside) the product directory.
|
|
6460
|
+
codebase_root: ..
|
|
6426
6461
|
|
|
6427
6462
|
comprehension:
|
|
6428
6463
|
mode: documents
|
|
@@ -7623,8 +7658,17 @@ async function generateProductModelViaAgent(opts) {
|
|
|
7623
7658
|
const provider = new AgentBridgeLLMProvider({
|
|
7624
7659
|
mode: "spawn",
|
|
7625
7660
|
command: opts.command ?? "claude",
|
|
7626
|
-
args: opts.args ?? [
|
|
7627
|
-
|
|
7661
|
+
args: opts.args ?? [
|
|
7662
|
+
"-p",
|
|
7663
|
+
"--output-format",
|
|
7664
|
+
"json",
|
|
7665
|
+
// Explicitly allow read-only filesystem tools so claude doesn't stall on
|
|
7666
|
+
// permission ambiguity in headless mode. Comma-separated per claude CLI syntax.
|
|
7667
|
+
"--allowedTools",
|
|
7668
|
+
"Read,Glob,Grep"
|
|
7669
|
+
],
|
|
7670
|
+
// 90s cap — was 180s in v1.1.6, dropped here because failures should fail fast.
|
|
7671
|
+
timeoutMs: opts.timeoutMs ?? 9e4,
|
|
7628
7672
|
cwd: opts.projectDir
|
|
7629
7673
|
});
|
|
7630
7674
|
const userPrompt = [
|
|
@@ -7748,6 +7792,7 @@ async function understandCommand(opts) {
|
|
|
7748
7792
|
let outputRel = opts.output ?? "./product_model.json";
|
|
7749
7793
|
let sources = [];
|
|
7750
7794
|
let llmConfig;
|
|
7795
|
+
let loadedCodebaseRoot;
|
|
7751
7796
|
if (opts.config) {
|
|
7752
7797
|
let loaded;
|
|
7753
7798
|
try {
|
|
@@ -7765,6 +7810,7 @@ async function understandCommand(opts) {
|
|
|
7765
7810
|
}
|
|
7766
7811
|
sources = loaded.config.comprehension.sources.map((s) => ({ path: s.path, type: s.type }));
|
|
7767
7812
|
llmConfig = loaded.config.llm;
|
|
7813
|
+
loadedCodebaseRoot = loaded.config.project.codebase_root;
|
|
7768
7814
|
const envFile = resolve25(loaded.configDir, ".env");
|
|
7769
7815
|
try {
|
|
7770
7816
|
process.loadEnvFile(envFile);
|
|
@@ -7776,8 +7822,19 @@ async function understandCommand(opts) {
|
|
|
7776
7822
|
let productModel;
|
|
7777
7823
|
try {
|
|
7778
7824
|
if (opts.agent) {
|
|
7779
|
-
const projectDir =
|
|
7780
|
-
|
|
7825
|
+
const projectDir = resolveAgentProjectDir({
|
|
7826
|
+
flagOverride: opts.projectDir,
|
|
7827
|
+
codebaseRoot: opts.config ? loadedCodebaseRoot : void 0,
|
|
7828
|
+
configDir: opts.config ? configDir : void 0
|
|
7829
|
+
});
|
|
7830
|
+
logger.info("Generating product_model via local `claude` agent", {
|
|
7831
|
+
projectDir,
|
|
7832
|
+
source: resolveAgentProjectDirSource({
|
|
7833
|
+
flagOverride: opts.projectDir,
|
|
7834
|
+
codebaseRoot: opts.config ? loadedCodebaseRoot : void 0,
|
|
7835
|
+
configDir: opts.config ? configDir : void 0
|
|
7836
|
+
})
|
|
7837
|
+
});
|
|
7781
7838
|
productModel = await generateProductModelViaAgent({ projectDir });
|
|
7782
7839
|
} else if (opts.interactive) {
|
|
7783
7840
|
const answers = await runInteractiveQA();
|
|
@@ -7822,6 +7879,24 @@ async function understandCommand(opts) {
|
|
|
7822
7879
|
await writeFile15(outputPath, JSON.stringify(productModel, null, 2) + "\n", "utf8");
|
|
7823
7880
|
logger.info("Wrote product_model.json", { path: outputPath });
|
|
7824
7881
|
}
|
|
7882
|
+
function resolveAgentProjectDir(input) {
|
|
7883
|
+
if (input.flagOverride) {
|
|
7884
|
+
return isAbsolute13(input.flagOverride) ? input.flagOverride : resolve25(process.cwd(), input.flagOverride);
|
|
7885
|
+
}
|
|
7886
|
+
if (input.codebaseRoot && input.configDir) {
|
|
7887
|
+
return isAbsolute13(input.codebaseRoot) ? input.codebaseRoot : resolve25(input.configDir, input.codebaseRoot);
|
|
7888
|
+
}
|
|
7889
|
+
if (input.configDir) {
|
|
7890
|
+
return resolve25(input.configDir, "..");
|
|
7891
|
+
}
|
|
7892
|
+
return process.cwd();
|
|
7893
|
+
}
|
|
7894
|
+
function resolveAgentProjectDirSource(input) {
|
|
7895
|
+
if (input.flagOverride) return "--project-dir flag";
|
|
7896
|
+
if (input.codebaseRoot && input.configDir) return "project.codebase_root in demo.yaml";
|
|
7897
|
+
if (input.configDir) return "configDir/.. (default; set project.codebase_root to override)";
|
|
7898
|
+
return "cwd (no -c flag)";
|
|
7899
|
+
}
|
|
7825
7900
|
|
|
7826
7901
|
// src/commands/instrument.ts
|
|
7827
7902
|
import { mkdir as mkdir16, readdir as readdir4, writeFile as writeFile16 } from "fs/promises";
|
|
@@ -8442,7 +8517,7 @@ async function fileExists10(path) {
|
|
|
8442
8517
|
|
|
8443
8518
|
// src/cli.ts
|
|
8444
8519
|
var program = new Command();
|
|
8445
|
-
program.name("showrunner").description("Automated product demo recording & production tool").version("1.1.
|
|
8520
|
+
program.name("showrunner").description("Automated product demo recording & production tool").version("1.1.8").option("--json", "emit structured JSON logs to stdout").option("--log-level <level>", "log level (debug|info|warn|error)").hook("preAction", (thisCmd) => {
|
|
8446
8521
|
const opts = thisCmd.opts();
|
|
8447
8522
|
if (opts.json) logger.setJson(true);
|
|
8448
8523
|
if (opts.logLevel) logger.setLevel(opts.logLevel);
|
|
@@ -8481,7 +8556,10 @@ program.command("doctor").description("Run preflight checks on the current confi
|
|
|
8481
8556
|
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);
|
|
8482
8557
|
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
8558
|
"--agent",
|
|
8484
|
-
"delegate to the local `claude` CLI: it explores the
|
|
8559
|
+
"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."
|
|
8560
|
+
).option(
|
|
8561
|
+
"--project-dir <path>",
|
|
8562
|
+
"directory the --agent run should explore. Overrides project.codebase_root from demo.yaml. Defaults to configDir/.. when -c is given, or cwd otherwise."
|
|
8485
8563
|
).option("--output <path>", "output path for product_model.json").action(understandCommand);
|
|
8486
8564
|
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);
|
|
8487
8565
|
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);
|
|
@@ -8506,14 +8584,28 @@ async function printWelcome() {
|
|
|
8506
8584
|
inProject = true;
|
|
8507
8585
|
} catch {
|
|
8508
8586
|
}
|
|
8509
|
-
const
|
|
8510
|
-
const
|
|
8511
|
-
|
|
8512
|
-
|
|
8587
|
+
const missing = await detectMissingPrereqs();
|
|
8588
|
+
const anyMissing = missing.ffmpeg || missing.ffprobe || missing.chromium;
|
|
8589
|
+
const lines = ["", `Showrunner v1.1.8`, ""];
|
|
8590
|
+
if (anyMissing) {
|
|
8591
|
+
lines.push(`First-time setup. Showrunner needs these system tools:`);
|
|
8513
8592
|
lines.push(``);
|
|
8514
|
-
|
|
8515
|
-
|
|
8516
|
-
|
|
8593
|
+
if (missing.ffmpeg || missing.ffprobe) {
|
|
8594
|
+
const which = missing.ffmpeg && missing.ffprobe ? "ffmpeg / ffprobe" : missing.ffmpeg ? "ffmpeg" : "ffprobe";
|
|
8595
|
+
lines.push(` ${which} \u2014 install via your OS package manager:`);
|
|
8596
|
+
lines.push(` Linux (apt): sudo apt install ffmpeg`);
|
|
8597
|
+
lines.push(` Linux (pacman): sudo pacman -S ffmpeg`);
|
|
8598
|
+
lines.push(` Linux (dnf): sudo dnf install ffmpeg`);
|
|
8599
|
+
lines.push(` macOS: brew install ffmpeg`);
|
|
8600
|
+
lines.push(` Windows: winget install Gyan.FFmpeg`);
|
|
8601
|
+
lines.push(``);
|
|
8602
|
+
}
|
|
8603
|
+
if (missing.chromium) {
|
|
8604
|
+
lines.push(` chromium recording browser \u2014 install via:`);
|
|
8605
|
+
lines.push(` showrunner install-browser`);
|
|
8606
|
+
lines.push(``);
|
|
8607
|
+
}
|
|
8608
|
+
lines.push(`Re-run \`showrunner\` once those are in place.`);
|
|
8517
8609
|
} else if (inProject) {
|
|
8518
8610
|
lines.push(`This is a Showrunner project (found demo.yaml).`);
|
|
8519
8611
|
lines.push(``);
|
|
@@ -8524,22 +8616,60 @@ async function printWelcome() {
|
|
|
8524
8616
|
} else {
|
|
8525
8617
|
lines.push(`No Showrunner project in this directory. To create one:`);
|
|
8526
8618
|
lines.push(``);
|
|
8527
|
-
lines.push(`
|
|
8619
|
+
lines.push(` cd <your-product's-root-directory> # the dir with package.json / etc.`);
|
|
8620
|
+
lines.push(` showrunner init # then run init from THERE`);
|
|
8528
8621
|
lines.push(``);
|
|
8529
|
-
lines.push(`\`init\` scaffolds
|
|
8622
|
+
lines.push(`\`init\` scaffolds inside cwd, so cwd must be your product. The wizard will`);
|
|
8623
|
+
lines.push(`confirm the path before doing anything destructive.`);
|
|
8530
8624
|
}
|
|
8531
8625
|
lines.push("");
|
|
8532
8626
|
process.stdout.write(lines.join("\n"));
|
|
8533
8627
|
}
|
|
8534
|
-
async function
|
|
8628
|
+
async function detectMissingPrereqs() {
|
|
8629
|
+
const [ffmpegOk, ffprobeOk, chromiumOk] = await Promise.all([
|
|
8630
|
+
binaryOnPath("ffmpeg"),
|
|
8631
|
+
binaryOnPath("ffprobe"),
|
|
8632
|
+
chromiumInstalled2()
|
|
8633
|
+
]);
|
|
8634
|
+
return {
|
|
8635
|
+
ffmpeg: !ffmpegOk,
|
|
8636
|
+
ffprobe: !ffprobeOk,
|
|
8637
|
+
chromium: !chromiumOk
|
|
8638
|
+
};
|
|
8639
|
+
}
|
|
8640
|
+
async function binaryOnPath(name) {
|
|
8641
|
+
const { spawn: spawn7 } = await import("child_process");
|
|
8642
|
+
return new Promise((resolve28) => {
|
|
8643
|
+
const useShell = process.platform === "win32";
|
|
8644
|
+
const child = spawn7(name, ["-version"], {
|
|
8645
|
+
stdio: ["ignore", "ignore", "ignore"],
|
|
8646
|
+
shell: useShell
|
|
8647
|
+
});
|
|
8648
|
+
let settled = false;
|
|
8649
|
+
const finish = (ok) => {
|
|
8650
|
+
if (settled) return;
|
|
8651
|
+
settled = true;
|
|
8652
|
+
resolve28(ok);
|
|
8653
|
+
};
|
|
8654
|
+
child.on("error", () => finish(false));
|
|
8655
|
+
child.on("exit", (code) => finish(code === 0));
|
|
8656
|
+
setTimeout(() => {
|
|
8657
|
+
if (!settled) {
|
|
8658
|
+
child.kill("SIGKILL");
|
|
8659
|
+
finish(false);
|
|
8660
|
+
}
|
|
8661
|
+
}, 3e3);
|
|
8662
|
+
});
|
|
8663
|
+
}
|
|
8664
|
+
async function chromiumInstalled2() {
|
|
8535
8665
|
try {
|
|
8536
8666
|
const { chromium: chromium6 } = await import("playwright-core");
|
|
8537
8667
|
const exec = chromium6.executablePath();
|
|
8538
8668
|
const { stat: stat17 } = await import("fs/promises");
|
|
8539
8669
|
await stat17(exec);
|
|
8540
|
-
return false;
|
|
8541
|
-
} catch {
|
|
8542
8670
|
return true;
|
|
8671
|
+
} catch {
|
|
8672
|
+
return false;
|
|
8543
8673
|
}
|
|
8544
8674
|
}
|
|
8545
8675
|
function parseStages(value) {
|