@t3lnet/sceneforge 1.0.9 → 1.0.11

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.
Files changed (35) hide show
  1. package/README.md +57 -0
  2. package/cli/cli.js +6 -0
  3. package/cli/commands/add-audio-to-steps.js +9 -3
  4. package/cli/commands/concat-final-videos.js +6 -2
  5. package/cli/commands/context.js +791 -0
  6. package/cli/commands/split-video.js +3 -1
  7. package/context/context-builder.ts +318 -0
  8. package/context/index.ts +52 -0
  9. package/context/template-loader.ts +161 -0
  10. package/context/templates/base/actions-reference.md +299 -0
  11. package/context/templates/base/cli-reference.md +236 -0
  12. package/context/templates/base/project-overview.md +58 -0
  13. package/context/templates/base/selectors-guide.md +233 -0
  14. package/context/templates/base/yaml-schema.md +210 -0
  15. package/context/templates/skills/balance-timing.md +136 -0
  16. package/context/templates/skills/debug-selector.md +193 -0
  17. package/context/templates/skills/generate-actions.md +94 -0
  18. package/context/templates/skills/optimize-demo.md +218 -0
  19. package/context/templates/skills/review-demo-yaml.md +164 -0
  20. package/context/templates/skills/write-step-script.md +136 -0
  21. package/context/templates/stages/stage1-actions.md +236 -0
  22. package/context/templates/stages/stage2-scripts.md +197 -0
  23. package/context/templates/stages/stage3-balancing.md +229 -0
  24. package/context/templates/stages/stage4-rebalancing.md +228 -0
  25. package/context/tests/context-builder.test.ts +237 -0
  26. package/context/tests/template-loader.test.ts +181 -0
  27. package/context/tests/tool-formatter.test.ts +198 -0
  28. package/context/tool-formatter.ts +189 -0
  29. package/dist/index.cjs +416 -11
  30. package/dist/index.cjs.map +1 -1
  31. package/dist/index.d.cts +182 -1
  32. package/dist/index.d.ts +182 -1
  33. package/dist/index.js +391 -11
  34. package/dist/index.js.map +1 -1
  35. package/package.json +2 -1
package/dist/index.js CHANGED
@@ -180,8 +180,8 @@ function formatValidationError(error) {
180
180
  }
181
181
  const issues = error.issues;
182
182
  return issues.map((issue) => {
183
- const path5 = issue.path.length ? issue.path.join(".") : "root";
184
- return `${path5}: ${issue.message}`;
183
+ const path7 = issue.path.length ? issue.path.join(".") : "root";
184
+ return `${path7}: ${issue.message}`;
185
185
  }).join("; ");
186
186
  }
187
187
  function parseDemoDefinition(input) {
@@ -311,7 +311,7 @@ function createEmptyStep(id) {
311
311
  };
312
312
  }
313
313
  var SECRET_PATTERN = /\$\{(ENV|SECRET):([A-Za-z0-9_]+)\}/g;
314
- function resolveSecrets(value, resolver, path5 = "root") {
314
+ function resolveSecrets(value, resolver, path7 = "root") {
315
315
  if (typeof value === "string") {
316
316
  if (!SECRET_PATTERN.test(value)) {
317
317
  return value;
@@ -320,20 +320,20 @@ function resolveSecrets(value, resolver, path5 = "root") {
320
320
  return value.replace(SECRET_PATTERN, (_match, _type, key) => {
321
321
  const resolved = resolver(key);
322
322
  if (resolved === void 0) {
323
- throw new Error(`Missing secret for ${key} at ${path5}`);
323
+ throw new Error(`Missing secret for ${key} at ${path7}`);
324
324
  }
325
325
  return resolved;
326
326
  });
327
327
  }
328
328
  if (Array.isArray(value)) {
329
329
  return value.map(
330
- (entry, index) => resolveSecrets(entry, resolver, `${path5}[${index}]`)
330
+ (entry, index) => resolveSecrets(entry, resolver, `${path7}[${index}]`)
331
331
  );
332
332
  }
333
333
  if (value && typeof value === "object") {
334
334
  const result = {};
335
335
  for (const [key, entry] of Object.entries(value)) {
336
- result[key] = resolveSecrets(entry, resolver, `${path5}.${key}`);
336
+ result[key] = resolveSecrets(entry, resolver, `${path7}.${key}`);
337
337
  }
338
338
  return result;
339
339
  }
@@ -412,10 +412,10 @@ function createTypeAction(selector, text) {
412
412
  text
413
413
  };
414
414
  }
415
- function createNavigateAction(path5) {
415
+ function createNavigateAction(path7) {
416
416
  return {
417
417
  action: "navigate",
418
- path: path5
418
+ path: path7
419
419
  };
420
420
  }
421
421
  function createWaitAction(duration) {
@@ -839,7 +839,7 @@ function runCommand(command, args, options = {}) {
839
839
  cwd,
840
840
  maxOutputBytes = DEFAULT_MAX_OUTPUT_BYTES
841
841
  } = options;
842
- return new Promise((resolve2, reject) => {
842
+ return new Promise((resolve3, reject) => {
843
843
  const child = spawn(command, args, { stdio, cwd });
844
844
  let stdout = "";
845
845
  let stderr = "";
@@ -858,7 +858,7 @@ function runCommand(command, args, options = {}) {
858
858
  });
859
859
  child.on("close", (code) => {
860
860
  if (code === 0) {
861
- resolve2({ stdout, stderr });
861
+ resolve3({ stdout, stderr });
862
862
  return;
863
863
  }
864
864
  const error = new Error(`${command} exited with code ${code}`);
@@ -889,7 +889,7 @@ async function probeMediaDurationMs(filePath) {
889
889
  return null;
890
890
  }
891
891
  function sleep(ms) {
892
- return new Promise((resolve2) => setTimeout(resolve2, ms));
892
+ return new Promise((resolve3) => setTimeout(resolve3, ms));
893
893
  }
894
894
  function getStatusCode(error) {
895
895
  if (!error || typeof error !== "object") return void 0;
@@ -2385,10 +2385,369 @@ async function discoverDemos(demoDir) {
2385
2385
  const files = await fs4.readdir(demoDir);
2386
2386
  return files.filter((f) => f.endsWith(".yaml") || f.endsWith(".yml")).map((f) => path4.join(demoDir, f));
2387
2387
  }
2388
+
2389
+ // context/template-loader.ts
2390
+ import * as fs5 from "fs/promises";
2391
+ import * as path5 from "path";
2392
+ import { fileURLToPath } from "url";
2393
+ var __filename = fileURLToPath(import.meta.url);
2394
+ var __dirname = path5.dirname(__filename);
2395
+ function getTemplatesDir() {
2396
+ return path5.join(__dirname, "templates");
2397
+ }
2398
+ async function loadTemplate(category, name) {
2399
+ const templatesDir = getTemplatesDir();
2400
+ const filePath = path5.join(templatesDir, category, `${name}.md`);
2401
+ try {
2402
+ const content = await fs5.readFile(filePath, "utf-8");
2403
+ return { name, content, category };
2404
+ } catch (error) {
2405
+ throw new Error(`Failed to load template ${category}/${name}: ${error}`);
2406
+ }
2407
+ }
2408
+ async function loadTemplatesByCategory(category) {
2409
+ const templatesDir = getTemplatesDir();
2410
+ const categoryDir = path5.join(templatesDir, category);
2411
+ try {
2412
+ const files = await fs5.readdir(categoryDir);
2413
+ const templates = [];
2414
+ for (const file of files) {
2415
+ if (file.endsWith(".md")) {
2416
+ const name = file.replace(/\.md$/, "");
2417
+ const template = await loadTemplate(category, name);
2418
+ templates.push(template);
2419
+ }
2420
+ }
2421
+ return templates;
2422
+ } catch (error) {
2423
+ throw new Error(`Failed to load templates from ${category}: ${error}`);
2424
+ }
2425
+ }
2426
+ function interpolateVariables(content, variables) {
2427
+ return content.replace(/\{\{(\w+)\}\}/g, (match, key) => {
2428
+ const value = variables[key];
2429
+ if (value === void 0) {
2430
+ return match;
2431
+ }
2432
+ return String(value);
2433
+ });
2434
+ }
2435
+ function composeTemplates(templates, options) {
2436
+ const { separator = "\n\n---\n\n", includeHeaders = false } = options ?? {};
2437
+ return templates.map((template) => {
2438
+ if (includeHeaders) {
2439
+ return `<!-- Template: ${template.category}/${template.name} -->
2440
+
2441
+ ${template.content}`;
2442
+ }
2443
+ return template.content;
2444
+ }).join(separator);
2445
+ }
2446
+ async function listTemplates() {
2447
+ const templatesDir = getTemplatesDir();
2448
+ const result = {
2449
+ base: [],
2450
+ stages: [],
2451
+ skills: []
2452
+ };
2453
+ for (const category of ["base", "stages", "skills"]) {
2454
+ const categoryDir = path5.join(templatesDir, category);
2455
+ try {
2456
+ const files = await fs5.readdir(categoryDir);
2457
+ result[category] = files.filter((f) => f.endsWith(".md")).map((f) => f.replace(/\.md$/, ""));
2458
+ } catch {
2459
+ result[category] = [];
2460
+ }
2461
+ }
2462
+ return result;
2463
+ }
2464
+ async function templateExists(category, name) {
2465
+ const templatesDir = getTemplatesDir();
2466
+ const filePath = path5.join(templatesDir, category, `${name}.md`);
2467
+ try {
2468
+ await fs5.access(filePath);
2469
+ return true;
2470
+ } catch {
2471
+ return false;
2472
+ }
2473
+ }
2474
+
2475
+ // context/tool-formatter.ts
2476
+ var TOOL_CONFIGS = {
2477
+ cursor: {
2478
+ name: "Cursor",
2479
+ description: "Cursor AI IDE with .cursorrules support",
2480
+ combinedFile: ".cursorrules",
2481
+ splitDir: ".cursor/rules",
2482
+ splitFilePrefix: "",
2483
+ fileExtension: ".md",
2484
+ supportsSkills: true
2485
+ },
2486
+ copilot: {
2487
+ name: "GitHub Copilot",
2488
+ description: "GitHub Copilot with instructions file",
2489
+ combinedFile: ".github/copilot-instructions.md",
2490
+ splitDir: ".github/copilot",
2491
+ splitFilePrefix: "",
2492
+ fileExtension: ".md",
2493
+ supportsSkills: false
2494
+ },
2495
+ claude: {
2496
+ name: "Claude Code",
2497
+ description: "Claude Code CLI with CLAUDE.md support",
2498
+ combinedFile: "CLAUDE.md",
2499
+ splitDir: ".claude/rules",
2500
+ splitFilePrefix: "",
2501
+ fileExtension: ".md",
2502
+ supportsSkills: true
2503
+ },
2504
+ codex: {
2505
+ name: "Codex",
2506
+ description: "OpenAI Codex with AGENTS.md support",
2507
+ combinedFile: "AGENTS.md",
2508
+ splitDir: ".codex",
2509
+ splitFilePrefix: "",
2510
+ fileExtension: ".md",
2511
+ supportsSkills: false
2512
+ }
2513
+ };
2514
+ function getSupportedTools() {
2515
+ return Object.keys(TOOL_CONFIGS);
2516
+ }
2517
+ function getToolConfig(tool) {
2518
+ return TOOL_CONFIGS[tool];
2519
+ }
2520
+ function formatForTool(tool, content, options) {
2521
+ const config = TOOL_CONFIGS[tool];
2522
+ const { stage, includeToolHeader = true } = options ?? {};
2523
+ const lines = [];
2524
+ if (includeToolHeader) {
2525
+ lines.push(`# SceneForge LLM Context`);
2526
+ lines.push(``);
2527
+ lines.push(`> Generated for ${config.name}`);
2528
+ if (stage) {
2529
+ lines.push(`> Stage: ${stage}`);
2530
+ }
2531
+ lines.push(``);
2532
+ lines.push(`---`);
2533
+ lines.push(``);
2534
+ }
2535
+ lines.push(content);
2536
+ return lines.join("\n");
2537
+ }
2538
+ function getOutputPath(tool, format, outputDir, stageName) {
2539
+ const config = TOOL_CONFIGS[tool];
2540
+ if (format === "combined") {
2541
+ return `${outputDir}/${config.combinedFile}`;
2542
+ }
2543
+ const fileName = stageName ? `${config.splitFilePrefix}${stageName}${config.fileExtension}` : `${config.splitFilePrefix}main${config.fileExtension}`;
2544
+ return `${outputDir}/${config.splitDir}/${fileName}`;
2545
+ }
2546
+ function getSplitOutputPaths(tool, outputDir, stageNames) {
2547
+ return stageNames.map((stage) => getOutputPath(tool, "split", outputDir, stage));
2548
+ }
2549
+ function formatStageName(stage) {
2550
+ const stageMap = {
2551
+ actions: "Stage 1: Action Generation",
2552
+ scripts: "Stage 2: Script Writing",
2553
+ balance: "Stage 3: Step Balancing",
2554
+ rebalance: "Stage 4: Rebalancing"
2555
+ };
2556
+ return stageMap[stage] ?? stage;
2557
+ }
2558
+ function getStageFileName(stage) {
2559
+ const stageFileMap = {
2560
+ actions: "stage1-actions",
2561
+ scripts: "stage2-scripts",
2562
+ balance: "stage3-balancing",
2563
+ rebalance: "stage4-rebalancing"
2564
+ };
2565
+ return stageFileMap[stage] ?? stage;
2566
+ }
2567
+ function isValidTool(tool) {
2568
+ return tool in TOOL_CONFIGS;
2569
+ }
2570
+ function isValidFormat(format) {
2571
+ return format === "combined" || format === "split";
2572
+ }
2573
+
2574
+ // context/context-builder.ts
2575
+ import * as fs6 from "fs/promises";
2576
+ import * as path6 from "path";
2577
+ async function buildContext(tool, stage, variables) {
2578
+ const templates = [];
2579
+ const baseTemplates = await loadTemplatesByCategory("base");
2580
+ templates.push(...baseTemplates);
2581
+ if (stage === "all") {
2582
+ const stageTemplates = await loadTemplatesByCategory("stages");
2583
+ templates.push(...stageTemplates);
2584
+ } else {
2585
+ const stageFileName = getStageFileName(stage);
2586
+ try {
2587
+ const stageTemplate = await loadTemplate("stages", stageFileName);
2588
+ templates.push(stageTemplate);
2589
+ } catch {
2590
+ }
2591
+ }
2592
+ let content = composeTemplates(templates, {
2593
+ separator: "\n\n---\n\n",
2594
+ includeHeaders: false
2595
+ });
2596
+ if (variables) {
2597
+ content = interpolateVariables(content, variables);
2598
+ }
2599
+ const stageName = stage === "all" ? void 0 : formatStageName(stage);
2600
+ return formatForTool(tool, content, { stage: stageName });
2601
+ }
2602
+ async function deployContext(options) {
2603
+ const { target, stage, format, outputDir, variables } = options;
2604
+ const results = [];
2605
+ const tools = target === "all" ? getSupportedTools() : [target];
2606
+ const stages = stage === "all" ? ["actions", "scripts", "balance", "rebalance"] : [stage];
2607
+ for (const tool of tools) {
2608
+ if (format === "combined") {
2609
+ try {
2610
+ const content = await buildContext(tool, "all", variables);
2611
+ const filePath = getOutputPath(tool, format, outputDir);
2612
+ const absolutePath = path6.resolve(filePath);
2613
+ await fs6.mkdir(path6.dirname(absolutePath), { recursive: true });
2614
+ await fs6.writeFile(absolutePath, content, "utf-8");
2615
+ results.push({
2616
+ tool,
2617
+ filePath: absolutePath,
2618
+ created: true
2619
+ });
2620
+ } catch (error) {
2621
+ results.push({
2622
+ tool,
2623
+ filePath: getOutputPath(tool, format, outputDir),
2624
+ created: false,
2625
+ error: error instanceof Error ? error.message : String(error)
2626
+ });
2627
+ }
2628
+ } else {
2629
+ for (const stg of stages) {
2630
+ try {
2631
+ const content = await buildContext(tool, stg, variables);
2632
+ const stageName = getStageFileName(stg);
2633
+ const filePath = getOutputPath(tool, format, outputDir, stageName);
2634
+ const absolutePath = path6.resolve(filePath);
2635
+ await fs6.mkdir(path6.dirname(absolutePath), { recursive: true });
2636
+ await fs6.writeFile(absolutePath, content, "utf-8");
2637
+ results.push({
2638
+ tool,
2639
+ filePath: absolutePath,
2640
+ stage: stg,
2641
+ created: true
2642
+ });
2643
+ } catch (error) {
2644
+ results.push({
2645
+ tool,
2646
+ filePath: getOutputPath(tool, format, outputDir, stg),
2647
+ stage: stg,
2648
+ created: false,
2649
+ error: error instanceof Error ? error.message : String(error)
2650
+ });
2651
+ }
2652
+ }
2653
+ }
2654
+ }
2655
+ return results;
2656
+ }
2657
+ async function previewContext(tool, stage, variables) {
2658
+ const content = await buildContext(tool, stage, variables);
2659
+ return {
2660
+ tool,
2661
+ stage: stage === "all" ? void 0 : stage,
2662
+ content
2663
+ };
2664
+ }
2665
+ async function listDeployedContext(outputDir) {
2666
+ const tools = getSupportedTools();
2667
+ const files = [];
2668
+ for (const tool of tools) {
2669
+ const config = getToolConfig(tool);
2670
+ const combinedPath = path6.join(outputDir, config.combinedFile);
2671
+ try {
2672
+ await fs6.access(combinedPath);
2673
+ files.push({ tool, path: combinedPath, exists: true });
2674
+ } catch {
2675
+ files.push({ tool, path: combinedPath, exists: false });
2676
+ }
2677
+ const splitDir = path6.join(outputDir, config.splitDir);
2678
+ try {
2679
+ const splitFiles = await fs6.readdir(splitDir);
2680
+ for (const file of splitFiles) {
2681
+ if (file.endsWith(config.fileExtension)) {
2682
+ files.push({
2683
+ tool,
2684
+ path: path6.join(splitDir, file),
2685
+ exists: true
2686
+ });
2687
+ }
2688
+ }
2689
+ } catch {
2690
+ }
2691
+ }
2692
+ return { files };
2693
+ }
2694
+ async function removeContext(outputDir, target) {
2695
+ const tools = target === "all" ? getSupportedTools() : [target];
2696
+ const results = [];
2697
+ for (const tool of tools) {
2698
+ const config = getToolConfig(tool);
2699
+ const combinedPath = path6.join(outputDir, config.combinedFile);
2700
+ try {
2701
+ await fs6.unlink(combinedPath);
2702
+ results.push({ path: combinedPath, removed: true });
2703
+ } catch (error) {
2704
+ if (error.code !== "ENOENT") {
2705
+ results.push({
2706
+ path: combinedPath,
2707
+ removed: false,
2708
+ error: error instanceof Error ? error.message : String(error)
2709
+ });
2710
+ }
2711
+ }
2712
+ const splitDir = path6.join(outputDir, config.splitDir);
2713
+ try {
2714
+ await fs6.rm(splitDir, { recursive: true });
2715
+ results.push({ path: splitDir, removed: true });
2716
+ } catch (error) {
2717
+ if (error.code !== "ENOENT") {
2718
+ results.push({
2719
+ path: splitDir,
2720
+ removed: false,
2721
+ error: error instanceof Error ? error.message : String(error)
2722
+ });
2723
+ }
2724
+ }
2725
+ }
2726
+ return results;
2727
+ }
2728
+ async function getSkill(name) {
2729
+ try {
2730
+ const template = await loadTemplate("skills", name);
2731
+ return { name: template.name, content: template.content };
2732
+ } catch {
2733
+ return null;
2734
+ }
2735
+ }
2736
+ async function listSkills() {
2737
+ const templates = await listTemplates();
2738
+ return templates.skills;
2739
+ }
2740
+ async function hasTemplates() {
2741
+ const templates = await listTemplates();
2742
+ return templates.base.length > 0;
2743
+ }
2388
2744
  export {
2389
2745
  DEMO_SCHEMA_VERSION,
2390
2746
  ScriptGenerator,
2747
+ TOOL_CONFIGS,
2391
2748
  VoiceSynthesizer,
2749
+ buildContext,
2750
+ composeTemplates,
2392
2751
  createClickAction,
2393
2752
  createDragAction,
2394
2753
  createEmptyDemo,
@@ -2407,15 +2766,35 @@ export {
2407
2766
  demoDefinitionSchema,
2408
2767
  demoHover,
2409
2768
  demoType,
2769
+ deployContext,
2410
2770
  discoverDemos,
2771
+ formatForTool,
2772
+ formatStageName,
2411
2773
  formatValidationError,
2412
2774
  generateTimingManifest,
2775
+ getOutputPath,
2776
+ getSkill,
2777
+ getSplitOutputPaths,
2778
+ getStageFileName,
2779
+ getSupportedTools,
2780
+ getToolConfig,
2781
+ hasTemplates,
2413
2782
  highlightElement,
2414
2783
  injectCursorOverlay,
2784
+ interpolateVariables,
2785
+ isValidFormat,
2786
+ isValidTool,
2787
+ listDeployedContext,
2788
+ listSkills,
2789
+ listTemplates,
2415
2790
  loadDemoDefinition,
2791
+ loadTemplate,
2792
+ loadTemplatesByCategory,
2416
2793
  moveCursorTo,
2417
2794
  parseDemoDefinition,
2418
2795
  parseFromYAML,
2796
+ previewContext,
2797
+ removeContext,
2419
2798
  removeCursorOverlay,
2420
2799
  resolvePath,
2421
2800
  resolveTarget,
@@ -2423,6 +2802,7 @@ export {
2423
2802
  runDemoFromFile,
2424
2803
  safeParseDemoDefinition,
2425
2804
  serializeToYAML,
2805
+ templateExists,
2426
2806
  triggerClickRipple,
2427
2807
  validateDemoDefinition
2428
2808
  };