
Dna Extraction
- 340 installs
- 133 repo stars
- Updated February 24, 2026
- jwynia/agent-skills
dna-extraction is an agent-building skill that distills recurring style, values, constraints, and patterns from sample artifacts into project DNA profiles agents use to replicate voice, architecture choices, or brand beh
About
dna-extraction is a Claude Code skill for developers configuring AI agents who need consistent voice, architecture decisions, or brand behavior across long-running projects. The skill analyzes sample code, copy, or design artifacts to distill recurring style patterns, stated values, hard constraints, and behavioral norms into a compact project DNA profile. Developers reach for dna-extraction when agents drift from established conventions between sessions or when onboarding a new agent context requires encoding tacit project rules that never made it into formal docs. The output feeds agent system prompts, skill libraries, and codegen guardrails so generated code and copy match the project's established character.
- Pattern mining from samples
- Invariant trait summaries
- Reusable style or rule cards
- Anti-pattern identification
- Seed prompts from DNA
Dna Extraction by the numbers
- 340 all-time installs (skills.sh)
- +6 installs in the week ending Aug 2, 2026 (Skillselion tracking)
- Ranked #2,149 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/jwynia/agent-skills --skill dna-extractionAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 340 |
|---|---|
| repo stars | ★ 133 |
| Last updated | February 24, 2026 |
| Repository | jwynia/agent-skills ↗ |
How do you encode project style for AI agents?
Distill recurring style, values, constraints, and patterns—the project DNA—from samples so agents can replicate voice, architecture choices, or brand behavior consistently.
Who is it for?
Developers onboarding agents to long-lived codebases or brand-heavy products where consistent voice and architecture choices matter across sessions.
Skip if: Greenfield projects with no sample artifacts to analyze or teams that only need one-off code generation without persistent style encoding.
When should I use this skill?
Agent outputs drift from project conventions or a new agent context needs encoded style, values, and constraints extracted from existing samples.
What you get
A project DNA profile documenting distilled style patterns, values, constraints, and recurring behavioral norms extracted from sample artifacts.
- Project DNA profile
- Agent context encoding
Files
DNA Extraction: Functional Analysis for Adaptation
You help extract the functional DNA from existing works. Your role is to identify what makes a work function—not its surface elements, but the underlying structures, relationships, and emotional mechanics that could be preserved in an adaptation.
Core Principle
The first ideas when adapting are surface elements. The functional DNA is what those elements DO, not what they ARE.
Hamlet's prince status is not the DNA—it's a form. The DNA is:
- "Protagonist has proximity to power center but is not the power holder"
- "Protagonist has structural obligation that conflicts with personal desire"
- "Protagonist has insider access to observe corruption they cannot act against"
The States
State EX0: No Extraction
Symptoms: Work identified but no analysis started. User says "I want to adapt X" without having analyzed what makes X work. Key Questions:
- What work are we extracting from?
- What medium is the source? (affects extraction approach)
- What's your extraction goal? (adaptation, trope mapping, analysis)
Interventions: Begin with emotional core identification. Use genre-conventions skill to identify primary/secondary genres.
State EX1: Surface Reading
Symptoms: Analysis focuses on what happens, not why it works. "It's about a prince who sees a ghost." Plot summaries without function identification. User conflates events with functions. Key Questions:
- Why does this element exist?
- What would break if we removed it?
- What does the audience feel because of this element?
- Is this what the work IS or what the work DOES?
Interventions: Four-axis function extraction. Apply "function not form" reframe to each element.
State EX2: Single-Axis Extraction
Symptoms: Functions extracted only for plot OR character OR theme. Missing interconnections. "The ghost provides inciting incident." (True, but incomplete—what about character function? Emotional function? Relational function?) Key Questions:
- What other functions does this element serve?
- How does this connect to character arc?
- What genre promise does it fulfill?
- What relationships does it create or complicate?
Interventions: Multi-axis checklist. Cross-reference with genre-conventions skill. Force extraction on all six axes.
State EX3: Missing Emotional Core
Symptoms: Functions extracted but no clarity on what emotional experience the work creates. Mechanical analysis without genre promise. Can describe plot functions but not audience feeling. Key Questions:
- What does the audience feel while experiencing this work?
- Which elemental genre(s) does this work deliver?
- Where are the emotional peaks and valleys?
- What would someone who loved this work say about WHY they loved it?
Interventions: Genre-conventions integration. Emotional beat mapping with emotional-beat-map.ts. Primary/secondary genre identification.
State EX4: Structural/Stylistic Conflation
Symptoms: Analysis treats stylistic choices as structural necessities. Shakespeare's language treated as structural when it's stylistic. Period setting treated as essential when it's adaptable. Key Questions:
- If we changed this, would the story break?
- Is this essential to function or characteristic of form?
- Could another form serve the same function?
- Would a different setting make this impossible?
Interventions: Structural/stylistic classification with structural-stylistic.ts. Test each element against "would the story still work?" criterion.
State EX5: Missing Relationships
Symptoms: Individual character functions extracted but relationship dynamics aren't. "Hamlet is indecisive" without "Claudius represents what Hamlet could become if he acted." Characters analyzed in isolation. Key Questions:
- What does this character mean TO other characters?
- What choice does this relationship force?
- What would be lost if this relationship didn't exist?
- How do characters define each other through contrast?
Interventions: Relationship function mapping. Character web analysis. Identify foil pairs and what they illuminate.
State EX6: No Hierarchy
Symptoms: Everything treated as equally important. No distinction between load-bearing elements and removable details. Every scene, character, subplot given equal weight. Key Questions:
- Which functions are primary (story breaks without them)?
- Which are reinforcing (story weakens without them)?
- Which are optional flavor (nice but not necessary)?
- What's the minimum viable extraction?
Interventions: Function hierarchy classification. Impact scoring. Identify which 3-5 elements are truly non-negotiable.
State EX7: Extraction Complete
Symptoms: Comprehensive extraction document exists. Functions identified at multiple levels. Emotional core clear. Structural/stylistic separated. Hierarchy established. Links to clusters documented. Key Questions:
- Is this extraction complete enough to generate a new work?
- Are there gaps that would cause synthesis to fail?
- Have you validated against the emotional experience?
- Are cluster links identified?
Interventions: Validation checklist. Hand-off to adaptation-synthesis skill.
The Six Extraction Axes
For every story element, extract functions across all six axes:
| Axis | Question | What It Reveals |
|---|---|---|
| Form | What is it? | The surface element (adaptable container) |
| Structural Function | What does it enable in the plot? | Story mechanics, cause-effect chains |
| Character Function | What does it enable in character journeys? | Arc requirements, transformation catalysts |
| Emotional Function | What does it make the audience feel? | Genre promise delivery, emotional beats |
| Thematic Function | What ideas does it explore? | Meaning, questions, resonance |
| Relational Function | What dynamics does it create between elements? | Web of connections, contrasts, tensions |
Tone and Voice Extraction
Beyond structural functions, works have distinctive tonal signatures that define their feel. Extract these separately:
Tonal Registers
| Register | Description | Examples |
|---|---|---|
| Sincerity Level | Earnest vs. ironic/detached | Killjoys: high sincerity despite humor. Bebop: detached cool masking pain |
| Humor Mode | How comedy functions | Banter (Killjoys), deadpan (Bebop), physical (Jackie Chan), dark (Breaking Bad) |
| Emotional Expression | How feelings are shown | Direct statement, subtext-heavy, action-reveals-feeling, denial/deflection |
| Dialogue Density | Talk-to-action ratio | Quippy/rapid-fire vs. sparse/weighted silence |
| Conflict Style | How characters fight | Verbal sparring, cold silence, explosive outbursts, passive aggression |
Voice Patterns to Extract
Character Voice Distinctiveness:
- Do characters sound different from each other?
- What speech patterns mark each character? (Jargon, formality, sentence length)
- How do characters reveal vs. conceal through dialogue?
Dialogue Functions:
- Information delivery (exposition handling)
- Relationship expression (how connection shows in speech)
- Conflict escalation (how arguments build)
- Subtext density (what's said vs. what's meant)
Tonal Consistency:
- Does tone shift between scenes/episodes? How?
- What triggers tonal shifts?
- Is there a baseline tone that anchors the work?
Example: Killjoys vs. Cowboy Bebop Tonal Extraction
| Element | Killjoys | Cowboy Bebop |
|---|---|---|
| Sincerity | High - characters mean what they say | Low - ironic distance masks vulnerability |
| Humor | Banter, quips, playful antagonism | Deadpan, absurdist, melancholy comedy |
| Emotional expression | Direct - "I love you, asshole" | Deflected - shown through action, not words |
| Dialogue density | High - constant verbal play | Varied - heavy silence punctuated by sparse lines |
| Conflict style | Loud, direct, resolved quickly | Avoidant, simmering, often unresolved |
Both serve "bounty hunter sci-fi" structural functions but feel completely different because of tonal choices.
Example: The Ghost in Hamlet
| Axis | Extraction |
|---|---|
| Form | Supernatural visitation from murdered father |
| Structural | Provides privileged information protagonist cannot verify; creates inciting obligation |
| Character | Forces Hamlet to confront impossible duty; represents idealized father replaced by corrupt one |
| Emotional | Horror at revelation; dread of obligation; uncertainty about reliability |
| Thematic | Questions reliability of testimony; explores duty to the dead; introduces supernatural/moral uncertainty |
| Relational | Creates Hamlet-Claudius dynamic (secret knowledge); creates Hamlet-Gertrude tension (she doesn't know) |
Extraction Depth Levels
| Depth | Scope | Use Case |
|---|---|---|
| quick | Core functions, primary genre, 3-5 key characters | Exploration, comparing multiple works, feasibility check |
| standard | Full six-axis extraction, relationships, plot structures | Most adaptation projects |
| detailed | Beat-level mapping, episode structures, tonal variations, dialogue patterns | Serious long-form adaptation, academic analysis |
Use --depth quick|standard|detailed with extraction tools.
Diagnostic Process
1. Identify the Source - What work? What medium? What's your goal? 2. Map the Emotional Experience - What genre(s)? What does the audience feel? When? 3. List Major Elements - Characters, settings, plot structures, relationships 4. For Each Element, Extract Functions across all six axes 5. Classify Structural vs. Stylistic - What must stay? What can change? 6. Build Hierarchy - Primary functions, reinforcing functions, optional functions 7. Identify Clusters - What trope patterns does this belong to? 8. Validate Completeness - Could someone synthesize a new work from this? 9. Generate DNA Document - Structured output for synthesis
Key Questions
For Emotional Core
- What does someone who LOVES this work love about it?
- What genre promise does it make? Does it deliver?
- Where are the emotional high points? Low points?
- What would betray audience expectations?
For Character Functions
- What lie does this character believe? (character-arc integration)
- What do they want vs. what do they need?
- What transformation do they undergo?
- Who are they contrasted with? What does the contrast reveal?
For Structural Functions
- What would break if we removed this?
- What information does this convey? To whom? When?
- What does this enable later in the story?
- Is this a cause or an effect?
For Adaptability
- Is this specific to the setting, or universal?
- Could this function be served by a different form?
- What's essential vs. what's characteristic?
- What other works serve similar functions differently?
Anti-Patterns
The Plot Summary Trap
Pattern: Extraction that reads like a plot summary with "function" labels attached. Problem: Confuses events with purposes. "The ghost appears and reveals the murder" is not a function. Fix: For every element, force the question "What does this ENABLE?" not "What does this DO?" Detection: If your extraction could be written by someone who didn't understand the work, it's too surface-level.
The Favorite Element Bias
Pattern: Over-extracting from beloved elements while under-extracting from others. Problem: Creates lopsided extraction that emphasizes what analyst likes, not what work needs. Fix: Force yourself to extract functions from elements you find boring or annoying. Detection: If extraction depth varies dramatically between elements without justification, bias is present.
The Everything-Is-Essential Trap
Pattern: Marking all elements as structurally necessary to avoid hard decisions. Problem: Creates unusable extraction—if everything is essential, nothing can be adapted. Fix: Force hierarchy. What are the 5 things that CANNOT change? Now what are the next 5? Detection: If your "adaptable" list is shorter than your "essential" list, you're probably wrong.
The Form-As-Function Conflation
Pattern: Treating the specific form as the function. "The function of the sword fight is to have a sword fight." Problem: Makes adaptation impossible because you can't see past the surface. Fix: Ask "What would HAPPEN if we removed this?" The answer reveals the function. Detection: If your function description includes the element's name, you're describing form, not function.
Available Tools
extract-functions.ts
Interactive questionnaire for element-by-element extraction. Guides through six-axis analysis.
# Start extraction session
deno run --allow-read scripts/extract-functions.ts "Hamlet"
# Extract at specific depth
deno run --allow-read scripts/extract-functions.ts "Killjoys" --depth quick
# Extract specific element
deno run --allow-read scripts/extract-functions.ts --element "The Ghost"
# Validate existing extraction
deno run --allow-read scripts/extract-functions.ts --validate extraction.jsonemotional-beat-map.ts
Maps emotional peaks/valleys across a work's timeline.
# Generate beat map template
deno run --allow-read scripts/emotional-beat-map.ts "Hamlet" --acts 5
# For episodic work
deno run --allow-read scripts/emotional-beat-map.ts "Killjoys S1" --episodes 10
# Compare against genre expectations
deno run --allow-read scripts/emotional-beat-map.ts --compare drama,thrillerstructural-stylistic.ts
Checklist for classifying elements as structural (must keep) vs stylistic (can adapt).
# Classification questionnaire
deno run --allow-read scripts/structural-stylistic.ts "royal court setting"
# Batch classification
deno run --allow-read scripts/structural-stylistic.ts --file elements.jsonDNA Document Output
Extractions are saved to a linked network:
{project}/dna-library/
├── extractions/ # Work-specific extractions
│ ├── hamlet.json
│ └── killjoys.json
├── clusters/ # Trope cluster documents
│ └── bounty-hunter-scifi.json
└── syntheses/ # Generated synthesis plans
└── my-project.jsonWork Extraction Schema
{
"_meta": {
"type": "work-extraction",
"source_work": "Hamlet",
"source_author": "William Shakespeare",
"source_medium": "stage play",
"extraction_date": "2025-01-15",
"extraction_depth": "standard",
"clusters": ["revenge-tragedy", "political-drama"]
},
"emotional_core": {
"primary_genre": "drama",
"secondary_genres": ["thriller", "horror"],
"emotional_experience": "The dread of knowing truth but being unable to act",
"emotional_beats": [
{"position": 0.05, "emotion": "unease", "element": "Guards report ghost"},
{"position": 0.15, "emotion": "horror/obligation", "element": "Ghost reveals murder"}
]
},
"tone": {
"sincerity_level": "high",
"humor_mode": "dark/ironic",
"emotional_expression": "soliloquy-heavy, internal made external",
"dialogue_density": "high - language-forward",
"conflict_style": "verbal sparring, passive aggression, delayed explosion",
"baseline_tone": "melancholic brooding punctuated by dark wit",
"tonal_shifts": [
{"trigger": "players arrive", "shift": "lightens temporarily"},
{"trigger": "Ophelia's death", "shift": "pure tragedy"}
]
},
"characters": {
"hamlet": {
"form": "Prince of Denmark",
"functions": {
"structural": ["Proximity to power without holding it"],
"character": ["Lie: I can know truth absolutely before acting"],
"emotional": ["Audience vehicle for knowing-but-not-acting"],
"thematic": ["Embodies question: Is certainty possible?"],
"relational": ["To Claudius: corrupt mirror of what he could become"]
},
"structural_necessity": "high",
"adaptable_elements": ["royal status", "gender", "era", "name"]
}
},
"plot_structures": {},
"relationships": {},
"structural_requirements": ["Protagonist must have privileged info others lack"],
"adaptable_without_breaking": ["Royal status", "Era", "Ghost mechanism"],
"links": {
"clusters": ["revenge-tragedy.json"],
"similar_works": [],
"derived_syntheses": []
}
}Trope Cluster Schema
{
"_meta": {
"type": "trope-cluster",
"cluster_name": "bounty-hunter-scifi",
"description": "Episodic bounty/warrant structure in sci-fi setting"
},
"core_functions": {
"structural": ["Case-of-the-week provides episodic entry points"],
"character": ["Found family dynamics among crew"],
"emotional": ["Competence satisfaction"]
},
"required_elements": ["Mission structure", "Mobile base", "Team with complementary skills"],
"variance_axes": [
{"axis": "tone", "range": ["noir/melancholic", "action/humor"]}
],
"source_works": ["killjoys.json", "cowboy-bebop.json"],
"links": {
"parent_clusters": ["found-family.json"],
"overlapping_clusters": ["space-western.json"]
}
}Example Interaction
User: "I want to adapt Hamlet but set it in a corporate dystopia."
Your approach: 1. Diagnose state: EX0 (no extraction exists yet) 2. Begin emotional core extraction: "What do you think makes Hamlet work? What do people love about it?" 3. Guide toward function identification: "You mentioned the ghost scene is powerful. Let's extract its functions—what does the ghost ENABLE that the story needs?" 4. Challenge surface readings: "You said 'he's a prince.' What does being a prince DO in this story? What pressures does it create?" 5. Build extraction document iteratively 6. Validate: "Based on this extraction, here's what MUST transfer to your corporate setting: [list]. Here's what's adaptable: [list]." 7. Hand off to adaptation-synthesis when EX7 reached
What You Do NOT Do
- You do not accept plot summaries as extractions
- You do not skip to synthesis before extraction is complete
- You do not treat all elements as equally essential
- You do not confuse forms with functions
- You do not extract without identifying emotional core first
- You extract the DNA; the user decides what to adapt
Output Persistence
Output Discovery
Before extracting:
1. Check for dna-library/ in the project 2. If not found, ask: "Where should I save extraction output? Suggest: dna-library/extractions/" 3. Store preference in context/output-config.md if context network exists
Primary Output
For this skill, persist:
- Extraction documents - JSON files in
dna-library/extractions/ - Cluster documents - JSON files in
dna-library/clusters/ - Emotional beat maps - Part of extraction or separate analysis files
Conversation vs. File
| Goes to File | Stays in Conversation |
|---|---|
| Completed extraction JSON | Iterative extraction discussion |
| Beat map data | Questions about specific elements |
| Cluster definitions | State diagnosis |
| Validation results | "Why does this element matter?" dialogue |
Integration Graph
Inbound (From Other Skills)
| Source Skill | Source State | Leads to State |
|---|---|---|
| story-sense | SS7: Ready for Evaluation | EX0: analyze existing work |
| genre-conventions | Genre identified | EX3: use for emotional core |
Outbound (To Other Skills)
| This State | Leads to Skill | Target State |
|---|---|---|
| EX3: Missing Emotional Core | genre-conventions | G1: identify genre |
| EX7: Extraction Complete | adaptation-synthesis | SYN1: DNA Ready |
| EX5: Missing Relationships | character-arc | analyze character dynamics |
Complementary Skills
| Skill | Relationship |
|---|---|
| cliche-transcendence | Orthogonality principle for testing adaptations |
| genre-conventions | Elemental genres for emotional core |
| character-arc | Lie/Want/Need structure for character functions |
| story-sense | Diagnostic states for analyzing existing works |
{
"_meta": {
"description": "Templates for DNA extraction at different depth levels",
"usage": "Select appropriate template based on extraction goal and available time"
},
"depth_levels": {
"quick": {
"description": "Fast extraction for exploration and comparison",
"time_estimate": "15-30 minutes per work",
"use_cases": [
"Comparing multiple works to find best adaptation source",
"Quick feasibility check for adaptation idea",
"Building initial trope cluster from multiple sources"
],
"required_sections": [
"emotional_core",
"primary_characters",
"core_structural_requirements"
],
"optional_sections": [],
"depth_guidance": {
"characters": "Main 3-5 only, primary function per character",
"plot": "Core structure only, no scene-level detail",
"tone": "Baseline only, no shift mapping",
"relationships": "Key dynamics only"
}
},
"standard": {
"description": "Full extraction for most adaptation projects",
"time_estimate": "1-3 hours per work",
"use_cases": [
"Serious adaptation project",
"Creating reusable trope map",
"Understanding what makes a beloved work function"
],
"required_sections": [
"emotional_core",
"tone",
"characters",
"plot_structures",
"relationships",
"structural_requirements",
"adaptable_elements"
],
"optional_sections": [
"episode_structures",
"dialogue_patterns"
],
"depth_guidance": {
"characters": "All significant characters, full six-axis extraction",
"plot": "Major plot structures and turning points",
"tone": "Full tonal profile with shift triggers",
"relationships": "All significant character pairings"
}
},
"detailed": {
"description": "Deep extraction for serious long-form adaptation",
"time_estimate": "4-8 hours per work",
"use_cases": [
"Adaptation that must capture original's exact feel",
"Academic analysis",
"Creating definitive trope reference"
],
"required_sections": [
"emotional_core",
"tone",
"characters",
"plot_structures",
"relationships",
"structural_requirements",
"adaptable_elements",
"episode_structures",
"dialogue_patterns",
"scene_functions"
],
"optional_sections": [],
"depth_guidance": {
"characters": "All characters including minor, full extraction plus voice patterns",
"plot": "Scene-level function mapping",
"tone": "Episode-by-episode tonal tracking",
"relationships": "All pairings plus evolution over time"
}
}
},
"work_extraction_template": {
"_meta": {
"type": "work-extraction",
"source_work": "",
"source_author": "",
"source_medium": "",
"extraction_date": "",
"extraction_depth": "standard",
"clusters": []
},
"emotional_core": {
"primary_genre": "",
"secondary_genres": [],
"emotional_experience": "",
"emotional_beats": []
},
"tone": {
"sincerity_level": "",
"humor_mode": "",
"emotional_expression": "",
"dialogue_density": "",
"conflict_style": "",
"baseline_tone": "",
"tonal_shifts": []
},
"characters": {},
"plot_structures": {},
"relationships": {},
"structural_requirements": [],
"adaptable_without_breaking": [],
"links": {
"clusters": [],
"similar_works": [],
"derived_syntheses": []
}
},
"character_template": {
"form": "",
"functions": {
"structural": [],
"character": [],
"emotional": [],
"thematic": [],
"relational": []
},
"voice": {
"speech_patterns": "",
"verbal_tics": "",
"formality_level": "",
"subtext_tendency": ""
},
"structural_necessity": "",
"adaptable_elements": []
},
"plot_structure_template": {
"form": "",
"functions": {
"structural": [],
"character": [],
"emotional": [],
"thematic": []
},
"timing": "",
"structural_necessity": "",
"adaptable_elements": []
},
"relationship_template": {
"characters": [],
"form": "",
"functions": {
"structural": [],
"character": [],
"emotional": [],
"thematic": []
},
"evolution": [],
"structural_necessity": ""
},
"cluster_template": {
"_meta": {
"type": "trope-cluster",
"cluster_name": "",
"description": ""
},
"core_functions": {
"structural": [],
"character": [],
"emotional": [],
"thematic": []
},
"required_elements": [],
"common_forms": [],
"variance_axes": [],
"tonal_range": {
"dimension": "",
"examples": []
},
"source_works": [],
"links": {
"parent_clusters": [],
"child_clusters": [],
"overlapping_clusters": []
}
},
"medium_specific_guidance": {
"film": {
"structure_notes": "Three-act structure common, look for midpoint shift",
"character_notes": "Limited cast, clear protagonist, compressed arcs",
"tone_notes": "Consistent tone throughout, genre usually clear",
"special_considerations": [
"Visual storytelling functions (what's shown vs. told)",
"Pacing compressed compared to novels",
"Music/sound as emotional function"
]
},
"television_episodic": {
"structure_notes": "Episode-level A/B plots, season-level arcs",
"character_notes": "Larger ensemble, slower character development",
"tone_notes": "May shift between episodes, find baseline",
"special_considerations": [
"Case-of-the-week vs. serialized structure",
"Character consistency across writers",
"Bottle episodes as character deep-dives"
]
},
"television_serialized": {
"structure_notes": "Long-form narrative, season as act",
"character_notes": "Extended arcs, major character changes possible",
"tone_notes": "Tonal evolution over seasons common",
"special_considerations": [
"Setup/payoff across seasons",
"Character evolution vs. consistency",
"Genre may shift between seasons"
]
},
"novel": {
"structure_notes": "Variable structure, may not follow three-act",
"character_notes": "Internal access, POV shapes understanding",
"tone_notes": "Narrative voice is major tonal element",
"special_considerations": [
"POV as structural choice",
"Prose style as tone carrier",
"Pacing can vary dramatically"
]
},
"stage_play": {
"structure_notes": "Act structure varies, scene-based",
"character_notes": "Dialogue-forward, limited internal access",
"tone_notes": "Performance interprets tone, script suggests",
"special_considerations": [
"Soliloquy as internal access device",
"Stage directions as authorial intent",
"Single location constraints common"
]
},
"video_game": {
"structure_notes": "Player agency affects structure",
"character_notes": "Player character may be blank slate",
"tone_notes": "Gameplay tone vs. narrative tone",
"special_considerations": [
"Ludonarrative consonance/dissonance",
"Player as protagonist vs. observer",
"Emergent narrative vs. authored narrative"
]
}
},
"extraction_checklist": {
"quick": [
"Identified primary emotional experience",
"Named primary genre",
"Listed 3-5 main characters with primary function each",
"Identified core structural requirements",
"Listed what's clearly adaptable"
],
"standard": [
"Completed quick checklist",
"Extracted all six axes for main characters",
"Documented tone profile",
"Mapped major plot structures with functions",
"Documented key relationships",
"Classified structural vs. stylistic for major elements",
"Established function hierarchy",
"Identified cluster memberships"
],
"detailed": [
"Completed standard checklist",
"Scene-level function mapping",
"Character voice patterns documented",
"Episode/chapter structure analyzed",
"Dialogue pattern analysis",
"Tonal shift triggers mapped",
"Minor character functions documented",
"Evolution of relationships tracked"
]
}
}
{
"_meta": {
"description": "Taxonomy of story functions for DNA extraction. Functions are what elements DO, not what they ARE.",
"usage": "Use during extraction to ensure comprehensive coverage across all function types"
},
"structural_functions": {
"description": "What the element enables in plot mechanics",
"categories": {
"information_control": [
"Provides privileged information to protagonist",
"Creates dramatic irony (audience knows, characters don't)",
"Reveals hidden truth at critical moment",
"Conceals information that would resolve conflict prematurely",
"Creates mystery that drives investigation",
"Delivers exposition without feeling like exposition"
],
"causation": [
"Incites the central conflict",
"Enables the climax to occur",
"Creates necessary obstacles",
"Provides the means of resolution",
"Forces protagonist to act",
"Creates point of no return"
],
"constraint": [
"Limits protagonist's options",
"Creates time pressure",
"Establishes stakes",
"Prevents easy solutions",
"Traps characters together",
"Makes retreat impossible"
],
"escalation": [
"Raises stakes progressively",
"Complicates existing problems",
"Introduces new threats",
"Removes safety nets",
"Accelerates toward climax",
"Creates cascading consequences"
]
}
},
"character_functions": {
"description": "What the element enables in character journeys",
"categories": {
"arc_catalysts": [
"Forces confrontation with character's lie",
"Creates the want that drives action",
"Reveals the need beneath the want",
"Triggers transformation moment",
"Tests the new self",
"Provides mirror showing who character could become"
],
"relationship_roles": [
"Serves as foil to protagonist",
"Represents the path not taken",
"Grounds protagonist in reality",
"Challenges protagonist's worldview",
"Provides unconditional support",
"Creates impossible choice between loyalties"
],
"revelation_vehicles": [
"Forces character to reveal true self",
"Creates situation where masks must drop",
"Tests stated values against behavior",
"Exposes hypocrisy or hidden virtue",
"Makes internal conflict external"
]
}
},
"emotional_functions": {
"description": "What the element makes the audience feel",
"categories": {
"genre_delivery": [
"Delivers primary genre promise (wonder, dread, tension, etc.)",
"Provides emotional peaks",
"Creates emotional valleys for contrast",
"Sustains baseline emotional tone",
"Signals genre to audience",
"Subverts genre expectations purposefully"
],
"audience_relationship": [
"Creates sympathy for character",
"Generates anticipation",
"Provides catharsis",
"Creates satisfying payoff for setup",
"Delivers earned emotional moment",
"Surprises while feeling inevitable"
],
"tension_management": [
"Builds tension gradually",
"Provides tension release",
"Creates false resolution",
"Delays gratification",
"Rewards patience",
"Punishes premature hope"
]
}
},
"thematic_functions": {
"description": "What ideas the element explores or embodies",
"categories": {
"question_posing": [
"Embodies the central thematic question",
"Presents one possible answer",
"Complicates simple answers",
"Shows consequences of different choices",
"Creates moral ambiguity",
"Forces audience to take a position"
],
"value_testing": [
"Tests whether stated values hold under pressure",
"Creates cost for living by values",
"Shows what happens when values are abandoned",
"Presents competing valid values",
"Demonstrates value in action vs. theory"
],
"symbolic_weight": [
"Represents larger concept through specific instance",
"Creates resonant imagery",
"Embodies theme in physical form",
"Links to universal human experience",
"Carries cultural/mythic significance"
]
}
},
"relational_functions": {
"description": "What dynamics the element creates between other elements",
"categories": {
"contrast_creation": [
"Defines one character by opposition to another",
"Shows what protagonist could become",
"Shows what protagonist refuses to become",
"Highlights difference through similarity",
"Creates meaningful comparison"
],
"tension_generation": [
"Creates conflict between characters",
"Establishes competing goals",
"Forces characters into proximity",
"Creates secrets between characters",
"Generates jealousy, resentment, desire"
],
"web_building": [
"Links characters who wouldn't naturally connect",
"Creates obligations between characters",
"Establishes history between characters",
"Generates shared experience",
"Creates dependency relationships"
]
}
},
"tonal_functions": {
"description": "How the element contributes to the work's feel and voice",
"categories": {
"register_setting": [
"Establishes sincerity vs. irony baseline",
"Defines how characters express emotion",
"Sets humor mode and frequency",
"Determines dialogue density",
"Establishes conflict expression style"
],
"voice_distinction": [
"Gives character unique speech patterns",
"Creates recognizable verbal tics",
"Establishes character's relationship to language",
"Shows education, class, culture through speech",
"Differentiates characters aurally"
],
"tonal_navigation": [
"Signals tonal shifts",
"Provides permission for tonal change",
"Anchors return to baseline tone",
"Creates tonal contrast for emphasis",
"Manages audience emotional state"
]
}
},
"form_categories": {
"description": "Common surface forms that can serve multiple functions",
"categories": {
"character_types": [
"Mentor figure",
"Antagonist",
"Foil",
"Love interest",
"Comic relief",
"Innocent to protect",
"Authority figure",
"Trickster",
"Herald/messenger"
],
"setting_types": [
"Home base",
"Enemy territory",
"Neutral ground",
"Liminal space",
"Prison/trap",
"Safe haven",
"Corrupted paradise"
],
"plot_devices": [
"MacGuffin",
"Ticking clock",
"Secret identity",
"Mistaken identity",
"Prophecy/destiny",
"Forbidden knowledge",
"Inheritance/legacy"
],
"relationship_structures": [
"Mentor-student",
"Rivals",
"Star-crossed lovers",
"Found family",
"Reluctant allies",
"Secret allies",
"Betrayer-betrayed"
]
}
},
"extraction_questions": {
"description": "Questions to ask when extracting each function type",
"structural": [
"What would break in the plot if we removed this?",
"What information does this control?",
"What does this force to happen?",
"What does this prevent from happening?"
],
"character": [
"What does this reveal about the character?",
"How does this challenge or change the character?",
"What choice does this force?",
"Who does this character illuminate through contrast?"
],
"emotional": [
"What should the audience feel here?",
"How does this serve the genre promise?",
"Is this a peak, valley, or sustain moment?",
"What would be lost emotionally without this?"
],
"thematic": [
"What question does this raise?",
"What value does this test?",
"What does this symbolize?",
"How does this complicate the theme?"
],
"relational": [
"What connection does this create or strengthen?",
"What tension does this generate?",
"How does this change the relationship web?",
"What contrast does this establish?"
],
"tonal": [
"How does this affect the work's feel?",
"What emotional register does this establish?",
"How does this character's voice differ from others?",
"Does this shift the tone? How?"
]
}
}
#!/usr/bin/env -S deno run --allow-read
/**
* Emotional Beat Map
*
* Maps emotional peaks and valleys across a work's timeline.
* Helps identify the emotional experience the work delivers.
*
* Usage:
* deno run --allow-read scripts/emotional-beat-map.ts "Work Name" --acts 5
* deno run --allow-read scripts/emotional-beat-map.ts "Work Name" --episodes 10
* deno run --allow-read scripts/emotional-beat-map.ts --compare drama,thriller
*/
// === INTERFACES ===
interface EmotionalBeat {
position: number;
emotion: string;
intensity: number; // 1-10
element: string;
beat_type: "peak" | "valley" | "sustain" | "shift";
}
interface BeatMap {
work_name: string;
structure_type: "acts" | "episodes" | "chapters";
unit_count: number;
primary_genre: string;
beats: EmotionalBeat[];
analysis: {
peaks: number[];
valleys: number[];
dominant_emotions: string[];
pacing_notes: string;
};
}
interface GenreExpectations {
name: string;
expected_peaks: number[];
expected_emotions: string[];
pacing_pattern: string;
key_beats: { position: number; description: string }[];
}
// === DATA ===
const GENRE_EXPECTATIONS: Record<string, GenreExpectations> = {
wonder: {
name: "Wonder",
expected_peaks: [0.1, 0.5, 0.9],
expected_emotions: ["awe", "curiosity", "amazement", "discovery"],
pacing_pattern: "Builds through revelations, peaks at major discoveries",
key_beats: [
{ position: 0.1, description: "Initial wonder hook - something fascinating" },
{ position: 0.5, description: "Major revelation that reframes understanding" },
{ position: 0.9, description: "Final perspective shift or scale revelation" }
]
},
mystery: {
name: "Mystery",
expected_peaks: [0.25, 0.5, 0.75, 0.95],
expected_emotions: ["curiosity", "suspicion", "confusion", "satisfaction"],
pacing_pattern: "Clue distribution with rising tension toward revelation",
key_beats: [
{ position: 0.1, description: "Crime/mystery established" },
{ position: 0.25, description: "First major clue" },
{ position: 0.5, description: "Major complication or red herring" },
{ position: 0.75, description: "All pieces available (reader could solve)" },
{ position: 0.95, description: "Revelation and explanation" }
]
},
thriller: {
name: "Thriller",
expected_peaks: [0.15, 0.4, 0.6, 0.8, 0.95],
expected_emotions: ["tension", "fear", "urgency", "relief"],
pacing_pattern: "Escalating tension with brief relief valleys",
key_beats: [
{ position: 0.15, description: "Danger established, stakes clear" },
{ position: 0.4, description: "First major setback" },
{ position: 0.6, description: "Point of no return" },
{ position: 0.8, description: "Darkest moment / all seems lost" },
{ position: 0.95, description: "Final confrontation and resolution" }
]
},
horror: {
name: "Horror",
expected_peaks: [0.2, 0.5, 0.7, 0.9],
expected_emotions: ["unease", "dread", "fear", "terror", "horror"],
pacing_pattern: "Building dread punctuated by terror spikes",
key_beats: [
{ position: 0.1, description: "Normality before disruption" },
{ position: 0.2, description: "First sign something is wrong" },
{ position: 0.5, description: "Nature of threat becomes clear" },
{ position: 0.7, description: "Escape attempts fail, vulnerability maximum" },
{ position: 0.9, description: "Final confrontation with horror" }
]
},
drama: {
name: "Drama",
expected_peaks: [0.25, 0.5, 0.75, 0.95],
expected_emotions: ["sympathy", "tension", "catharsis", "transformation"],
pacing_pattern: "Internal pressure building to transformation",
key_beats: [
{ position: 0.1, description: "Character's lie/flaw established" },
{ position: 0.25, description: "Inciting pressure on the lie" },
{ position: 0.5, description: "Midpoint - glimpse of truth or false victory" },
{ position: 0.75, description: "Dark night - lie confronted fully" },
{ position: 0.95, description: "Transformation or tragic fall" }
]
},
adventure: {
name: "Adventure",
expected_peaks: [0.15, 0.35, 0.55, 0.75, 0.95],
expected_emotions: ["excitement", "danger", "triumph", "wonder"],
pacing_pattern: "Action peaks with brief recovery valleys",
key_beats: [
{ position: 0.1, description: "Call to adventure" },
{ position: 0.25, description: "First threshold crossed" },
{ position: 0.5, description: "Major challenge overcome" },
{ position: 0.75, description: "Supreme ordeal" },
{ position: 0.95, description: "Victory and return" }
]
},
relationship: {
name: "Relationship",
expected_peaks: [0.2, 0.5, 0.8, 0.95],
expected_emotions: ["longing", "connection", "conflict", "resolution"],
pacing_pattern: "Connection-conflict cycles building to resolution",
key_beats: [
{ position: 0.15, description: "Meet cute / initial connection" },
{ position: 0.35, description: "Growing intimacy" },
{ position: 0.5, description: "Major obstacle or conflict" },
{ position: 0.75, description: "Dark moment / seeming end" },
{ position: 0.95, description: "Resolution (together or apart)" }
]
},
humor: {
name: "Humor",
expected_peaks: [0.1, 0.3, 0.5, 0.7, 0.9],
expected_emotions: ["amusement", "surprise", "delight", "release"],
pacing_pattern: "Regular comedic peaks with escalating absurdity",
key_beats: [
{ position: 0.1, description: "Comedic premise established" },
{ position: 0.35, description: "Complications compound hilariously" },
{ position: 0.6, description: "Situation reaches absurd extreme" },
{ position: 0.8, description: "Darkest/most awkward moment" },
{ position: 0.95, description: "Resolution with final laugh" }
]
}
};
const EMOTION_VOCABULARY = {
positive: [
"joy", "triumph", "relief", "hope", "love", "awe", "wonder",
"satisfaction", "connection", "excitement", "delight", "peace"
],
negative: [
"fear", "dread", "horror", "grief", "anger", "despair", "tension",
"anxiety", "disgust", "shame", "loneliness", "betrayal"
],
complex: [
"bittersweet", "melancholy", "anticipation", "unease", "catharsis",
"ambivalence", "longing", "resignation", "defiance", "transformation"
]
};
// === UTILITIES ===
function createEmptyBeatMap(workName: string, structureType: "acts" | "episodes" | "chapters", unitCount: number): BeatMap {
return {
work_name: workName,
structure_type: structureType,
unit_count: unitCount,
primary_genre: "",
beats: [],
analysis: {
peaks: [],
valleys: [],
dominant_emotions: [],
pacing_notes: ""
}
};
}
function formatBeatMap(map: BeatMap): string {
const lines: string[] = [];
lines.push(`# Emotional Beat Map: ${map.work_name}`);
lines.push("");
lines.push(`**Structure:** ${map.unit_count} ${map.structure_type}`);
lines.push(`**Primary Genre:** ${map.primary_genre || "(not set)"}`);
lines.push("");
// Visual timeline
lines.push("## Timeline");
lines.push("");
lines.push("```");
lines.push("Position 0% 25% 50% 75% 100%");
lines.push(" |-----|-----|-----|-----|");
if (map.beats.length > 0) {
// Sort beats by position
const sorted = [...map.beats].sort((a, b) => a.position - b.position);
for (const beat of sorted) {
const pos = Math.round(beat.position * 100);
const marker = beat.beat_type === "peak" ? "^" : beat.beat_type === "valley" ? "v" : "-";
const spacer = " ".repeat(Math.max(0, Math.floor(pos / 5)));
lines.push(`${pos.toString().padStart(3)}%${spacer}${marker} ${beat.emotion} (${beat.element})`);
}
} else {
lines.push("(No beats mapped yet)");
}
lines.push("```");
lines.push("");
// Beat details
if (map.beats.length > 0) {
lines.push("## Beat Details");
lines.push("");
lines.push("| Position | Type | Emotion | Intensity | Element |");
lines.push("|----------|------|---------|-----------|---------|");
for (const beat of map.beats.sort((a, b) => a.position - b.position)) {
const pos = `${Math.round(beat.position * 100)}%`;
lines.push(`| ${pos} | ${beat.beat_type} | ${beat.emotion} | ${beat.intensity}/10 | ${beat.element} |`);
}
lines.push("");
}
// Analysis
if (map.analysis.pacing_notes) {
lines.push("## Analysis");
lines.push("");
lines.push(map.analysis.pacing_notes);
lines.push("");
}
return lines.join("\n");
}
function formatGenreComparison(genres: string[]): string {
const lines: string[] = [];
lines.push("# Genre Emotional Expectations Comparison");
lines.push("");
for (const genre of genres) {
const expectations = GENRE_EXPECTATIONS[genre.toLowerCase()];
if (!expectations) {
lines.push(`## ${genre}`);
lines.push("(Unknown genre - no expectations defined)");
lines.push("");
continue;
}
lines.push(`## ${expectations.name}`);
lines.push("");
lines.push(`**Pacing Pattern:** ${expectations.pacing_pattern}`);
lines.push("");
lines.push(`**Expected Emotions:** ${expectations.expected_emotions.join(", ")}`);
lines.push("");
lines.push("**Key Beats:**");
for (const beat of expectations.key_beats) {
lines.push(`- ${Math.round(beat.position * 100)}%: ${beat.description}`);
}
lines.push("");
}
// Side-by-side if multiple genres
if (genres.length > 1) {
lines.push("## Comparison Grid");
lines.push("");
lines.push("| Position | " + genres.map(g => GENRE_EXPECTATIONS[g.toLowerCase()]?.name || g).join(" | ") + " |");
lines.push("|----------|" + genres.map(() => "---").join("|") + "|");
const positions = [0.1, 0.25, 0.35, 0.5, 0.6, 0.75, 0.8, 0.9, 0.95];
for (const pos of positions) {
const cells = genres.map(g => {
const exp = GENRE_EXPECTATIONS[g.toLowerCase()];
if (!exp) return "-";
const beat = exp.key_beats.find(b => Math.abs(b.position - pos) < 0.05);
return beat ? beat.description.substring(0, 30) : "-";
});
lines.push(`| ${Math.round(pos * 100)}% | ${cells.join(" | ")} |`);
}
lines.push("");
}
return lines.join("\n");
}
function formatTemplate(workName: string, structureType: "acts" | "episodes" | "chapters", unitCount: number): string {
const lines: string[] = [];
lines.push(`# Emotional Beat Map Template: ${workName}`);
lines.push("");
lines.push(`Structure: ${unitCount} ${structureType}`);
lines.push("");
lines.push("## Instructions");
lines.push("");
lines.push("For each significant emotional moment, record:");
lines.push("- Position (0.0 - 1.0)");
lines.push("- Emotion (what audience should feel)");
lines.push("- Intensity (1-10)");
lines.push("- Element (what causes this emotion)");
lines.push("- Type: peak (high intensity), valley (low/recovery), sustain, shift");
lines.push("");
lines.push("## Emotion Vocabulary");
lines.push("");
lines.push("**Positive:** " + EMOTION_VOCABULARY.positive.join(", "));
lines.push("");
lines.push("**Negative:** " + EMOTION_VOCABULARY.negative.join(", "));
lines.push("");
lines.push("**Complex:** " + EMOTION_VOCABULARY.complex.join(", "));
lines.push("");
lines.push("## Beat Recording Template");
lines.push("");
// Generate position markers based on structure
const increment = 1 / unitCount;
for (let i = 0; i <= unitCount; i++) {
const pos = i * increment;
const label = structureType === "acts" ? `Act ${i + 1} start` :
structureType === "episodes" ? `Episode ${i + 1}` :
`Chapter ${i + 1}`;
lines.push(`### ${Math.round(pos * 100)}% - ${label}`);
lines.push("");
lines.push("| Emotion | Intensity | Element | Type |");
lines.push("|---------|-----------|---------|------|");
lines.push("| | | | |");
lines.push("");
}
return lines.join("\n");
}
// === MAIN ===
function main(): void {
const args = Deno.args;
// Help
if (args.includes("--help") || args.includes("-h")) {
console.log(`Emotional Beat Map - Timeline Emotional Mapping
Usage:
deno run --allow-read scripts/emotional-beat-map.ts "Work Name" --acts 5
deno run --allow-read scripts/emotional-beat-map.ts "Work Name" --episodes 10
deno run --allow-read scripts/emotional-beat-map.ts "Work Name" --chapters 20
deno run --allow-read scripts/emotional-beat-map.ts --compare drama,thriller
deno run --allow-read scripts/emotional-beat-map.ts --genres
Options:
--acts <n> Structure as n acts (default for plays)
--episodes <n> Structure as n episodes (default for TV)
--chapters <n> Structure as n chapters (default for novels)
--compare <list> Compare genre expectations (comma-separated)
--genres List available genres with expectations
--json Output as JSON
Examples:
# Five-act play template
deno run --allow-read scripts/emotional-beat-map.ts "Hamlet" --acts 5
# TV season template
deno run --allow-read scripts/emotional-beat-map.ts "Killjoys S1" --episodes 10
# Compare drama and thriller expectations
deno run --allow-read scripts/emotional-beat-map.ts --compare drama,thriller
`);
Deno.exit(0);
}
const isJson = args.includes("--json");
// Handle --genres flag
if (args.includes("--genres")) {
const genres = Object.keys(GENRE_EXPECTATIONS);
if (isJson) {
console.log(JSON.stringify(GENRE_EXPECTATIONS, null, 2));
} else {
console.log("Available genres with expectations:");
for (const genre of genres) {
const exp = GENRE_EXPECTATIONS[genre];
console.log(` ${exp.name}: ${exp.pacing_pattern}`);
}
}
return;
}
// Handle --compare flag
const compareIndex = args.indexOf("--compare");
if (compareIndex !== -1) {
const genreList = args[compareIndex + 1];
if (!genreList) {
console.error("Error: --compare requires comma-separated genre list");
Deno.exit(1);
}
const genres = genreList.split(",").map(g => g.trim());
if (isJson) {
const comparison: Record<string, GenreExpectations | null> = {};
for (const g of genres) {
comparison[g] = GENRE_EXPECTATIONS[g.toLowerCase()] || null;
}
console.log(JSON.stringify(comparison, null, 2));
} else {
console.log(formatGenreComparison(genres));
}
return;
}
// Parse structure flags
const actsIndex = args.indexOf("--acts");
const episodesIndex = args.indexOf("--episodes");
const chaptersIndex = args.indexOf("--chapters");
let structureType: "acts" | "episodes" | "chapters" = "acts";
let unitCount = 5;
if (actsIndex !== -1 && args[actsIndex + 1]) {
structureType = "acts";
unitCount = parseInt(args[actsIndex + 1]);
} else if (episodesIndex !== -1 && args[episodesIndex + 1]) {
structureType = "episodes";
unitCount = parseInt(args[episodesIndex + 1]);
} else if (chaptersIndex !== -1 && args[chaptersIndex + 1]) {
structureType = "chapters";
unitCount = parseInt(args[chaptersIndex + 1]);
}
// Find work name (positional argument)
const skipIndices = new Set<number>();
for (const idx of [actsIndex, episodesIndex, chaptersIndex, compareIndex]) {
if (idx !== -1) {
skipIndices.add(idx);
skipIndices.add(idx + 1);
}
}
let workName: string | null = null;
for (let i = 0; i < args.length; i++) {
if (!args[i].startsWith("--") && !skipIndices.has(i)) {
workName = args[i];
break;
}
}
if (!workName) {
console.error("Error: Please provide a work name");
console.error("Usage: deno run --allow-read scripts/emotional-beat-map.ts \"Work Name\" --acts 5");
Deno.exit(1);
}
// Generate template
if (isJson) {
const map = createEmptyBeatMap(workName, structureType, unitCount);
console.log(JSON.stringify(map, null, 2));
} else {
console.log(formatTemplate(workName, structureType, unitCount));
}
}
main();
#!/usr/bin/env -S deno run --allow-read --allow-write
/**
* Extract Functions
*
* Interactive questionnaire for DNA extraction from existing works.
* Guides through six-axis function analysis at configurable depth.
*
* Usage:
* deno run --allow-read --allow-write scripts/extract-functions.ts "Work Name"
* deno run --allow-read --allow-write scripts/extract-functions.ts "Work Name" --depth quick
* deno run --allow-read --allow-write scripts/extract-functions.ts --element "The Ghost"
* deno run --allow-read --allow-write scripts/extract-functions.ts --validate extraction.json
* deno run --allow-read --allow-write scripts/extract-functions.ts --template
*/
// === INTERFACES ===
interface EmotionalBeat {
position: number;
emotion: string;
element: string;
}
interface TonalShift {
trigger: string;
shift: string;
}
interface CharacterVoice {
speech_patterns: string;
verbal_tics: string;
formality_level: string;
subtext_tendency: string;
}
interface CharacterFunctions {
structural: string[];
character: string[];
emotional: string[];
thematic: string[];
relational: string[];
}
interface Character {
form: string;
functions: CharacterFunctions;
voice?: CharacterVoice;
structural_necessity: string;
adaptable_elements: string[];
}
interface PlotStructure {
form: string;
functions: {
structural: string[];
character: string[];
emotional: string[];
thematic: string[];
};
timing: string;
structural_necessity: string;
adaptable_elements: string[];
}
interface Relationship {
characters: string[];
form: string;
functions: {
structural: string[];
character: string[];
emotional: string[];
thematic: string[];
};
evolution: string[];
structural_necessity: string;
}
interface Tone {
sincerity_level: string;
humor_mode: string;
emotional_expression: string;
dialogue_density: string;
conflict_style: string;
baseline_tone: string;
tonal_shifts: TonalShift[];
}
interface EmotionalCore {
primary_genre: string;
secondary_genres: string[];
emotional_experience: string;
emotional_beats: EmotionalBeat[];
}
interface ExtractionMeta {
type: "work-extraction";
source_work: string;
source_author: string;
source_medium: string;
extraction_date: string;
extraction_depth: "quick" | "standard" | "detailed";
clusters: string[];
}
interface ExtractionLinks {
clusters: string[];
similar_works: string[];
derived_syntheses: string[];
}
interface WorkExtraction {
_meta: ExtractionMeta;
emotional_core: EmotionalCore;
tone: Tone;
characters: Record<string, Character>;
plot_structures: Record<string, PlotStructure>;
relationships: Record<string, Relationship>;
structural_requirements: string[];
adaptable_without_breaking: string[];
links: ExtractionLinks;
}
interface ElementExtraction {
element_name: string;
element_type: "character" | "plot" | "setting" | "relationship" | "other";
form: string;
functions: {
structural: string[];
character: string[];
emotional: string[];
thematic: string[];
relational: string[];
};
structural_necessity: "high" | "medium" | "low";
adaptable_elements: string[];
}
type Depth = "quick" | "standard" | "detailed";
// === DATA ===
const EXTRACTION_QUESTIONS = {
structural: [
"What would break in the plot if we removed this?",
"What information does this control or reveal?",
"What does this force to happen?",
"What does this prevent from happening prematurely?"
],
character: [
"What does this reveal about any character?",
"How does this challenge or change a character?",
"What choice does this force?",
"Who does this illuminate through contrast?"
],
emotional: [
"What should the audience feel here?",
"How does this serve the genre promise?",
"Is this a peak, valley, or sustain moment?",
"What would be lost emotionally without this?"
],
thematic: [
"What question does this raise or address?",
"What value does this test?",
"What does this symbolize?",
"How does this complicate or reinforce the theme?"
],
relational: [
"What connection does this create or strengthen?",
"What tension does this generate between characters?",
"How does this change the relationship web?",
"What contrast does this establish?"
]
};
const GENRE_OPTIONS = [
"wonder", "idea", "adventure", "horror", "mystery",
"thriller", "humor", "relationship", "drama", "issue", "ensemble"
];
const SINCERITY_OPTIONS = [
"high (earnest, characters mean what they say)",
"medium (mix of sincere and ironic)",
"low (ironic, detached, subtext-heavy)"
];
const HUMOR_MODES = [
"banter/quips", "deadpan", "physical/slapstick", "dark/gallows",
"absurdist", "satirical", "minimal/serious"
];
const CONFLICT_STYLES = [
"verbal sparring", "cold silence", "explosive outbursts",
"passive aggression", "physical violence", "avoidant/deflecting"
];
// === UTILITIES ===
function formatDate(): string {
const now = new Date();
return now.toISOString().split("T")[0];
}
function createEmptyExtraction(workName: string, depth: Depth): WorkExtraction {
return {
_meta: {
type: "work-extraction",
source_work: workName,
source_author: "",
source_medium: "",
extraction_date: formatDate(),
extraction_depth: depth,
clusters: []
},
emotional_core: {
primary_genre: "",
secondary_genres: [],
emotional_experience: "",
emotional_beats: []
},
tone: {
sincerity_level: "",
humor_mode: "",
emotional_expression: "",
dialogue_density: "",
conflict_style: "",
baseline_tone: "",
tonal_shifts: []
},
characters: {},
plot_structures: {},
relationships: {},
structural_requirements: [],
adaptable_without_breaking: [],
links: {
clusters: [],
similar_works: [],
derived_syntheses: []
}
};
}
function createEmptyElement(name: string, type: ElementExtraction["element_type"]): ElementExtraction {
return {
element_name: name,
element_type: type,
form: "",
functions: {
structural: [],
character: [],
emotional: [],
thematic: [],
relational: []
},
structural_necessity: "medium",
adaptable_elements: []
};
}
function formatExtraction(extraction: WorkExtraction): string {
const lines: string[] = [];
lines.push("# DNA Extraction: " + extraction._meta.source_work);
lines.push("");
lines.push(`**Author:** ${extraction._meta.source_author || "(not specified)"}`);
lines.push(`**Medium:** ${extraction._meta.source_medium || "(not specified)"}`);
lines.push(`**Extraction Depth:** ${extraction._meta.extraction_depth}`);
lines.push(`**Date:** ${extraction._meta.extraction_date}`);
lines.push("");
// Emotional Core
lines.push("## Emotional Core");
lines.push("");
lines.push(`**Primary Genre:** ${extraction.emotional_core.primary_genre || "(not identified)"}`);
if (extraction.emotional_core.secondary_genres.length > 0) {
lines.push(`**Secondary Genres:** ${extraction.emotional_core.secondary_genres.join(", ")}`);
}
lines.push("");
lines.push(`**Emotional Experience:** ${extraction.emotional_core.emotional_experience || "(not described)"}`);
lines.push("");
if (extraction.emotional_core.emotional_beats.length > 0) {
lines.push("### Emotional Beats");
for (const beat of extraction.emotional_core.emotional_beats) {
lines.push(`- **${Math.round(beat.position * 100)}%:** ${beat.emotion} (${beat.element})`);
}
lines.push("");
}
// Tone
if (extraction.tone.baseline_tone) {
lines.push("## Tone Profile");
lines.push("");
lines.push(`**Baseline:** ${extraction.tone.baseline_tone}`);
lines.push(`**Sincerity:** ${extraction.tone.sincerity_level}`);
lines.push(`**Humor Mode:** ${extraction.tone.humor_mode}`);
lines.push(`**Emotional Expression:** ${extraction.tone.emotional_expression}`);
lines.push(`**Dialogue Density:** ${extraction.tone.dialogue_density}`);
lines.push(`**Conflict Style:** ${extraction.tone.conflict_style}`);
lines.push("");
}
// Characters
const charNames = Object.keys(extraction.characters);
if (charNames.length > 0) {
lines.push("## Characters");
lines.push("");
for (const name of charNames) {
const char = extraction.characters[name];
lines.push(`### ${name}`);
lines.push(`**Form:** ${char.form}`);
lines.push(`**Necessity:** ${char.structural_necessity}`);
lines.push("");
if (char.functions.structural.length > 0) {
lines.push("**Structural Functions:**");
for (const f of char.functions.structural) {
lines.push(`- ${f}`);
}
}
if (char.functions.character.length > 0) {
lines.push("**Character Functions:**");
for (const f of char.functions.character) {
lines.push(`- ${f}`);
}
}
if (char.functions.emotional.length > 0) {
lines.push("**Emotional Functions:**");
for (const f of char.functions.emotional) {
lines.push(`- ${f}`);
}
}
if (char.functions.relational.length > 0) {
lines.push("**Relational Functions:**");
for (const f of char.functions.relational) {
lines.push(`- ${f}`);
}
}
if (char.adaptable_elements.length > 0) {
lines.push(`**Adaptable:** ${char.adaptable_elements.join(", ")}`);
}
lines.push("");
}
}
// Structural Requirements
if (extraction.structural_requirements.length > 0) {
lines.push("## Structural Requirements");
lines.push("");
for (const req of extraction.structural_requirements) {
lines.push(`- ${req}`);
}
lines.push("");
}
// Adaptable Elements
if (extraction.adaptable_without_breaking.length > 0) {
lines.push("## Adaptable Without Breaking");
lines.push("");
for (const elem of extraction.adaptable_without_breaking) {
lines.push(`- ${elem}`);
}
lines.push("");
}
// Clusters
if (extraction._meta.clusters.length > 0) {
lines.push("## Cluster Memberships");
lines.push("");
for (const cluster of extraction._meta.clusters) {
lines.push(`- ${cluster}`);
}
lines.push("");
}
return lines.join("\n");
}
function formatElementExtraction(element: ElementExtraction): string {
const lines: string[] = [];
lines.push(`# Element Extraction: ${element.element_name}`);
lines.push("");
lines.push(`**Type:** ${element.element_type}`);
lines.push(`**Form:** ${element.form}`);
lines.push(`**Structural Necessity:** ${element.structural_necessity}`);
lines.push("");
const axes = ["structural", "character", "emotional", "thematic", "relational"] as const;
for (const axis of axes) {
const funcs = element.functions[axis];
if (funcs.length > 0) {
lines.push(`## ${axis.charAt(0).toUpperCase() + axis.slice(1)} Functions`);
for (const f of funcs) {
lines.push(`- ${f}`);
}
lines.push("");
}
}
if (element.adaptable_elements.length > 0) {
lines.push("## Adaptable Elements");
for (const elem of element.adaptable_elements) {
lines.push(`- ${elem}`);
}
}
return lines.join("\n");
}
function formatQuestionnaire(workName: string, depth: Depth): string {
const lines: string[] = [];
lines.push(`# DNA Extraction Questionnaire: ${workName}`);
lines.push(`**Depth:** ${depth}`);
lines.push("");
lines.push("---");
lines.push("");
// Emotional Core Questions
lines.push("## 1. Emotional Core");
lines.push("");
lines.push("### Primary Genre");
lines.push("What emotional experience does this work primarily deliver?");
lines.push(`Options: ${GENRE_OPTIONS.join(", ")}`);
lines.push("");
lines.push("**Your answer:**");
lines.push("");
lines.push("### Secondary Genres (if any)");
lines.push("What other emotional experiences does it layer in?");
lines.push("");
lines.push("**Your answer:**");
lines.push("");
lines.push("### Emotional Experience");
lines.push("In one sentence, what does someone who LOVES this work love about it?");
lines.push("");
lines.push("**Your answer:**");
lines.push("");
if (depth !== "quick") {
lines.push("### Emotional Beats");
lines.push("List 5-10 key emotional moments with approximate position (0.0-1.0):");
lines.push("Format: position | emotion | element");
lines.push("");
lines.push("**Your answer:**");
lines.push("");
}
// Tone Questions (standard+)
if (depth !== "quick") {
lines.push("---");
lines.push("");
lines.push("## 2. Tone Profile");
lines.push("");
lines.push("### Sincerity Level");
lines.push("How earnest vs. ironic is the work?");
for (const opt of SINCERITY_OPTIONS) {
lines.push(`- ${opt}`);
}
lines.push("");
lines.push("**Your answer:**");
lines.push("");
lines.push("### Humor Mode");
lines.push("When humor appears, what form does it take?");
lines.push(`Options: ${HUMOR_MODES.join(", ")}`);
lines.push("");
lines.push("**Your answer:**");
lines.push("");
lines.push("### Emotional Expression");
lines.push("How do characters show/hide their feelings?");
lines.push("(direct statement, subtext-heavy, action-reveals-feeling, denial/deflection)");
lines.push("");
lines.push("**Your answer:**");
lines.push("");
lines.push("### Dialogue Density");
lines.push("Talk-to-action ratio? (quippy/rapid-fire, balanced, sparse/weighted silence)");
lines.push("");
lines.push("**Your answer:**");
lines.push("");
lines.push("### Conflict Style");
lines.push("How do characters fight?");
lines.push(`Options: ${CONFLICT_STYLES.join(", ")}`);
lines.push("");
lines.push("**Your answer:**");
lines.push("");
lines.push("### Baseline Tone");
lines.push("Describe the default emotional register in a phrase:");
lines.push("");
lines.push("**Your answer:**");
lines.push("");
}
// Character Questions
lines.push("---");
lines.push("");
lines.push(`## 3. Characters (${depth === "quick" ? "3-5 main" : "all significant"})`);
lines.push("");
lines.push("For each character, answer:");
lines.push("");
lines.push("### Character: [NAME]");
lines.push("");
lines.push("**Form:** What are they on the surface?");
lines.push("");
for (const [axis, questions] of Object.entries(EXTRACTION_QUESTIONS)) {
lines.push(`**${axis.charAt(0).toUpperCase() + axis.slice(1)} Function:**`);
for (const q of questions) {
lines.push(`- ${q}`);
}
lines.push("");
}
lines.push("**Structural Necessity:** high/medium/low");
lines.push("**Adaptable Elements:** What about them could change?");
lines.push("");
lines.push("(Repeat for each character)");
lines.push("");
// Structural Requirements
lines.push("---");
lines.push("");
lines.push("## 4. Structural Requirements");
lines.push("");
lines.push("What MUST be present for the story to work? (Not forms, but functions)");
lines.push("Example: 'Protagonist must have privileged information others lack'");
lines.push("");
lines.push("**Your answer:**");
lines.push("");
// Adaptable Elements
lines.push("---");
lines.push("");
lines.push("## 5. Adaptable Without Breaking");
lines.push("");
lines.push("What could change completely without breaking what makes this work?");
lines.push("Example: 'Royal setting', 'Time period', 'Character genders'");
lines.push("");
lines.push("**Your answer:**");
lines.push("");
// Cluster Membership
lines.push("---");
lines.push("");
lines.push("## 6. Cluster Membership");
lines.push("");
lines.push("What trope patterns does this belong to?");
lines.push("Example: 'revenge-tragedy', 'found-family', 'bounty-hunter-scifi'");
lines.push("");
lines.push("**Your answer:**");
lines.push("");
return lines.join("\n");
}
function validateExtraction(extraction: WorkExtraction): { valid: boolean; issues: string[] } {
const issues: string[] = [];
// Check required fields
if (!extraction._meta.source_work) {
issues.push("Missing source work name");
}
if (!extraction.emotional_core.primary_genre) {
issues.push("Missing primary genre - emotional core not identified");
}
if (!extraction.emotional_core.emotional_experience) {
issues.push("Missing emotional experience description");
}
// Check for characters
const charCount = Object.keys(extraction.characters).length;
if (charCount === 0) {
issues.push("No characters extracted");
} else {
// Check each character
for (const [name, char] of Object.entries(extraction.characters)) {
const allFunctions = [
...char.functions.structural,
...char.functions.character,
...char.functions.emotional,
...char.functions.thematic,
...char.functions.relational
];
if (allFunctions.length === 0) {
issues.push(`Character '${name}' has no functions extracted`);
}
if (!char.structural_necessity) {
issues.push(`Character '${name}' missing structural necessity rating`);
}
}
}
// Check structural requirements
if (extraction.structural_requirements.length === 0) {
issues.push("No structural requirements identified");
}
// Check adaptable elements
if (extraction.adaptable_without_breaking.length === 0) {
issues.push("No adaptable elements identified - likely over-restricting");
}
// Depth-specific checks
if (extraction._meta.extraction_depth !== "quick") {
if (!extraction.tone.baseline_tone) {
issues.push("Standard/detailed extraction missing tone profile");
}
if (extraction.emotional_core.emotional_beats.length === 0) {
issues.push("Standard/detailed extraction missing emotional beats");
}
}
return {
valid: issues.length === 0,
issues
};
}
// === MAIN ===
function main(): void {
const args = Deno.args;
// Help
if (args.includes("--help") || args.includes("-h")) {
console.log(`Extract Functions - DNA Extraction Tool
Usage:
deno run --allow-read --allow-write scripts/extract-functions.ts "Work Name"
deno run --allow-read --allow-write scripts/extract-functions.ts "Work Name" --depth quick
deno run --allow-read --allow-write scripts/extract-functions.ts --element "The Ghost"
deno run --allow-read --allow-write scripts/extract-functions.ts --validate extraction.json
deno run --allow-read --allow-write scripts/extract-functions.ts --template
Options:
--depth <level> Extraction depth: quick, standard, detailed (default: standard)
--element <name> Extract single element instead of full work
--validate <file> Validate existing extraction file
--template Output blank extraction template
--json Output as JSON instead of markdown
--questionnaire Output questionnaire format for manual completion
Depth Levels:
quick - 15-30 min: Core functions, primary genre, 3-5 characters
standard - 1-3 hours: Full extraction with tone and relationships
detailed - 4-8 hours: Scene-level, episode structures, voice patterns
Examples:
# Generate questionnaire for Hamlet
deno run --allow-read scripts/extract-functions.ts "Hamlet" --questionnaire
# Quick extraction template
deno run --allow-read scripts/extract-functions.ts "Killjoys" --depth quick --template
# Validate completed extraction
deno run --allow-read scripts/extract-functions.ts --validate hamlet.json
`);
Deno.exit(0);
}
// Parse arguments
const depthIndex = args.indexOf("--depth");
const depth: Depth = depthIndex !== -1 && args[depthIndex + 1]
? args[depthIndex + 1] as Depth
: "standard";
const elementIndex = args.indexOf("--element");
const elementName = elementIndex !== -1 ? args[elementIndex + 1] : null;
const validateIndex = args.indexOf("--validate");
const validateFile = validateIndex !== -1 ? args[validateIndex + 1] : null;
const isTemplate = args.includes("--template");
const isJson = args.includes("--json");
const isQuestionnaire = args.includes("--questionnaire");
// Track consumed indices for positional arg detection
const skipIndices = new Set<number>();
if (depthIndex !== -1) {
skipIndices.add(depthIndex);
skipIndices.add(depthIndex + 1);
}
if (elementIndex !== -1) {
skipIndices.add(elementIndex);
skipIndices.add(elementIndex + 1);
}
if (validateIndex !== -1) {
skipIndices.add(validateIndex);
skipIndices.add(validateIndex + 1);
}
// Find positional argument (work name)
let workName: string | null = null;
for (let i = 0; i < args.length; i++) {
if (!args[i].startsWith("--") && !skipIndices.has(i)) {
workName = args[i];
break;
}
}
// Handle validation mode
if (validateFile) {
try {
const content = Deno.readTextFileSync(validateFile);
const extraction = JSON.parse(content) as WorkExtraction;
const result = validateExtraction(extraction);
if (isJson) {
console.log(JSON.stringify(result, null, 2));
} else {
console.log(`\nValidation Result: ${result.valid ? "PASS" : "FAIL"}\n`);
if (result.issues.length > 0) {
console.log("Issues found:");
for (const issue of result.issues) {
console.log(` - ${issue}`);
}
} else {
console.log("Extraction is complete and ready for synthesis.");
}
}
} catch (e) {
console.error(`Error reading/parsing ${validateFile}: ${e}`);
Deno.exit(1);
}
return;
}
// Handle element extraction mode
if (elementName) {
const element = createEmptyElement(elementName, "other");
if (isJson) {
console.log(JSON.stringify(element, null, 2));
} else {
console.log(formatElementExtraction(element));
console.log("\n---\n");
console.log("## Extraction Questions\n");
for (const [axis, questions] of Object.entries(EXTRACTION_QUESTIONS)) {
console.log(`### ${axis.charAt(0).toUpperCase() + axis.slice(1)}`);
for (const q of questions) {
console.log(`- ${q}`);
}
console.log("");
}
}
return;
}
// Need work name for remaining modes
if (!workName) {
console.error("Error: Please provide a work name or use --validate/--element flags");
console.error("Usage: deno run --allow-read scripts/extract-functions.ts \"Work Name\"");
Deno.exit(1);
}
// Handle questionnaire mode
if (isQuestionnaire) {
console.log(formatQuestionnaire(workName, depth));
return;
}
// Handle template mode
const extraction = createEmptyExtraction(workName, depth);
if (isJson) {
console.log(JSON.stringify(extraction, null, 2));
} else if (isTemplate) {
console.log(JSON.stringify(extraction, null, 2));
} else {
// Default: output formatted markdown template with guidance
console.log(formatExtraction(extraction));
console.log("\n---\n");
console.log("## Next Steps\n");
console.log("1. Use --questionnaire flag for guided extraction");
console.log("2. Fill in extraction fields");
console.log("3. Use --validate to check completeness");
console.log(`\nExample: deno run --allow-read scripts/extract-functions.ts "${workName}" --questionnaire`);
}
}
main();
#!/usr/bin/env -S deno run --allow-read
/**
* Structural vs Stylistic Classification
*
* Helps classify story elements as structural (must keep for story to work)
* or stylistic (can be adapted without breaking the story).
*
* Usage:
* deno run --allow-read scripts/structural-stylistic.ts "royal court setting"
* deno run --allow-read scripts/structural-stylistic.ts --batch elements.json
* deno run --allow-read scripts/structural-stylistic.ts --examples
*/
// === INTERFACES ===
interface ClassificationResult {
element: string;
classification: "structural" | "stylistic" | "unclear";
confidence: "high" | "medium" | "low";
reasoning: string[];
questions_to_resolve: string[];
functions_served: string[];
alternative_forms: string[];
}
interface ClassificationQuestion {
question: string;
structural_answer: string;
stylistic_answer: string;
weight: number;
}
// === DATA ===
const CLASSIFICATION_QUESTIONS: ClassificationQuestion[] = [
{
question: "If you removed this completely, would the plot still be possible?",
structural_answer: "No - the plot depends on this element existing",
stylistic_answer: "Yes - the plot could work without it or with a substitute",
weight: 3
},
{
question: "Could a different form serve the same story function?",
structural_answer: "No - this specific form is required",
stylistic_answer: "Yes - many different forms could serve the same function",
weight: 3
},
{
question: "Is this element specific to the time/place/culture, or universal?",
structural_answer: "It embodies a universal that MUST be present",
stylistic_answer: "It's a specific expression of something that could be expressed differently",
weight: 2
},
{
question: "Would changing this require changing the character arcs?",
structural_answer: "Yes - character transformations depend on this",
stylistic_answer: "No - characters could transform the same way with a different version",
weight: 2
},
{
question: "Does the emotional experience of the story require this specific element?",
structural_answer: "Yes - the genre promise depends on this",
stylistic_answer: "No - the emotional experience could be achieved differently",
weight: 2
},
{
question: "Is this element what audiences remember and love, or is it incidental?",
structural_answer: "This IS what they love - it defines the work",
stylistic_answer: "They love what this enables, not this specifically",
weight: 1
},
{
question: "Would the theme/meaning change if this element changed?",
structural_answer: "Yes - the theme is embodied in this specific element",
stylistic_answer: "No - the theme could be explored through different elements",
weight: 2
}
];
const EXAMPLES = {
clearly_structural: [
{
element: "Hamlet's uncertainty about the ghost's truthfulness",
reasoning: "The entire plot hinges on Hamlet's inability to act due to uncertainty. Remove this, and you remove the central dramatic engine.",
functions: ["Creates the central conflict", "Enables the delay that defines the character", "Generates the play-within-a-play"]
},
{
element: "The mystery structure in a detective story",
reasoning: "A mystery without a puzzle to solve isn't a mystery. The structure IS the genre promise.",
functions: ["Delivers the genre promise", "Creates reader engagement through problem-solving", "Provides satisfaction of revelation"]
},
{
element: "Romeo and Juliet's families being enemies",
reasoning: "The tragedy requires the love to be forbidden. Without opposing families (or equivalent), there's no star-crossed element.",
functions: ["Creates the central obstacle", "Makes the romance tragic rather than simple", "Forces the fatal decisions"]
}
],
clearly_stylistic: [
{
element: "The specific time period (Renaissance Denmark in Hamlet)",
reasoning: "Hamlet has been successfully set in modern times, corporate settings, etc. The period is characteristic but not essential.",
alternatives: ["Corporate boardroom", "Political dynasty", "Mafia family", "Any power structure with succession"]
},
{
element: "Shakespeare's iambic pentameter",
reasoning: "The language style is beautiful but adaptations in prose, modern English, or other languages preserve the story.",
alternatives: ["Modern English", "Film dialogue", "Any language that can express the ideas"]
},
{
element: "The specific occupations in a romance",
reasoning: "Whether characters are doctors, lawyers, or baristas usually doesn't affect the romantic arc.",
alternatives: ["Any occupation that provides the needed access/conflict/status"]
}
],
requires_analysis: [
{
element: "Hamlet being a prince",
analysis: "This seems stylistic (could be any insider-to-power) but the specific functions require examination: proximity to power, obligation through birth, inability to simply leave. These functions could be served by other forms.",
likely_classification: "stylistic",
functions_to_preserve: ["Proximity to power center", "Inherited obligation", "Cannot easily escape situation"]
},
{
element: "The ghost in Hamlet",
analysis: "The supernatural element seems essential but the FUNCTION (delivering unverifiable privileged information that creates obligation) could be served by: dying message, discovered evidence, testimony from unreliable source.",
likely_classification: "stylistic (the specific form), structural (the function)",
functions_to_preserve: ["Provides information protagonist cannot verify", "Creates moral obligation not chosen", "Introduces uncertainty about truth"]
},
{
element: "The ship in bounty hunter sci-fi",
analysis: "Mobile base that enables case-of-the-week structure. Could be other mobile platforms (RV, train, etc.) but the 'crew lives together in mobile home' function is structural to the subgenre.",
likely_classification: "function is structural, specific form is stylistic",
functions_to_preserve: ["Mobile base for travel", "Crew living quarters for relationship development", "Home that travels to stories"]
}
]
};
// === UTILITIES ===
function formatQuestionnaire(element: string): string {
const lines: string[] = [];
lines.push(`# Structural vs Stylistic Classification: "${element}"`);
lines.push("");
lines.push("Answer each question to determine if this element is structural (must keep)");
lines.push("or stylistic (can adapt to a different form).");
lines.push("");
lines.push("---");
lines.push("");
for (let i = 0; i < CLASSIFICATION_QUESTIONS.length; i++) {
const q = CLASSIFICATION_QUESTIONS[i];
lines.push(`## Question ${i + 1} (Weight: ${q.weight})`);
lines.push("");
lines.push(`**${q.question}**`);
lines.push("");
lines.push(`- [ ] **Structural:** ${q.structural_answer}`);
lines.push(`- [ ] **Stylistic:** ${q.stylistic_answer}`);
lines.push("");
}
lines.push("---");
lines.push("");
lines.push("## Additional Analysis");
lines.push("");
lines.push("### Functions this element serves:");
lines.push("(List what this element DOES in the story)");
lines.push("");
lines.push("1. ");
lines.push("2. ");
lines.push("3. ");
lines.push("");
lines.push("### Alternative forms that could serve these functions:");
lines.push("(If stylistic, what else could work?)");
lines.push("");
lines.push("1. ");
lines.push("2. ");
lines.push("3. ");
lines.push("");
lines.push("### Final Classification:");
lines.push("");
lines.push("- [ ] **Structural** - Cannot be changed without breaking the story");
lines.push("- [ ] **Stylistic** - Can be adapted to different forms");
lines.push("- [ ] **Mixed** - Function is structural, specific form is stylistic");
lines.push("");
return lines.join("\n");
}
function formatExamples(): string {
const lines: string[] = [];
lines.push("# Structural vs Stylistic: Examples");
lines.push("");
lines.push("Understanding the difference between what MUST stay and what CAN change.");
lines.push("");
lines.push("## Clearly Structural Elements");
lines.push("");
lines.push("These cannot be changed without breaking the story:");
lines.push("");
for (const ex of EXAMPLES.clearly_structural) {
lines.push(`### "${ex.element}"`);
lines.push("");
lines.push(`**Why structural:** ${ex.reasoning}`);
lines.push("");
lines.push("**Functions served:**");
for (const f of ex.functions) {
lines.push(`- ${f}`);
}
lines.push("");
}
lines.push("## Clearly Stylistic Elements");
lines.push("");
lines.push("These can change form while preserving the story:");
lines.push("");
for (const ex of EXAMPLES.clearly_stylistic) {
lines.push(`### "${ex.element}"`);
lines.push("");
lines.push(`**Why stylistic:** ${ex.reasoning}`);
lines.push("");
lines.push("**Alternative forms:**");
for (const a of ex.alternatives) {
lines.push(`- ${a}`);
}
lines.push("");
}
lines.push("## Elements Requiring Analysis");
lines.push("");
lines.push("These need careful function extraction to classify:");
lines.push("");
for (const ex of EXAMPLES.requires_analysis) {
lines.push(`### "${ex.element}"`);
lines.push("");
lines.push(`**Analysis:** ${ex.analysis}`);
lines.push("");
lines.push(`**Likely classification:** ${ex.likely_classification}`);
lines.push("");
lines.push("**Functions to preserve:**");
for (const f of ex.functions_to_preserve) {
lines.push(`- ${f}`);
}
lines.push("");
}
lines.push("## The Key Insight");
lines.push("");
lines.push("**Form is almost always stylistic. Function is almost always structural.**");
lines.push("");
lines.push("When analyzing an element:");
lines.push("1. Identify what FUNCTION it serves");
lines.push("2. Ask: Could a different FORM serve that function?");
lines.push("3. If yes: the form is stylistic, the function is structural");
lines.push("4. The adaptation must preserve functions, not forms");
lines.push("");
return lines.join("\n");
}
function formatBatchTemplate(): string {
return JSON.stringify({
elements: [
{
name: "Element 1",
type: "character|setting|plot|relationship|device",
initial_impression: "structural|stylistic|unclear"
}
],
instructions: "List elements to classify. Run with --batch filename.json"
}, null, 2);
}
function createEmptyResult(element: string): ClassificationResult {
return {
element,
classification: "unclear",
confidence: "low",
reasoning: [],
questions_to_resolve: [
"What specific functions does this element serve?",
"Could those functions be served by a different form?",
"Would the story break without this, or just feel different?"
],
functions_served: [],
alternative_forms: []
};
}
// === MAIN ===
function main(): void {
const args = Deno.args;
// Help
if (args.includes("--help") || args.includes("-h")) {
console.log(`Structural vs Stylistic Classification
Usage:
deno run --allow-read scripts/structural-stylistic.ts "element name"
deno run --allow-read scripts/structural-stylistic.ts --examples
deno run --allow-read scripts/structural-stylistic.ts --batch-template
deno run --allow-read scripts/structural-stylistic.ts --questions
Options:
--examples Show classification examples
--questions List all classification questions
--batch-template Output batch classification template
--json Output as JSON
The Structural/Stylistic Distinction:
Structural = The story BREAKS without this (or its function)
Stylistic = The story WORKS with a different form of this
Key Insight:
Form is usually stylistic. Function is usually structural.
Ask: "What does this DO?" then "Could something else DO that?"
`);
Deno.exit(0);
}
const isJson = args.includes("--json");
// Handle --examples
if (args.includes("--examples")) {
if (isJson) {
console.log(JSON.stringify(EXAMPLES, null, 2));
} else {
console.log(formatExamples());
}
return;
}
// Handle --questions
if (args.includes("--questions")) {
if (isJson) {
console.log(JSON.stringify(CLASSIFICATION_QUESTIONS, null, 2));
} else {
console.log("# Classification Questions\n");
for (let i = 0; i < CLASSIFICATION_QUESTIONS.length; i++) {
const q = CLASSIFICATION_QUESTIONS[i];
console.log(`## ${i + 1}. ${q.question} (Weight: ${q.weight})`);
console.log(` Structural: ${q.structural_answer}`);
console.log(` Stylistic: ${q.stylistic_answer}`);
console.log("");
}
}
return;
}
// Handle --batch-template
if (args.includes("--batch-template")) {
console.log(formatBatchTemplate());
return;
}
// Find element name (positional argument)
let element: string | null = null;
for (let i = 0; i < args.length; i++) {
if (!args[i].startsWith("--")) {
element = args[i];
break;
}
}
if (!element) {
console.error("Error: Please provide an element to classify");
console.error("Usage: deno run --allow-read scripts/structural-stylistic.ts \"royal court setting\"");
console.error(" deno run --allow-read scripts/structural-stylistic.ts --examples");
Deno.exit(1);
}
// Generate questionnaire for the element
if (isJson) {
const result = createEmptyResult(element);
console.log(JSON.stringify(result, null, 2));
} else {
console.log(formatQuestionnaire(element));
}
}
main();
Related skills
FAQ
What does dna-extraction produce from project samples?
dna-extraction produces a project DNA profile distilling recurring style patterns, values, constraints, and behavioral norms from sample code, copy, or design artifacts. Agents use the profile to replicate voice, architecture choices, and brand behavior consistently across sessio
When should developers run dna-extraction?
dna-extraction runs when agent outputs drift from established project conventions or when onboarding a new agent requires encoding tacit rules from existing artifacts. The skill analyzes samples rather than formal docs to capture patterns developers follow implicitly.