Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 14 additions & 3 deletions src/cli/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import { promptEngineCommand } from "./prompt-engine-command.js";
import { publishEngineCommand } from "./publish-engine-command.js";
import { renderEngineCommand } from "./render-engine-command.js";
import { runEngineCommand } from "./run-engine-command.js";
import { scenarioEngineCommand } from "./scenario-engine-command.js";
import { studiosEngineCommand } from "./studios-engine-command.js";
import { wizardEngineCommand } from "./wizard-engine-command.js";
import { executeEngineCommand } from "./execute-engine-command.js";
import { ttsEngineCommand } from "./tts-engine-command.js";
Expand All @@ -25,21 +27,30 @@ const simulate = flags.includes("--simulate");

if (!command) {
process.stderr.write(
"Usage: engine <run|prompt|create|wizard|execute|tts|upload|config|doctor|analyze|render|publish> [request.json] [--json] [--simulate]\n",
"Usage: engine <run|prompt|create|wizard|execute|tts|upload|config|doctor|analyze|render|publish|scenario|studios> [request.json] [--json] [--simulate]\n",
);
process.exit(EXIT_CODE_INTERNAL_ERROR);
}

const dry_run = flags.includes("--dry-run");
const result = await executeCommand(command, positionals, { json, simulate, dry_run });
const result = await executeCommand(command, positionals, rest, { json, simulate, dry_run });
process.stdout.write(result.output);
process.exit(result.exitCode);

async function executeCommand(
commandName: string,
positionals: string[],
rawArgs: string[],
options: { json: boolean; simulate: boolean; dry_run: boolean },
) {
if (commandName === "scenario") {
return scenarioEngineCommand(rawArgs, { json: options.json });
}

if (commandName === "studios") {
return studiosEngineCommand(rawArgs, { json: options.json });
}

if (commandName === "config") {
return configEngineCommand({ json: options.json });
}
Expand Down Expand Up @@ -112,7 +123,7 @@ async function executeCommand(
) {
return {
exitCode: EXIT_CODE_INTERNAL_ERROR,
output: "Usage: engine <run|prompt|create|wizard|execute|tts|upload|config|doctor|analyze|render|publish> [request.json] [--json] [--simulate]\n",
output: "Usage: engine <run|prompt|create|wizard|execute|tts|upload|config|doctor|analyze|render|publish|scenario|studios> [request.json] [--json] [--simulate]\n",
};
}

Expand Down
2 changes: 2 additions & 0 deletions src/cli/prompt-engine-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export async function promptEngineCommand(
schema_version: "0.1",
request_id: requestId,
validation: loaded.validation,
narrative_payload: null,
normalized_request: null,
platform_output_spec: null,
novel_shorts_plan: null,
Expand All @@ -50,6 +51,7 @@ export async function promptEngineCommand(
effectiveRequest: planningContext.effective_request,
learningState: planningContext.learning_state,
motionPlan: planningContext.motion_plan,
narrativePayload: planningContext.narrative_payload,
novelShortsPlan: planningContext.novel_shorts_plan,
platformOutputSpec: planningContext.platform_output_spec,
routing: planningContext.routing,
Expand Down
2 changes: 2 additions & 0 deletions src/cli/render-output.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ export function renderOutput(result: EngineRunResult, json: boolean): string {
const lines = [
`Request ID: ${result.request_id}`,
`Validation: ${result.validation.valid ? "valid" : "invalid"}`,
`Studio: ${result.narrative_payload?.studio_id ?? "n/a"}`,
`Scene archetype: ${result.narrative_payload?.scene_archetype ?? "n/a"}`,
`Platform: ${result.platform_output_spec?.platform ?? "n/a"}`,
`Effective duration: ${result.platform_output_spec?.effective_duration_sec ?? "n/a"}${result.platform_output_spec ? "s" : ""}`,
`Warnings: ${result.platform_output_spec?.warnings.length ?? 0}`,
Expand Down
18 changes: 18 additions & 0 deletions src/cli/render-scenario-output.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import type { NarrativePayload } from "../domain/contracts.js";

export function renderScenarioOutput(result: NarrativePayload, json: boolean): string {
if (json) {
return JSON.stringify(result, null, 2);
}

const lines = [
`Studio: ${result.studio_id}`,
`Scene archetype: ${result.scene_archetype}`,
`Philosophy note: ${result.philosophy_note}`,
`Key prop: ${result.key_prop}`,
`Key silence: ${result.key_silence_sec}s`,
`Beat count: ${result.beats.length}`,
];

return `${lines.join("\n")}\n`;
}
35 changes: 35 additions & 0 deletions src/cli/render-studios-output.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import type { StudioDefinition } from "../domain/contracts.js";

export function renderStudiosListOutput(result: StudioDefinition[], json: boolean): string {
if (json) {
return JSON.stringify(
result.map((studio) => ({
studio_id: studio.studio_id,
display_name: studio.display_name,
philosophy_summary: studio.philosophy_summary,
})),
null,
2,
);
}

const lines = result.map(
(studio) => `${studio.studio_id}: ${studio.display_name} - ${studio.philosophy_summary}`,
);

return `${lines.join("\n")}\n`;
}

export function renderStudioDetailOutput(result: StudioDefinition, json: boolean): string {
if (json) {
return JSON.stringify(result, null, 2);
}

const lines = [
`Studio: ${result.display_name} (${result.studio_id})`,
`Philosophy: ${result.philosophy_summary}`,
`Archetypes: ${result.scene_archetypes.map((item) => item.name).join(", ")}`,
];

return `${lines.join("\n")}\n`;
}
5 changes: 5 additions & 0 deletions src/cli/resolve-planning-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type {
ExecutionPlan,
LearningState,
MotionPlan,
NarrativePayload,
NormalizedRequest,
NovelShortsPlan,
PlatformOutputSpec,
Expand All @@ -24,8 +25,10 @@ import { resolvePlatformOutputSpec } from "../platform/resolve-platform-output-s
import { buildExecutionPlan } from "../simulation/build-execution-plan.js";
import { simulateRecovery } from "../simulation/simulate-recovery.js";
import { resolveBrollPlan } from "../broll/resolve-broll-plan.js";
import { resolveNarrativePayload } from "../scenario/build-narrative-payload.js";

export interface PlanningContext {
narrative_payload: NarrativePayload | null;
normalized_request: NormalizedRequest;
effective_request: NormalizedRequest;
novel_shorts_plan: NovelShortsPlan | null;
Expand All @@ -43,6 +46,7 @@ export function resolvePlanningContext(request: EngineRequest): PlanningContext
const normalizedRequest = normalizeRequest(request);
const novelShortsPlan = resolveNovelShortsPlan(normalizedRequest);
const effectiveRequest = applyNovelIntentOverrides(normalizedRequest, novelShortsPlan);
const narrativePayload = resolveNarrativePayload(effectiveRequest.base);
const platformOutputSpec = resolvePlatformOutputSpec(effectiveRequest);
const motionPlan = resolveMotionPlan(effectiveRequest, platformOutputSpec);
const brollPlan = resolveBrollPlan(effectiveRequest, platformOutputSpec, motionPlan);
Expand All @@ -58,6 +62,7 @@ export function resolvePlanningContext(request: EngineRequest): PlanningContext
const recoverySimulation = simulateRecovery(executionPlan);

return {
narrative_payload: narrativePayload,
normalized_request: normalizedRequest,
effective_request: effectiveRequest,
novel_shorts_plan: novelShortsPlan,
Expand Down
19 changes: 17 additions & 2 deletions src/cli/run-engine-command.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { EngineRunResult } from "../domain/contracts.js";
import type { EngineRunResult, NarrativePayload } from "../domain/contracts.js";
import { createRequestId } from "../shared/request-id.js";
import {
EXIT_CODE_INTERNAL_ERROR,
Expand Down Expand Up @@ -32,7 +32,18 @@ export async function runEngineCommand(
isRecord(loaded.raw_request) && typeof loaded.raw_request['version'] === 'string'
? loaded.raw_request['version']
: '0.1',
loaded.validation, null, null, null, null, null, null, null, null, null, null,
loaded.validation,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
),
options.json,
),
Expand All @@ -48,6 +59,7 @@ export async function runEngineCommand(
requestId,
loaded.request.version,
loaded.validation,
planningContext.narrative_payload,
planningContext.normalized_request,
planningContext.platform_output_spec,
planningContext.novel_shorts_plan,
Expand Down Expand Up @@ -82,6 +94,7 @@ export async function runEngineCommand(
null,
null,
null,
null,
);

const output = options.json
Expand All @@ -106,6 +119,7 @@ function createRunResult(
requestId: string,
schemaVersion: string,
validation: EngineRunResult["validation"],
narrativePayload: NarrativePayload | null,
normalizedRequest: EngineRunResult["normalized_request"],
platformOutputSpec: EngineRunResult["platform_output_spec"],
novelShortsPlan: EngineRunResult["novel_shorts_plan"],
Expand All @@ -121,6 +135,7 @@ function createRunResult(
schema_version: schemaVersion,
request_id: requestId,
validation,
narrative_payload: narrativePayload,
normalized_request: normalizedRequest,
platform_output_spec: platformOutputSpec,
novel_shorts_plan: novelShortsPlan,
Expand Down
57 changes: 57 additions & 0 deletions src/cli/scenario-engine-command.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { buildNarrativePayload } from "../scenario/build-narrative-payload.js";
import { EXIT_CODE_INTERNAL_ERROR, EXIT_CODE_SUCCESS } from "./exit-codes.js";
import { renderScenarioOutput } from "./render-scenario-output.js";

export async function scenarioEngineCommand(
args: string[],
options: { json: boolean },
): Promise<{ exitCode: number; output: string }> {
const studioId = readFlagValue(args, "--studio");
const topic = readFlagValue(args, "--topic");

if (!studioId || !topic) {
return {
exitCode: EXIT_CODE_INTERNAL_ERROR,
output: "Usage: engine scenario --studio <studio_id> --topic <topic> [--subject <subject>] [--goal <goal>] [--emotion <emotion>] [--json]\n",
};
}

const subject = readFlagValue(args, "--subject") ?? topic;
const goal = readFlagValue(args, "--goal") ?? "turn tension into a clear emotional shift";
const emotion = readFlagValue(args, "--emotion") ?? "wonder";

try {
const payload = buildNarrativePayload({
studio_id: studioId as never,
topic,
subject,
goal,
emotion,
});

return {
exitCode: EXIT_CODE_SUCCESS,
output: renderScenarioOutput(payload, options.json),
};
} catch (error) {
return {
exitCode: EXIT_CODE_INTERNAL_ERROR,
output: options.json
? JSON.stringify(
{ fatal_error: error instanceof Error ? error.message : "Unknown error" },
null,
2,
)
: `Fatal error: ${error instanceof Error ? error.message : "Unknown error"}\n`,
};
}
}

function readFlagValue(args: string[], flag: string): string | undefined {
const index = args.indexOf(flag);
if (index === -1) {
return undefined;
}

return args[index + 1];
}
50 changes: 50 additions & 0 deletions src/cli/studios-engine-command.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { listStudioDefinitions, loadStudioDefinition } from "../scenario/load-studio-definition.js";
import { EXIT_CODE_INTERNAL_ERROR, EXIT_CODE_SUCCESS } from "./exit-codes.js";
import { renderStudioDetailOutput, renderStudiosListOutput } from "./render-studios-output.js";

export async function studiosEngineCommand(
args: string[],
options: { json: boolean },
): Promise<{ exitCode: number; output: string }> {
if (args.includes("--list")) {
return {
exitCode: EXIT_CODE_SUCCESS,
output: renderStudiosListOutput(listStudioDefinitions(), options.json),
};
}

const studioId = readFlagValue(args, "--show");
if (!studioId) {
return {
exitCode: EXIT_CODE_INTERNAL_ERROR,
output: "Usage: engine studios --list [--json] | engine studios --show <studio_id> [--json]\n",
};
}

try {
return {
exitCode: EXIT_CODE_SUCCESS,
output: renderStudioDetailOutput(loadStudioDefinition(studioId as never), options.json),
};
} catch (error) {
return {
exitCode: EXIT_CODE_INTERNAL_ERROR,
output: options.json
? JSON.stringify(
{ fatal_error: error instanceof Error ? error.message : "Unknown error" },
null,
2,
)
: `Fatal error: ${error instanceof Error ? error.message : "Unknown error"}\n`,
};
}
}

function readFlagValue(args: string[], flag: string): string | undefined {
const index = args.indexOf(flag);
if (index === -1) {
return undefined;
}

return args[index + 1];
}
Loading