Skill: athola/nm-scribe-style-learner (version 1.0.2)

--- name: style-learner description: Learn and extract writing style patterns from exemplar text for consistent version: 1.9.5 triggers: - style - voice - tone - exemplar - learning - consistency metadata: {"openclaw": {"homepage": "https://github.com/athola/claude-night-market/tree/master/plugins/scribe", "emoji": "\u270d\ufe0f", "requires": {"config": ["night-market.scribe:shared", "night-market.scribe:slop-detector"]}}} source: claude-night-market source_plugin: scribe --- > **Night Market Skill** — ported from [claude-night-market/scribe](https://github.com/athola/claude-night-market/tree/master/plugins/scribe). For the full experience with agents, hooks, and commands, install the Claude Code plugin. # Style Learning Skill Extract and codify writing style from exemplar text for consistent application. ## Approach: Feature Extraction + Exemplar Reference This skill combines two complementary methods: 1. **Feature Extraction**: Quantifiable style metrics (sentence length, vocabulary complexity, structural patterns) 2. **Exemplar Reference**: Specific passages that demonstrate desired style Together, these create a comprehensive style profile that can guide content generation and editing. ## Required TodoWrite Items 1. `style-learner:exemplar-collected` - Source texts gathered 2. `style-learner:features-extracted` - Quantitative metrics computed 3. `style-learner:exemplars-selected` - Representative passages identified 4. `style-learner:profile-generated` - Style guide created 5. `style-learner:validation-complete` - Profile tested against new content ## Step 1: Collect Exemplar Text Gather representative samples of the target style. **Minimum requirements**: - At least 1000 words of exemplar text - Multiple samples preferred (shows consistency) - Same genre/context as target output ```markdown ## Exemplar Sources | Source | Word Count | Type | |--------|------------|------| | README.md | 850 | Technical | | blog-post-1.md | 1200 | Narrative | | api-guide.md | 2100 | Reference | ``` ## Step 2: Feature Extraction Load: `@modules/feature-extraction.md` ### Vocabulary Metrics | Metric | How to Measure | What It Indicates | |--------|----------------|-------------------| | Average word length | chars/word | Complexity level | | Unique word ratio | unique/total | Vocabulary breadth | | Jargon density | technical terms/100 words | Audience level | | Contraction rate | contractions/sentences | Formality | ### Sentence Metrics | Metric | How to Measure | What It Indicates | |--------|----------------|-------------------| | Average length | words/sentence | Complexity | | Length variance | std dev of lengths | Natural variation | | Question frequency | questions/100 sentences | Engagement style | | Fragment usage | fragments/100 sentences | Stylistic punch | ### Structural Metrics | Metric | How to Measure | What It Indicates | |--------|----------------|-------------------| | Paragraph length | sentences/paragraph | Density | | List ratio | bullet lines/total lines | Format preference | | Header depth | max header level | Organization style | | Code block frequency | code blocks/1000 words | Technical density | ### Punctuation Profile | Metric | Normal Range | Style Indicator | |--------|--------------|-----------------| | Em dash rate | 0-3/1000 words | Parenthetical style | | Semicolon rate | 0-2/1000 words | Formal complexity | | Exclamation rate | 0-1/1000 words | Enthusiasm level | | Ellipsis rate | 0-1/1000 words | Trailing thought style | ## Step 3: Exemplar Selection Load: `@modules/exemplar-reference.md` Select 3-5 passages (50-150 words each) that best represent the target style. **Selection criteria**: - Demonstrates characteristic sentence rhythm - Shows typical vocabulary choices - Represents the desired tone - Avoids atypical or exceptional passages ### Exemplar Template ```markdown ### Exemplar 1: [Label] **Source**: [filename, lines X-Y] **Demonstrates**: [what aspect of style] > [Quoted passage] **Key characteristics**: - [Observation 1] - [Observation 2] ``` ## Step 4: Generate Style Profile Combine extracted features and exemplars into a usable style guide. ### Profile Format ```yaml # Style Profile: [Name] # Generated: [Date] # Exemplar sources: [List] voice: tone: [professional/casual/academic/conversational] perspective: [first-person/third-person/second-person] formality: [formal/neutral/informal] vocabulary: average_word_length: X.X jargon_level: [none/light/moderate/heavy] contractions: [avoid/occasional/frequent] preferred_terms: - "use" over "utilize" - "help" over "facilitate" avoided_terms: - delve - leverage - comprehensive sentences: average_length: XX words length_variance: [low/medium/high] fragments_allowed: [yes/no/sparingly] questions_used: [yes/no/sparingly] structure: paragraphs: [short/medium/long] (X-Y sentences) lists: [prefer prose/balanced/prefer lists] headers: [descriptive/terse/question-style] punctuation: em_dashes: [avoid/sparingly/freely] semicolons: [avoid/sparingly/freely] oxford_comma: [yes/no] exemplars: - label: "[Exemplar 1 label]" text: | [Quoted passage] - label: "[Exemplar 2 label]" text: | [Quoted passage] anti_patterns: - [Pattern to avoid 1] - [Pattern to avoid 2] ``` ## Step 5: Validation Test the profile against new content: 1. Generate sample content using the profile 2. Compare metrics to extracted features 3. Have user evaluate voice/tone match 4. Refine profile based on feedback ### Validation Checklist - [ ] Metrics within 20% of exemplar averages - [ ] No anti-pattern violations - [ ] Tone matches user expectation - [ ] Vocabulary aligns with exemplars - [ ] Structure follows profile guidelines ## Usage in Generation When generating new content, reference the profile: ```markdown Generate [content type] following the style profile: - Voice: [from profile] - Sentence length: target ~[X] words, vary between [Y-Z] - Use exemplar passage as tone reference: > [exemplar quote] - Avoid: [anti-patterns from profile] ``` ## Module Reference - See `modules/style-application.md` for applying learned styles to new content ## Integration with slop-detector After generating content, run slop-detector to verify: 1. No AI markers introduced 2. Style metrics match profile 3. Anti-patterns avoided ## Exit Criteria - Style profile document created - At least 3 exemplar passages included - Quantitative metrics extracted - Anti-patterns from slop-detector integrated - Validation test passed

path: modules/exemplar-reference.md
content: ---
module: exemplar-reference
category: style-analysis
dependencies: [Read]
estimated_tokens: 350
---

# Exemplar Reference Module

Select and document representative passages for style guidance.

## Selection Criteria

Choose passages that demonstrate:

1. **Characteristic rhythm**: Sentence length variation patterns
2. **Vocabulary choices**: Typical word selection
3. **Tone markers**: How formality/informality is expressed
4. **Structural preferences**: Paragraph and list usage

## Anti-Selection Criteria

Avoid passages that:

- Are atypically long or short
- Contain unusual formatting
- Quote external sources
- Are transitional/boilerplate
- Contain code blocks (unless style includes code)

## Passage Length

Optimal exemplar length: **50-150 words**

- Too short: Insufficient pattern demonstration
- Too long: Dilutes key characteristics

## Annotation Format

```markdown
### Exemplar: Technical Explanation

**Source**: docs/architecture.md, lines 45-52
**Word count**: 87
**Demonstrates**: Concise technical explanation with grounded examples

> The cache layer sits between the API and database. When a request
> arrives, we check Redis first. Cache hits return in under 5ms;
> misses fall through to Postgres, adding 50-200ms depending on
> query complexity. We chose Redis over Memcached for its richer
> data structures—sorted sets power our leaderboard feature.

**Key characteristics**:
- Short, direct sentences (avg 12 words)
- Specific numbers (5ms, 50-200ms)
- One em dash for aside
- Trade-off explanation ("chose X over Y because")
- No filler phrases
- Technical but accessible
```

## Minimum Exemplar Set

For a complete style profile, collect at least:

| Type | Purpose |
|------|---------|
| Explanation | How concepts are introduced |
| Instruction | How steps are given |
| Transition | How sections connect |

For narrative content, add:

| Type | Purpose |
|------|---------|
| Description | Scene/object portrayal |
| Dialogue | Character voice |
| Action | Event pacing |

## Usage in Generation

When generating new content, present exemplars as reference:

```
Write in a style similar to this passage:

> [exemplar text]

Key aspects to match:
- Sentence length around X words
- [Specific vocabulary preferences]
- [Tone markers to include]
- [Patterns to avoid]
```


path: modules/feature-extraction.md
content: ---
module: feature-extraction
category: style-analysis
dependencies: [Bash, Read]
estimated_tokens: 400
---

# Feature Extraction Module

Quantitative style metrics extraction from exemplar text.

## Vocabulary Analysis

```bash
# Average word length
awk '{for(i=1;i<=NF;i++){sum+=length($i);count++}}END{print sum/count}' file.md

# Unique word ratio
words=$(tr '[:space:]' '\n' < file.md | grep -v '^$' | wc -l)
unique=$(tr '[:space:]' '\n' < file.md | grep -v '^$' | sort -u | wc -l)
echo "scale=2; $unique / $words" | bc

# Contraction count
grep -oE "\b\w+'(t|s|d|ll|ve|re|m)\b" file.md | wc -l
```

## Sentence Analysis

```python
import re

def analyze_sentences(text):
    # Split on sentence boundaries
    sentences = re.split(r'[.!?]+', text)
    sentences = [s.strip() for s in sentences if s.strip()]

    lengths = [len(s.split()) for s in sentences]

    return {
        'count': len(sentences),
        'avg_length': sum(lengths) / len(lengths),
        'min_length': min(lengths),
        'max_length': max(lengths),
        'std_dev': statistics.stdev(lengths) if len(lengths) > 1 else 0,
        'questions': sum(1 for s in sentences if '?' in s),
        'fragments': sum(1 for l in lengths if l < 5)
    }
```

## Structural Analysis

```bash
# Paragraph lengths (sentences per paragraph)
awk -v RS='\n\n' '{
    gsub(/[.!?]/, "&\n");
    n = split($0, a, "\n");
    print n
}' file.md

# List ratio
bullets=$(grep -c '^\s*[-*]' file.md)
total=$(wc -l < file.md)
echo "scale=2; $bullets / $total" | bc

# Header depth
grep -E '^#{1,6}\s' file.md | head -1 | grep -o '#' | wc -c
```

## Punctuation Profile

```bash
# Per 1000 words
words=$(wc -w < file.md)

em_dashes=$(grep -o '—' file.md | wc -l)
semicolons=$(grep -o ';' file.md | wc -l)
exclamations=$(grep -o '!' file.md | wc -l)
colons=$(grep -o ':' file.md | wc -l)

echo "Em dashes: $((em_dashes * 1000 / words)) per 1000"
echo "Semicolons: $((semicolons * 1000 / words)) per 1000"
```

## Output Format

```yaml
vocabulary:
  avg_word_length: 5.2
  unique_ratio: 0.42
  contraction_rate: 3.5  # per 100 sentences

sentences:
  avg_length: 18.4
  std_dev: 8.2
  question_rate: 2.1  # per 100
  fragment_rate: 1.5  # per 100

structure:
  avg_paragraph_sentences: 4.2
  list_ratio: 0.15
  max_header_depth: 3

punctuation:
  em_dash_rate: 1.8  # per 1000 words
  semicolon_rate: 0.5
  exclamation_rate: 0.2
```


path: modules/style-application.md
content: ---
module: style-application
category: writing-quality
dependencies: [Write, Edit]
estimated_tokens: 350
---

# Style Application Module

Apply learned style profiles to new content generation and editing.

## Generation Prompting

When generating new content with a style profile:

```markdown
## Style Guidelines

**Voice**: [profile.voice.tone] with [profile.voice.perspective] perspective

**Sentence targets**:
- Average length: [profile.sentences.average_length] words
- Vary between [min] and [max]
- [Fragment guidance from profile]

**Vocabulary**:
- Prefer: [profile.vocabulary.preferred_terms]
- Avoid: [profile.vocabulary.avoided_terms]
- Contractions: [profile.vocabulary.contractions]

**Structure**:
- Paragraphs: [profile.structure.paragraphs]
- Lists: [profile.structure.lists]

**Reference exemplar**:
> [Most relevant exemplar passage]

**Anti-patterns** (will be checked by slop-detector):
[profile.anti_patterns]
```

## Editing to Match Style

When editing existing content to match a profile:

### Step 1: Measure Current State

Extract metrics from current content and compare to profile.

| Metric | Current | Target | Gap |
|--------|---------|--------|-----|
| Avg sentence length | 24 | 18 | -6 |
| Contraction rate | 0.5 | 3.5 | +3.0 |
| List ratio | 0.45 | 0.15 | -0.30 |

### Step 2: Prioritize Changes

1. **High gap** metrics first
2. **Anti-pattern** violations
3. **Vocabulary** substitutions
4. **Structural** adjustments

### Step 3: Section-by-Section Editing

For each section:
1. Show current metrics
2. Propose specific changes
3. Present exemplar for reference
4. Wait for approval
5. Apply changes
6. Re-measure

## Validation Loop

After applying style:

```
1. Run slop-detector on output
2. Re-extract metrics
3. Compare to profile targets
4. Flag remaining gaps > 20%
5. Iterate if needed
```

## Style Drift Detection

For ongoing content:

```bash
# Compare new content metrics to profile
new_metrics=$(extract_metrics new-doc.md)
profile_metrics=$(cat .scribe/style-profile.yaml)

# Alert if drift > threshold
if [ $avg_sentence_diff -gt 5 ]; then
    echo "WARNING: Sentence length drifting from profile"
fi
```

## Integration Points

| Tool | Integration |
|------|-------------|
| slop-detector | Validate anti-patterns |
| doc-generator | Apply during generation |
| pre-commit | Check style conformance |


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: clean (LOW)