| Server IP : 3.147.158.171 / Your IP : 216.73.216.88 Web Server : Apache/2.4.67 (Amazon Linux) OpenSSL/3.5.5 System : Linux ip-172-31-2-178.us-east-2.compute.internal 6.1.172-216.329.amzn2023.x86_64 #1 SMP PREEMPT_DYNAMIC Wed May 20 06:31:34 UTC 2026 x86_64 User : ec2-user ( 1000) PHP Version : 8.4.21 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : OFF Directory : /tsai/repo/.claude/skills/create-series/ |
Upload File : |
---
name: create-series
description: Create a multi-part article series — propose story concepts, build a series bible with per-part synopses, then write each part with images on a production server.
argument-hint: <env> <site_slug> <character_name> [--continue]
---
# Create Series
Create or continue a multi-part article series for a character on a production WordPress site.
**Arguments:** $ARGUMENTS
---
## Overview
This skill orchestrates multi-part article series using `create_articles.py`'s `--series` and `--character-description-file` flags. It produces two artifacts:
- **Series bible** — a markdown file with the full story/topic arc and per-part synopses
- **Part description files** — one per part, injected via `--character-description-file` to tell the AI what to write
The `--series` flag handles WordPress taxonomy terms and continuity (previous parts' summaries, content, images, category/tag inheritance). The `--character-description-file` provides the plan for what comes next. Both are needed — dual context gives the AI the story direction plus awareness of what was actually written.
---
## Argument parsing
Parse as:
- `env` — first token: `tsai` or `scike`
- `site_slug` — second token (e.g. `creative-writing`, `news`, `guitar`)
- `character_name` — remaining tokens before any flag, or a quoted string (e.g. `"Kade Renner"`)
- `--continue` — optional flag: skip story proposal, continue an existing series
If `env` is missing or not `tsai`/`scike`, ask the user before proceeding.
---
## Environment constants
| env | SSH host | Repo path | TSAI_ENV | WP domain suffix |
|-----|----------|-----------|----------|-----------------|
| `tsai` | `ssh -i ~/.ssh/tsai.pem ec2-user@3.147.158.171` | `/tsai/repo` | `production` | `{site_slug}.turmansolutions.ai` |
| `scike` | `ssh -i ~/.ssh/tsai.pem ec2-user@scike.ai` | `/data/tsai` | `scike_ai` | `{site_slug}.scike.ai` |
Resolve from the table:
- `{ssh}` — SSH command for the env
- `{repo}` — repo path for the env
- `{tsai_env}` — TSAI_ENV value for the env
- `{wp_base_url}` — `https://{site_slug}.{domain_suffix}`
Additional constants (same for both envs):
- API venv: `{repo}/api/venv/`
- Series files (server): `{repo}/api/series/`
- Series files (local): `/Users/jturman/development/john/docs/series/`
- WP credentials: username `Scike`, password `Ts4iServ1ceKey2025`
File naming convention:
- Series bible: `{site_slug}--{series-slug}.md`
- Part description: `{site_slug}--{series-slug}--part-{N}.txt`
Where `{series-slug}` is the series name lowercased with spaces replaced by hyphens.
---
## Pre-flight
### 1. Resolve the character
Fetch the character's description from the site doc or Drupal. Check the site doc first:
```
/Users/jturman/development/john/docs/{site_slug}.md
```
Find the character by name under the `## Authors` section. Extract their description — this is the **base voice** that must be included verbatim at the top of every part description file.
If not in the site doc, fetch from the API:
```bash
{ssh} \
"curl -s 'http://localhost:8000/api/v1/cms/character/by-slug/{character_slug}'" | python3 -m json.tool
```
### 2. Ensure directories exist
```bash
mkdir -p /Users/jturman/development/john/docs/series
{ssh} "mkdir -p {repo}/api/series"
```
---
## Mode A: New Series (default)
### Step 1 — Propose story/topic concepts
Based on the character's description and writing style, propose 3-4 series concepts. Each should include:
- **Series title** (becomes the WordPress `article_series` taxonomy term)
- **Logline** (one sentence)
- **Premise** (2-3 sentences)
- **Why it fits this character** (one sentence)
- **Suggested part count** (typically 3-5)
Use AskUserQuestion to let the user pick one.
### Step 2 — Confirm structure
After the user picks a concept, confirm:
- The series name (exact string for `--series`)
- The number of parts
- High-level part breakdown (one sentence per part)
Use AskUserQuestion if the user wants adjustments.
### Step 2.5 — Optional seed image for Part 1
Ask the user (via AskUserQuestion) whether they want to supply a **seed image** for Part 1. A seed image is a reference image (URL) that anchors the featured image of Part 1 — useful for prototyping a specific character likeness, locking in a setting, or pinning down an art style up front.
Important: the seed is applied **only to Part 1's featured image**. From Part 2 onward, the existing `--series` mechanism propagates Part 1's generated featured image as the visual anchor — so the seed's influence flows through the chain automatically, no need to re-pass it.
If the user says yes, prompt for:
- **Seed image URL** — any `https://` URL (paste from a browser, WordPress media library, etc.)
- **Seed image instructions** — short free text on how to use the image (see guidance below).
**Writing effective seed instructions.** Vague instructions ("use this image") tend to produce loose interpretations and often require a Part 1 regen. The instructions should answer three things explicitly:
1. **What is in the image** — name the elements: *"two people (a man and a woman)"*, *"a Brooklyn brownstone interior"*, *"a watercolor illustration in muted earth tones"*. Don't assume the model will infer this.
2. **Which elements must transfer** — be explicit about what the generated image must preserve: characters, setting, art style, palette, mood. *"The two people shown are the main characters; both must appear together in the generated image, with their likenesses preserved."*
3. **What latitude the model has** — what can change? *"The setting and composition can vary, but the two faces should match."*
Strong example: *"The two people (a man and a woman) shown in the seed image are the main characters of the story. Both of them must appear together in the generated image, rendered to match the likenesses in the seed image. The setting and lighting can vary by scene."*
Weak example: *"Use the man and woman as the main characters."* (Doesn't say they must appear *together*, doesn't say likenesses must match, doesn't specify what can vary.)
If the user supplies vague instructions, gently suggest a tightened version using the template above and confirm before proceeding.
Hold these two values in working memory for Part 1's invocation. Do **not** persist them in the series bible — they're ephemeral. You may add a one-line note in the bible's Series Concept (e.g. *"Visual anchor: <one-sentence description of the seed image>"*) for human reference, but the bible is not re-read for the seed on subsequent parts.
### Step 3 — Generate series bible
Write a comprehensive synopsis document:
```markdown
# {Series Name} -- Series Bible
## Character Voice
{Character's base description, verbatim from site doc}
## Series Concept
{Expanded premise, 4-5 sentences}
## Series Arc
{The overall arc across all parts — what is the reader's/audience's journey?}
## Part Synopses
### Part 1: "{Working Title}"
{3-5 paragraph synopsis: what happens, what is established, how it ends}
### Part 2: "{Working Title}"
{3-5 paragraph synopsis}
... (one per part)
## Recurring Elements
{Characters, locations, motifs, themes that thread through the series}
```
Save to both locations:
- Local: `docs/series/{site_slug}--{series-slug}.md`
- Server: `scp` to `{repo}/api/series/{site_slug}--{series-slug}.md`
### Step 4 — Choose pacing, then write parts
Before writing any parts, ask the user (via AskUserQuestion) how they want to pace the run:
- **Pause after every part** — checkpoint between every part, with continue/regenerate/abort options. Use when the user wants to read each installment as it lands and catch tone/visual drift early.
- **Pause after Part 1 only** (default) — checkpoint after Part 1, then run the remaining parts straight through. Good middle ground: confirms voice + visual anchor, then commits.
- **Run to completion** — no pauses at all, including Part 1. Use only when the user is confident in the plan and wants to walk away.
Then write parts sequentially — each part must be posted before the next begins, so `--series` can pull in the previous part's content for continuity. Follow the **Write a Part** procedure below for each part.
**At each checkpoint** (per the chosen pacing), do the following:
1. Report the result: part number, title, URL, and the featured image URL.
2. Use AskUserQuestion to offer:
- **Continue** — proceed to the next part (or finish, if this was the last).
- **Regenerate this part** — delete the just-created post (see "Image permission errors" below for the delete recipe) and re-run it. For Part 1, the user may supply new seed values; otherwise reuse the originals. For Parts 2+, the regen uses the existing series chain (no seed flags).
- **Abort** — stop the skill; the bible, all part files, and any already-published parts remain in place for later.
3. Only proceed past the checkpoint once the user picks Continue.
If the chosen pacing skips a checkpoint, briefly report each part's result (part number, title, URL) and continue to the next without asking. The user can still interrupt manually if needed.
---
## Mode B: Continue Series (`--continue`)
### Step 1 — Find the series bible
Check for existing series bible files for this character's site:
```bash
ls /Users/jturman/development/john/docs/series/{site_slug}--*.md
```
If multiple series exist, list them and use AskUserQuestion to let the user pick one. If only one, use it.
Read the series bible to determine:
- Total number of parts planned
- Which parts have been written (check WordPress for existing articles in the series)
### Step 2 — Check existing parts on WordPress
```bash
{ssh} \
"curl -s '{wp_base_url}/wp-json/wp/v2/article_series' | python3 -m json.tool"
```
Find the series term, then fetch articles:
```bash
{ssh} \
"curl -s '{wp_base_url}/wp-json/wp/v2/posts?article_series={term_id}&orderby=date&order=asc&_fields=id,title,date,meta' | python3 -m json.tool"
```
Determine the next part number.
### Step 3 — Write remaining parts
For each remaining part, follow the **Write a Part** procedure below.
---
## Write a Part
This procedure writes a single part and is used by both Mode A and Mode B.
### 1. Build the part description file
Create a text file with this structure:
```
{Character's base voice description, verbatim}
---
## Series: "{Series Name}"
{Series concept — 3-4 sentences, including what happened in previous parts if this is Part 2+}
This is Part {N} of {total}{" -- the final installment" if last part}.
## Part {N}: "{Working Title}" -- Synopsis
{Full synopsis from the series bible, 3-5 paragraphs}
## Part {N} Objectives
{5-7 bullet points: what this part must accomplish narratively}
{For Part 1: establish characters, setting, inciting incident, end on a hook}
{For middle parts: raise stakes, deepen mystery/conflict, close escape routes}
{For final part: deliver confrontation, resolution, emotional payoff, thematic closure}
## Voice & Pacing Notes
{3-5 bullet points tailored to this part's needs}
{Word count target: 2000-3000 for short series, 2500-3500 for longer ones}
{Pacing guidance specific to this part's role in the arc}
```
Save locally: `docs/series/{site_slug}--{series-slug}--part-{N}.txt`
Upload to server: `scp` to `{repo}/api/series/{site_slug}--{series-slug}--part-{N}.txt`
### 2. Run create_articles.py
For **Part 2+**, or for Part 1 when no seed image was supplied:
```bash
{ssh} \
"cd {repo}/api && source venv/bin/activate && \
python create_articles.py {site_slug} \
--character '{character_name}' \
--series '{Series Name}' \
--character-description-file series/{site_slug}--{series-slug}--part-{N}.txt \
--featured-image \
--secondary-image"
```
For **Part 1 with a seed image** (from Step 2.5), add the two seed flags:
```bash
{ssh} \
"cd {repo}/api && source venv/bin/activate && \
python create_articles.py {site_slug} \
--character '{character_name}' \
--series '{Series Name}' \
--character-description-file series/{site_slug}--{series-slug}--part-1.txt \
--featured-image \
--secondary-image \
--seed-image-url '{seed_image_url}' \
--seed-image-instructions '{seed_image_instructions}'"
```
If the instructions contain single quotes or are long/multiline, scp them to `{repo}/api/series/{site_slug}--{series-slug}--seed.txt` and use `--seed-image-instructions-file series/{site_slug}--{series-slug}--seed.txt` instead.
Do **not** pass `--seed-image-url` or `--seed-image-instructions` on Parts 2+; the backend ignores them past Part 1 and emits a warning. Parts 2+ pick up the visual style from Part 1's generated featured image via the existing `--series` reference chain.
Use a 10-minute timeout — image generation adds time.
**Important:** The `--character` flag takes the character's **display name** (e.g. `"Kade Renner"`), not a slug.
### 3. Verify
Fetch the new article:
```bash
curl -s "{wp_base_url}/wp-json/wp/v2/posts?per_page=1&orderby=date&order=desc&_fields=id,link,title,date,featured_media,article_series" | python3 -m json.tool
```
Confirm:
- Title and URL are correct
- `featured_media` is non-zero (image attached)
- `article_series` includes the correct term ID
Report: part number, title, URL.
---
## Error handling
### Image permission errors
If image generation fails with `Permission denied`, fix the image directories:
```bash
{ssh} "sudo chmod -R 777 /data/tsai/drupal/web/sites/default/files/scike/{site_slug}/create /data/tsai/drupal/web/sites/default/files/scike/{site_slug}/edit /data/tsai/drupal/web/sites/default/files/scike/{site_slug}/upload /data/tsai/drupal/web/sites/default/files/scike/{site_slug}/variant"
```
For tsai env, the path prefix is `/tsai/drupal/web/sites/default/files/tsai/` instead.
Then delete the imageless post and re-run:
```bash
TOKEN=$({ssh} "curl -s -X POST '{wp_base_url}/wp-json/jwt-auth/v1/token' -d 'username=Scike&password=Ts4iServ1ceKey2025'" | python3 -c "import sys,json; print(json.load(sys.stdin)['token'])")
{ssh} "curl -s -X DELETE '{wp_base_url}/wp-json/wp/v2/posts/{post_id}?force=true' -H 'Authorization: Bearer $TOKEN'"
```
### Character not found
The `--character` flag matches against the character's display name. Check the available characters in the error output and use the exact display name (with spaces and capitalization).
---
## Final summary
After all parts are written, report:
1. **Series:** name and total parts
2. **Articles:** part number, title, and URL for each
3. **Files saved:**
- Series bible location (local + server)
- Part description files (local + server)
4. **Next steps:** note that the series bible and part files persist for reference