
Cinematic Script Writer
- 759 installs
- 1 repo stars
- Updated February 10, 2026
- praveenspeaks/cinematic-script-writer
cinematic-script-writer is a Claude Code skill that generates cinematic scripts, character consistency sheets, and AI video prompts for developers building generative media pipelines with tools like Midjourney, Sora, and
About
cinematic-script-writer is a Claude Code skill (version 1.4.0) for professional cinematic scripts aimed at AI video generation pipelines. It produces story contexts with character consistency sheets, voice profiles, image prompts for Midjourney, Sora, and Veo, plus cinematography guidance covering camera angles, lighting, and color grading. The skill also supports anachronism detection and optional Google Drive script saves, requiring Node.js per metadata. Developers and creative technologists reach for cinematic-script-writer when a generative video workflow needs shot-level structure instead of ad-hoc prompt strings.
- Generates full cinematic scripts optimized for AI video generation
- Creates and manages story contexts with characters, era, and settings
- Produces character consistency sheets and voice profiles
- Performs anachronism detection for era-accurate storytelling
- Saves scripts and assets directly to Google Drive
Cinematic Script Writer by the numbers
- 759 all-time installs (skills.sh)
- +11 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #322 of 1,335 Generative Media skills by installs in the Skillselion catalog
- Security screen: HIGH risk (skills.sh audit)
- Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/praveenspeaks/cinematic-script-writer --skill cinematic-script-writerAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 759 |
|---|---|
| repo stars | ★ 1 |
| Security audit | 2 / 3 scanners passed |
| Last updated | February 10, 2026 |
| Repository | praveenspeaks/cinematic-script-writer ↗ |
How do you write cinematic scripts for AI video tools?
Generate professional cinematic scripts, character consistency sheets, image prompts and cinematography guidance for AI video tools like Midjourney, Sora and Veo.
Who is it for?
Developers building AI video pipelines who need structured scripts and consistent character prompts for Sora, Veo, or Midjourney.
Skip if: Traditional film production logistics, code-only backend tasks, or teams not using generative video or image tools.
When should I use this skill?
The user wants cinematic scripts, character sheets, cinematography guidance, or AI video image prompts for generative tools.
What you get
Cinematic scripts, character consistency sheets, voice profiles, image prompts, and cinematography shot notes.
- Cinematic script documents
- Character consistency sheets
- AI image and video prompts
By the numbers
- Skill version 1.4.0
- Targets 3 generative tools: Midjourney, Sora, and Veo
Files
Cinematic Script Writer
Create professional cinematic scripts for AI video generation with character consistency and cinematography knowledge.
Installation
# Install via npm
npm install -g cinematic-script-writer
# Or install via OpenClaw CLI
openclaw skills install cinematic-script-writerCLI Usage
Context Management
Create and manage story contexts with characters, era, and settings:
# Create a new story context
cinematic-script create-context --name "My Story" --era "Ancient India" --period "Ramayana Era"
# List all saved contexts
cinematic-script list-contexts
# Get a specific context
cinematic-script get-context --id <context-id>
# Delete a context
cinematic-script delete-context --id <context-id>Story Generation
Generate story ideas and create cinematic scripts:
# Generate story ideas for a context
cinematic-script generate-ideas --context-id <context-id> --count 3
# Create a full cinematic script from an idea
cinematic-script create-script --context-id <context-id> --idea-id <idea-id>
# Generate YouTube metadata for a script
cinematic-script generate-metadata --script-id <script-id>Cinematography Reference
Access camera angles, lighting, and shot type databases:
# List all camera angles
cinematic-script list-angles
# List all camera movements
cinematic-script list-movements
# List all shot types
cinematic-script list-shots
# Get camera setup recommendation
cinematic-script suggest-camera --scene-type "dialogue" --mood "dramatic"
# Get lighting suggestions
cinematic-script suggest-lighting --scene-type "interior" --mood "mysterious"
# Get color grading suggestions
cinematic-script suggest-grading --genre "action"
# Search cinematography database
cinematic-script search --query "low angle lighting"Character Consistency
Create character references and validate prompts:
# Create a character reference sheet
cinematic-script create-character-ref --character-id "char1" --name "Kutil" --visual "Purple rakshasa with golden eyes" --era "Ancient" --style "Pixar 3D"
# Create a voice profile for dialogue consistency
cinematic-script create-voice --character-id "char1" --name "Kutil" --personality "Mischievous, witty" --age "adult" --role "protagonist"
# Validate a prompt for anachronisms
cinematic-script validate-prompt --prompt "Your prompt here" --character-ids "char1,char2" --context-id <context-id>Storage
Save projects to Google Drive or local storage:
# Connect to Google Drive
cinematic-script connect-drive
# Connect to local storage
cinematic-script connect-local
# Check storage connection status
cinematic-script storage-status
# Save project to storage
cinematic-script save --title "My Story" --context-id <context-id> --script-id <script-id>Storage implementation details:
- Google Drive: Uses Google OAuth2 for authentication. Credentials are stored securely in memory.
- Local Storage: Saves to the user's downloads folder as fallback.
- Library: Uses
googleapisfor Google Drive integration.
Export
Export scripts in various formats:
# Export as Markdown (default)
cinematic-script export --script-id <script-id> --format markdown
# Export as JSON
cinematic-script export --script-id <script-id> --format json
# Export as plain text
cinematic-script export --script-id <script-id> --format textFeatures
- Story Context Management: Create and manage story settings, characters, and eras
- Story Idea Generation: Generate multiple story concepts with hooks and twists
- Cinematic Script Writing: Full scripts with camera angles, lighting, and shot types
- Character Consistency: Reference sheets and voice profiles for consistent characters
- Environment Consistency: Era-appropriate style guides and anachronism detection
- YouTube Metadata: Generate titles, descriptions, and SEO tags
- Storage Integration: Save to Google Drive or local storage
- Export Options: JSON, Markdown, or plain text formats
When to Use
- Writing cinematic scripts or screenplays
- Creating stories with characters for animation/video
- Generating image/video prompts for AI tools (Midjourney, Sora, Veo, Runway)
- Getting cinematography guidance (camera angles, lighting, color grading)
- Maintaining character consistency across scenes
- Saving script projects to Google Drive
Cinematography Reference
Camera Angles
| Angle | Emotional Impact | Best For |
|---|---|---|
| Eye-level | Connection, equality, neutrality | Dialogue, emotional moments |
| Low-angle | Power, dominance, heroism | Villain reveals, hero moments |
| High-angle | Vulnerability, weakness, overview | Defeat, establishing scale |
| Bird-eye | Insignificance, detachment, patterns | Epic scale, isolation |
| Worm-eye | Awe, grandeur, overwhelming presence | Monuments, giants, deities |
| Dutch angle | Unease, disorientation, tension | Chaos, dreams, horror |
| Overhead | Omniscience, surveillance | Table scenes, fight choreography |
| Shoulder-level | Intimate, casual, documentary feel | Walking conversations |
| Hip-level | Cowboy feel, casual tension | Westerns, standoffs |
| Knee-level | Childlike perspective, grounding | Children's stories, humility |
Camera Movements
| Movement | Effect | Use For |
|---|---|---|
| Static | Stability, observation | Contemplation, portraits |
| Pan | Revealing space | Following action horizontally |
| Tilt | Revealing height | Following vertical action |
| Dolly | Immersion, intimacy | Moving toward/away from subject |
| Truck | Following action | Side-to-side parallel movement |
| Crane | Epic scale, drama | Sweeping reveals, transitions |
| Handheld | Urgency, realism | Documentary, action, chaos |
| Steadicam | Smooth floating | Following through space, dreams |
| Zoom | Sudden focus, surprise | Dramatic emphasis, comedy |
| Rack-focus | Revealing connections | Shifting attention between subjects |
Shot Types
| Shot | Framing | Emotional Impact |
|---|---|---|
| Establishing | Wide location | Sets scene, geography, time |
| Wide/Full | Subject + surroundings | Context, environment, scale |
| Medium | Waist up | Dialogue, body language |
| Close-up | Head/shoulders | Emotion, reaction, intimacy |
| Extreme close-up | Detail only (eyes, hands) | Intense emotion, symbolism |
| Over-shoulder | Past one subject to another | Conversation, perspective |
| POV | Character's view | Immersion, subjectivity |
| Insert | Object detail | Plot info, symbolism |
| Two-shot | Two subjects together | Relationship, tension |
Lighting Techniques
| Technique | Mood | Best For |
|---|---|---|
| Three-point | Professional, balanced | Dialogue, interviews |
| High-key | Happy, optimistic, bright | Comedy, commercials |
| Low-key | Dramatic, mysterious | Drama, horror, noir |
| Golden-hour | Romantic, nostalgic, magical | Romance, emotional moments |
| Blue-hour | Melancholic, mysterious | Urban, cityscapes |
| Chiaroscuro | Dramatic contrast | Art films, period pieces |
| Rim/backlight | Separation, ethereal | Silhouettes, divine presence |
| Practical | Realistic, natural | Candles, fires, lamps |
| God-rays | Divine, revelation | Spiritual moments, forests |
| Neon | Urban, futuristic | Cyberpunk, nightlife |
Color Grading
| Style | Look | Genre |
|---|---|---|
| Teal-orange | Blockbuster cinematic | Action, sci-fi |
| Noir | High-contrast desaturated | Crime, mystery |
| Vintage/sepia | Warm, nostalgic | Period pieces, memory |
| Pastel | Soft, dreamy | Romance, coming-of-age |
| Bleach bypass | Desaturated, gritty | War, thriller |
| Cross-process | Surreal colors | Music videos, dreams |
Image Prompt Format
When generating image prompts for AI tools:
[Shot type] [camera angle] of [subject doing action], [visual style] style,
[lighting technique], [composition rule], [color grading],
[era-appropriate details], [mood keywords], highly detailed, cinematicExample:
Low-angle close-up of Kutil the purple rakshasa with mischievous golden eyes,
Pixar 3D style, dramatic underlighting with rim light, rule-of-thirds composition,
warm golden color grading, ancient Lanka palace background with ornate pillars,
playful yet mysterious mood, highly detailed, cinematic, 8kOutput Structure
When saving a project, the following files are generated:
Story Title/
├── 00_INDEX.md # Navigation
├── 01_SCRIPT_README.md # Human-readable script
├── 02_IMAGE_PROMPTS.md # All AI generation prompts
├── 03_CHARACTER_REFS.md # Character design guides
├── 04_VOICE_GUIDES.md # Dialogue consistency guides
├── 05_YOUTUBE_META.md # Title, description, tags
└── 99_CONTEXT_INFO.md # Story context and backgroundImportant Rules
1. Always maintain character consistency - include character's full visual description in every image prompt 2. Never include anachronisms - validate props, clothing, objects against the era 3. Match cinematography to emotion - use low angles for power, high angles for vulnerability 4. Include both image and video prompts - image prompts are static, video prompts describe motion 5. Production-ready output - every script should include enough detail for a team to produce it 6. Respect the tone - comedy needs comedic timing; drama needs longer holds on reactions
License
MIT
Author
Praveen Kumar
{
"permissions": {
"allow": [
"Bash(tasklist:*)",
"Bash(findstr:*)",
"Bash(taskkill:*)",
"Bash(git status -u)",
"Bash(npm whoami:*)",
"Bash(npm publish:*)",
"Bash(npm org:*)"
]
}
}
{
"env": {
"browser": true,
"es2021": true,
"node": true
},
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended"
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": "latest",
"sourceType": "module"
},
"plugins": [
"@typescript-eslint"
],
"rules": {
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-unused-vars": ["warn", { "argsIgnorePattern": "^_" }],
"prefer-const": "warn",
"no-console": "off"
},
"ignorePatterns": [
"dist/",
"node_modules/"
]
}
name: CI
on:
push:
branches: [ main, master ]
pull_request:
branches: [ main, master ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18.x, 20.x]
steps:
- uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
- name: Run tests
run: npm test
- name: Lint
run: npm run lint
node_modules/
dist/
*.log
.env
.env.local
.DS_Store
*.tsbuildinfo
coverage/
.vscode/
.idea/
#!/usr/bin/env node
/**
* Cinematic Script Writer CLI
* Thin wrapper around the CinematicScriptWriter class for shell usage.
*/
import { CinematicScriptWriter, SkillConfig, SkillContext } from '../skills/cinematic-script-writer/index';
// ---------------------------------------------------------------------------
// Minimal in-memory implementations of MemoryStore and Logger
// ---------------------------------------------------------------------------
const memoryData = new Map<string, any>();
const memory = {
async get(key: string) { return memoryData.get(key) ?? null; },
async set(key: string, value: any) { memoryData.set(key, value); },
async delete(key: string) { memoryData.delete(key); },
};
const logger = {
debug(msg: string) { if (process.env.DEBUG) console.error('[debug]', msg); },
info(msg: string) { console.error('[info]', msg); },
warn(msg: string) { console.error('[warn]', msg); },
error(msg: string) { console.error('[error]', msg); },
};
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
function createSkill(): CinematicScriptWriter {
const config: SkillConfig = {};
const context: SkillContext = { userId: 'cli-user', memory, logger };
return new CinematicScriptWriter(config, context);
}
function printJson(data: any): void {
console.log(JSON.stringify(data, null, 2));
}
function requiredArg(args: string[], flag: string): string {
const idx = args.indexOf(flag);
if (idx === -1 || idx + 1 >= args.length) {
console.error(`Missing required argument: ${flag}`);
process.exit(1);
}
return args[idx + 1];
}
function optionalArg(args: string[], flag: string, fallback: string): string {
const idx = args.indexOf(flag);
if (idx === -1 || idx + 1 >= args.length) return fallback;
return args[idx + 1];
}
// ---------------------------------------------------------------------------
// Command handlers
// ---------------------------------------------------------------------------
const commands: Record<string, (skill: CinematicScriptWriter, args: string[]) => Promise<void>> = {
// -- Context Management ---------------------------------------------------
'create-context': async (skill, args) => {
const name = requiredArg(args, '--name');
const description = optionalArg(args, '--description', '');
const period = optionalArg(args, '--period', 'Modern');
const era = optionalArg(args, '--era', 'Contemporary');
const location = optionalArg(args, '--location', 'City');
const videoType = optionalArg(args, '--video-type', 'short') as any;
const tone = optionalArg(args, '--tone', 'comedy') as any;
const audience = optionalArg(args, '--audience', 'General');
const style = optionalArg(args, '--style', 'cinematic');
const ctx = await skill.createContext(
name, description, [], period, era, location, videoType, tone, audience, style,
);
printJson(ctx);
},
'list-contexts': async (skill) => {
printJson(await skill.listContexts());
},
'get-context': async (skill, args) => {
const id = requiredArg(args, '--id');
const ctx = await skill.getContext(id);
if (!ctx) { console.error('Context not found'); process.exit(1); }
printJson(ctx);
},
'delete-context': async (skill, args) => {
const id = requiredArg(args, '--id');
await skill.deleteContext(id);
console.log('Deleted.');
},
// -- Story Generation -----------------------------------------------------
'generate-ideas': async (skill, args) => {
const contextId = requiredArg(args, '--context-id');
const count = parseInt(optionalArg(args, '--count', '3'), 10);
printJson(await skill.generateStoryIdeas(contextId, count));
},
'create-script': async (skill, args) => {
const contextId = requiredArg(args, '--context-id');
const ideaId = requiredArg(args, '--idea-id');
// Load saved ideas to find the matching one
const ideas = await (skill as any).context.memory.get(
(skill as any).getStorageKey('ideas', contextId),
);
const idea = ideas?.find((i: any) => i.id === ideaId);
if (!idea) { console.error('Idea not found. Run generate-ideas first.'); process.exit(1); }
printJson(await skill.createCinematicScript(contextId, ideaId, idea));
},
'generate-metadata': async (skill, args) => {
const scriptId = requiredArg(args, '--script-id');
printJson(await skill.generateYouTubeMetadata(scriptId));
},
// -- Cinematography -------------------------------------------------------
'list-angles': async (skill) => { printJson(skill.getAllCameraAngles()); },
'list-movements': async (skill) => { printJson(skill.getAllCameraMovements()); },
'list-shots': async (skill) => { printJson(skill.getAllShotTypes()); },
'suggest-camera': async (skill, args) => {
const sceneType = requiredArg(args, '--scene-type');
const mood = requiredArg(args, '--mood');
const level = optionalArg(args, '--level', 'intermediate') as any;
printJson(skill.getRecommendedCameraSetup(sceneType, mood, level));
},
'suggest-lighting': async (skill, args) => {
const sceneType = requiredArg(args, '--scene-type');
const mood = requiredArg(args, '--mood');
printJson(skill.suggestLighting(sceneType, mood));
},
'suggest-grading': async (skill, args) => {
const genre = requiredArg(args, '--genre');
printJson(skill.suggestColorGrading(genre));
},
'search': async (skill, args) => {
const query = requiredArg(args, '--query');
printJson(skill.searchCinematography(query));
},
// -- Consistency ----------------------------------------------------------
'create-character-ref': async (skill, args) => {
const characterId = requiredArg(args, '--character-id');
const name = requiredArg(args, '--name');
const visual = requiredArg(args, '--visual');
const era = requiredArg(args, '--era');
const style = requiredArg(args, '--style');
printJson(skill.createCharacterReference(characterId, name, visual, era, style));
},
'create-voice': async (skill, args) => {
const characterId = requiredArg(args, '--character-id');
const name = requiredArg(args, '--name');
const personality = requiredArg(args, '--personality');
const age = optionalArg(args, '--age', 'adult');
const role = optionalArg(args, '--role', 'supporting');
printJson(skill.createVoiceProfile(characterId, name, personality, age, role));
},
'validate-prompt': async (skill, args) => {
const prompt = requiredArg(args, '--prompt');
const charIds = requiredArg(args, '--character-ids').split(',');
const contextId = requiredArg(args, '--context-id');
printJson(skill.validatePrompt(prompt, charIds, contextId));
},
// -- Storage --------------------------------------------------------------
'connect-drive': async (skill) => {
printJson(await skill.connectGoogleDrive());
},
'connect-local': async (skill, args) => {
const basePath = optionalArg(args, '--path', '');
printJson(await skill.connectLocalStorage(basePath || undefined));
},
'storage-status': async (skill) => {
printJson(await skill.getStorageStatus());
},
'save': async (skill, args) => {
const title = requiredArg(args, '--title');
const contextId = requiredArg(args, '--context-id');
const scriptId = optionalArg(args, '--script-id', '');
printJson(await skill.saveScriptToStorage(title, contextId, scriptId));
},
// -- Export ---------------------------------------------------------------
'export': async (skill, args) => {
const scriptId = requiredArg(args, '--script-id');
const format = optionalArg(args, '--format', 'markdown') as 'json' | 'text' | 'markdown';
console.log(await skill.exportScript(scriptId, format));
},
};
// ---------------------------------------------------------------------------
// Main
// ---------------------------------------------------------------------------
async function main() {
const args = process.argv.slice(2);
const command = args[0];
if (!command || command === '--help' || command === '-h') {
console.log(`
Cinematic Script Writer CLI
Usage: cinematic-script <command> [options]
Commands:
Context:
create-context Create a new story context
list-contexts List all contexts
get-context Get a specific context
delete-context Delete a context
Story:
generate-ideas Generate story ideas for a context
create-script Create a cinematic script from an idea
generate-metadata Generate YouTube metadata for a script
Cinematography:
list-angles List all camera angles
list-movements List all camera movements
list-shots List all shot types
suggest-camera Get camera setup recommendation
suggest-lighting Get lighting suggestions
suggest-grading Get color grading suggestions
search Search cinematography database
Consistency:
create-character-ref Create a character reference sheet
create-voice Create a voice profile
validate-prompt Validate a prompt for anachronisms
Storage:
connect-drive Connect Google Drive
connect-local Connect local storage
storage-status Check storage connection
save Save project to storage
Export:
export Export a script (json, text, markdown)
Run "cinematic-script <command> --help" for command-specific options.
`.trim());
process.exit(0);
}
const handler = commands[command];
if (!handler) {
console.error(`Unknown command: ${command}`);
console.error('Run "cinematic-script --help" for available commands.');
process.exit(1);
}
const skill = createSkill();
await skill.loadStorageConfig();
await handler(skill, args.slice(1));
}
main().catch((err) => {
console.error(err.message || err);
process.exit(1);
});
Changelog
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[1.3.0] - 2026-02-10
Added
- Google Drive Storage Integration
- Save all generated content to Google Drive
- Organized folders with story title
- OAuth authentication
- Local storage option (downloads)
- Complete project export (script, prompts, consistency, voice, metadata)
- Shareable links
New Files
storage-adapter.ts- Storage adapters (Google Drive, Local)storage-manager.ts- File organization and savingEXAMPLE-STORAGE.md- Storage usage examples
[1.2.0] - 2026-02-10
Added
- Consistency System
- Character reference sheets for visual consistency
- Voice profiles for dialogue consistency
- Environment style guides for era-appropriate content
- Prompt builder with consistency enforcement
- Anachronism detection (validates era-appropriate elements)
- Validation system for prompts
New Files
consistency-system.ts- Consistency managementprompt-builder.ts- Consistent prompt generationEXAMPLE-CONSISTENCY.md- Consistency examples
[1.1.0] - 2026-02-10
Added
- Comprehensive Cinematography Database
- 20+ camera angles with emotional impact
- 20+ camera movements
- 25+ shot types
- 30+ lighting techniques
- 20+ composition rules
- 20+ color grading styles
- 25+ visual aesthetics
- 15+ genre cinematography guides
- Indian cinematography styles
New Files
cinematography-db.ts- Camera techniques databasecinematography-api.ts- Unified APIlighting-db.ts- Lighting and compositionvisual-styles-db.ts- Visual aesthetics
[1.0.0] - 2026-02-10
Added
- Initial release of Cinematic Script Writer Skill
- Context management (characters, era, settings)
- Story idea generation
- Cinematic script creation with shots and camera angles
- Image/Video generation prompts
- YouTube metadata generation
- Basic camera techniques
Files
skill-template/- Template for creating new skillsexamples/- Example skills (weather, todo, file-manager)skills/cinematic-script-writer/- Main skill implementationEXAMPLE-KUTIL.md- Complete usage example
Contributing to OpenClaw Skills
Thank you for your interest in contributing! This document provides guidelines for contributing to this project.
How to Contribute
Reporting Bugs
- Check if the bug has already been reported in Issues
- Include a clear description of the bug
- Provide steps to reproduce
- Include error messages and stack traces
- Specify your environment (OS, Node.js version, etc.)
Suggesting Features
- Check if the feature has already been suggested in Issues
- Describe the feature and its use case
- Explain why it would be useful
Pull Requests
1. Fork the repository 2. Create a new branch: git checkout -b feature/your-feature-name 3. Make your changes 4. Add tests if applicable 5. Ensure all tests pass: npm test 6. Commit your changes: git commit -am 'Add new feature' 7. Push to the branch: git push origin feature/your-feature-name 8. Submit a pull request
Development Setup
# Clone your fork
git clone https://github.com/yourusername/openclawskills.git
cd openclawskills
# Install dependencies
npm install
# Run tests
npm test
# Build
npm run buildCode Style
- Use TypeScript for type safety
- Follow existing code formatting
- Add JSDoc comments for public methods
- Keep functions focused and modular
Commit Messages
- Use present tense ("Add feature" not "Added feature")
- Use imperative mood ("Move cursor to..." not "Moves cursor to...")
- Reference issues and pull requests where appropriate
License
By contributing, you agree that your contributions will be licensed under the MIT License.
/**
* File Manager Skill
* Basic file operations
*/
import * as fs from 'fs/promises';
import * as path from 'path';
interface FileManagerConfig {
basePath?: string;
allowedExtensions?: string[];
}
interface SkillContext {
userId: string;
logger: Logger;
}
interface Logger {
debug(msg: string): void;
info(msg: string): void;
error(msg: string): void;
}
interface FileInfo {
name: string;
path: string;
size: number;
isDirectory: boolean;
modifiedAt: Date;
}
export class FileManagerSkill {
private config: FileManagerConfig;
private context: SkillContext;
constructor(config: FileManagerConfig, context: SkillContext) {
this.config = config;
this.context = context;
}
private resolvePath(filePath: string): string {
const base = this.config.basePath || process.cwd();
// Prevent directory traversal
const resolved = path.resolve(base, filePath);
if (!resolved.startsWith(path.resolve(base))) {
throw new Error('Access denied: Path outside base directory');
}
return resolved;
}
/**
* Read file contents
*/
async readFile(filePath: string, encoding: 'utf-8' | 'base64' = 'utf-8'): Promise<string> {
const fullPath = this.resolvePath(filePath);
this.context.logger.debug(`Reading file: ${fullPath}`);
try {
const content = await fs.readFile(fullPath, { encoding });
return content;
} catch (error) {
this.context.logger.error(`Failed to read file: ${error}`);
throw new Error(`Could not read file: ${filePath}`);
}
}
/**
* Write to a file
*/
async writeFile(filePath: string, content: string, encoding: 'utf-8' | 'base64' = 'utf-8'): Promise<void> {
const fullPath = this.resolvePath(filePath);
// Check extension if restrictions apply
if (this.config.allowedExtensions) {
const ext = path.extname(filePath);
if (!this.config.allowedExtensions.includes(ext)) {
throw new Error(`File type not allowed: ${ext}`);
}
}
this.context.logger.info(`Writing file: ${fullPath}`);
// Ensure directory exists
await fs.mkdir(path.dirname(fullPath), { recursive: true });
await fs.writeFile(fullPath, content, { encoding });
}
/**
* List files in directory
*/
async listFiles(dirPath: string = ''): Promise<FileInfo[]> {
const fullPath = this.resolvePath(dirPath);
this.context.logger.debug(`Listing files: ${fullPath}`);
try {
const entries = await fs.readdir(fullPath, { withFileTypes: true });
const files: FileInfo[] = [];
for (const entry of entries) {
const entryPath = path.join(fullPath, entry.name);
const stats = await fs.stat(entryPath);
files.push({
name: entry.name,
path: path.relative(this.config.basePath || process.cwd(), entryPath),
size: stats.size,
isDirectory: entry.isDirectory(),
modifiedAt: stats.mtime
});
}
return files;
} catch (error) {
this.context.logger.error(`Failed to list files: ${error}`);
throw new Error(`Could not list files in: ${dirPath}`);
}
}
/**
* Delete a file
*/
async deleteFile(filePath: string): Promise<void> {
const fullPath = this.resolvePath(filePath);
this.context.logger.info(`Deleting file: ${fullPath}`);
await fs.unlink(fullPath);
}
/**
* Check if file exists
*/
async exists(filePath: string): Promise<boolean> {
try {
const fullPath = this.resolvePath(filePath);
await fs.access(fullPath);
return true;
} catch {
return false;
}
}
}
export default function createSkill(config: FileManagerConfig, context: SkillContext) {
return new FileManagerSkill(config, context);
}
export type { FileManagerConfig, FileInfo };
{
"$schema": "https://clawhub.ai/schemas/skill.json",
"name": "file-manager-skill",
"version": "1.0.0",
"description": "Read, write, and manage files",
"author": "You",
"license": "MIT",
"entry": "index.ts",
"config": {
"schema": "schema.json",
"required": false
},
"permissions": ["fs:read", "fs:write", "fs:delete"],
"tools": [
{ "name": "readFile", "description": "Read file contents" },
{ "name": "writeFile", "description": "Write to a file" },
{ "name": "listFiles", "description": "List files in a directory" },
{ "name": "deleteFile", "description": "Delete a file" }
],
"tags": ["filesystem", "utility"]
}
/**
* Todo Skill
* Simple todo list with persistent storage
*/
interface TodoConfig {
storagePath?: string;
}
interface SkillContext {
userId: string;
memory: MemoryStore;
logger: Logger;
}
interface MemoryStore {
get(key: string): Promise<any>;
set(key: string, value: any): Promise<void>;
}
interface Logger {
debug(msg: string): void;
info(msg: string): void;
}
interface Todo {
id: string;
text: string;
completed: boolean;
createdAt: string;
completedAt?: string;
priority: 'low' | 'medium' | 'high';
}
interface TodoList {
todos: Todo[];
lastModified: string;
}
export class TodoSkill {
private context: SkillContext;
private storageKey: string;
constructor(config: TodoConfig, context: SkillContext) {
this.context = context;
this.storageKey = `todos:${context.userId}`;
}
private async getTodos(): Promise<Todo[]> {
const data = await this.context.memory.get(this.storageKey);
return data?.todos || [];
}
private async saveTodos(todos: Todo[]): Promise<void> {
const list: TodoList = {
todos,
lastModified: new Date().toISOString()
};
await this.context.memory.set(this.storageKey, list);
}
/**
* Add a new todo
*/
async addTodo(text: string, priority: 'low' | 'medium' | 'high' = 'medium'): Promise<Todo> {
this.context.logger.info(`Adding todo: ${text}`);
const todo: Todo = {
id: Date.now().toString(),
text,
completed: false,
createdAt: new Date().toISOString(),
priority
};
const todos = await this.getTodos();
todos.push(todo);
await this.saveTodos(todos);
return todo;
}
/**
* List all todos
*/
async listTodos(filter?: 'all' | 'active' | 'completed'): Promise<Todo[]> {
const todos = await this.getTodos();
switch (filter) {
case 'active':
return todos.filter(t => !t.completed);
case 'completed':
return todos.filter(t => t.completed);
default:
return todos;
}
}
/**
* Mark a todo as complete
*/
async completeTodo(id: string): Promise<Todo | null> {
const todos = await this.getTodos();
const todo = todos.find(t => t.id === id);
if (!todo) return null;
todo.completed = true;
todo.completedAt = new Date().toISOString();
await this.saveTodos(todos);
this.context.logger.info(`Completed todo: ${todo.text}`);
return todo;
}
/**
* Delete a todo
*/
async deleteTodo(id: string): Promise<boolean> {
const todos = await this.getTodos();
const index = todos.findIndex(t => t.id === id);
if (index === -1) return false;
todos.splice(index, 1);
await this.saveTodos(todos);
return true;
}
/**
* Get stats
*/
async getStats(): Promise<{ total: number; active: number; completed: number }> {
const todos = await this.getTodos();
return {
total: todos.length,
active: todos.filter(t => !t.completed).length,
completed: todos.filter(t => t.completed).length
};
}
}
export default function createSkill(config: TodoConfig, context: SkillContext) {
return new TodoSkill(config, context);
}
export type { Todo, TodoConfig };
{
"$schema": "https://clawhub.ai/schemas/skill.json",
"name": "todo-skill",
"version": "1.0.0",
"description": "Simple todo list management with persistent storage",
"author": "You",
"license": "MIT",
"entry": "index.ts",
"config": {
"schema": "schema.json",
"required": false
},
"permissions": ["fs:read", "fs:write"],
"tools": [
{ "name": "addTodo", "description": "Add a new todo item" },
{ "name": "listTodos", "description": "List all todos" },
{ "name": "completeTodo", "description": "Mark a todo as complete" },
{ "name": "deleteTodo", "description": "Delete a todo" }
],
"tags": ["productivity", "storage"]
}
/**
* Weather Skill
* Fetches weather data from OpenWeatherMap API
*/
interface WeatherConfig {
apiKey: string;
defaultLocation?: string;
units?: 'metric' | 'imperial' | 'kelvin';
}
interface SkillContext {
userId: string;
memory: MemoryStore;
logger: Logger;
http: HttpClient;
}
interface MemoryStore {
get(key: string): Promise<any>;
set(key: string, value: any): Promise<void>;
}
interface Logger {
debug(msg: string): void;
info(msg: string): void;
error(msg: string): void;
}
interface HttpClient {
get(url: string, options?: any): Promise<any>;
}
interface WeatherData {
location: string;
temperature: number;
feelsLike: number;
humidity: number;
description: string;
windSpeed: number;
timestamp: string;
}
export class WeatherSkill {
private config: WeatherConfig;
private context: SkillContext;
private baseUrl = 'https://api.openweathermap.org/data/2.5';
constructor(config: WeatherConfig, context: SkillContext) {
this.config = config;
this.context = context;
}
/**
* Get current weather for a location
*/
async getCurrentWeather(location?: string): Promise<WeatherData> {
const targetLocation = location || this.config.defaultLocation || 'London';
this.context.logger.debug(`Fetching weather for: ${targetLocation}`);
try {
const response = await this.context.http.get(
`${this.baseUrl}/weather?q=${encodeURIComponent(targetLocation)}&appid=${this.config.apiKey}&units=${this.config.units || 'metric'}`
);
const weather: WeatherData = {
location: `${response.name}, ${response.sys.country}`,
temperature: response.main.temp,
feelsLike: response.main.feels_like,
humidity: response.main.humidity,
description: response.weather[0].description,
windSpeed: response.wind.speed,
timestamp: new Date().toISOString()
};
// Cache in memory
await this.context.memory.set(
`weather:last:${this.context.userId}`,
weather
);
return weather;
} catch (error) {
this.context.logger.error(`Weather fetch failed: ${error}`);
throw new Error(`Could not fetch weather for ${targetLocation}`);
}
}
/**
* Get 5-day forecast
*/
async getForecast(location?: string): Promise<any> {
const targetLocation = location || this.config.defaultLocation || 'London';
this.context.logger.debug(`Fetching forecast for: ${targetLocation}`);
try {
const response = await this.context.http.get(
`${this.baseUrl}/forecast?q=${encodeURIComponent(targetLocation)}&appid=${this.config.apiKey}&units=${this.config.units || 'metric'}`
);
// Simplify forecast data
const dailyForecasts = response.list
.filter((item: any, index: number) => index % 8 === 0) // One per day
.slice(0, 5)
.map((item: any) => ({
date: new Date(item.dt * 1000).toDateString(),
temp: item.main.temp,
description: item.weather[0].description,
humidity: item.main.humidity
}));
return {
location: `${response.city.name}, ${response.city.country}`,
forecast: dailyForecasts
};
} catch (error) {
this.context.logger.error(`Forecast fetch failed: ${error}`);
throw new Error(`Could not fetch forecast for ${targetLocation}`);
}
}
/**
* Get last cached weather
*/
async getLastWeather(): Promise<WeatherData | null> {
return await this.context.memory.get(`weather:last:${this.context.userId}`);
}
}
export default function createSkill(config: WeatherConfig, context: SkillContext) {
return new WeatherSkill(config, context);
}
export type { WeatherConfig, WeatherData };
Weather Skill
Get weather information using OpenWeatherMap API.
Setup
1. Get a free API key from OpenWeatherMap 2. Configure the skill with your API key
Configuration
{
"apiKey": "your-api-key-here",
"defaultLocation": "New York",
"units": "metric"
}Tools
getCurrentWeather(location?)- Get current weathergetForecast(location?)- Get 5-day forecastgetLastWeather()- Get cached weather data
Usage Example
const weather = await agent.tools.weather.getCurrentWeather("Tokyo");
// Returns: { location, temperature, feelsLike, humidity, description, windSpeed, timestamp }{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"apiKey": {
"type": "string",
"description": "OpenWeatherMap API key"
},
"defaultLocation": {
"type": "string",
"description": "Default location for weather queries",
"default": "London"
},
"units": {
"type": "string",
"enum": ["metric", "imperial", "kelvin"],
"default": "metric"
}
},
"required": ["apiKey"]
}
{
"$schema": "https://clawhub.ai/schemas/skill.json",
"name": "weather-skill",
"version": "1.0.0",
"description": "Get weather information for any location",
"author": "You",
"license": "MIT",
"entry": "index.ts",
"config": {
"schema": "schema.json",
"required": ["apiKey"]
},
"permissions": ["http:request", "fs:read"],
"tools": [
{
"name": "getCurrentWeather",
"description": "Get current weather for a location"
},
{
"name": "getForecast",
"description": "Get 5-day weather forecast"
}
],
"dependencies": {},
"tags": ["weather", "api", "utility"]
}
MIT License
Copyright (c) 2026 OpenClaw Skills
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
{
"name": "cinematic-script-writer",
"version": "1.4.3",
"description": "OpenClaw Agent Skills - Cinematic Script Writer with consistency, cinematography, and storage",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"bin": {
"cinematic-script": "dist/bin/cinematic-script.js"
},
"scripts": {
"build": "tsc",
"test": "jest",
"lint": "eslint . --ext .ts",
"lint:fix": "eslint . --ext .ts --fix",
"clean": "rm -rf dist",
"prepare": "npm run build"
},
"keywords": [
"openclaw",
"cinematic-script-writer",
"ai",
"agent",
"skills",
"cinematography",
"script-writing",
"video-generation",
"character-consistency",
"google-drive"
],
"author": "Praveen Kumar",
"license": "MIT",
"repository": {
"type": "git",
"url": "git+https://github.com/praveenspeaks/cinematic-script-writer.git"
},
"bugs": {
"url": "https://github.com/praveenspeaks/cinematic-script-writer/issues"
},
"homepage": "https://github.com/praveenspeaks/cinematic-script-writer#readme",
"devDependencies": {
"@types/jest": "^29.5.0",
"@types/node": "^20.0.0",
"@types/uuid": "^9.0.0",
"@typescript-eslint/eslint-plugin": "^6.0.0",
"@typescript-eslint/parser": "^6.0.0",
"eslint": "^8.45.0",
"jest": "^29.0.0",
"ts-jest": "^29.1.0",
"typescript": "^5.0.0"
},
"dependencies": {
"uuid": "^9.0.0"
},
"files": [
"dist/**/*",
"skills/cinematic-script-writer/skill.json",
"SKILL.md",
"README.md",
"LICENSE"
],
"engines": {
"node": ">=18.0.0"
}
}
Cinematic Script Writer Skill
  
Professional cinematic script generation for AI video creation with character consistency, comprehensive cinematography knowledge, and Google Drive integration.
Features
| Feature | Description |
|---|---|
| 175+ Cinematography Techniques | Camera angles, movements, shots, lighting, composition |
| Character Consistency | Reference sheets ensuring same appearance across all shots |
| Voice Consistency | Speech profiles for consistent dialogue |
| Environment Consistency | Era-appropriate architecture, clothing, props |
| Anachronism Detection | Validates no modern elements in historical settings |
| Google Drive Integration | Auto-save all content to organized folders |
| YouTube Metadata | Titles, descriptions, tags for upload |
| CLI Tool | cinematic-script command for all operations |
Quick Start
# Install globally
npm install -g cinematic-script-writer
# Create a story context
cinematic-script create-context --name "My Story" --genre comedy --era "Modern"
# Browse cinematography techniques
cinematic-script list-angles
cinematic-script suggest-lighting --scene-type interior-day --mood comedySee SKILL.md for full CLI documentation.
Repository Structure
openclawskills/
├── SKILL.md # Skill definition (ClawHub auto-detects this)
├── bin/
│ └── cinematic-script.ts # CLI entry point
├── skills/
│ └── cinematic-script-writer/ # Main skill implementation
│ ├── index.ts # Main class
│ ├── skill.json # Tool definitions (55 methods)
│ ├── cinematography-*.ts # Camera techniques database
│ ├── consistency-*.ts # Consistency system
│ ├── storage-*.ts # Storage system
│ └── EXAMPLE-*.md # Usage examples
├── examples/ # Reference skill examples
├── skill-template/ # Template for new skills
├── README.md # This file
└── SETUP-GUIDE.md # Complete setup guidePublishing to ClawHub
1. Push to GitHub (make repo public):
git remote add origin https://github.com/YOUR_USERNAME/openclawskills.git
git push -u origin main2. Import to ClawHub:
- Go to https://clawhub.ai/import
- Enter your GitHub URL
- Click Detect - it will find
SKILL.mdautomatically - Fill in details and click Publish
ClawHub auto-updates when you push new commits.
Development
# Install dependencies
npm install
# Build
npm run build
# Test
npm test
# Lint
npm run lintDocumentation
- SKILL.md - Full skill definition and CLI reference
- SETUP-GUIDE.md - Complete setup and testing guide
- skills/cinematic-script-writer/EXAMPLE-KUTIL.md - Complete Kutil example
- skills/cinematic-script-writer/EXAMPLE-CONSISTENCY.md - Consistency guide
- skills/cinematic-script-writer/EXAMPLE-STORAGE.md - Storage guide
Requirements
- Node.js 18+
- TypeScript 5.0+
- OpenClaw Agent with memory permissions
License
MIT License - see LICENSE
---
Made for OpenClaw
Cinematic Script Writer Skill - Issue Report & Fix Guide
Problem Summary
During installation, it took multiple workarounds to get this skill recognized by OpenClaw. Here's everything that's wrong and how to fix it for a clean one-line install.
---
Issue 1: Missing YAML Frontmatter (CRITICAL)
The #1 reason this skill doesn't work after installation.
The published SKILL.md on ClawHub starts directly with:
# Cinematic Script WriterBut OpenClaw requires YAML frontmatter at the top of every SKILL.md. Without it, the skill is completely invisible to the engine. OpenClaw uses a 3-level loading system:
- Level 1 (always in context):
name+descriptionfrom YAML frontmatter — this is how OpenClaw decides when to activate your skill - Level 2 (loaded on trigger): The full SKILL.md body
- Level 3 (on demand): Scripts, references, assets
Without frontmatter, Level 1 never loads, so the skill never triggers.
Fix: Add this to the very top of SKILL.md:
---
name: cinematic-script-writer
description: >
Create professional cinematic scripts for AI video generation with character
consistency and cinematography knowledge. Use when the user wants to write
a cinematic script, create story contexts with characters, generate image
prompts for AI video tools, or needs cinematography guidance (camera angles,
lighting, color grading). Also use for character consistency sheets, voice
profiles, and saving scripts to Google Drive.
metadata:
{
"openclaw": {
"emoji": "🎬"
}
}
---Notice the description includes "Use when..." trigger phrases. This is how well-written bundled skills (like summarize, clawhub, github) help OpenClaw know when to activate the skill.
---
Issue 2: Weak Description (No Trigger Phrases)
Even the version we manually fixed only had:
"Create professional cinematic scripts for AI video generation with character consistency and cinematography knowledge."
Compare with a well-written bundled skill (summarize):
"Summarize or extract text/transcripts from URLs, podcasts, and local files (great fallback for 'transcribe this YouTube/video')."
The description is OpenClaw's primary trigger mechanism. It should include natural-language triggers that match how users actually ask for things.
---
Issue 3: Body Content is a JavaScript API Reference, Not AI Instructions
The SKILL.md body contains code like:
const context = await skill.createContext("Kutil's Adventure", ...);
const ideas = await skill.generateStoryIdeas(context.id, 3);This is a developer SDK reference. But OpenClaw skills are meant to be AI agent instructions — they tell the AI how to accomplish the task, not show JavaScript function signatures. The AI model can't call skill.createContext() — it needs plain-language instructions or executable scripts in a scripts/ directory.
Fix: Rewrite the body as instructions for the AI agent. For example:
## How to Use This Skill
When a user wants to create a cinematic script:
1. Ask for the story concept, era/setting, and visual style
2. Create character profiles with consistent visual descriptions
3. Generate 3 story ideas and let the user pick one
4. Write the full cinematic script with:
- Scene descriptions with camera angles and lighting
- Character dialogue with voice consistency notes
- Image generation prompts for each shot
5. Validate for anachronisms (no modern items in historical settings)
6. Offer to save to Google Drive or local storage---
Issue 4: No requires or install Metadata
Bundled skills that need external tools declare them in frontmatter:
metadata:
{
"openclaw": {
"emoji": "🍌",
"requires": { "bins": ["uv"], "env": ["GEMINI_API_KEY"] },
"primaryEnv": "GEMINI_API_KEY",
"install": [
{ "id": "uv-brew", "kind": "brew", "formula": "uv", "bins": ["uv"] }
]
}
}If cinematic-script-writer needs Google Drive API credentials or any external tools, these should be declared so OpenClaw can check/install them automatically.
---
Issue 5: ClawHub CLI Installation Path
When we ran clawdhub install cinematic-script-writer, it installed to ./skills/ (current working directory) instead of OpenClaw's skill directories. Users then have to manually copy files to the right location. This is a clawdhub CLI issue, not your skill's issue — but it's worth noting.
---
Issue 6: Version Mismatch
_meta.json says "version": "0.1.1" but the SKILL.md body says Version 1.3.0. These should match.
---
What You Need to Do (Checklist)
To make this a clean one-line install (openclaw skill install cinematic-script-writer or clawdhub install cinematic-script-writer):
| # | Fix | Priority |
|---|---|---|
| 1 | Add YAML frontmatter with name, description, and metadata | CRITICAL |
| 2 | Write a rich description with "Use when..." trigger phrases | HIGH |
| 3 | Rewrite body as AI agent instructions, not JS API docs | HIGH |
| 4 | Add requires/install in metadata if external deps are needed | MEDIUM |
| 5 | Sync version number between _meta.json and SKILL.md | LOW |
| 6 | Add executable scripts in scripts/ dir for deterministic tasks (e.g., Google Drive upload, prompt generation) | MEDIUM |
| 7 | Remove unnecessary files (README.md, CHANGELOG.md, etc.) if any exist — skill-creator docs say not to | LOW |
---
Ideal SKILL.md Structure
Here's the template based on how properly working bundled skills are structured:
---
name: cinematic-script-writer
description: >
Create professional cinematic scripts for AI video generation with character
consistency and cinematography knowledge. Use when the user wants to write
a cinematic script, create story contexts, generate AI image prompts for
video scenes, or needs camera angle/lighting/color grading guidance.
metadata:
{
"openclaw": {
"emoji": "🎬"
}
}
---
# Cinematic Script Writer
[AI agent instructions here — what to do step by step,
not JavaScript API references]
## Cinematography Reference
[Camera angles, lighting techniques, etc. as reference
material the AI can use when writing scripts]
## Character Consistency Rules
[Rules for maintaining consistent character appearance
across shots]
## Output Format
[What the final script should look like]---
Comparison with Working Bundled Skills
| Feature | nano-banana-pro | summarize | github | cinematic-script-writer |
|---|---|---|---|---|
| YAML frontmatter | Yes | Yes | Yes | MISSING |
| Trigger phrases in description | Yes | Yes | Yes | No |
requires metadata | bins + env | bins | bins | None |
install instructions | brew | brew | brew + apt | None |
| Body format | AI instructions | AI instructions | AI instructions | JS API docs |
| Scripts directory | Yes (scripts/) | No (not needed) | No (uses gh CLI) | No |
---
Summary
Once you fix the SKILL.md with proper frontmatter and rewrite the body as AI agent instructions, then republish to ClawHub, anyone should be able to install it with:
clawdhub install cinematic-script-writerAnd OpenClaw will automatically recognize and trigger it when users ask for cinematic scripts.
📖 Complete Setup, Testing & Publishing Guide
This guide walks you through: 1. Creating a GitHub repository 2. Testing the skill 3. Publishing to ClawHub
---
Step 1: Create GitHub Repository
Option A: Using GitHub Website (Recommended)
1. Go to GitHub: https://github.com/new
2. Create New Repository:
- Repository name:
Cinematic-Writer - Description:
OpenClaw skill for cinematic script generation with consistency and storage - Make it Public (or Private if you prefer)
- DON'T initialize with README (we already have one)
- Click "Create repository"
3. Connect Local Repository:
# In your terminal (PowerShell/CMD), navigate to the project
cd "D:\My Professional Projects\openclawskills"
# Add the GitHub remote
git remote add origin https://github.com/YOUR_USERNAME/openclaw-cinematic-writer.git
# Rename branch to main
git branch -M main
# Push to GitHub
git push -u origin main4. Verify: Visit https://github.com/YOUR_USERNAME/openclaw-cinematic-writer
Option B: Using GitHub CLI
# Install GitHub CLI if not already installed
# https://cli.github.com/
# Login to GitHub
gh auth login
# Create repository from current directory
gh repo create openclaw-cinematic-writer --public --source=. --push---
Step 2: Testing the Skill
A. TypeScript Compilation Test
# Navigate to project
cd "D:\My Professional Projects\openclawskills"
# Install dependencies
npm install
# Build/compile TypeScript
npm run build
# Check for compilation errors
# Should create dist/ folder with compiled JSB. Lint Test
# Check code style
npm run lint
# Fix auto-fixable issues
npm run lint:fixC. Manual Testing Script
Create a test file test-skill.ts:
import { CinematicScriptWriter } from './skills/cinematic-script-writer';
// Mock context for testing
const mockContext = {
userId: 'test-user',
memory: {
get: async () => null,
set: async () => {},
delete: async () => {}
},
logger: {
debug: console.log,
info: console.log,
warn: console.warn,
error: console.error
}
};
async function testSkill() {
console.log('🧪 Testing Cinematic Script Writer Skill\n');
const skill = new CinematicScriptWriter({}, mockContext);
// Test 1: Cinematography Database
console.log('✓ Testing Cinematography Database...');
const angles = skill.getAllCameraAngles();
console.log(` - Found ${Object.keys(angles).length} camera angles`);
const movements = skill.getAllCameraMovements();
console.log(` - Found ${Object.keys(movements).length} camera movements`);
const shots = skill.getAllShotTypes();
console.log(` - Found ${Object.keys(shots).length} shot types`);
// Test 2: Consistency System
console.log('\n✓ Testing Consistency System...');
const charRef = skill.createCharacterReference(
'test-char',
'Test Character',
'Purple fur, golden eyes',
'Ramayana Era',
'pixar-3d'
);
console.log(` - Created character reference: ${charRef.characterName}`);
// Test 3: Environment Guide
console.log('\n✓ Testing Environment Guide...');
const envGuide = skill.createEnvironmentStyleGuide(
'test-context',
'Ramayana Era',
'Treta Yuga',
'Lanka',
'pixar-3d'
);
console.log(` - Created environment guide: ${envGuide.era}`);
console.log(` - Forbidden items: ${envGuide.eraSpecs.anachronismsForbidden.slice(0, 3).join(', ')}...`);
// Test 4: Validation
console.log('\n✓ Testing Anachronism Validation...');
const badResult = skill.validatePrompt(
'Character wearing sunglasses',
['test-char'],
'test-context'
);
console.log(` - Detected anachronism: ${!badResult.valid}`);
console.log(` - Errors: ${badResult.errors.join(', ')}`);
// Test 5: Prompt Builder
console.log('\n✓ Testing Prompt Builder...');
const prompts = skill.buildConsistentPrompts({
characterIds: ['test-char'],
contextId: 'test-context',
shotType: 'close-up',
cameraAngle: 'eye-level',
cameraMovement: 'static',
lighting: 'golden-hour',
mood: 'happy'
});
console.log(` - Generated image prompt length: ${prompts.imagePrompt.length} chars`);
console.log(` - Generated negative prompt length: ${prompts.negativePrompt.length} chars`);
console.log('\n✅ All tests passed!');
}
testSkill().catch(console.error);Run the test:
npx ts-node test-skill.tsD. Testing Storage (Manual)
// Test storage connection
async function testStorage() {
const skill = new CinematicScriptWriter({}, mockContext);
// Check status
const status = await skill.getStorageStatus();
console.log('Storage status:', status);
// Test Google Drive connection (requires auth)
// const result = await skill.connectGoogleDrive();
// console.log('Auth URL:', result.authUrl);
}---
Step 3: Publishing to ClawHub
Prerequisites
1. ✅ GitHub repository created 2. ✅ All code committed 3. ✅ README.md is complete 4. ✅ skill.json is properly configured 5. ✅ Tests pass
ClawHub Submission Process
Option 1: Web Interface (Recommended for first time)
1. Visit ClawHub: https://clawhub.ai/publish
2. Fill in the form:
# Basic Information
Skill Name: cinematic-script-writer
Version: 1.3.0
Description: Professional cinematic script generation with character consistency,
comprehensive cinematography (175+ techniques), and Google Drive storage.
# Repository
GitHub URL: https://github.com/YOUR_USERNAME/openclaw-cinematic-writer
License: MIT
# Entry Point
Main File: skills/cinematic-script-writer/index.ts
# Configuration
Config Schema: skills/cinematic-script-writer/schema.json
# Permissions Required
Permissions:
- memory:read (Store/retrieve contexts, scripts)
- memory:write (Save generated content)
- http:request (Google Drive API)
# Tags
Tags:
- creative
- video
- script
- cinematography
- consistency
- character-design
- voice
- storage
- google-drive
- youtube
# Documentation
README: https://github.com/YOUR_USERNAME/openclaw-cinematic-writer/blob/main/README.md
Examples:
- https://github.com/YOUR_USERNAME/openclaw-cinematic-writer/blob/main/skills/cinematic-script-writer/EXAMPLE-KUTIL.md3. Upload or Provide:
- Screenshot/demo GIF (optional but recommended)
- Video tutorial link (optional)
4. Submit and wait for review (usually 1-3 days)
Option 2: ClawHub CLI (When Available)
# Install ClawHub CLI
npm install -g @clawhub/cli
# Login
clawhub login
# Publish from repository
clawhub publish
# Or specify directory
clawhub publish ./skills/cinematic-script-writerOption 3: API Submission (Advanced)
# Create skill bundle
cd skills/cinematic-script-writer
tar -czvf cinematic-script-writer-v1.3.0.tar.gz .
# Submit via API (requires API key from ClawHub)
curl -X POST https://api.clawhub.ai/v1/skills \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: multipart/form-data" \
-F "name=cinematic-script-writer" \
-F "version=1.3.0" \
-F "bundle=@cinematic-script-writer-v1.3.0.tar.gz" \
-F "repository=https://github.com/YOUR_USERNAME/openclaw-cinematic-writer"---
Step 4: Post-Publication
Create GitHub Release
1. Go to GitHub → Releases → "Draft a new release" 2. Choose tag: v1.3.0 3. Title: v1.3.0 - Cinematic Script Writer 4. Description:
## What's New
- Google Drive storage integration
- Character/voice/environment consistency
- 175+ cinematography techniques
- Anachronism detection
- YouTube metadata generation
## Installationnpx clawhub@latest install cinematic-script-writer
5. Publish release
Announce
- Share on Twitter/X with #OpenClaw hashtag
- Post in OpenClaw Discord/community
- Write a blog post about your skill
---
Troubleshooting
Git Push Issues
# If you get "fatal: unable to access"
# Make sure you're using HTTPS URL
git remote set-url origin https://github.com/YOUR_USERNAME/openclaw-cinematic-writer.git
# Or use SSH
git remote set-url origin git@github.com:YOUR_USERNAME/openclaw-cinematic-writer.gitBuild Errors
# Clear node_modules and reinstall
rm -rf node_modules package-lock.json
npm install
# Check TypeScript version
npx tsc --version # Should be 5.0+ClawHub Rejection
Common reasons and fixes:
| Issue | Fix |
|---|---|
| Missing documentation | Add more examples in README |
| Tests fail | Fix errors and add more tests |
| Security concerns | Review and limit permissions |
| Code quality | Run linter, add comments |
| Duplicate skill | Ensure unique functionality |
---
Quick Reference
# Full workflow
npm install # Install dependencies
npm run build # Compile TypeScript
npm run lint # Check code style
npm test # Run tests
git add . # Stage changes
git commit -m "..." # Commit
git push origin main # Push to GitHub
# ClawHub commands
clawhub validate # Validate skill before publishing
clawhub publish # Publish to ClawHub
clawhub update # Update published skill---
Need Help?
- ClawHub Docs: https://clawhub.ai/docs
- OpenClaw Community: Discord
- GitHub Issues: Create issue in your repo
Good luck! 🚀
/**
* Skill Template
*
* This is the main entry point for your skill.
* Export functions that the agent can call as tools.
*/
// Types for skill context and configuration
interface SkillContext {
userId: string;
sessionId: string;
memory: MemoryStore;
logger: Logger;
}
interface SkillConfig {
greetingPrefix?: string;
enableLogging?: boolean;
}
interface MemoryStore {
get(key: string): Promise<any>;
set(key: string, value: any): Promise<void>;
delete(key: string): Promise<void>;
}
interface Logger {
debug(msg: string): void;
info(msg: string): void;
warn(msg: string): void;
error(msg: string): void;
}
// Skill class - main implementation
export class SkillTemplate {
private config: SkillConfig;
private context: SkillContext;
constructor(config: SkillConfig, context: SkillContext) {
this.config = config;
this.context = context;
}
/**
* Greet the user
* This is a simple example tool
*/
async greet(name: string): Promise<string> {
const prefix = this.config.greetingPrefix || "Hello";
const message = `${prefix}, ${name}! 👋`;
if (this.config.enableLogging) {
this.context.logger.debug(`Greeting user: ${name}`);
}
// Store greeting in memory
await this.context.memory.set(`lastGreeting:${this.context.userId}`, {
name,
timestamp: new Date().toISOString()
});
return message;
}
/**
* Perform a calculation
* Example of a tool that does something useful
*/
async calculate(expression: string): Promise<{ result: number; expression: string }> {
if (this.config.enableLogging) {
this.context.logger.debug(`Calculating: ${expression}`);
}
// Simple calculation - in production, use a proper math parser
try {
// WARNING: eval is dangerous, use a safe math parser in production
// This is just for demonstration
const result = Function('"use strict"; return (' + expression + ')')();
return {
result,
expression
};
} catch (error) {
throw new Error(`Invalid expression: ${expression}`);
}
}
/**
* Get last greeting from memory
*/
async getLastGreeting(): Promise<any> {
return await this.context.memory.get(`lastGreeting:${this.context.userId}`);
}
}
// Factory function - the agent calls this to create your skill instance
export default function createSkill(config: SkillConfig, context: SkillContext) {
return new SkillTemplate(config, context);
}
// Export types for TypeScript users
export type { SkillConfig, SkillContext };
Skill Template
A template for creating OpenClaw-compatible skills.
Skill Structure
my-skill/
├── skill.json # Skill manifest
├── index.ts # Main entry point
├── schema.json # Configuration schema
├── README.md # Documentation
└── tests/
└── index.test.ts # TestsQuick Start
1. Copy this template: cp -r skill-template my-new-skill 2. Update skill.json with your skill info 3. Implement your logic in index.ts 4. Test your skill
Skill Manifest (skill.json)
{
"name": "my-skill",
"version": "1.0.0",
"description": "What this skill does",
"author": "Your Name",
"entry": "index.ts",
"config": {
"schema": "schema.json"
},
"permissions": ["fs:read", "http:request"],
"tools": [
{
"name": "doSomething",
"description": "Does something useful"
}
]
}{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"greetingPrefix": {
"type": "string",
"description": "Prefix for greeting messages",
"default": "Hello"
},
"enableLogging": {
"type": "boolean",
"description": "Enable debug logging",
"default": false
}
},
"required": []
}
{
"$schema": "https://clawhub.ai/schemas/skill.json",
"name": "skill-template",
"version": "1.0.0",
"description": "Template for creating OpenClaw skills",
"author": "Your Name",
"license": "MIT",
"entry": "index.ts",
"config": {
"schema": "schema.json",
"required": false
},
"permissions": [
"fs:read",
"fs:write",
"http:request"
],
"tools": [
{
"name": "greet",
"description": "Greets the user with a personalized message"
},
{
"name": "calculate",
"description": "Performs a calculation"
}
],
"dependencies": {},
"tags": ["template", "example"]
}
import createSkill, { SkillTemplate } from '../index';
describe('SkillTemplate', () => {
let skill: SkillTemplate;
let mockMemory: any;
let mockLogger: any;
beforeEach(() => {
mockMemory = {
get: jest.fn(),
set: jest.fn(),
delete: jest.fn(),
};
mockLogger = {
debug: jest.fn(),
info: jest.fn(),
warn: jest.fn(),
error: jest.fn(),
};
skill = createSkill(
{ greetingPrefix: 'Hi', enableLogging: true },
{ userId: 'user-123', sessionId: 'session-456', memory: mockMemory, logger: mockLogger }
);
});
describe('greet', () => {
it('should greet user with custom prefix', async () => {
const result = await skill.greet('Alice');
expect(result).toBe('Hi, Alice! 👋');
});
it('should store greeting in memory', async () => {
await skill.greet('Alice');
expect(mockMemory.set).toHaveBeenCalledWith(
'lastGreeting:user-123',
expect.objectContaining({ name: 'Alice' })
);
});
});
describe('calculate', () => {
it('should perform basic calculation', async () => {
const result = await skill.calculate('2 + 2');
expect(result.result).toBe(4);
expect(result.expression).toBe('2 + 2');
});
it('should handle complex expressions', async () => {
const result = await skill.calculate('10 * 5 + 3');
expect(result.result).toBe(53);
});
});
});
/**
* Cinematography API - Unified access to all camera, lighting, and visual techniques
*/
import { CINEMATOGRAPHY_DB } from './cinematography-db';
import { LIGHTING_DB } from './lighting-db';
import { VISUAL_STYLES_DB } from './visual-styles-db';
// ============================================================================
// Camera Techniques API
// ============================================================================
export class CameraTechniquesAPI {
/**
* Get all camera angles
*/
static getAllAngles() {
return CINEMATOGRAPHY_DB.angles;
}
/**
* Get specific camera angle details
*/
static getAngle(angleName: string) {
return CINEMATOGRAPHY_DB.angles[angleName as keyof typeof CINEMATOGRAPHY_DB.angles] || null;
}
/**
* Get angles by emotional impact
*/
static getAnglesByEmotion(emotion: string) {
const angles = Object.entries(CINEMATOGRAPHY_DB.angles);
return angles
.filter(([_, data]) =>
data.emotionalImpact.toLowerCase().includes(emotion.toLowerCase())
)
.map(([name, data]) => ({ name, ...data }));
}
/**
* Get angles by difficulty level
*/
static getAnglesByDifficulty(difficulty: 'beginner' | 'intermediate' | 'advanced') {
const angles = Object.entries(CINEMATOGRAPHY_DB.angles);
return angles
.filter(([_, data]) => data.difficulty === difficulty)
.map(([name, data]) => ({ name, ...data }));
}
/**
* Get all camera movements
*/
static getAllMovements() {
return CINEMATOGRAPHY_DB.movements;
}
/**
* Get specific camera movement details
*/
static getMovement(movementName: string) {
return CINEMATOGRAPHY_DB.movements[movementName as keyof typeof CINEMATOGRAPHY_DB.movements] || null;
}
/**
* Get movements by emotional impact
*/
static getMovementsByEmotion(emotion: string) {
const movements = Object.entries(CINEMATOGRAPHY_DB.movements);
return movements
.filter(([_, data]) =>
data.emotionalImpact.toLowerCase().includes(emotion.toLowerCase())
)
.map(([name, data]) => ({ name, ...data }));
}
/**
* Get movements by speed
*/
static getMovementsBySpeed(speed: string) {
const movements = Object.entries(CINEMATOGRAPHY_DB.movements);
return movements
.filter(([_, data]) =>
data.speed.toLowerCase().includes(speed.toLowerCase())
)
.map(([name, data]) => ({ name, ...data }));
}
/**
* Get all shot types
*/
static getAllShots() {
return CINEMATOGRAPHY_DB.shots;
}
/**
* Get specific shot type details
*/
static getShot(shotName: string) {
return CINEMATOGRAPHY_DB.shots[shotName as keyof typeof CINEMATOGRAPHY_DB.shots] || null;
}
/**
* Get shots by emotional impact
*/
static getShotsByEmotion(emotion: string) {
const shots = Object.entries(CINEMATOGRAPHY_DB.shots);
return shots
.filter(([_, data]) =>
data.emotionalImpact.toLowerCase().includes(emotion.toLowerCase())
)
.map(([name, data]) => ({ name, ...data }));
}
/**
* Suggest camera technique based on context
*/
static suggestTechnique(
purpose: 'intimacy' | 'power' | 'chaos' | 'reveal' | 'action' | 'emotion' | 'comedy',
difficulty?: 'beginner' | 'intermediate' | 'advanced'
) {
const suggestions: any = {
intimacy: {
angles: ['eye-level', 'close-up', 'over-shoulder'],
movements: ['dolly-in', 'static', 'slow-pan'],
shots: ['close-up', 'extreme-close-up', 'medium-close-up']
},
power: {
angles: ['low-angle', 'worm-eye', 'low-angle-shot'],
movements: ['static', 'slow-dolly', 'crane'],
shots: ['low-angle-shot', 'wide', 'silhouette']
},
chaos: {
angles: ['dutch-angle', 'handheld', 'whip-pan'],
movements: ['handheld', 'whip-pan', 'shaky'],
shots: ['handheld', 'medium', 'reaction-shot']
},
reveal: {
angles: ['high-angle', 'overhead', 'crane-shot'],
movements: ['crane', 'drone', 'dolly-out', 'zoom-out'],
shots: ['wide', 'establishing', 'extreme-wide']
},
action: {
angles: ['eye-level', 'low-angle', 'POV'],
movements: ['handheld', 'steadicam', 'gimbal', 'whip-pan'],
shots: ['medium', 'wide', 'POV', 'reaction-shot']
},
emotion: {
angles: ['eye-level', 'close-up'],
movements: ['static', 'slow-dolly-in', 'rack-focus'],
shots: ['close-up', 'extreme-close-up', 'reaction-shot']
},
comedy: {
angles: ['eye-level', 'slight-low-angle'],
movements: ['static', 'dolly'],
shots: ['medium', 'two-shot', 'reaction-shot']
}
};
return suggestions[purpose] || suggestions.emotion;
}
/**
* Get complete camera setup recommendation
*/
static getRecommendedSetup(
sceneType: string,
emotion: string,
skillLevel: 'beginner' | 'intermediate' | 'advanced' = 'intermediate'
) {
const setups: Record<string, any> = {
'dialogue-intimate': {
angle: 'eye-level',
movement: 'static',
shot: 'medium-close-up',
lighting: 'soft-key',
lens: '85mm'
},
'dialogue-confrontation': {
angle: 'slight-low-angle',
movement: 'slow-dolly',
shot: 'medium',
lighting: 'chiaroscuro',
lens: '50mm'
},
'hero-entrance': {
angle: 'low-angle',
movement: 'crane-down',
shot: 'wide',
lighting: 'rim-light',
lens: '24mm'
},
'villain-reveal': {
angle: 'low-angle',
movement: 'slow-push',
shot: 'medium',
lighting: 'low-key',
lens: '35mm'
},
'chase-action': {
angle: 'eye-level',
movement: 'steadicam',
shot: 'medium',
lighting: 'natural',
lens: '24-70mm'
},
'emotional-moment': {
angle: 'eye-level',
movement: 'rack-focus',
shot: 'close-up',
lighting: 'soft-key',
lens: '85mm'
},
'horror-suspense': {
angle: 'dutch-angle',
movement: 'slow-push',
shot: 'medium',
lighting: 'low-key',
lens: '35mm'
},
'comedic-fall': {
angle: 'high-angle',
movement: 'static',
shot: 'wide',
lighting: 'high-key',
lens: '35mm'
}
};
return setups[sceneType] || setups['emotional-moment'];
}
}
// ============================================================================
// Lighting API
// ============================================================================
export class LightingAPI {
/**
* Get all lighting techniques
*/
static getAllTechniques() {
return LIGHTING_DB.techniques;
}
/**
* Get specific lighting technique
*/
static getTechnique(techniqueName: string) {
return LIGHTING_DB.techniques[techniqueName as keyof typeof LIGHTING_DB.techniques] || null;
}
/**
* Get techniques by emotional impact
*/
static getTechniquesByEmotion(emotion: string) {
const techniques = Object.entries(LIGHTING_DB.techniques);
return techniques
.filter(([_, data]) =>
data.emotionalImpact.toLowerCase().includes(emotion.toLowerCase())
)
.map(([name, data]) => ({ name, ...data }));
}
/**
* Get techniques by difficulty
*/
static getTechniquesByDifficulty(difficulty: 'beginner' | 'intermediate' | 'advanced') {
const techniques = Object.entries(LIGHTING_DB.techniques);
return techniques
.filter(([_, data]) => data.difficulty === difficulty)
.map(([name, data]) => ({ name, ...data }));
}
/**
* Suggest lighting for scene type
*/
static suggestLighting(sceneType: string, mood: string) {
const suggestions: Record<string, string[]> = {
'interior-day': ['available-light', 'window-light', 'practical-lighting'],
'interior-night': ['practical-lighting', 'moonlight', 'tungsten', 'low-key'],
'exterior-day': ['daylight', 'overcast', 'golden-hour'],
'exterior-night': ['moonlight', 'street-light', 'neon', 'practical-lighting'],
'studio': ['three-point', 'high-key', 'book-light'],
'horror': ['low-key', 'under-light', 'single-source', 'chiaroscuro'],
'romance': ['soft-key', 'golden-hour', 'candlelight', 'high-key'],
'drama': ['motivated-lighting', 'chiaroscuro', 'practical-lighting'],
'comedy': ['high-key', 'three-point', 'soft-key'],
'noir': ['chiaroscuro', 'venetian-blind', 'low-key'],
'sci-fi': ['neon', 'practical-lighting', 'motivated-lighting'],
'fantasy': ['god-rays', 'golden-hour', 'volume-light']
};
const key = `${sceneType}-${mood}`;
return suggestions[key] || suggestions['interior-day'] || ['three-point'];
}
/**
* Get all composition rules
*/
static getAllCompositionRules() {
return LIGHTING_DB.composition;
}
/**
* Get specific composition rule
*/
static getCompositionRule(ruleName: string) {
return LIGHTING_DB.composition[ruleName as keyof typeof LIGHTING_DB.composition] || null;
}
/**
* Get all color grading styles
*/
static getAllColorGradingStyles() {
return LIGHTING_DB.colorGrading;
}
/**
* Get specific color grading style
*/
static getColorGradingStyle(styleName: string) {
return LIGHTING_DB.colorGrading[styleName as keyof typeof LIGHTING_DB.colorGrading] || null;
}
/**
* Suggest color grading for genre
*/
static suggestColorGrading(genre: string) {
const suggestions: Record<string, string[]> = {
'action': ['teal-orange', 'high-saturation', 'desaturated'],
'comedy': ['warm', 'high-saturation', 'natural'],
'drama': ['natural', 'desaturated', 'warm', 'cool'],
'horror': ['desaturated', 'cool', 'bleach-bypass'],
'romance': ['warm', 'golden', 'soft'],
'sci-fi': ['teal-orange', 'cool', 'matrix-green', 'dayglow'],
'fantasy': ['golden', 'high-saturation', 'warm'],
'thriller': ['desaturated', 'cool', 'teal-orange'],
'documentary': ['natural', 'desaturated', 'vintage'],
'noir': ['noir', 'desaturated', 'high-contrast'],
'period': ['sepia', 'vintage', 'warm', 'desaturated'],
'music-video': ['high-saturation', 'cross-process', 'dayglow']
};
return suggestions[genre.toLowerCase()] || ['natural'];
}
}
// ============================================================================
// Visual Styles API
// ============================================================================
export class VisualStylesAPI {
/**
* Get all visual aesthetics
*/
static getAllAesthetics() {
return VISUAL_STYLES_DB.aesthetics;
}
/**
* Get specific aesthetic
*/
static getAesthetic(aestheticName: string) {
return VISUAL_STYLES_DB.aesthetics[aestheticName as keyof typeof VISUAL_STYLES_DB.aesthetics] || null;
}
/**
* Get aesthetics by type
*/
static getAestheticsByType(type: 'animation' | 'live-action' | 'artistic' | 'genre') {
const animation = ['pixar-3d', 'disney-classic', 'anime', 'spider-verse', 'stop-motion', 'claymation', 'cut-out', 'motion-graphics', 'low-poly', 'voxel'];
const liveAction = ['documentary', 'cinema-verite', 'found-footage', 'mockumentary', 'music-video', 'commercial'];
const artistic = ['film-noir', 'german-expressionism', 'french-new-wave', 'Dogme-95', 'surrealist'];
const genre = ['horror', 'sci-fi', 'fantasy', 'western', 'war', 'romance', 'comedy', 'thriller', 'indian-miniature', 'indian-classical-art'];
const map: Record<string, string[]> = { animation, 'live-action': liveAction, artistic, genre };
const names = map[type] || [];
return names.map(name => ({
name,
...VISUAL_STYLES_DB.aesthetics[name as keyof typeof VISUAL_STYLES_DB.aesthetics]
}));
}
/**
* Get all genre cinematography
*/
static getAllGenreCinematography() {
return VISUAL_STYLES_DB.genre;
}
/**
* Get genre cinematography
*/
static getGenreCinematography(genre: string) {
return VISUAL_STYLES_DB.genre[genre as keyof typeof VISUAL_STYLES_DB.genre] || null;
}
/**
* Get Indian cinematography styles
*/
static getIndianCinematography(style?: string) {
if (style) {
return VISUAL_STYLES_DB.indian[style as keyof typeof VISUAL_STYLES_DB.indian] || null;
}
return VISUAL_STYLES_DB.indian;
}
/**
* Suggest visual style for content
*/
static suggestVisualStyle(
contentType: string,
targetAudience: string,
tone: string
) {
const suggestions: Record<string, any> = {
'animation-family': {
primary: 'pixar-3d',
alternatives: ['disney-classic', 'claymation'],
lighting: 'three-point',
colorGrading: 'high-saturation'
},
'animation-action': {
primary: 'spider-verse',
alternatives: ['anime', 'low-poly'],
lighting: 'dramatic',
colorGrading: 'high-saturation'
},
'animation-horror': {
primary: 'stop-motion',
alternatives: ['claymation', 'cut-out'],
lighting: 'low-key',
colorGrading: 'desaturated'
},
'live-action-drama': {
primary: 'documentary',
alternatives: ['cinema-verite'],
lighting: 'natural',
colorGrading: 'natural'
},
'live-action-comedy': {
primary: 'mockumentary',
alternatives: ['commercial'],
lighting: 'high-key',
colorGrading: 'warm'
},
'music-promotion': {
primary: 'music-video',
alternatives: ['commercial'],
lighting: 'stylized',
colorGrading: 'high-saturation'
},
'indian-mythological': {
primary: 'indian-miniature',
alternatives: ['indian-classical-art', 'fantasy'],
lighting: 'god-rays',
colorGrading: 'golden'
}
};
const key = `${contentType}-${targetAudience}-${tone}`;
return suggestions[key] || suggestions['animation-family'];
}
}
// ============================================================================
// Complete Cinematography Guide
// ============================================================================
export class CinematographyGuide {
/**
* Get complete cinematography package for a scene
*/
static getScenePackage(
genre: string,
mood: string,
sceneType: string,
skillLevel: 'beginner' | 'intermediate' | 'advanced' = 'intermediate'
) {
const genreData = VisualStylesAPI.getGenreCinematography(genre);
const cameraSetup = CameraTechniquesAPI.getRecommendedSetup(sceneType, mood, skillLevel);
const lighting = LightingAPI.suggestLighting(sceneType, mood);
const colorGrading = LightingAPI.suggestColorGrading(genre);
return {
genreConventions: genreData,
camera: cameraSetup,
lighting: lighting,
colorGrading: colorGrading,
completeSetup: {
angle: cameraSetup.angle,
movement: cameraSetup.movement,
shot: cameraSetup.shot,
lighting: lighting[0],
colorGrading: colorGrading[0],
lens: cameraSetup.lens
}
};
}
/**
* Generate prompt for image generation with cinematography
*/
static generateImagePrompt(
subject: string,
angle: string,
shot: string,
lighting: string,
style: string
) {
const angleData = CameraTechniquesAPI.getAngle(angle);
const shotData = CameraTechniquesAPI.getShot(shot);
const lightingData = LightingAPI.getTechnique(lighting);
const styleData = VisualStylesAPI.getAesthetic(style);
const parts = [
`${shot} shot`,
angleData?.description || angle,
lightingData?.description || lighting,
styleData?.characteristics ? Object.values(styleData.characteristics).join(', ') : style,
subject
];
return parts.join(', ');
}
/**
* Search all techniques
*/
static search(query: string) {
const results: any = {
angles: [],
movements: [],
shots: [],
lighting: [],
composition: [],
colorGrading: [],
aesthetics: []
};
const q = query.toLowerCase();
// Search angles
Object.entries(CINEMATOGRAPHY_DB.angles).forEach(([name, data]) => {
if (data.description.toLowerCase().includes(q) ||
data.emotionalImpact.toLowerCase().includes(q)) {
results.angles.push({ name, ...data });
}
});
// Search movements
Object.entries(CINEMATOGRAPHY_DB.movements).forEach(([name, data]) => {
if (data.description.toLowerCase().includes(q) ||
data.emotionalImpact.toLowerCase().includes(q)) {
results.movements.push({ name, ...data });
}
});
// Search shots
Object.entries(CINEMATOGRAPHY_DB.shots).forEach(([name, data]) => {
if (data.description.toLowerCase().includes(q) ||
data.emotionalImpact.toLowerCase().includes(q)) {
results.shots.push({ name, ...data });
}
});
// Search lighting
Object.entries(LIGHTING_DB.techniques).forEach(([name, data]) => {
if (data.description.toLowerCase().includes(q) ||
data.emotionalImpact.toLowerCase().includes(q)) {
results.lighting.push({ name, ...data });
}
});
return results;
}
}
// Export all APIs
export const CinematographyAPI = {
camera: CameraTechniquesAPI,
lighting: LightingAPI,
visual: VisualStylesAPI,
guide: CinematographyGuide
};
export default CinematographyAPI;
/**
* Comprehensive Cinematography Database
* Camera techniques, lighting, composition, visual styles, and genre-specific approaches
*/
// ============================================================================
// CAMERA ANGLES - Expanded
// ============================================================================
export const CAMERA_ANGLES = {
// Standard Angles
'eye-level': {
description: 'Camera at subject\'s eye level - neutral, natural perspective',
emotionalImpact: 'Creates connection, equality, neutrality, immersion',
bestFor: 'Dialogue scenes, emotional moments, establishing character presence, POV sequences',
commonUses: ['Conversations', 'Character introductions', 'Intimate moments'],
difficulty: 'beginner',
lensRecommendation: '35mm-50mm for natural look'
},
'low-angle': {
description: 'Camera below subject looking up - subject dominates frame',
emotionalImpact: 'Power, dominance, heroism, intimidation, authority, superiority',
bestFor: 'Revealing villains, heroic moments, making subject imposing, establishing hierarchy',
commonUses: ['Hero entrances', 'Villain reveals', 'Monument shots', 'Power dynamics'],
difficulty: 'beginner',
lensRecommendation: 'Wide angle 16-24mm for dramatic distortion'
},
'high-angle': {
description: 'Camera above subject looking down - subject appears smaller',
emotionalImpact: 'Vulnerability, weakness, inferiority, submission, overview, insignificance',
bestFor: 'Showing subject small/helpless, establishing scale, surveillance feel',
commonUses: ['Character defeat', 'Establishing geography', 'God\'s eye view', 'Vulnerability'],
difficulty: 'beginner',
lensRecommendation: 'Standard 35-50mm'
},
'bird-eye': {
description: 'Extreme high angle directly above subject',
emotionalImpact: 'Subject is insignificant, lost, or part of larger system, detachment',
bestFor: 'Epic scale, isolation, maze-like situations, patterns, strategic overview',
commonUses: ['Battlefield overview', 'Character lost in city', 'Pattern recognition', 'Chess game'],
difficulty: 'intermediate',
lensRecommendation: 'Wide angle or aerial lens'
},
'worm-eye': {
description: 'Extreme low angle from ground level looking up',
emotionalImpact: 'Overwhelming presence, massive scale, awe, grandeur, oppression',
bestFor: 'Monuments, giants, towering structures, dramatic architecture, imposing figures',
commonUses: ['Skyscrapers', 'Gods/deities', 'Tall characters', 'Dramatic architecture'],
difficulty: 'intermediate',
lensRecommendation: 'Ultra-wide 10-16mm'
},
// Dramatic Angles
'dutch-angle': {
description: 'Tilted camera, horizon not level (also called canted angle)',
emotionalImpact: 'Unease, disorientation, tension, psychological distress, unnatural state',
bestFor: 'Chaos, dreams, insanity, danger moments, intoxication, supernatural',
commonUses: ['Nightmares', 'Villain lairs', 'Action sequences', 'Drug sequences', 'Horror'],
difficulty: 'intermediate',
lensRecommendation: 'Any, but wide angles enhance the effect'
},
'overhead': {
description: 'Directly above subject but not as extreme as bird-eye',
emotionalImpact: 'Omniscience, detachment, pattern recognition, surveillance',
bestFor: 'Table scenes, bed scenes, action sequences, rituals, patterns',
commonUses: ['Dining scenes', 'Fight choreography', 'Board games', 'Surgery scenes'],
difficulty: 'intermediate',
lensRecommendation: 'Wide angle 16-24mm'
},
'shoulder-level': {
description: 'Camera at subject\'s shoulder height',
emotionalImpact: 'Intimate but not invasive, casual observation, documentary feel',
bestFor: 'Following shots, casual conversations, documentary style, walking scenes',
commonUses: ['Walking conversations', 'Following characters', 'Documentary'],
difficulty: 'beginner',
lensRecommendation: '35-50mm'
},
'knee-level': {
description: 'Camera at knee height',
emotionalImpact: 'Child\'s perspective, unusual viewpoint, grounded feeling',
bestFor: 'Child POV, pet POV, unusual perspectives, emphasizing height',
commonUses: ['Child protagonist', 'Pet views', 'Tall character introduction'],
difficulty: 'intermediate',
lensRecommendation: 'Wide angle 24-35mm'
},
'ground-level': {
description: 'Camera at ground level',
emotionalImpact: 'Vulnerability, connection to earth, small creatures\' perspective',
bestFor: 'Small animals, insects, low objects, dramatic entrances',
commonUses: ['Insect documentaries', 'Child playing', 'Dramatic entrances'],
difficulty: 'intermediate',
lensRecommendation: 'Macro or wide angle'
},
// Special Angles
'pov': {
description: 'Character\'s point of view - what the character sees',
emotionalImpact: 'Immersion, subjectivity, identification with character',
bestFor: 'First-person sequences, horror, action, building tension',
commonUses: ['First-person sequences', 'Horror reveals', 'Shooting scenes', 'Exploration'],
difficulty: 'intermediate',
lensRecommendation: '50mm to match human vision'
},
'over-shoulder': {
description: 'Looking past one subject\'s shoulder at another',
emotionalImpact: 'Conversation intimacy, spatial relationship, connection',
bestFor: 'Dialogue scenes, establishing spatial relationships, conversations',
commonUses: ['Two-person conversations', 'Interviews', 'Confrontations'],
difficulty: 'beginner',
lensRecommendation: '50-85mm for compression'
},
'profile': {
description: 'Side view of subject',
emotionalImpact: 'Objectivity, detachment, movement emphasis, contemplation',
bestFor: 'Movement shots, contemplative moments, silhouettes, mystery',
commonUses: ['Walking silhouettes', 'Contemplation', 'Mystery characters'],
difficulty: 'beginner',
lensRecommendation: 'Any'
},
'three-quarter': {
description: 'Between front and profile (45-degree angle)',
emotionalImpact: 'Dynamic, flattering, revealing but not fully frontal',
bestFor: 'Portraits, character introductions, beauty shots',
commonUses: ['Portrait photography', 'Character introductions', 'Glamour shots'],
difficulty: 'beginner',
lensRecommendation: '85mm for portraits'
},
'reflection': {
description: 'Shot of subject in mirror, water, or reflective surface',
emotionalImpact: 'Introspection, duality, alternate reality, self-examination',
bestFor: 'Character introspection, dual personalities, artistic shots',
commonUses: ['Mirror scenes', 'Identity crisis', 'Artistic sequences'],
difficulty: 'advanced',
lensRecommendation: 'Any with attention to reflection angles'
},
'aerial': {
description: 'From aircraft or drone',
emotionalImpact: 'Epic scale, overview, detachment, grandeur',
bestFor: 'Establishing shots, landscapes, action sequences, cityscapes',
commonUses: ['Opening shots', 'Chase sequences', 'Nature documentaries', 'City reveals'],
difficulty: 'advanced',
lensRecommendation: 'Wide angle with ND filters'
},
'crane-shot': {
description: 'High angle using crane equipment',
emotionalImpact: 'Dramatic revelation, transition, epic scale, overview',
bestFor: 'Opening/closing shots, dramatic reveals, scene transitions',
commonUses: ['Movie openings', 'Wedding reveals', 'Concert openings', 'Epic moments'],
difficulty: 'advanced',
lensRecommendation: 'Wide angle 16-35mm'
},
'worms-view': {
description: 'Camera on ground looking straight up through objects',
emotionalImpact: 'Overwhelmed, trapped, surrounded, looking up at threats',
bestFor: 'Forest canopy, city streets, surrounded feeling, claustrophobia',
commonUses: ['Forest shots', 'City street canyons', 'Surrounded by enemies'],
difficulty: 'intermediate',
lensRecommendation: 'Ultra-wide fisheye 8-15mm'
}
};
// ============================================================================
// CAMERA MOVEMENTS - Expanded
// ============================================================================
export const CAMERA_MOVEMENTS = {
// Basic Movements
'static': {
description: 'Fixed position - no movement',
emotionalImpact: 'Stability, observation, contemplation, objectivity, formality',
bestFor: 'Dialogue, interviews, stable moments, establishing shots, formal scenes',
technicalNotes: 'Use tripod or solid surface. Consider slight breathing for realism.',
speed: 'none',
difficulty: 'beginner'
},
'pan': {
description: 'Horizontal rotation left or right',
emotionalImpact: 'Revealing space, searching, following horizontal action, discovery',
bestFor: 'Landscape reveals, following movement, establishing surroundings',
technicalNotes: 'Smooth controlled motion. Speed depends on scene energy.',
speed: 'variable',
difficulty: 'beginner'
},
'tilt': {
description: 'Vertical rotation up or down',
emotionalImpact: 'Revealing height, following vertical action, awe, looking up/down',
bestFor: 'Tall structures, vertical reveals, looking up at hero, sky to ground',
technicalNotes: 'Smooth vertical sweep. Often combined with pan.',
speed: 'variable',
difficulty: 'beginner'
},
// Dolly Movements
'dolly-in': {
description: 'Camera moves closer to subject',
emotionalImpact: 'Intimacy, intensity, revelation, focusing attention, importance',
bestFor: 'Emotional moments, reveals, emphasizing reaction, building tension',
technicalNotes: 'Smooth track or wheels. Speed: slow for drama, fast for action.',
speed: 'slow to medium',
difficulty: 'intermediate'
},
'dolly-out': {
description: 'Camera moves away from subject',
emotionalImpact: 'Isolation, context, withdrawal, scale, loneliness',
bestFor: 'Revealing environment, ending scenes, showing isolation, epic scale',
technicalNotes: 'Smooth backward motion. Often reveals something important.',
speed: 'slow to medium',
difficulty: 'intermediate'
},
'dolly-zoom': {
description: 'Dolly in while zooming out (or vice versa) - Vertigo effect',
emotionalImpact: 'Disorientation, realization, shock, psychological unease',
bestFor: 'Moments of realization, horror reveals, psychological distress',
technicalNotes: 'Requires coordination of dolly and zoom. Classic Hitchcock technique.',
speed: 'slow',
difficulty: 'advanced'
},
'truck': {
description: 'Camera moves left or right parallel to scene',
emotionalImpact: 'Following parallel action, revealing side details, tracking',
bestFor: 'Walking alongside characters, revealing wall details, parallel action',
technicalNotes: 'Sideways movement. Can be handheld or tracked.',
speed: 'medium',
difficulty: 'intermediate'
},
'pedestal': {
description: 'Camera moves up or down vertically (not tilting)',
emotionalImpact: 'Revealing vertical space, rising above, descending into',
bestFor: 'Rising above crowd, descending into pit, height changes',
technicalNotes: 'Vertical movement maintaining same angle. Requires jib or crane.',
speed: 'medium',
difficulty: 'intermediate'
},
// Complex Movements
'crane': {
description: 'Sweeping vertical arcs using crane or jib',
emotionalImpact: 'Epic scale, transitions, dramatic reveals, grandeur',
bestFor: 'Opening/closing shots, dramatic reveals, concert openings, weddings',
technicalNotes: 'Large vertical and horizontal arc. Majestic, flowing movement.',
speed: 'slow to medium',
difficulty: 'advanced'
},
'jib': {
description: 'Smaller crane movements, more intimate',
emotionalImpact: 'Elegance, smooth transitions, floating quality',
bestFor: 'Product shots, food photography, intimate reveals, music videos',
technicalNotes: 'Smaller arc than crane. Great for precise movements.',
speed: 'smooth and controlled',
difficulty: 'intermediate'
},
'steadicam': {
description: 'Smooth floating movement using Steadicam rig',
emotionalImpact: 'Dream-like, following through space, fluid, natural movement',
bestFor: 'Following characters through spaces, long takes, music videos, walking scenes',
technicalNotes: 'Requires Steadicam operator. Smooth gliding motion.',
speed: 'variable, smooth',
difficulty: 'advanced'
},
'handheld': {
description: 'Shaky natural camera movement',
emotionalImpact: 'Documentary feel, urgency, realism, chaos, immediacy',
bestFor: 'Documentary, action scenes, found footage, realistic drama, running',
technicalNotes: 'Controlled shakiness. Adds energy and realism.',
speed: 'variable',
difficulty: 'intermediate'
},
'gimbal': {
description: 'Smooth stabilized handheld movement',
emotionalImpact: 'Professional fluidity, modern feel, dynamic smoothness',
bestFor: 'Modern action, music videos, vlogs, smooth following shots',
technicalNotes: 'Electronic stabilization. Smooth but with handheld freedom.',
speed: 'variable, smooth',
difficulty: 'intermediate'
},
'drone': {
description: 'Aerial movement using drone',
emotionalImpact: 'Epic scale, freedom, bird\'s perspective, grandeur',
bestFor: 'Establishing shots, landscapes, action sequences, impossible angles',
technicalNotes: 'Remote controlled flight. Wide range of movement possibilities.',
speed: 'variable',
difficulty: 'advanced'
},
// Zoom and Focus
'zoom-in': {
description: 'Changing focal length to magnify subject',
emotionalImpact: 'Sudden focus, emphasis, surprise, discovery, importance',
bestFor: 'Emphasis, reveals, dramatic moments, directing attention',
technicalNotes: 'Optical zoom. Different feel than dolly - compresses space.',
speed: 'fast or slow depending on effect',
difficulty: 'beginner'
},
'zoom-out': {
description: 'Changing focal length to widen view',
emotionalImpact: 'Context, revelation, isolation, showing the bigger picture',
bestFor: 'Revealing surroundings, ending shots, showing scale',
technicalNotes: 'Optical zoom out. Classic ending technique.',
speed: 'slow to medium',
difficulty: 'beginner'
},
'rack-focus': {
description: 'Shifting focus from one plane to another',
emotionalImpact: 'Connection, choice, revelation, shifting attention',
bestFor: 'Revealing connections, foreground/background relationships, choices',
technicalNotes: 'Precise focus pull. Requires planning of focal points.',
speed: 'smooth transition',
difficulty: 'advanced'
},
'whip-pan': {
description: 'Very fast pan creating motion blur',
emotionalImpact: 'Energy, chaos, transition, disorientation, excitement',
bestFor: 'Action transitions, fast reveals, chaotic moments, music videos',
technicalNotes: 'Fast blur transition. Often used as transition device.',
speed: 'very fast',
difficulty: 'intermediate'
},
'whip-tilt': {
description: 'Very fast tilt creating vertical motion blur',
emotionalImpact: 'Sudden vertical shift, surprise, energy',
bestFor: 'Vertical reveals, falling, rising, dramatic transitions',
technicalNotes: 'Fast vertical blur. Less common than whip-pan.',
speed: 'very fast',
difficulty: 'intermediate'
},
'orbit': {
description: 'Camera circles around subject',
emotionalImpact: 'Scrutiny, revelation, 360 view, importance, celebration',
bestFor: 'Character reveals, important moments, showcasing subject',
technicalNotes: 'Circular path around subject. Requires space or circular track.',
speed: 'slow to medium',
difficulty: 'advanced'
},
'snorricam': {
description: 'Camera rigged to actor\'s body',
emotionalImpact: 'Intense subjectivity, disorientation, drunkenness, instability',
bestFor: 'Intoxicated POV, disorientation, intense action, drug sequences',
technicalNotes: 'Camera attached to actor. Face stays in frame while background moves.',
speed: 'matches actor movement',
difficulty: 'advanced'
},
'follow-focus': {
description: 'Continuous focus adjustment to keep moving subject sharp',
emotionalImpact: 'Professional tracking, importance on moving subject',
bestFor: 'Moving subjects, shallow depth of field tracking',
technicalNotes: 'Requires focus puller skill. Critical for shallow DOF.',
speed: 'matches subject',
difficulty: 'advanced'
}
};
// ============================================================================
// SHOT TYPES - Expanded
// ============================================================================
export const SHOT_TYPES = {
// Distance Shots
'extreme-wide': {
description: 'Subject very small in vast environment',
emotionalImpact: 'Isolation, scale, epic scope, insignificance, environment dominates',
bestFor: 'Establishing vast landscapes, showing scale, isolation, opening shots',
framing: 'Subject takes up less than 1/4 of frame',
lens: 'Wide angle 10-24mm'
},
'wide': {
description: 'Full subject plus extensive surroundings',
emotionalImpact: 'Context, environment, scale, spatial relationships',
bestFor: 'Establishing shots, action sequences, showing full body movement',
framing: 'Full subject visible with environment',
lens: 'Wide angle 16-35mm'
},
'medium-wide': {
description: 'Subject from knees up with some environment',
emotionalImpact: 'Some context with character focus, movement emphasis',
bestFor: 'Character movement, slight environment context, action',
framing: 'Knees to head',
lens: '35-50mm'
},
'medium': {
description: 'Subject from waist up',
emotionalImpact: 'Dialogue focus with some body language, interaction',
bestFor: 'Dialogue, interaction, showing body language',
framing: 'Waist to head',
lens: '50mm'
},
'medium-close-up': {
description: 'Subject from chest up',
emotionalImpact: 'Intimate dialogue, facial expressions, personality',
bestFor: 'Dialogue, reactions, interviews, showing emotion',
framing: 'Chest to head',
lens: '50-85mm'
},
'close-up': {
description: 'Subject head and shoulders',
emotionalImpact: 'Intimacy, emotion, reaction, importance',
bestFor: 'Emotional moments, reactions, emphasizing importance',
framing: 'Head and shoulders',
lens: '85mm'
},
'extreme-close-up': {
description: 'Detail only - eyes, mouth, hands, objects',
emotionalImpact: 'Intense emotion, symbolism, detail importance, intimacy',
bestFor: 'Eyes, hands holding object, important details, intense emotion',
framing: 'Fills frame with detail',
lens: 'Macro or 100mm+'
},
// Special Shots
'establishing': {
description: 'Wide shot establishing location and time',
emotionalImpact: 'Setting the scene, geography, time period, atmosphere',
bestFor: 'Scene openings, location establishment, time/place setting',
framing: 'Wide view of location',
lens: 'Wide angle'
},
'master': {
description: 'Wide shot showing all characters and action in scene',
emotionalImpact: 'Full scene context, spatial relationships, staging',
bestFor: 'Scene coverage, showing all actors, action staging',
framing: 'Wide enough for all action',
lens: 'Wide to standard'
},
'two-shot': {
description: 'Two characters in frame',
emotionalImpact: 'Relationship, interaction, equality, dialogue',
bestFor: 'Conversations between two people, relationship scenes',
framing: 'Two subjects',
lens: '35-50mm'
},
'three-shot': {
description: 'Three characters in frame',
emotionalImpact: 'Group dynamic, triangle relationship, interaction',
bestFor: 'Small group interactions, trio dynamics',
framing: 'Three subjects',
lens: '28-35mm'
},
'group-shot': {
description: 'Multiple characters in frame',
emotionalImpact: 'Team, ensemble, crowd, social dynamics',
bestFor: 'Group scenes, ensemble cast, crowd reactions',
framing: 'Multiple subjects',
lens: 'Wide angle'
},
'over-shoulder': {
description: 'Looking past one subject at another',
emotionalImpact: 'Conversation intimacy, spatial relationship',
bestFor: 'Dialogue, conversations, interviews',
framing: 'Shoulder in foreground, face in background',
lens: '50-85mm'
},
'point-of-view': {
description: 'What a character sees',
emotionalImpact: 'Immersion, subjectivity, identification',
bestFor: 'First person, character perspective, reactions to POV',
framing: 'Character\'s exact view',
lens: '50mm (human eye)'
},
'reaction-shot': {
description: 'Close-up of character reacting to something',
emotionalImpact: 'Emotional response, internal state, impact',
bestFor: 'Emotional beats, comedy reactions, dramatic moments',
framing: 'Face showing reaction',
lens: '50-85mm'
},
'insert': {
description: 'Detail shot of object or action detail',
emotionalImpact: 'Importance of detail, information, symbolism',
bestFor: 'Objects, details, important information, hands doing something',
framing: 'Object fills frame',
lens: 'Macro or any'
},
'cutaway': {
description: 'Shot of something other than main action',
emotionalImpact: 'Context, foreshadowing, parallel action, B-roll',
bestFor: 'Environment details, parallel action, atmosphere',
framing: 'Varies',
lens: 'Any'
},
'aerial': {
description: 'From above, bird\'s eye view',
emotionalImpact: 'Scale, overview, patterns, detachment',
bestFor: 'Landscapes, cityscapes, action overview',
framing: 'Top-down view',
lens: 'Wide angle'
},
'underwater': {
description: 'Submerged camera',
emotionalImpact: 'Otherworldly, floating, dreamlike, danger',
bestFor: 'Underwater scenes, drowning, dream sequences',
framing: 'Submerged perspective',
lens: 'Underwater housing'
},
'mirror-shot': {
description: 'Reflection in mirror or reflective surface',
emotionalImpact: 'Introspection, duality, self-examination',
bestFor: 'Character reflection, identity, vanity',
framing: 'Mirror reflection',
lens: 'Any with angle consideration'
},
'silhouette': {
description: 'Subject as dark shape against bright background',
emotionalImpact: 'Mystery, anonymity, drama, shape emphasis',
bestFor: 'Anonymous figures, dramatic reveals, shape emphasis',
framing: 'Backlit subject',
lens: 'Any'
},
'shadow-shot': {
description: 'Focusing on shadow rather than subject',
emotionalImpact: 'Threat, mystery, foreboding, symbolism',
bestFor: 'Threat, mystery, horror, artistic shots',
framing: 'Shadow prominent',
lens: 'Any'
},
'low-angle-shot': {
description: 'Shot from below subject (different from low-angle)',
emotionalImpact: 'Power, threat, dominance, awe',
bestFor: 'Powerful figures, threats, heroic poses',
framing: 'Looking up at subject',
lens: 'Wide angle'
},
'high-angle-shot': {
description: 'Shot from above subject (different from high-angle)',
emotionalImpact: 'Vulnerability, weakness, surveillance',
bestFor: 'Vulnerable characters, overview shots',
framing: 'Looking down at subject',
lens: 'Any'
},
'through-frame': {
description: 'Subject viewed through foreground element',
emotionalImpact: 'Voyeurism, obstruction, depth, layers',
bestFor: 'Peeking, observation, depth composition',
framing: 'Subject through foreground',
lens: 'Shallow depth of field'
},
'dirty-single': {
description: 'Close-up with some foreground element visible',
emotionalImpact: 'Intimacy with context, depth, environment present',
bestFor: 'Intimate shots with location context',
framing: 'Close-up with foreground blur',
lens: '50-85mm shallow DOF'
}
};
// Export all cinematography data
export const CINEMATOGRAPHY_DB = {
angles: CAMERA_ANGLES,
movements: CAMERA_MOVEMENTS,
shots: SHOT_TYPES
};
export default CINEMATOGRAPHY_DB;
🎯 Character & Environment Consistency Example
This example shows how to use the consistency system to ensure your characters, voices, and environments stay consistent across all generated content.
The Problem
Without consistency:
- ❌ Kutil looks different in every shot
- ❌ Characters wear modern clothes in Ramayana era
- ❌ Buildings look like modern architecture
- ❌ Characters sound different in each scene
With consistency:
- ✅ Kutil always has purple fur, small horns, golden eyes
- ✅ Everyone wears era-appropriate clothing (dhotis, sarees, no glasses!)
- ✅ Architecture is stone temples and mud huts
- ✅ Each character has a distinct, consistent voice
Step-by-Step Setup
Step 1: Create Context with Consistency
const skill = agent.tools['cinematic-script-writer'];
// First, create your base context
const kutilContext = await skill.createContext(
"Kutil - The Cursed Rakshasa",
"A lovable rakshasa's misadventures",
[
{
name: "Kutil",
description: "A small, cute rakshasa with purple fur",
personality: "Mischievous, determined, secretly kind",
appearance: "Purple fluffy fur, small curved horns, big expressive golden eyes",
role: "protagonist",
backstory: "Cursed by Saint Vardhan - bad deeds become good",
specialTraits: ["Curse of unintended goodness", "Loves sweets"]
},
{
name: "Saint Vardhan",
description: "An ancient wise sage",
personality: "Wise, patient, mischievous sense of humor",
appearance: "Long white beard with flowers, saffron robes, peaceful aura",
role: "supporting"
},
{
name: "Maya",
description: "A clever village girl",
personality: "Intelligent, brave, quick-witted",
appearance: "Young girl in simple village cotton clothes, curious eyes",
role: "supporting"
}
],
"Ramayana Era",
"Ancient India - Treta Yuga",
"Lanka and surrounding villages",
"short",
"comedy",
"All ages",
"Stylized 3D animation with Indian art influences"
);
// Now setup consistency guides
const { guides } = await skill.setupContextWithConsistency(
kutilContext,
{
// Detailed visual descriptions for each character
[kutilContext.characters[0].id]: `
Kutil is a small cute rakshasa (mythical being) with:
- Fluffy purple fur covering entire body
- Two small curved horns on head (cream colored tips)
- Large round golden eyes with black pupils
- Small fangs visible when smiling
- Pointed ears with pink insides
- Short tail with purple fur
- About 3 feet tall, chibi proportions
- Expressive face showing emotions clearly
`,
[kutilContext.characters[1].id]: `
Saint Vardhan is an elderly sage with:
- Long flowing white beard with flowers woven in
- Kind wrinkled face with twinkling blue eyes
- Saffron/orange traditional robes
- Wooden staff with carvings
- Peaceful glowing aura
- Barefoot
`,
[kutilContext.characters[2].id]: `
Maya is a young village girl with:
- Dark brown hair in simple braid
- Brown eyes full of curiosity
- Simple white cotton saree/dress
- Barefoot
- Carries a basket
- Around 10 years old
`
}
);
console.log("✅ Consistency guides created!");
console.log("Character References:", Object.keys(guides.characters));
console.log("Voice Profiles:", Object.keys(guides.voices));
console.log("Environment Guide:", guides.environment.era);Step 2: View Consistency Guides
// Get Kutil's character reference
const kutilRef = skill.getCharacterReference(kutilContext.characters[0].id);
console.log("Kutil's Reference Sheet:", {
baseDescription: kutilRef.visual.baseDescription,
signatureColor: kutilRef.visual.colorPalette.signature,
keyFeatures: kutilRef.visual.features,
wardrobe: kutilRef.wardrobe.defaultOutfit.description
});
// Get voice profile
const kutilVoice = skill.getVoiceProfile(kutilContext.characters[0].id);
console.log("Kutil's Voice:", {
pitch: kutilVoice.speech.pitch,
catchphrases: kutilVoice.language.catchphrases,
examples: kutilVoice.examples
});
// Get environment guide
const envGuide = skill.getEnvironmentGuide(kutilContext.id);
console.log("Environment Rules:", {
era: envGuide.eraSpecs.name,
forbiddenItems: envGuide.eraSpecs.anachronismsForbidden.slice(0, 5),
architecture: envGuide.architecture.buildingStyles.map(b => b.type),
clothingMaterials: envGuide.clothing.materials
});Step 3: Build Consistent Image Prompts
// Build prompts with full consistency
const prompts = skill.buildConsistentPrompts({
characterIds: [kutilContext.characters[0].id], // Kutil
contextId: kutilContext.id,
shotType: 'close-up',
cameraAngle: 'low-angle',
cameraMovement: 'static',
lighting: 'golden-hour',
mood: 'determined',
action: 'trying to look evil but looking cute',
timeOfDay: 'afternoon',
includeEnvironment: true
});
console.log("=== IMAGE PROMPT ===");
console.log(prompts.imagePrompt);
// Output: close-up shot, low-angle camera angle, Kutil is a small cute rakshasa with purple fluffy fur,
// small curved horns, large round golden eyes, purple, golden, fluffy, wearing simple traditional dhoti,
// Ramayana Era setting, Lanka and surrounding villages, stone temple architecture, golden-hour lighting,
// afternoon light, determined mood, Stylized 3D animation with Indian art influences,
// consistent character design, same character across frames, highly detailed, 8k, cinematic composition
console.log("\n=== NEGATIVE PROMPT ===");
console.log(prompts.negativePrompt);
// Output: inconsistent character design, different character in each frame, changing features,
// wrong eye color, wrong hair color, modern clothing, glasses, watches, plastic, synthetic fabrics,
// modern furniture, blurry, low quality, deformed...
console.log("\n=== CONSISTENCY NOTES ===");
console.log(prompts.consistencyNotes);
console.log("\n=== VALIDATION WARNINGS ===");
console.log(prompts.validationWarnings);Step 4: Generate Multiple Shots with Consistency
// Generate a series of shots for the same scene
const shots = [
{
type: 'close-up',
angle: 'low-angle',
action: 'evil grin attempt',
mood: 'mischievous'
},
{
type: 'medium',
angle: 'eye-level',
action: 'confused expression',
mood: 'confused'
},
{
type: 'wide',
angle: 'high-angle',
action: 'accidentally helping villagers',
mood: 'heroic-irony'
}
];
const shotPrompts = shots.map(shot => {
return skill.buildConsistentPrompts({
characterIds: [kutilContext.characters[0].id],
contextId: kutilContext.id,
shotType: shot.type,
cameraAngle: shot.angle,
cameraMovement: 'static',
lighting: 'golden-hour',
mood: shot.mood,
action: shot.action
});
});
// All prompts will have the SAME character description!
// Kutil will look consistent across all shotsStep 5: Voice Consistency in Dialogue
// Get voice guidelines for writing dialogue
const kutilVoiceGuide = skill.generateVoiceGuidelines(kutilContext.characters[0].id);
console.log(kutilVoiceGuide);
// Output:
// Voice Profile for Kutil:
// - Pitch: high, Speed: fast, Volume: normal
// - Vocabulary: simple, Formality: casual
// - Catchphrases: "I am evil!", "Curse you!"
// - Speech Examples:
// - Greeting: "Tremble before me!"
// - Question: "What do you mean I'm helping?"
// - Exclamation: "No, not again!"
// Use this to write consistent dialogue
const dialogue = {
kutil: [
{
text: "Today I shall steal all the sweets! Muahaha!",
tone: "trying-to-be-evil",
notes: "High pitch, fast speech, overconfident"
},
{
text: "Wait... why am I arranging them beautifully?",
tone: "confused",
notes: "Speed slows down, pitch raises"
},
{
text: "Curse you, Saint Vardhan!",
tone: "frustrated",
notes: "Use catchphrase"
}
]
};Step 6: Validate for Anachronisms
// Test a prompt that might have issues
const badPrompt = "Kutil wearing sunglasses and holding a smartphone in a stone temple";
const validation = skill.validatePrompt(
badPrompt,
[kutilContext.characters[0].id],
kutilContext.id
);
console.log(validation);
// Output:
// {
// valid: false,
// errors: [
// 'Anachronism detected: "glasses" does not belong in Ramayana Era',
// 'Anachronism detected: "smartphone" does not belong in Ramayana Era'
// ],
// warnings: [...],
// suggestions: [
// 'Consider using era-appropriate material: cotton',
// 'Consider using era-appropriate material: silk'
// ]
// }
// Fix the prompt
const goodPrompt = "Kutil in traditional dhoti holding a clay pot in a stone temple";
const goodValidation = skill.validatePrompt(
goodPrompt,
[kutilContext.characters[0].id],
kutilContext.id
);
console.log(goodValidation.valid); // trueStep 7: Complete Workflow
async function createConsistentScene(contextId: string, sceneNumber: number) {
const skill = agent.tools['cinematic-script-writer'];
const context = await skill.getContext(contextId);
// Get all consistency data
const characterRefs = {};
const voiceProfiles = {};
for (const char of context.characters) {
characterRefs[char.id] = skill.getCharacterReference(char.id);
voiceProfiles[char.id] = skill.getVoiceProfile(char.id);
}
const envGuide = skill.getEnvironmentGuide(contextId);
// Define shots for the scene
const shots = [
{
number: 1,
description: "Kutil plans mischief",
type: "close-up",
angle: "low-angle",
characters: [context.characters[0].id],
action: "evil plotting expression",
lighting: "dramatic-shadows"
},
{
number: 2,
description: "Curse activates",
type: "wide",
angle: "high-angle",
characters: [context.characters[0].id],
action: "magical transformation",
lighting: "golden-magic-light"
},
{
number: 3,
description: "Villagers celebrate",
type: "medium",
angle: "eye-level",
characters: context.characters.map(c => c.id),
action: "celebration scene",
lighting: "warm-sunlight"
}
];
// Generate consistent prompts for all shots
const shotPrompts = shots.map(shot => {
return {
...shot,
prompts: skill.buildConsistentPrompts({
characterIds: shot.characters,
contextId,
shotType: shot.type,
cameraAngle: shot.angle,
cameraMovement: 'static',
lighting: shot.lighting,
mood: 'comedy',
action: shot.action
})
};
});
return {
scene: sceneNumber,
context,
characterRefs,
voiceProfiles,
environment: envGuide,
shots: shotPrompts
};
}
// Use it
const scene1 = await createConsistentScene(kutilContext.id, 1);
// scene1 now contains:
// - Character references ensuring visual consistency
// - Voice profiles ensuring dialogue consistency
// - Environment guide ensuring era-appropriate elements
// - Shot prompts with consistency built-inConsistency Features Summary
Character Consistency
- ✅ Reference sheets with detailed visual breakdown
- ✅ Color palette enforcement
- ✅ Wardrobe variations for different situations
- ✅ Key features tracking (eyes, hair, build)
- ✅ Style keywords for AI generation
Voice Consistency
- ✅ Pitch, speed, volume profiles
- ✅ Vocabulary and formality levels
- ✅ Catchphrases and recurring phrases
- ✅ Emotional variations
- ✅ Speech examples
Environment Consistency
- ✅ Era-appropriate architecture
- ✅ Period-accurate clothing materials
- ✅ Forbidden anachronism detection
- ✅ Props and objects validation
- ✅ Society and customs guidelines
Validation
- ✅ Anachronism detection
- ✅ Era-accurate material suggestions
- ✅ Consistency warnings
- ✅ Prompt corrections
Example Output
// Generated prompt for Kutil
{
imagePrompt: "close-up shot, low-angle camera angle, Kutil is a small cute rakshasa with fluffy purple fur covering entire body, two small curved horns on head cream colored tips, large round golden eyes with black pupils, small fangs visible when smiling, pointed ears with pink insides, short tail with purple fur, about 3 feet tall chibi proportions, expressive face, purple golden fluffy, wearing simple traditional cotton dhoti, Ramayana Era setting, Lanka and surrounding villages, stone temple architecture with intricate carvings, mud huts with thatched roofs, golden-hour lighting, afternoon light, mischievous mood, Stylized 3D animation with Indian art influences, consistent character design, same character across frames, highly detailed, 8k, cinematic composition",
negativePrompt: "inconsistent character design, different character in each frame, changing features, wrong eye color, wrong hair color, anatomical errors, modern clothing, glasses, watches, plastic, synthetic fabrics, modern furniture, modern buildings, electric lights, metal utensils, blurry, low quality, deformed, mutated, extra limbs, missing limbs, bad anatomy",
consistencyNotes: "=== CHARACTER CONSISTENCY ===\nKutil:\n Base: Kutil is a small cute rakshasa...\n Signature Colors: purple, golden\n Key Features: golden eyes, purple fluffy hair, small build\n Wardrobe: Simple cotton dhoti...\n\n=== ENVIRONMENT CONSISTENCY ===\nEra: Ramayana Era\nLocation: Lanka and surrounding villages\nArchitecture: Temple, Palace, Hut, Market\nForbidden Elements: glasses, watches, plastic, synthetic fabrics...",
validationWarnings: []
}This ensures Kutil looks the same in every shot, wears appropriate clothing, and stays true to the Ramayana era setting! 🎬
{
"name": "cinematic-script-writer",
"version": "1.4.3",
"description": "Professional cinematic script generation with consistency, cinematography, and storage",
"type": "module",
"main": "index.ts",
"keywords": [
"openclaw",
"skill",
"cinematography",
"script",
"video",
"consistency",
"google-drive"
],
"author": "Praveen Kumar",
"license": "MIT",
"dependencies": {
"uuid": "^9.0.0"
},
"engines": {
"node": ">=18.0.0"
}
}
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"llmProvider": {
"type": "string",
"enum": ["openai", "anthropic", "local"],
"default": "anthropic",
"description": "LLM provider for generating scripts"
},
"apiKey": {
"type": "string",
"description": "API key for LLM provider"
},
"model": {
"type": "string",
"description": "Model name (e.g., claude-3-opus, gpt-4)",
"default": "claude-3-opus"
},
"defaultVideoDuration": {
"type": "number",
"description": "Default video duration in seconds",
"default": 60
},
"cameraStyle": {
"type": "string",
"enum": ["cinematic", "documentary", "anime", "comic-book", "minimalist"],
"default": "cinematic",
"description": "Default visual style for camera work"
}
},
"required": []
}
{
"compilerOptions": {
"target": "ES2022",
"module": "CommonJS",
"moduleResolution": "node",
"esModuleInterop": true,
"lib": ["ES2022"],
"outDir": "./dist",
"rootDir": ".",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true
},
"include": [
"**/index.ts",
"bin/**/*.ts"
],
"exclude": [
"node_modules",
"dist",
"**/*.test.ts"
]
}
Related skills
How it compares
Use cinematic-script-writer for structured AI video scripts; use generic copy skills for non-cinematic marketing text only.
FAQ
Which AI video tools does cinematic-script-writer target?
cinematic-script-writer targets Midjourney, Sora, and Veo workflows, producing image prompts, shot contexts, and cinematography notes aligned with each tool's generative video or image pipeline.
What extra outputs does cinematic-script-writer provide?
cinematic-script-writer version 1.4.0 adds character consistency sheets, voice profiles, anachronism detection, and optional Google Drive saves alongside camera, lighting, and color-grading guidance.
Is Cinematic Script Writer safe to install?
skills.sh reports 2 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.