Claude Skill

nous-branding

Generate images and content consistent with the Nous Research brand identity. Use when creating visuals in the Nous / Theia / Hermes ecosystem: a "cyber-classical" style blending neo-classical statuary, cyberpunk/industrial grunge, and retro anime illustration. Covers official br

LLM Mart · 0 points · 15 views 13 listing impressions 0 install-command copies

#image-generation

Virus-scanned Reviewed automatically before listing.

Full trust report

Download magnus919-agent-skills-nous-branding-addad86.zip · 10196 KB
Part of magnus919/agent-skills — 145 skills

Install

skills CLI npx skills add https://github.com/magnus919/agent-skills/tree/main/nous-branding
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install magnus919-agent-skills@llmmart
Git git clone https://github.com/magnus919/agent-skills.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole magnus919/agent-skills collection as a plugin from our marketplace. Git is the plain clone.

README

Nous Research Brand Identity — Image & Content Generation

Generate images and content consistent with the Nous Research brand identity — a "cyber-classical" style blending neo-classical statuary, cyberpunk grunge, and retro manga illustration.

Why Install This Skill

When your agent loads this skill, it can create on-brand visuals for the Nous / Theia / Hermes ecosystem. That means:

  • Understand the brand DNA — classical Greek myth meets cyberpunk meets retro anime
  • Use the correct color palette — electric blue, dark grunge, marble tones — with hex-accurate values
  • Depict the Nous Girl mascot — canonical poses, expressions, and accessories
  • Generate consistent imagery — reference-image-driven workflows for texture, grain, and style
  • Match brand typography — Inter, IBM Plex Sans, JetBrains Mono, distressed display faces

What You Get

Directory Purpose
SKILL.md Complete brand identity reference with style fusion, color palette, typography, mascot specs
assets/ 4+ reference images: color palette card, official mascot, style reference, brand collage — usable as img2img inputs

Triggers

Load this when creating visuals in the Nous Research ecosystem — blog headers, social media, presentation slides, or brand assets.

Requirements

Image generation backend for prompt-based workflows. Reference-image (img2img) workflows require an API supporting image inputs.

Quick Start

Start with the setup and first workflow in SKILL.md, then use the linked resources for the specific task you need to complete.

Skill manifest

Nous Branding

Generate images and brand-consistent visual content inspired by Nous Research ("The AI accelerator company").

When not to use

  • Non-Nous design work — this skill encodes one specific brand system; do not apply its palette, textures, mascot, or compliance rules to unrelated brands or generic design requests.
  • Official brand representation — the Nous Girl mascot is not a substitute for the logo; use the Nous Research wordmark/symbol for official brand use.

Reference Images

This skill ships reference images in assets/ that can be used as visual anchors for img2img, style transfer, image variation, or prompt construction:

File Description Usage
assets/palette-typography-reference.png Brand identity system card showing the 6-color palette swatches with hex codes, typography specimen (Inter, IBM Plex Sans, JetBrains Mono, heavy display), and classified-dossier layout Upload as reference for color palette and typography style
assets/nous-girl-official.webp Official high-resolution (2669×2709) Nous Girl mascot from nousresearch.com. High-contrast black-and-white retro manga portrait. Three-quarter profile facing left, white headband (primary badge variant). 51% dark / 47% light, pure b&w with no gray. Primary mascot reference — the single most authentic brand image
assets/nous-girl-official-badge.png Official badge portrait from the brand booklet (5760×7454). Shows the Nous Girl in her canonical form: white headband, three-quarter profile, neutral attentive expression, stark black/white manga style. Use when the badge/primary variant is needed
assets/nous-girl-sketch-sheet.png Official character sheet from the brand booklet showing all 4 canonical poses: primary badge, headphone ¾ profile, headphone profile left, and headphone small profile. Use for pose reference and character consistency
assets/nous-girl-philosophy.png Brand philosophy page from the booklet showing the Nous Girl alongside the "decentralization of good design" mission statement. Use for brand context and philosophy reference
assets/nous-girl-style-reference.png Generated reference portrait with "NOUS" on headphones, electric blue accents, and color swatch label Color-application reference and prompt examples
assets/brand-collage-reference.png Cyber-classical brand collage with Theia marble statue, glowing electric blue eye with targeting reticle, system architecture diagram, CRT noise overlay Multi-panel brand layout and HUD aesthetic reference

Brand Identity Overview

Nous Research's visual identity is a three-way fusion:

Influence Expression
Classical / Greek myth Statuary of Theia (Titaness of Sight), marble textures, mythological naming
Cyberpunk / Industrial Grunge textures, CRT scan lines, photocopy noise, distressed type, dark palette
Retro Anime / Manga The "Nous Girl" mascot, cel-shaded illustration, large expressive eyes, 1970s-80s manga aesthetic
Tech / Brutalist Heavy display typography, system diagrams, blueprint-style layouts, monospace code labels

Tagline: "The AI accelerator company" Key phrases: "Advance human rights and freedoms", "Open source language models", "Unrestricted availability and use" Vibe: Intellectual but gritty — a cutting-edge research lab operating in the shadows


Loading Guide

Load references on demand — do not load everything at once.

File Load when
references/style-lanes.md Choosing a style lane or writing a lane-specific prompt — full lane grammar, prompt cues, example prompts, and the asset-to-lane Reference Catalog
references/visual-system.md Constructing or reviewing an image against the visual system — hero + extended palettes, Nous Girl spec and pose variants, typography, texture system, art style
references/post-processing.md Delivering any generated image — mandatory post-process modes (imprint/nous/standard) and intensity calibration for scripts/postprocess.py
references/pitfalls.md Output doesn't match expectations — known failure modes and mitigations

Image Prompt Templates

Method 1: Full Brand Portrait

A cyber-classical brand identity illustration in the style of Nous Research / Project Theia.
[SUBJECT DESCRIPTION]. High-contrast dramatic lighting with deep near-black background (#00000E).
Electric blue (#3847FF) primary accent. Soft lavender (#BDA6FF) and burnt orange (#D6825A)
secondary accents. Deep teal (#2E706B) shadow tones. Overlaid with risograph grain texture,
photocopy noise, and subtle CRT scan lines. Retro anime cel-shading combined with neo-classical
sculptural forms. Geometric HUD overlay lines in burnt orange. Bold, distressed display typography.
Raw, analog, imperfect finish. No corporate polish.
Palette: #00000E bg, #3847FF accent, #BDA6FF secondary, #D6825A warm, #E6E6E6 text.

Method 2: Nous Girl Mascot

High-contrast retro manga anime portrait, 1970s-80s cel-shaded style.
A young woman with large anime eyes, shoulder-length dark hair with blunt
straight-across bangs. White over-ear headphones. Three-quarter profile facing left.
Melancholic introspective expression. Bold heavy outlines. Pure black and white with
no grayscale. [Optional: Electric blue #3847FF hair highlights for color version].

Method 3: Brand System Sheet / Collage

Multi-panel brand identity system sheet in Nous Research / Project Theia style.
Grid layout. [Describe panels]. Color palette: #3847FF electric blue, #BDA6FF lavender,
#D6825A burnt orange, #2E706B deep teal, #E6E6E6 off-white, #00000E near-black.
Texture swatches: risograph grain, photocopy noise, CRT scan lines, paper fiber, ink smudge.
Typography: heavy distressed display for titles, Inter/IBM Plex Sans for labels,
JetBrains Mono for technical data. Grunge textures throughout. Dark near-black background.

Method 4: Reference-Image-Driven Generation (Recommended)

This is the preferred method for generating brand-consistent images. Use the scripts/generate-with-ref.py script which reads your Hermes config, determines the active image provider, and hits the API directly with the reference image as contextual input — bypassing the built-in image_generate tool which only supports text prompts.

python3 scripts/generate-with-ref.py \
  --prompt "Your prompt describing the desired image" \
  --reference assets/nous-girl-official-badge.png \
  --aspect landscape \
  --quality medium

Features:

  • --prompt (required) — image description
  • --reference (required) — path to a reference image (use assets/ images from this skill)
  • --aspect — landscape (1536×1024), portrait (1024×1536), or square (1024×1024)
  • --quality — low, medium (default), or high
  • --output — custom output path
  • --dry-run — preview without executing

How it works:

  1. Reads ~/.hermes/config.yaml to find your active image generation provider
  2. For OpenAI: uses /v1/images/edits with multipart upload — the only endpoint that accepts image input with gpt-image-2
  3. Automatically crops the reference to square (1024×1024) as required by the edits endpoint
  4. Saves output to ~/.hermes/cache/images/
  5. Returns JSON with image, model, aspect_ratio, and provider

Why this matters: Text-only generation loses the precise manga style, character proportions, and contrast balance of the Nous Girl. Uploading the official badge preserves the specific 1970s–80s cel-shaded ink style.

Prompting for reference workflows: State what to preserve from the reference, then what to add:

  • "Keep the character's white over-ear headphones, white collared shirt, solid black hair with blunt bangs, neutral expression"
  • "Maintain the same high-contrast black ink on white manga style"
  • Then add the scene details, text, lighting, etc.

Prompt Formula

[STYLE: cyber-classical / Nous Research]
+ [SUBJECT DESCRIPTION]
+ [PALETTE: #00000E bg, #3847FF accent, #BDA6FF, #D6825A]
+ [TEXTURES: risograph grain, photocopy noise, CRT scan lines, paper fiber, ink smudge]
+ [LIGHTING: high-contrast chiaroscuro, dramatic spot, neon edge highlights]
+ [MOOD: intellectual, gritty, underground, calm/attentive]
+ [TYPOGRAPHY: heavy distressed display, Inter/IBM Plex Sans labels, JetBrains Mono code]

Post-Processing

Raw AI-generated images are too clean for the Nous aesthetic. Post-processing is mandatory after every generation — the raw generated image is never the final deliverable.

python3 scripts/postprocess.py input.png output.png --mode imprint --intensity 0.7

Run it as the final step after any generation method (text-only, img2img, multi-pass, or any provider). For mode selection (imprint / nous / standard) and intensity calibration per output type, load references/post-processing.md.


API Workflow Notes

API Approach
DALL-E 3 Text-only. Use Method 1–3 prompts with hex values.
OpenAI Variations / Edit Upload assets/*.png as image input. Prompt describes differences.
Midjourney --sref <asset-url> with --iw 1.5–2.0. Include palette hex values in prompt.
ComfyUI IPAdapter or Reference-Only ControlNet from assets. Denoise 0.6–0.7. Post-process with grain overlay.
Replicate / SD img2img Upload reference. Prompt strength 0.7–0.8. CFG 7.

What Is NOT On-Brand

Avoid these common anti-patterns:

Anti-Pattern Why It's Wrong
Sad/melancholic expression Model defaults to sad for manga characters unless explicitly told "not sad, not crying"
Dark/black headphones Nous Girl wears white over-ear headphones in all canonical poses
Facial markings (teardrop, tattoos, scars) The character has clean, clear skin — no markings whatsoever
Wrong ethnicity (Asian instead of French) Model defaults to Asian features for anime style; explicitly state "French Caucasian"
Busy/cluttered compositions The brand is restrained — dark background, 1-2 accent colors, 2-3 text elements max
Smooth digital illustration The brand is never clean — every image needs grain, noise, or analog texture
Cartoon/anime with glossy rendering The manga style is stark black ink on white paper — no soft shading, no gradients
Corporate/sterile tech aesthetic The finish should feel like an underground research lab, not a SaaS landing page
Over-detailed backgrounds Let the subject breathe. Negative space is a feature.

For a complete list of known failure modes and mitigations, see references/pitfalls.md.


Brand Compliance Checklist

  • Background is near-black (#00000E) or very dark
  • Electric blue (#3847FF) is used as primary accent
  • At least one grunge texture visibly applied (grain, noise, scan lines, paper, ink)
  • High contrast — dramatic light/dark difference
  • Palette is restricted to the specified colors
  • If mascot appears: white headphones, manga style, neutral attentive expression, three-quarter profile
  • If text appears: heavy distressed display for titles, clean sans for labels, monospace for code
  • No flat/clean/corporate polish — finish is raw and tactile
  • Overall impression: intellectual, gritty, underground research lab

Full color tables, the complete Nous Girl mascot specification with pose variants, typography roles, texture definitions, and art-style attributes live in references/visual-system.md; lane-specific prompt construction lives in references/style-lanes.md.

Available Scripts

Script Purpose Invocation
scripts/generate-with-ref.py Reference-image-driven generation: reads the active image provider from ~/.hermes/config.yaml, uploads the reference via the provider's image-input endpoint (square-cropped), and saves the result as JSON with output path. Run it for any Method 4 generation where brand fidelity matters — it preserves the manga style that text-only prompts lose. Use --dry-run first to preview provider and parameters. python3 scripts/generate-with-ref.py --prompt "..." --reference assets/nous-girl-official-badge.png --aspect landscape --quality medium
scripts/postprocess.py Mandatory analog post-processing: applies grain/noise/scan-line modes (standard, risograph, nous, imprint) at a calibrated intensity. Run it as the final step on every generated image — raw AI output is never the deliverable. Load references/post-processing.md to pick mode and intensity. python3 scripts/postprocess.py input.png output.png --mode imprint --intensity 0.7

Prerequisites

  • Python 3 for both scripts; postprocess.py additionally needs an imaging backend (Pillow) available.
  • For generate-with-ref.py: a configured Hermes image-generation provider in ~/.hermes/config.yaml (or an explicit --provider) whose API accepts image inputs — text-only endpoints cannot do reference-driven generation.
  • The bundled reference images in assets/, which serve as img2img anchors; pass one via --reference.
  • An agent or client capable of image generation when working outside the scripts (per compatibility).

Limitations

  • This skill encodes one specific brand system — the palette, mascot rules, textures, and compliance checklist here are not general design guidance and must not be applied to other brands (see When not to use).
  • The Nous Girl is not a logo substitute; official brand representation requires the wordmark/symbol, not generated mascot art.
  • Generated imagery approximates the style even with references: always run the Brand Compliance Checklist before delivering, and consult references/pitfalls.md when output drifts (sad expressions, dark headphones, glossy rendering).
  • Model/provider behavior changes over time — flag-specific details like endpoint names may drift from what the scripts assume; verify against your configured provider.
Files (agent-skills)
  • assets
    • brand-collage-reference.png 2.3 MB · in bundle
    • nous-girl-official-badge.png 1.7 MB · in bundle
    • nous-girl-official.webp 78.1 KB · in bundle
    • nous-girl-philosophy.png 1.1 MB · in bundle
    • nous-girl-sketch-sheet.png 2.6 MB · in bundle
    • nous-girl-style-reference.png 1.2 MB · in bundle
    • palette-typography-reference.png 1.7 MB · in bundle
  • evals
    • evals.json 8.3 KB
      {
        "schema_version": 1,
        "skill_name": "nous-branding",
        "evals": [
          {
            "id": "mascot-prompt-construction",
            "prompt": "Generate an image of the Nous Research mascot girl for a brand post. She keeps coming out sad and with black headphones — write me a prompt that gets her right.",
            "expected_output": "Uses Method 2 (Nous Girl Mascot) prompt construction from the skill: high-contrast retro manga/anime portrait, 1970s-80s cel-shaded style, large anime eyes, solid black bob with blunt straight-across bangs, white over-ear headphones, three-quarter profile facing left, pure black ink on white with no grayscale. Explicitly negates the two known failure modes: states 'neutral, calm, attentive expression — not sad, not crying' (calm alone is insufficient, models default to melancholy) and specifies white headphones because black headphones are a known model failure. Does not add facial markings. For color variants, electric blue #3847FF hair highlights are optional; the default is pure black-and-white.",
            "assertions": [
              "Specifies white over-ear headphones explicitly as part of the prompt.",
              "Includes explicit negation of sadness: neutral calm attentive, not sad, not crying.",
              "Describes the retro 1970s-80s manga cel-shaded style in high-contrast black and white.",
              "Requests three-quarter profile facing left with blunt bangs / voluminous black bob hair.",
              "Contains no facial markings (no teardrop, tattoos, or scars)."
            ]
          },
          {
            "id": "palette-and-texture-compliance",
            "prompt": "I need a hero image for our Nous Research landing page. Make it look clean and modern with a light background so it feels professional.",
            "expected_output": "Pushes back on the clean/light direction because it violates the brand system: the aesthetic is cyber-classical with deep near-black (#00000E) backgrounds, electric blue (#3847FF) as the primary accent, soft lavender #BDA6FF / burnt orange #D6825A / deep teal #2E706B support, off-white #E6E6E6 text, and mandatory grunge texture (risograph grain, photocopy noise, CRT scan lines) — never flat, clean color blocks. Corporate/sterile polish is listed as an anti-pattern; the correct vibe is intellectual but gritty, an underground research lab. Offers the compliant alternative: dark background, high-contrast chiaroscuro lighting, textured finish, palette restricted to the specified hex values, verified against the Brand Compliance Checklist.",
            "assertions": [
              "Cites near-black #00000E background and electric blue #3847FF primary accent from the hero palette.",
              "Explains that clean/corporate polish is off-brand and textures (grain, scan lines, photocopy noise) are mandatory.",
              "Restricts the palette to the specified hex values rather than inventing colors.",
              "Proposes the intellectual-gritty underground-lab mood instead of a SaaS landing-page feel."
            ],
            "case_set": "regression"
          },
          {
            "id": "style-lane-selection-and-reference-image",
            "prompt": "Announce our new open-source model release with a stark social media image in the Nous brand style. Which visual approach should I use, and should I attach any reference images?",
            "expected_output": "Selects the Xerox Poster lane: high-contrast xerox-style poster on pale cyan paper stock, black/dark teal ink with dense horizontal scanlines and harsh bitmap thresholding, bold block NOUS wordmark near top, centered anonymous subject, worn border and crop marks — best for release announcements and stark social images. Uses the lane's example prompt template adapted to the release subject. For references, recommends the palette-typography reference card (per the rule of thumb, more useful than the mascot badge for xerox lanes); if the mascot should appear, uses assets/nous-girl-sketch-sheet.png or the official badge for pose consistency. Reminds that the raw generation must be post-processed afterward (imprint mode) before publishing.",
            "assertions": [
              "Recommends the Xerox Poster lane for a release announcement social image.",
              "Describes the lane's grammar: pale cyan paper, black/dark teal ink, scanlines, NOUS wordmark, crop marks.",
              "Adapts or provides the lane's example prompt rather than generic brand prose.",
              "Points at the palette-typography reference image (or sketch sheet/badge if the mascot appears) per the asset-to-lane mapping.",
              "Notes that post-processing is required after generation."
            ]
          },
          {
            "id": "post-processing-mandatory-before-delivery",
            "prompt": "I just generated a Nous-branded image and it looks great raw. Can I ship it as-is?",
            "expected_output": "Says no — raw AI output is too clean for the Nous aesthetic and is never the final deliverable; post-processing is mandatory after every generation. Runs scripts/postprocess.py on the raw file first, e.g. `python3 scripts/postprocess.py input.png output.png --mode imprint --intensity 0.7`. Chooses the mode deliberately: imprint (all 14 effects) as the default for current targets, nous (base 9 effects) only for legacy luminous PNW/celestial requests, standard (light touch) when minimal texture is wanted. Calibrates intensity to content — roughly 0.45-0.55 for fine manga linework to keep detail visible, up to 0.65-0.8 for xerox-poster heaviness — and warns that 0.8+ can make typography hard to read.",
            "assertions": [
              "States that raw generated output must not be delivered without post-processing.",
              "Gives a concrete scripts/postprocess.py command with mode and intensity flags.",
              "Distinguishes the imprint, nous, and standard modes and when each applies.",
              "Calibrates intensity to the artwork type (lower for fine linework, higher for heavy print effect)."
            ],
            "case_set": "release"
          },
          {
            "id": "reference-image-workflow-over-text-only",
            "prompt": "What's the best way to generate consistent Nous Girl images across many generations?",
            "expected_output": "Recommends Method 4: reference-image-driven generation using scripts/generate-with-ref.py, which reads the Hermes config, resolves the active image provider, and uploads an official assets/ reference (e.g., assets/nous-girl-official-badge.png) as contextual input via the provider's image-edit endpoint — bypassing text-only image_generate. Explains why: text-only prompts lose the precise cel-shaded ink style, proportions, and contrast balance. Shows the command shape (--prompt, --reference, --aspect, --quality) and how to phrase prompts for reference workflows: state what to preserve from the reference (white over-ear headphones, collared shirt, blunt bangs, neutral expression, high-contrast black-on-white manga style), then add scene details. Notes the API alternatives table (OpenAI edits/variations, Midjourney --sref, ComfyUI IPAdapter, SD img2img strength 0.7-0.8) when not using the script.",
            "assertions": [
              "Recommends reference-image-driven generation over text-only prompting for consistency.",
              "Names scripts/generate-with-ref.py with its key flags including --reference pointing at an assets/ image.",
              "Instructs stating what to preserve from the reference before adding new scene details.",
              "Mentions at least one alternative API workflow for reference-based generation."
            ]
          },
          {
            "id": "non-brand-request-routes-away",
            "prompt": "Design a minimalist pastel logo for my bakery's new packaging, something soft and friendly.",
            "expected_output": "Recognizes this is outside the skill's scope and says so: nous-branding exists for visuals in the Nous Research / Theia / Hermes ecosystem and its brand rules (dark palettes, grunge textures, cyber-classical style, the Nous Girl) would be actively wrong for a friendly pastel bakery identity. Declines to apply Nous brand constraints and offers to help with a generic brand/logo design task instead, without invoking the Nous palette, mascot, texture system, or compliance checklist.",
            "assertions": [
              "Declines to apply the Nous brand system to a non-Nous design request.",
              "Does not inject Nous palette hex values, textures, or mascot elements into the deliverable.",
              "Offers a path forward for the user's actual (generic branding) need."
            ],
            "case_set": "dev"
          }
        ]
      }
      
  • references
    • pitfalls.md 9 KB
      # Nous Branding — Pitfalls & Known Failure Modes
      
      This document catalogs known failure modes, gotchas, and mitigations accumulated across multiple sessions of generating Nous-branded images. Use it as a troubleshooting reference when output doesn't match expectations.
      
      ---
      
      ## 1. Mascot Generation Failures
      
      ### 1.1 Sad / Melancholic Expression
      
      **Symptom:** The Nous Girl looks sad, crying, or melancholic.
      
      **Cause:** Image models default to a melancholic expression for manga-style characters when the prompt doesn't specify otherwise.
      
      **Fix:** Always include: `"Neutral calm attentive expression — not sad, not crying"` in the character description. This must be explicit negation — "calm" alone is insufficient.
      
      ### 1.2 Dark / Black Headphones
      
      **Symptom:** The mascot appears with black or dark-colored headphones matching the scene's palette.
      
      **Cause:** The model matches the headphones to the scene's dominant colors instead of the brand's canonical white headphones.
      
      **Fix:** Always state both the color AND the headphone structure: `"White over-ear headphones — the cushioned white band arches across the crown."`
      
      ### 1.3 Teardrop / Facial Markings
      
      **Symptom:** A teardrop appears under the eye, or the face has tattoos, scars, or cyberpunk markings.
      
      **Cause:** The model free-associates "manga" or "cyberpunk" with facial markings.
      
      **Fix:** Include explicit exclusions: `"No tattoos, no scars, no markings, clean clear skin. No teardrop."`
      
      ### 1.4 Wrong Ethnicity
      
      **Symptom:** The Nous Girl appears Asian instead of French/Caucasian.
      
      **Cause:** Image models default to Asian features when the prompt specifies "anime" or "manga" style.
      
      **Fix:** Explicitly state: `"French Caucasian, fair skin, light-colored eyes (blue or grey), delicate European features, about 13 years old — NOT Asian."`
      
      ### 1.5 Wrong Clothing / Costume
      
      **Symptom:** The character is dressed in a hoodie, dark cyberpunk outfit, or elaborate costume instead of the brand's white collared shirt.
      
      **Cause:** The model matches the character's clothing to the scene's aesthetic rather than the brand specification.
      
      **Fix:** Always state `"white collared shirt"` and add `"not a hoodie, not a dark top"` for complex scenes.
      
      ### 1.6 Generic Anime Instead of Retro Manga
      
      **Symptom:** Output looks like modern glossy anime rather than 1970s-80s cel-shaded manga.
      
      **Fix:** Use the exact phrase `"1970s-80s cel-shaded manga style, high-contrast black ink on white paper"`.
      
      ---
      
      ## 2. Image Editing / Reference Workflow Pitfalls
      
      ### 2.1 Text Disappears from Edited Images
      
      **Symptom:** When editing an image that contains text (meme captions, labels, headlines), the model corrupts or drops the text.
      
      **Fix:** Regenerate with `"Keep all text exactly as shown. Do not change or remove any text."` Or add text as a post-processing step. This is a known limitation of the `/v1/images/edits` endpoint.
      
      ### 2.2 Square Input Requirement
      
      **Symptom:** The edits endpoint rejects non-square input images.
      
      **Fix:** The `generate-with-ref.py` script handles this by center-cropping to 1024x1024. Use `--pad` to pad with average edge color instead of cropping.
      
      ### 2.3 Reference Is Not a Guarantee
      
      **Symptom:** Uploading the official Nous Girl badge as a reference doesn't guarantee the output matches the canonical design.
      
      **Cause:** `/v1/images/edits` does not extract style embeddings like IPAdapter or ControlNet. The reference provides pixel-level context but style transfer is not guaranteed.
      
      **Fix:** Always restate all key identifiers in the text prompt (white headphones, black hair, white collared shirt, neutral expression) AND all exclusions (no teardrop, no markings). Prefer the sketch sheet (`nous-girl-sketch-sheet.png`) over a single-pose badge for better character understanding.
      
      ### 2.4 Multi-Pass Editing Complexity
      
      **Symptom:** Each editing pass degrades image quality or loses fidelity.
      
      **Fix:** Limit to 3-4 passes maximum. Each pass handles ONE transformation. The working sequence: character swap → style conversion → demographic correction. More passes introduce cumulative artifacts.
      
      ### 2.5 Aspect Ratio Confusion
      
      **Symptom:** Output is cropped or distorted when using non-square source images.
      
      **Fix:** The edits endpoint outputs landscape (1536x1024), portrait (1024x1536), or square (1024x1024). Set the desired output size explicitly via the `size` parameter in `generate-with-ref.py` or via `--aspect` flag.
      
      ---
      
      ## 3. Safety / Moderation Blocks
      
      ### 3.1 OpenAI Moderation Rejects Horror Content
      
      **Symptom:** The safety system blocks prompts containing ominous language, body horror descriptors, or threat-adjacent vocabulary.
      
      **Fix:** Strip all emotional/response language from the prompt. Describe only neutral visual phenomena. Instead of "horrifying spiral consuming the town," use "spiral shapes in the clouds and on buildings." The concept survives through visual contrast alone — no need to label it as horror.
      
      **Known blockers:** terrifying, disturbing, grotesque, contorted, twisted (when applied to figures), Uzumaki, body horror.
      
      ### 3.2 Threatening Character Descriptions
      
      **Symptom:** `/v1/images/edits` blocks prompts with "henchmen," "weapons," "crime scene," or threatening-character groups.
      
      **Fix:** Rephrase as neutral physical descriptions: "a group of men in dark suits" not "henchmen." Remove weapon references. Describe the scene's visual elements without labeling their intent.
      
      ---
      
      ## 4. Provider-Specific Quirks
      
      ### 4.1 OpenAI /v1/images/edits
      
      | Issue | Workaround |
      |-------|------------|
      | Text vanishes from edits | Include explicit preservation instructions |
      | Square input only | Use `--pad` flag to preserve non-square content |
      | Extra auto-generated text (labels, headlines) | Explicitly specify "ONLY the following text: [exact text]. No other text anywhere." |
      | Reference doesn't guarantee fidelity | Restate all identifiers in prompt |
      
      ### 4.2 DALL-E 3 (Text-Only)
      
      | Issue | Workaround |
      |-------|------------|
      | Can't use reference images | Must specify all details in the prompt text |
      | Sad default expression | Explicit negation required |
      | Wrong headphones | Must specify color and structure |
      | Safety system blocks horror | Strip emotional language, describe visual phenomena only |
      
      ### 4.3 ComfyUI / IPAdapter
      
      | Issue | Workaround |
      |-------|------------|
      | Reference can overpower prompt | Lower denoise to 0.6-0.7 |
      | Texture/loss of analog feel | Post-process with `scripts/postprocess.py` |
      | Style transfer incomplete | Use Reference-Only ControlNet not just IPAdapter |
      
      ---
      
      ## 5. Prompt Engineering Anti-Patterns
      
      ### 5.1 Not Specifying Emotional State
      
      The single most important lesson: if the prompt says anything about the character's emotional state without specifying "not sad," the model defaults to sad. Always end character descriptions with explicit negations of the default failure modes.
      
      ### 5.2 Not Specifying Ethnicity
      
      For manga-style characters, the model defaults to Asian features. Always state "French Caucasian" for the Nous Girl.
      
      ### 5.3 Assuming Reference Images Replace Text Description
      
      Uploading a reference image does NOT replace the need for a detailed text prompt. The reference influences style and composition, but the model still needs to know what to generate. Always provide:
      - Character identifiers (hair, headphones, shirt)
      - Palette constraints (hex values if possible)
      - Expression (with exclusions)
      - What NOT to include (teardrop, markings, dark headphones)
      
      ---
      
      ## 6. Pipeline / Tooling Pitfalls
      
      ### 6.1 `generate-with-ref.py` Crashes
      
      If the script crashes, check:
      - Python dependencies: `pillow` installed?
      - Reference path exists: `ls -la assets/`
      - Config file readable: `~/.hermes/config.yaml`
      - Active image provider set in config
      
      ### 6.2 Post-Processing Too Aggressive
      
      If `scripts/postprocess.py --mode imprint` destroys too much detail, reduce intensity or switch mode:
      - `--intensity 0.45` for fine linework
      - `--mode nous` for legacy luminous targets (no xerox/registration/scuffs)
      - `--mode standard` for light touch only
      
      ### 6.3 Reference Images Are Placeholders
      
      The assets in `assets/` are full-resolution reference images. If the skill was installed via `cp -r` and the assets directory is empty, sync from the repo:
      ```bash
      cp -r /path/to/agent-skills/nous-branding/assets/ ~/.hermes/skills/creative/nous-branding/assets/
      ```
      
      ---
      
      ## Quick Reference: Fixes for Common Problems
      
      | Problem | Fix |
      |---------|-----|
      | Sad expression | Add "not sad, not crying" to prompt |
      | Black headphones | Specify "white over-ear headphones" + "cushioned white band arches across crown" |
      | Teardrop/markings | Add "no tattoos, no scars, no markings, no teardrop" |
      | Wrong ethnicity | Add "French Caucasian, NOT Asian" |
      | Wrong clothing | Add "white collared shirt, not a hoodie" |
      | Glossy anime instead of manga | Use "1970s-80s cel-shaded manga style, high-contrast black ink on white paper" |
      | Text disappears in edits | Add "Keep all text exactly as shown" or add text post-process |
      | Image too clean | Run `python3 scripts/postprocess.py input.png output.png --mode imprint --intensity 0.7` |
      | Reference not matching | Use sketch sheet, restate all identifiers in prompt |
      
    • post-processing.md 2.1 KB
      # Nous Branding — Post-Processing
      
      Load this file whenever a generated image is about to be delivered: raw AI
      output is never the final deliverable. This file holds the post-processing
      modes, intensity calibration, and integration commands for
      `scripts/postprocess.py`.
      
      Raw AI-generated images are too clean for the Nous aesthetic.
      **Post-processing is mandatory** after every generation. The
      `scripts/postprocess.py` script applies analog-print degradation effects
      locally using Pillow + numpy — no API calls needed.
      
      ## Quick Start
      
      ```bash
      python3 scripts/postprocess.py input.png output.png --mode imprint --intensity 0.7
      ```
      
      ## Modes
      
      | Mode | Effects | When |
      |------|---------|------|
      | `imprint` | All 14 effects: warm grade, CRT scanlines, film grain, Bayer dither, vignette, chromatic aberration, screen print texture, paper fiber, ink bleed, palette compression, xerox threshold, registration offset, plate wobble, print scuffs | **Default for v9/v10/v11 targets** — maximum analog print character |
      | `nous` | Base 9 effects (warm grade → ink bleed) — no xerox/registration/wobble/scuffs | Legacy luminous PNW/celestial requests |
      | `standard` | Base 6 effects (warm grade → chromatic aberration) only | When you want just a light texture touch |
      
      ## Intensity Calibration
      
      | Intensity | Best for |
      |-----------|----------|
      | `0.45–0.55` | Fine manga linework, Future Halftone, Portal Minimal — keep detail visible |
      | `0.55–0.65` | Blueprint Scene, general use — avoid crushing midtones |
      | `0.65–0.8` | Xerox Poster, Acid Signal, heavy print effect — when the raw output is too clean |
      | `0.8+` | Aggressive degradation — typography may become hard to read |
      
      ## Integration
      
      Run post-processing as the final step after any generation method (text-only,
      img2img, multi-pass, or any provider):
      
      ```bash
      # After any generation method:
      python3 scripts/postprocess.py output-raw.png output-final.png --mode imprint --intensity 0.7
      
      # For legacy luminous targets:
      python3 scripts/postprocess.py output-raw.png output-final.png --mode nous --intensity 0.5
      ```
      
      **The raw generated image is never the final deliverable.**
      
    • style-lanes.md 6.1 KB
      # Nous Branding — Style Lanes & Reference Catalog
      
      Load this file when choosing a style lane for a Nous-branded image: it encodes
      each lane's composition grammar, palette discipline, print technology, prompt
      cues, and example prompts, plus the mapping from `assets/` reference images to
      lanes. The lane overview table also lives in SKILL.md; this file is the deep
      version you need before writing a lane-specific prompt.
      
      ## Lane Selection
      
      Choose the lane that matches your output's purpose. Each lane encodes a
      distinct composition grammar, palette discipline, and print technology.
      
      | Lane | Palette | Best For |
      |------|---------|----------|
      | **Xerox Poster** | Pale cyan paper, black/dark teal ink, optional red accent | Release announcements, stark social images |
      | **Manual / Letterpress Cover** | Aged tan paper, dark teal-black ink, red rule | Specs, manuals, technical announcements |
      | **Industrial Duotone** | Cobalt blue, acid yellow, black | Product shots, system graphics, infrastructure |
      | **Minimal Stipple Field** | Cream paper, navy stipple, turquoise border | Quiet editorial covers, abstract banners |
      | **Blue Registration Character** | Deep blue, orange registration marks | Agent identity, symbolic personas |
      | **Legacy PNW / Celestial** | Electric blue, purple, amber, off-white | Classic luminous Hermes/PNW requests |
      
      ## Lane 1 — Xerox Poster
      
      For stark announcements and punchy social images. High-contrast xerox-style
      poster on colored paper stock.
      
      Prompt cues: high-contrast xerox poster, pale cyan paper stock, black/dark teal
      ink, dense horizontal scanlines, harsh bitmap thresholding, bold block `NOUS`
      wordmark near top, centered anonymous subject, worn border/crop marks.
      
      ```text
      High-contrast xerox-style poster release announcement on pale cyan paper. [Subject description]. Black ink with dense horizontal scanlines, rough halftone breakup. Bold block `NOUS` wordmark near top. Centered poster composition with worn border and crop marks. --ar 16:9
      ```
      
      ## Lane 2 — Manual / Letterpress Cover
      
      For specs, manuals, and technical documentation covers. Distressed letterpress
      on aged stock.
      
      Prompt cues: distressed letterpress technical manual cover, aged tan paper
      stock, thick dark teal-black border, compact all-caps condensed typography,
      red horizontal rule accent, scuffed ink, worn paper corners, `NOUS` as leading
      stamped brand word.
      
      ```text
      Distressed letterpress technical manual cover for [project]. Aged tan paper stock, thick dark teal-black border, compact all-caps condensed typography, red horizontal rule accent, scuffed ink and worn paper corners. `NOUS` as leading stamped brand word. --ar 4:5
      ```
      
      ## Lane 3 — Industrial Duotone Grid
      
      For product shots, system graphics, and infrastructure visuals. Edge-to-edge
      duotone with repeated artifacts.
      
      Prompt cues: edge-to-edge industrial duotone print, repeated rounded branded
      artifacts, saturated cobalt blue and acid yellow, blown-out ink highlights,
      dense halftone dots, small embedded `NOUS` marks.
      
      ```text
      Edge-to-edge industrial duotone print for [product/release]. Repeated branded components in cobalt blue and acid yellow against black. Dense halftone dots, blown-out ink highlights, small embedded `NOUS` marks on artifacts. --ar 16:9
      ```
      
      ## Lane 4 — Minimal Stipple Field
      
      For quiet, refined, abstract graphics with generous negative space.
      
      Prompt cues: abstract risograph/screenprint poster, cream paper base, navy
      stipple field fading downward, thin turquoise double border, small centered
      `NOUS` capsule mark, lots of negative space.
      
      ```text
      Abstract risograph poster on cream paper. Navy stipple field fading downward, thin turquoise double border, small centered `NOUS` capsule mark. Generous negative space. Restrained, editorial. --ar 4:5
      ```
      
      ## Lane 5 — Blue Registration Character
      
      For agent identity and symbolic character posters. Moody blue screenprint with
      technical registration marks.
      
      Prompt cues: moody blue screenprint poster, anonymous illustrated character,
      electric-blue posterized lighting, dark cyan-black field, orange registration
      marks, thin frame lines, scratches, analog grain.
      
      ```text
      Moody blue screenprint poster for [project/agent]. Anonymous character subject with electric-blue posterized lighting against dark cyan-black field. Orange registration marks, thin frame lines, scratches, analog grain. --ar 4:5
      ```
      
      ## Legacy Lane — PNW / Celestial
      
      For classic Nous/Hermes luminous imagery. Used when the request explicitly asks
      for the misty, glowing, portal-driven aesthetic.
      
      Prompt cues: dark navy background, misty atmospheric depth, portal/orb/beam
      light source, electric blue and purple accents, sacred geometry, lone figure.
      
      ```text
      Dark atmospheric scene with [subject description]. Misty PNW atmosphere, luminous portal/orb light source, electric blue accent, soft bloom, geometric framing. Match the classic luminous Hermes visual style. --ar 16:9
      ```
      
      ## Reference Catalog
      
      Each asset in `assets/` maps to specific style lanes. Use this table to find
      the right reference image for your generation:
      
      | Asset | Lanes | Best Used For |
      |-------|-------|---------------|
      | `assets/nous-girl-official-badge.png` | All lanes (mascot subject) | Primary Nous Girl reference — badge portrait, white headphones, 3/4 profile |
      | `assets/nous-girl-sketch-sheet.png` | All lanes (mascot subject) | Character pose reference — all 4 canonical poses |
      | `assets/nous-girl-philosophy.png` | Legacy PNW / Celestial | Brand philosophy visual context |
      | `assets/nous-girl-official.webp` | All lanes (mascot subject) | Web-resolution mascot from nousresearch.com |
      | `assets/palette-typography-reference.png` | All lanes (palette/style) | Color palette and typography specimen reference — use with any lane |
      | `assets/brand-collage-reference.png` | Legacy PNW / Celestial, Blue Registration | Cyber-classical HUD collage reference |
      | `assets/nous-girl-style-reference.png` | All lanes (headphone variant) | Stylized mascot with headphones and electric blue accents |
      
      **Rule of thumb:** Load the reference image that matches your lane's visual
      grammar. For Xerox Poster lanes, the palette-typography card is more useful
      than the mascot badge. For character-focused outputs, the sketch sheet is the
      primary reference.
      
    • visual-system.md 6.5 KB
      # Nous Branding — Visual System
      
      Load this file for the full visual-system specification: color palette, the
      Nous Girl mascot and her canonical poses, typography, texture system, and the
      cyber-classical art style (including what the style is NOT). SKILL.md keeps
      only the overview and compliance rules; this file holds the detail you need
      when constructing or reviewing an image against the brand system.
      
      ## Color Palette
      
      ### Hero Palette
      
      | Color | Hex | RGB | Usage |
      |-------|-----|-----|-------|
      | Electric Blue | `#3847FF` | (56, 71, 255) | Primary accent, mascot hair highlights, interactive elements, key brand color |
      | Soft Lavender | `#BDA6FF` | (189, 166, 255) | Secondary accent, gradient blends, soft highlights |
      | Burnt Orange | `#D6825A` | (214, 130, 90) | Geometric overlay lines, text accents, HUD elements |
      | Deep Teal | `#2E706B` | (46, 112, 107) | Secondary backgrounds, zine section fills |
      | Off-White | `#E6E6E6` | (230, 230, 230) | Text on dark backgrounds, paper backgrounds |
      | Near Black | `#00000E` | (0, 0, 14) | Primary background, heavy text, dark framing |
      
      ### Extended Palette
      
      | Color | Hex | Usage |
      |-------|-----|-------|
      | Deep Navy | `#003681` | Secondary brand color, headers |
      | Medium Blue | `#0051c3` | Interactive elements, links |
      | Coral Red | `#fc574a` | Alerts, emphasis accents |
      | Gold | `#E6C666` | Constellation lines, geometric overlays |
      | Charcoal | `#1d1d1d` | Dark UI backgrounds |
      
      ### Palette Principles
      
      - **High contrast is the rule** — deep near-black backgrounds against bright electric blue accents
      - **Electric blue (#3847FF) is the signature color** — use it for the most important accent elements
      - **Grunge textures over solid colors** — never use flat, clean color blocks; always overlay with grain, noise, or scan lines
      - **Color is reserved for hero/feature content** — the brand booklet is >95% grayscale
      - **Gold/orange geometric lines** (`#D6825A`, `#E6C666`) for HUD-style overlays and constellation motifs
      
      ## Logo & Mascot
      
      ### The Nous Girl (primary mascot)
      
      The single most recognizable brand element. Based on the official brand
      booklet (pages 8-9).
      
      | Element | Description |
      |---------|-------------|
      | **Style** | Retro 1970s-80s manga/anime cel-shaded, high-contrast pure black/white |
      | **Head-to-body** | Head proportionally large (manga proportions) |
      | **Pose** | Three-quarter profile, looking upward and to the left |
      | **Features** | One large detailed eye with long spiky lashes, small delicate nose, lips slightly parted |
      | **Hair** | Distinctive voluminous bob, solid black with sharp jagged edges against white background, straight bangs covering forehead |
      | **Headphones** | The white cushioned arch visible across the crown IS the headphone band — the ear cups are partially obscured by the hair in the badge portrait. **White over-ear headphones** in all canonical poses. Black headphones are incorrect. |
      | **Alternate** | 3 alternate poses showing different angles of the same headphone variant |
      | **Expression** | Neutral, calm, attentive — a quiet stillness. Not sad, not melancholic, not crying. The brand booklet describes her as "neutral, perhaps slightly surprised or attentive." |
      | **Shirt** | White structured collared shirt |
      | **Technique** | Pure black ink on white paper — no gray, no shading, no digital gradients |
      | **Facial markings** | None — clean, clear skin, no tattoo, no tear, no scar |
      
      ### Official Pose Variants
      
      | Pose | Angle | Primary Use |
      |------|-------|-------------|
      | **Primary Badge** | Three-quarter profile, looking upward-left | Main logo lockup, merchandise, official branding |
      | **Headphone — ¾ Profile** | Three-quarter facing right | Hermes/Theia ecosystem, tech contexts |
      | **Headphone — Profile Left** | Full profile facing left | Social media, alternate applications |
      | **Headphone — Small Profile** | Smaller profile facing left | Secondary placement, watermarks |
      
      ### Usage Rules
      
      - The mascot is **not a substitute for the logo** — use the Nous Research wordmark/symbol for official brand representation
      - **White over-ear headphones** in all poses — the ear cups are partially obscured by the voluminous hair in the badge portrait, but the white cushioned arch across the crown confirms headphones in every variant
      - Black headphones are incorrect
      - **No facial markings** — clean skin, no tattoo, no scar, no tear
      - Pure black-and-white high-contrast is the default; electric blue accents are for hero/feature content only
      
      ## Typography
      
      From the Hermes-Theia brand system sheet (visible in
      `assets/palette-typography-reference.png`):
      
      | Role | Font | Treatment |
      |------|------|-----------|
      | **Display / Headline** | Heavy sans-serif (Druk Condensed / Impact style) | Massive, uppercase, with distressed/grunge texture |
      | **Body / Supporting** | Inter or IBM Plex Sans | Clean sans-serif, uppercase with loose tracking |
      | **Code / Technical** | JetBrains Mono | Small, compact, monospace — for version numbers, technical labels |
      
      ## Texture System
      
      The brand defines five key textures that should be applied to all visuals.
      Reference examples available in `assets/palette-typography-reference.png`:
      
      | Texture | Description | Application |
      |---------|-------------|-------------|
      | **Risograph Grain** | Coarse, halftone-style dot grain | Background fills, image overlays |
      | **Photocopy Noise** | Speckled noise, static | Shadow areas, dark regions |
      | **CRT / Scan Lines** | Horizontal scan lines | Technical/screen elements |
      | **Paper Fiber** | Subtle paper texture | Backgrounds, zine sections |
      | **Ink Smudge** | Irregular ink spread/bleed | Edges of type, borders |
      
      **Key rule:** These textures should feel **raw, analog, and imperfect** — the
      opposite of polished corporate design.
      
      ## Art Style
      
      ### Core Attributes
      
      | Attribute | Description |
      |-----------|-------------|
      | **Primary aesthetic** | "Cyber-classical" — classical sculpture meets cyberpunk |
      | **Medium** | Digital mixed media: renders + photomontage + hand-drawn illustration |
      | **Technique** | Photomontage (classical statues with digital overlays), blueprint technical drawings, cel-shaded anime |
      | **Shading** | High-contrast chiaroscuro — deep shadows against bright highlights |
      | **Texture** | Always textured — never clean or flat |
      | **Lighting** | Dramatic spot lighting, light beams from eyes, neon edge highlights |
      | **Composition** | Multi-panel grid layouts (system sheets); dramatic offset subjects (hero images) |
      
      ### What the Style is NOT
      
      - NOT flat vector illustration
      - NOT corporate minimalist
      - NOT purely photographic
      - NOT bright, saturated, or cheerful
      - NOT glossy/skeuomorphic
      
  • scripts
    • generate-with-ref.py 10.9 KB
      #!/usr/bin/env python3
      """
      Nous Branding — Reference-Image-Driven Generation
      
      Reads the Hermes Agent config to determine which image-generation provider
      is active, then hits that provider's API directly with a reference image
      as contextual input (bypassing the built-in image_generate tool which only
      supports text prompts).
      
      Supported providers:
        - OpenAI (gpt-image-2 via /v1/images/edits)
        - More to come (ComfyUI, Replicate, etc.)
      
      Usage:
        python3 scripts/generate-with-ref.py [options]
      
      Options:
        --prompt TEXT        Generation prompt (required)
        --reference PATH     Path to reference image (required)
        --aspect RATIO       landscape|portrait|square (default: landscape)
        --quality Q          low|medium|high (default: medium)
        --output PATH        Output file path (default: auto-named in cache)
        --provider NAME      Force provider (default: read from config)
        --dry-run            Print what would be sent without executing
        --debug              Print full request/response details
      """
      
      import argparse
      import base64
      import json
      import os
      import re
      import sys
      import time
      from pathlib import Path
      from typing import Any, Dict, Optional, Tuple
      
      # ---------------------------------------------------------------------------
      # Config helpers
      # ---------------------------------------------------------------------------
      
      HERMES_HOME = Path(os.environ.get("HERMES_HOME", Path.home() / ".hermes"))
      CACHE_DIR = HERMES_HOME / "cache" / "images"
      CACHE_DIR.mkdir(parents=True, exist_ok=True)
      
      SIZES = {
          "landscape": (1536, 1024),
          "square": (1024, 1024),
          "portrait": (1024, 1536),
      }
      
      SIZE_STR = {
          "landscape": "1536x1024",
          "square": "1024x1024",
          "portrait": "1024x1536",
      }
      
      
      def load_config() -> Dict[str, Any]:
          """Load Hermes config.yaml — returns {} on failure."""
          try:
              import yaml
              cfg_path = HERMES_HOME / "config.yaml"
              if cfg_path.exists():
                  with open(cfg_path) as f:
                      return yaml.safe_load(f) or {}
              return {}
          except Exception:
              return {}
      
      
      def read_env(key: str) -> Optional[str]:
          """Read a key from ~/.hermes/.env"""
          env_path = HERMES_HOME / ".env"
          if not env_path.exists():
              return os.environ.get(key)
          with open(env_path) as f:
              for line in f:
                  line = line.strip()
                  if line.startswith(key + "="):
                      val = line.split("=", 1)[1].strip("\"'")
                      if val:
                          return val
          return os.environ.get(key)
      
      
      def get_image_gen_config() -> Tuple[str, str]:
          """
          Return (provider_name, model_id) from config.yaml.
          Falls back to ('openai', 'gpt-image-2-medium').
          """
          cfg = load_config()
          ig = cfg.get("image_gen", {}) or {}
          provider = ig.get("provider", "openai")
          model = ig.get("model", "gpt-image-2-medium")
          return provider, model
      
      
      # ---------------------------------------------------------------------------
      # Image helpers
      # ---------------------------------------------------------------------------
      
      def prepare_reference_image(path: str, aspect: str) -> bytes:
          """
          Load a reference image, crop to square (for edits endpoint),
          resize to 1024x1024, return PNG bytes.
          """
          try:
              from PIL import Image
          except ImportError:
              print("ERROR: PIL/Pillow is required. Install with: pip install Pillow",
                    file=sys.stderr)
              sys.exit(1)
      
          img = Image.open(path).convert("RGB")
      
          # For edits endpoint, the image must be square. Crop center square.
          target_size = 1024
          sz = min(img.size)
          left = (img.width - sz) // 2
          top = (img.height - sz) // 2
          img_cropped = img.crop((left, top, left + sz, top + sz))
          img_resized = img_cropped.resize((target_size, target_size), Image.LANCZOS)
      
          import io
          buf = io.BytesIO()
          img_resized.save(buf, format="PNG")
          return buf.getvalue()
      
      
      def save_output(b64: str, output_path: Optional[str] = None) -> str:
          """Save base64 image data to disk, return path."""
          if output_path is None:
              ts = time.strftime("%Y%m%d_%H%M%S")
              output_path = str(CACHE_DIR / f"nous_ref_{ts}.png")
          with open(output_path, "wb") as f:
              f.write(base64.b64decode(b64))
          return output_path
      
      
      def revised_prompt_from_meta(revised: Optional[str]) -> Optional[str]:
          """Return a revised_prompt if it differs meaningfully from original."""
          if revised and len(revised) > 10:
              return revised
          return None
      
      
      # ---------------------------------------------------------------------------
      # Provider handlers
      # ---------------------------------------------------------------------------
      
      def generate_openai(
          prompt: str,
          reference_bytes: bytes,
          aspect: str,
          quality: str,
          model: str,
          dry_run: bool = False,
          debug: bool = False,
      ) -> Dict[str, Any]:
          """
          Use OpenAI /v1/images/edits with a reference image.
          Supports gpt-image-2 (low/medium/high quality).
          """
          api_key = read_env("OPENAI_API_KEY")
          if not api_key:
              return {"error": "OPENAI_API_KEY not set in ~/.hermes/.env"}
      
          # Strip quality suffix from model name to get the API model
          api_model = "gpt-image-2"
      
          # Build multipart form-data
          boundary = f"----NousFormBoundary{int(time.time())}x"
      
          def field(name: str, value: str) -> bytes:
              return f"--{boundary}\r\nContent-Disposition: form-data; name=\"{name}\"\r\n\r\n{value}\r\n".encode()
      
          body = b""
          body += field("model", api_model)
          body += f'--{boundary}\r\nContent-Disposition: form-data; name="image"; filename="reference.png"\r\nContent-Type: image/png\r\n\r\n'.encode()
          body += reference_bytes
          body += b"\r\n"
          body += field("prompt", prompt)
          body += field("n", "1")
          body += field("size", SIZE_STR[aspect])
          body += field("quality", quality)
          body += f"--{boundary}--\r\n".encode()
      
          if dry_run:
              print(f"[DRY RUN] Would POST to https://api.openai.com/v1/images/edits")
              print(f"  Model: {api_model}")
              print(f"  Size: {SIZE_STR[aspect]}")
              print(f"  Quality: {quality}")
              print(f"  Prompt length: {len(prompt)} chars")
              print(f"  Image bytes: {len(reference_bytes)}")
              return {"status": "dry_run"}
      
          import urllib.request
          req = urllib.request.Request(
              "https://api.openai.com/v1/images/edits",
              data=body,
              headers={
                  "Content-Type": f"multipart/form-data; boundary={boundary}",
                  "Authorization": f"Bearer {api_key}",
              },
          )
      
          if debug:
              print(f"POST https://api.openai.com/v1/images/edits")
              print(f"  Model: {api_model}, Size: {SIZE_STR[aspect]}, Quality: {quality}")
              print(f"  Prompt: {prompt[:120]}...")
              print(f"  Image bytes: {len(reference_bytes)}")
              sys.stdout.flush()
      
          try:
              resp = urllib.request.urlopen(req, timeout=180)
              result = json.loads(resp.read().decode())
          except Exception as exc:
              error_body = ""
              if hasattr(exc, "read"):
                  try:
                      error_body = exc.read().decode()[:2000]
                  except Exception:
                      pass
              return {"error": f"API call failed: {exc}", "detail": error_body}
      
          import logging
          data_list = result.get("data", [])
          if not data_list:
              return {"error": "API returned no image data"}
      
          first = data_list[0]
          b64_json = first.get("b64_json")
          revised = first.get("revised_prompt")
      
          if not b64_json:
              return {"error": "API response missing b64_json"}
      
          return {
              "b64_json": b64_json,
              "revised_prompt": revised,
              "model": model,
              "api_model": api_model,
          }
      
      
      # ---------------------------------------------------------------------------
      # Main
      # ---------------------------------------------------------------------------
      
      PROVIDER_HANDLERS = {
          "openai": generate_openai,
      }
      
      
      def main():
          parser = argparse.ArgumentParser(
              description="Generate images with reference image as context"
          )
          parser.add_argument("--prompt", required=True, help="Generation prompt")
          parser.add_argument("--reference", required=True, help="Path to reference image")
          parser.add_argument("--aspect", choices=["landscape", "portrait", "square"],
                              default="landscape", help="Aspect ratio")
          parser.add_argument("--quality", choices=["low", "medium", "high"],
                              default="medium", help="Quality tier")
          parser.add_argument("--output", help="Output file path")
          parser.add_argument("--provider", help="Force provider (default: from config)")
          parser.add_argument("--dry-run", action="store_true", help="Preview without executing")
          parser.add_argument("--debug", action="store_true", help="Verbose output")
      
          args = parser.parse_args()
      
          # Resolve provider
          provider = args.provider
          if not provider:
              provider, model = get_image_gen_config()
          else:
              _, model = get_image_gen_config()
      
          # Read model from config
          _, model = get_image_gen_config()
      
          if args.debug:
              print(f"Provider: {provider}")
              print(f"Model: {model}")
              print(f"Reference: {args.reference}")
              sys.stdout.flush()
      
          # Validate reference image exists
          ref_path = Path(args.reference)
          if not ref_path.exists():
              print(f"ERROR: Reference image not found: {ref_path}", file=sys.stderr)
              sys.exit(1)
      
          # Get handler for this provider
          handler = PROVIDER_HANDLERS.get(provider)
          if not handler:
              print(f"ERROR: Unsupported provider '{provider}'. "
                    f"Supported: {list(PROVIDER_HANDLERS.keys())}", file=sys.stderr)
              sys.exit(1)
      
          # Prepare reference image
          try:
              ref_bytes = prepare_reference_image(str(ref_path), args.aspect)
          except Exception as exc:
              print(f"ERROR: Could not process reference image: {exc}", file=sys.stderr)
              sys.exit(1)
      
          if args.debug:
              from PIL import Image
              import io
              img = Image.open(io.BytesIO(ref_bytes))
              print(f"Reference prepared: {img.size[0]}x{img.size[1]} PNG, "
                    f"{len(ref_bytes) / 1024:.0f} KB")
              sys.stdout.flush()
      
          # Generate
          result = handler(
              prompt=args.prompt,
              reference_bytes=ref_bytes,
              aspect=args.aspect,
              quality=args.quality,
              model=model,
              dry_run=args.dry_run,
              debug=args.debug,
          )
      
          if args.dry_run:
              return
      
          if "error" in result:
              print(f"ERROR: {result['error']}", file=sys.stderr)
              if result.get("detail"):
                  print(f"Detail: {result['detail']}", file=sys.stderr)
              sys.exit(1)
      
          # Save output
          out_path = save_output(result["b64_json"], args.output)
      
          # Print result (machine-readable JSON for the agent)
          output = {
              "image": out_path,
              "model": result.get("model", "unknown"),
              "aspect_ratio": args.aspect,
              "provider": provider,
          }
          revised = result.get("revised_prompt")
          if revised:
              output["revised_prompt"] = revised
      
          print(json.dumps(output, indent=2))
      
      
      if __name__ == "__main__":
          main()
      
    • postprocess.py 11.4 KB
      #!/usr/bin/env python3
      """
      postprocess.py — Analog print effects for Nous-branded images.
      
      Applies a sequence of analog-print degradation effects to AI-generated images,
      transforming clean digital output into something that looks physically printed,
      xeroxed, or risographed.
      
      Inspired by plntrprotocol/nous-branding (MIT) — same goals, independent
      implementation. See https://github.com/plntrprotocol/nous-branding
      
      Modes:
        --mode imprint    Full 14-effect print degradation (default, for v9/v10/v11)
        --mode nous       Legacy: warm grade + grain + screen print (no xerox/registration)
        --mode standard   Light touch: grain + vignette + chroma aberration only
      
      Usage:
        python3 scripts/postprocess.py input.png output.png --mode imprint --intensity 0.7
      """
      
      import argparse
      import os
      import numpy as np
      from PIL import Image, ImageFilter, ImageEnhance, ImageOps
      
      # ── Imprint palette (constrained 2-4 ink print colors for palette compression) ──
      IMPRINT_PALETTE = [
          "#F7EDE3", "#E8E0D4", "#C15811", "#F59E0B",
          "#0E2723", "#1A3A32", "#00AEEF", "#8B5CF6",
          "#6B7280", "#2D5016", "#0A0A1A", "#D946EF",
          "#BFE8F2", "#071616", "#F04A23", "#D8D061",
      ]
      
      
      def warm_grade(img, strength=0.15):
          """Warm color grade — push shadows toward amber/gold."""
          arr = np.array(img).astype(np.float32)
          lum = (arr[:, :, 0] * 0.299 + arr[:, :, 1] * 0.587 + arr[:, :, 2] * 0.114) / 255
          shadow = 1.0 - lum
          arr[:, :, 0] += shadow * strength * 12
          arr[:, :, 2] -= shadow * strength * 6
          return Image.fromarray(np.clip(arr, 0, 255).astype(np.uint8))
      
      
      def crt_scanlines(img, opacity=0.06, every=2):
          """Faint horizontal CRT scanlines."""
          w, h = img.size
          overlay = Image.new("L", (w, h), 255)
          px = overlay.load()
          for y in range(h):
              if y % every == 0:
                  for x in range(w):
                      px[x, y] = int(255 * (1 - opacity))
          return Image.composite(img, Image.new("RGB", img.size, (0, 0, 0)), overlay)
      
      
      def film_grain(img, intensity=0.04):
          """Fine Gaussian noise across entire image."""
          w, h = img.size
          noise = np.random.normal(0, 255 * intensity, (h, w, 3)).astype(np.float32)
          return Image.fromarray(np.clip(np.array(img).astype(np.float32) + noise, 0, 255).astype(np.uint8))
      
      
      def bayer_dither(img, strength=0.3, levels=12):
          """4x4 ordered Bayer dither matrix — simulates risograph halftone."""
          bayer = np.array([[0, 8, 2, 10], [12, 4, 14, 6], [3, 11, 1, 9], [15, 7, 13, 5]], dtype=np.float32)
          bayer_n = (bayer / 16.0 - 0.5) * strength
          w, h = img.size
          tile = np.tile(bayer_n, ((h + 3) // 4, (w + 3) // 4))[:h, :w]
          tile3 = np.stack([tile] * 3, axis=-1) * 255
          arr = np.array(img).astype(np.float32)
          q = np.round(arr / (256 / levels)) * (256 / levels)
          d = q + tile3 * (256 / levels) / levels
          return Image.fromarray(np.clip(d, 0, 255).astype(np.uint8))
      
      
      def vignette(img, strength=0.35):
          """Darken and warm edges toward corners."""
          w, h = img.size
          x = np.linspace(-1, 1, w)
          y = np.linspace(-1, 1, h)
          xx, yy = np.meshgrid(x, y)
          dist = np.sqrt(xx**2 + yy**2)
          mask = np.clip(1.0 - (dist / np.sqrt(2)) * strength, 0.3, 1.0)
          arr = np.array(img).astype(np.float32)
          m3 = np.stack([mask] * 3, axis=-1)
          v = arr * m3
          edge = (1 - mask) * 8
          v[:, :, 0] += edge
          v[:, :, 2] -= edge * 0.5
          return Image.fromarray(np.clip(v, 0, 255).astype(np.uint8))
      
      
      def chromatic_aberration(img, shift=1.0):
          """Subtle RGB channel separation at edges."""
          arr = np.array(img)
          px = max(1, int(round(shift)))
          r = np.roll(arr[:, :, 0], -px, axis=1)
          b = np.roll(arr[:, :, 2], px, axis=1)
          out = arr.copy()
          out[:, :, 0] = r
          out[:, :, 2] = b
          return Image.fromarray(out)
      
      
      def screen_print_texture(img, intensity=0.3):
          """Simulate risograph/screen-print halftone dot pattern."""
          w, h = img.size
          dot_size = 3
          yy, xx = np.mgrid[0:h, 0:w]
          offset = (np.arange(h) // dot_size % 2) * (dot_size // 2)
          cx = (xx + offset[:, None]) % dot_size
          cy = yy % dot_size
          dist = np.sqrt((cx - dot_size / 2) ** 2 + (cy - dot_size / 2) ** 2)
          lum = np.array(img.convert("L")).astype(float) / 255.0
          dot_mask = np.clip(1.0 - dist / (dot_size * 0.7), 0, 1)
          dot_mask = dot_mask * (1.0 - lum) * intensity
          dot_3 = np.stack([dot_mask] * 3, axis=-1)
          arr = np.array(img).astype(np.float32)
          textured = arr * (1.0 - dot_3 * 0.15)
          return Image.fromarray(np.clip(textured, 0, 255).astype(np.uint8))
      
      
      def paper_texture(img, intensity=0.15):
          """Subtle paper/canvas fiber substrate."""
          w, h = img.size
          noise = np.random.normal(0, 1, (max(1, h // 4), max(1, w // 4))).astype(np.float32)
          denom = max(float(noise.max() - noise.min()), 1e-6)
          n_img = Image.fromarray(((noise - noise.min()) / denom * 255).astype(np.uint8))
          n_img = n_img.resize((w, h), Image.Resampling.BILINEAR)
          n_arr = np.array(n_img).astype(float) / 255.0
          n_3 = np.stack([n_arr] * 3, axis=-1)
          arr = np.array(img).astype(float)
          textured = arr * (1.0 + (n_3 - 0.5) * intensity)
          return Image.fromarray(np.clip(textured, 0, 255).astype(np.uint8))
      
      
      def ink_bleed(img, intensity=0.2):
          """Slight blur + darken at dark edges to simulate ink spread on paper."""
          blurred = img.filter(ImageFilter.GaussianBlur(radius=0.5))
          arr_orig = np.array(img).astype(float)
          arr_blur = np.array(blurred).astype(float)
          edges_img = img.convert("L").filter(ImageFilter.FIND_EDGES).filter(ImageFilter.GaussianBlur(radius=0.6))
          edges = np.array(edges_img).astype(float)
          if edges.max() > 0:
              edges = edges / edges.max()
          edge_3 = np.stack([edges] * 3, axis=-1)
          blended = arr_orig * (1 - edge_3 * intensity) + arr_blur * (edge_3 * intensity)
          return Image.fromarray(np.clip(blended, 0, 255).astype(np.uint8))
      
      
      def palette_compress(img, strength=0.45):
          """Pull clean RGB renders toward a limited 2-4 ink print palette."""
          palette = Image.new("P", (1, 1))
          colors = []
          for hx in IMPRINT_PALETTE:
              hx = hx.lstrip("#")
              colors.extend([int(hx[i:i + 2], 16) for i in (0, 2, 4)])
          colors.extend([0] * (768 - len(colors)))
          palette.putpalette(colors)
          quantized = img.quantize(palette=palette, dither=Image.Dither.FLOYDSTEINBERG).convert("RGB")
          return Image.blend(img, quantized, float(np.clip(strength, 0, 1)))
      
      
      def xerox_threshold(img, strength=0.25):
          """Degraded photocopy contrast with breakup."""
          gray = ImageOps.grayscale(img)
          gray = ImageEnhance.Contrast(gray).enhance(1.0 + 2.4 * strength)
          arr = np.array(gray).astype(np.float32)
          noise = np.random.normal(0, 24 * strength, arr.shape)
          arr = np.clip(arr + noise, 0, 255)
          poster = np.where(arr > (128 - 18 * strength), 235, 20).astype(np.uint8)
          tint = ImageOps.colorize(Image.fromarray(poster), black="#071616", white="#DCEAF0")
          return Image.blend(img, tint, float(np.clip(strength * 0.55, 0, 0.45)))
      
      
      def registration_offset(img, shift=1.0, opacity=0.35):
          """Simulate misregistered cyan/orange ink plates."""
          px = max(1, int(round(shift)))
          op = float(np.clip(opacity, 0, 1))
          arr = np.array(img).astype(np.float32)
          cyan = np.roll(arr, -px, axis=1)
          orange = np.roll(arr, px, axis=0)
          out = arr.copy()
          out[:, :, 1] = out[:, :, 1] * (1 - op * 0.16) + cyan[:, :, 1] * op * 0.16
          out[:, :, 2] = out[:, :, 2] * (1 - op * 0.24) + cyan[:, :, 2] * op * 0.24
          out[:, :, 0] = out[:, :, 0] * (1 - op * 0.18) + orange[:, :, 0] * op * 0.18
          return Image.fromarray(np.clip(out, 0, 255).astype(np.uint8))
      
      
      def plate_wobble(img, strength=0.35):
          """Subtle row-wise print wobble so crisp lines stop feeling digital."""
          arr = np.array(img)
          h, w = arr.shape[:2]
          rng = np.random.default_rng()
          coarse = rng.normal(0, max(0.15, strength), max(4, h // 48))
          offsets = np.interp(np.arange(h), np.linspace(0, h - 1, len(coarse)), coarse)
          out = np.empty_like(arr)
          for y in range(h):
              out[y] = np.roll(arr[y], int(round(offsets[y])), axis=0)
          return Image.fromarray(out)
      
      
      def print_scuffs(img, intensity=0.25):
          """Sparse scratches and imperfect ink pickup."""
          w, h = img.size
          arr = np.array(img).astype(np.float32)
          scuff = np.zeros((h, w), dtype=np.float32)
          rng = np.random.default_rng()
          for _ in range(int(24 * intensity) + 3):
              y = int(rng.integers(0, h))
              x0 = int(rng.integers(0, max(1, w - 1)))
              length = int(rng.integers(max(8, w // 24), max(12, w // 5)))
              thickness = int(rng.integers(1, 3))
              x1 = min(w, x0 + length)
              scuff[max(0, y - thickness):min(h, y + thickness + 1), x0:x1] = float(rng.uniform(0.25, 0.8))
          scuff_img = Image.fromarray((scuff * 255).astype(np.uint8)).filter(ImageFilter.GaussianBlur(radius=0.6))
          mask = (np.array(scuff_img).astype(np.float32) / 255.0)[:, :, None]
          paper = np.array(Image.new("RGB", img.size, "#F7EDE3")).astype(np.float32)
          out = arr * (1 - mask * intensity * 0.35) + paper * (mask * intensity * 0.35)
          return Image.fromarray(np.clip(out, 0, 255).astype(np.uint8))
      
      
      def process(input_path, output_path, intensity=0.5, mode="imprint"):
          """Run the full processing pipeline."""
          print(f"Processing: {input_path} (mode={mode}, intensity={intensity})")
          img = Image.open(input_path).convert("RGB")
          w, h = img.size
          print(f"  Input: {w}x{h}")
          s = intensity
      
          steps = [
              ("Warm grade", lambda: warm_grade(img, 0.12 * s)),
              ("CRT scanlines", lambda: crt_scanlines(img, 0.04 * s, 2)),
              ("Film grain", lambda: film_grain(img, 0.035 * s)),
              ("Bayer dither", lambda: bayer_dither(img, 0.2 * s, 12)),
              ("Vignette", lambda: vignette(img, 0.3 * s)),
              ("Chromatic aberr.", lambda: chromatic_aberration(img, 0.8 * s)),
          ]
      
          if mode in ("risograph", "nous", "imprint"):
              steps.extend([
                  ("Screen print", lambda: screen_print_texture(img, 0.25 * s)),
                  ("Paper texture", lambda: paper_texture(img, 0.12 * s)),
                  ("Ink bleed", lambda: ink_bleed(img, 0.15 * s)),
              ])
      
          if mode == "imprint":
              steps.extend([
                  ("Palette compress", lambda: palette_compress(img, 0.55 * s)),
                  ("Xerox threshold", lambda: xerox_threshold(img, 0.35 * s)),
                  ("Registration", lambda: registration_offset(img, 1.4 * s, 0.45 * s)),
                  ("Plate wobble", lambda: plate_wobble(img, 0.7 * s)),
                  ("Print scuffs", lambda: print_scuffs(img, 0.35 * s)),
              ])
      
          for i, (name, fn) in enumerate(steps):
              print(f"  [{i + 1}/{len(steps)}] {name}...")
              img = fn()
      
          img.save(output_path, "PNG")
          sz = os.path.getsize(output_path)
          print(f"  Saved: {output_path} ({sz // 1024}KB)")
          return True
      
      
      if __name__ == "__main__":
          parser = argparse.ArgumentParser(
              description="Analog print effects for Nous-branded images."
          )
          parser.add_argument("input", help="Input image path")
          parser.add_argument("output", nargs="?", default=None,
                              help="Output image path (default: input with -processed suffix)")
          parser.add_argument("--intensity", "-i", type=float, default=0.5,
                              help="Effect intensity 0.0-1.0 (default: 0.5)")
          parser.add_argument("--mode", "-m", choices=["standard", "risograph", "nous", "imprint"],
                              default="imprint",
                              help="Processing mode (default: imprint)")
          args = parser.parse_args()
          out = args.output or args.input.replace(".png", "-processed.png")
          process(args.input, out, args.intensity, args.mode)
          print("Done.")
      
  • README.md 1.6 KB
    # Nous Research Brand Identity — Image & Content Generation
    
    Generate images and content consistent with the Nous Research brand identity — a "cyber-classical" style blending neo-classical statuary, cyberpunk grunge, and retro manga illustration.
    
    ## Why Install This Skill
    
    When your agent loads this skill, it can **create on-brand visuals** for the Nous / Theia / Hermes ecosystem. That means:
    
    - **Understand the brand DNA** — classical Greek myth meets cyberpunk meets retro anime
    - **Use the correct color palette** — electric blue, dark grunge, marble tones — with hex-accurate values
    - **Depict the Nous Girl mascot** — canonical poses, expressions, and accessories
    - **Generate consistent imagery** — reference-image-driven workflows for texture, grain, and style
    - **Match brand typography** — Inter, IBM Plex Sans, JetBrains Mono, distressed display faces
    
    ## What You Get
    
    | Directory | Purpose |
    |-----------|---------|
    | `SKILL.md` | Complete brand identity reference with style fusion, color palette, typography, mascot specs |
    | `assets/` | 4+ reference images: color palette card, official mascot, style reference, brand collage — usable as img2img inputs |
    
    ## Triggers
    
    Load this when creating visuals in the Nous Research ecosystem — blog headers, social media, presentation slides, or brand assets.
    
    ## Requirements
    
    Image generation backend for prompt-based workflows. Reference-image (img2img) workflows require an API supporting image inputs.
    
    
    ## Quick Start
    
    Start with the setup and first workflow in SKILL.md, then use the linked resources for the specific task you need to complete.
    
  • SKILL.md 15.6 KB
    ---
    name: nous-branding
    description: >-
      Generate images and content consistent with the Nous Research brand identity. Use when
      creating visuals in the Nous / Theia / Hermes ecosystem: a "cyber-classical" style
      blending neo-classical statuary, cyberpunk/industrial grunge, and retro anime
      illustration. Covers official brand color palette, typography (Inter/IBM Plex Sans,
      JetBrains Mono, heavy distressed display faces), the Nous Girl mascot, texture system,
      and image prompt construction. Ships reference images for palette, mascot, and brand
      collage that can be used as img2img inputs. Do not use this skill for unrelated
      requests; route to the nearest named specialist.
    license: MIT
    compatibility: Compatible with any agent capable of image generation or brand analysis.
      Reference-image workflows (img2img, style transfer, variations) require an API endpoint
      supporting image inputs — use the assets/ images as input.
    metadata:
      tags: nous-research, theia, hermes, brand, illustration, mascot, image-generation,
        style-guide, cyber-classical
      sources: https://nousresearch.com, {"Reference image"=>"assets/palette-typography-reference.png"},
        {"Reference image"=>"assets/nous-girl-official.webp (official mascot, 2669×2709)"},
        {"Reference image"=>"assets/nous-girl-style-reference.png"}, {"Reference image"=>"assets/brand-collage-reference.png"},
        https://nousresearch.com/wp-content/uploads/2024/03/NOUS-BRAND-BOOKLET-firstedition_1.pdf
      version: 1.1.0
    ---
    
    # Nous Branding
    
    Generate images and brand-consistent visual content inspired by **Nous Research** ("The AI accelerator company").
    
    ## When not to use
    
    - **Non-Nous design work** — this skill encodes one specific brand system; do
      not apply its palette, textures, mascot, or compliance rules to unrelated
      brands or generic design requests.
    - **Official brand representation** — the Nous Girl mascot is not a substitute
      for the logo; use the Nous Research wordmark/symbol for official brand use.
    
    ## Reference Images
    
    This skill ships reference images in `assets/` that can be used as visual anchors for img2img, style transfer, image variation, or prompt construction:
    
    | File | Description | Usage |
    |------|-------------|-------|
    | `assets/palette-typography-reference.png` | Brand identity system card showing the 6-color palette swatches with hex codes, typography specimen (Inter, IBM Plex Sans, JetBrains Mono, heavy display), and classified-dossier layout | Upload as reference for color palette and typography style |
    | `assets/nous-girl-official.webp` | Official high-resolution (2669×2709) Nous Girl mascot from nousresearch.com. High-contrast black-and-white retro manga portrait. Three-quarter profile facing left, white headband (primary badge variant). 51% dark / 47% light, pure b&w with no gray. | Primary mascot reference — the single most authentic brand image |
    | `assets/nous-girl-official-badge.png` | Official badge portrait from the brand booklet (5760×7454). Shows the Nous Girl in her canonical form: white headband, three-quarter profile, neutral attentive expression, stark black/white manga style. | Use when the badge/primary variant is needed |
    | `assets/nous-girl-sketch-sheet.png` | Official character sheet from the brand booklet showing all 4 canonical poses: primary badge, headphone ¾ profile, headphone profile left, and headphone small profile. | Use for pose reference and character consistency |
    | `assets/nous-girl-philosophy.png` | Brand philosophy page from the booklet showing the Nous Girl alongside the "decentralization of good design" mission statement. | Use for brand context and philosophy reference |
    | `assets/nous-girl-style-reference.png` | Generated reference portrait with "NOUS" on headphones, electric blue accents, and color swatch label | Color-application reference and prompt examples |
    | `assets/brand-collage-reference.png` | Cyber-classical brand collage with Theia marble statue, glowing electric blue eye with targeting reticle, system architecture diagram, CRT noise overlay | Multi-panel brand layout and HUD aesthetic reference |
    
    ---
    
    ## Brand Identity Overview
    
    Nous Research's visual identity is a **three-way fusion**:
    
    | Influence | Expression |
    |-----------|-----------|
    | **Classical / Greek myth** | Statuary of Theia (Titaness of Sight), marble textures, mythological naming |
    | **Cyberpunk / Industrial** | Grunge textures, CRT scan lines, photocopy noise, distressed type, dark palette |
    | **Retro Anime / Manga** | The "Nous Girl" mascot, cel-shaded illustration, large expressive eyes, 1970s-80s manga aesthetic |
    | **Tech / Brutalist** | Heavy display typography, system diagrams, blueprint-style layouts, monospace code labels |
    
    **Tagline:** "The AI accelerator company"
    **Key phrases:** "Advance human rights and freedoms", "Open source language models", "Unrestricted availability and use"
    **Vibe:** Intellectual but gritty — a cutting-edge research lab operating in the shadows
    
    ---
    
    ## Loading Guide
    
    Load references on demand — do not load everything at once.
    
    | File | Load when |
    |------|-----------|
    | [references/style-lanes.md](references/style-lanes.md) | Choosing a style lane or writing a lane-specific prompt — full lane grammar, prompt cues, example prompts, and the asset-to-lane Reference Catalog |
    | [references/visual-system.md](references/visual-system.md) | Constructing or reviewing an image against the visual system — hero + extended palettes, Nous Girl spec and pose variants, typography, texture system, art style |
    | [references/post-processing.md](references/post-processing.md) | Delivering any generated image — mandatory post-process modes (`imprint`/`nous`/`standard`) and intensity calibration for `scripts/postprocess.py` |
    | [`references/pitfalls.md`](references/pitfalls.md) | Output doesn't match expectations — known failure modes and mitigations |
    
    ---
    
    ## Image Prompt Templates
    
    ### Method 1: Full Brand Portrait
    
    ```
    A cyber-classical brand identity illustration in the style of Nous Research / Project Theia.
    [SUBJECT DESCRIPTION]. High-contrast dramatic lighting with deep near-black background (#00000E).
    Electric blue (#3847FF) primary accent. Soft lavender (#BDA6FF) and burnt orange (#D6825A)
    secondary accents. Deep teal (#2E706B) shadow tones. Overlaid with risograph grain texture,
    photocopy noise, and subtle CRT scan lines. Retro anime cel-shading combined with neo-classical
    sculptural forms. Geometric HUD overlay lines in burnt orange. Bold, distressed display typography.
    Raw, analog, imperfect finish. No corporate polish.
    Palette: #00000E bg, #3847FF accent, #BDA6FF secondary, #D6825A warm, #E6E6E6 text.
    ```
    
    ### Method 2: Nous Girl Mascot
    
    ```
    High-contrast retro manga anime portrait, 1970s-80s cel-shaded style.
    A young woman with large anime eyes, shoulder-length dark hair with blunt
    straight-across bangs. White over-ear headphones. Three-quarter profile facing left.
    Melancholic introspective expression. Bold heavy outlines. Pure black and white with
    no grayscale. [Optional: Electric blue #3847FF hair highlights for color version].
    ```
    
    ### Method 3: Brand System Sheet / Collage
    
    ```
    Multi-panel brand identity system sheet in Nous Research / Project Theia style.
    Grid layout. [Describe panels]. Color palette: #3847FF electric blue, #BDA6FF lavender,
    #D6825A burnt orange, #2E706B deep teal, #E6E6E6 off-white, #00000E near-black.
    Texture swatches: risograph grain, photocopy noise, CRT scan lines, paper fiber, ink smudge.
    Typography: heavy distressed display for titles, Inter/IBM Plex Sans for labels,
    JetBrains Mono for technical data. Grunge textures throughout. Dark near-black background.
    ```
    
    ### Method 4: Reference-Image-Driven Generation (Recommended)
    
    **This is the preferred method for generating brand-consistent images.** Use the `scripts/generate-with-ref.py` script which reads your Hermes config, determines the active image provider, and hits the API directly with the reference image as contextual input — bypassing the built-in `image_generate` tool which only supports text prompts.
    
    ```
    python3 scripts/generate-with-ref.py \
      --prompt "Your prompt describing the desired image" \
      --reference assets/nous-girl-official-badge.png \
      --aspect landscape \
      --quality medium
    ```
    
    **Features:**
    - `--prompt` (required) — image description
    - `--reference` (required) — path to a reference image (use `assets/` images from this skill)
    - `--aspect` — `landscape` (1536×1024), `portrait` (1024×1536), or `square` (1024×1024)
    - `--quality` — `low`, `medium` (default), or `high`
    - `--output` — custom output path
    - `--dry-run` — preview without executing
    
    **How it works:**
    1. Reads `~/.hermes/config.yaml` to find your active image generation provider
    2. For **OpenAI**: uses `/v1/images/edits` with multipart upload — the only endpoint that accepts image input with gpt-image-2
    3. Automatically crops the reference to square (1024×1024) as required by the edits endpoint
    4. Saves output to `~/.hermes/cache/images/`
    5. Returns JSON with `image`, `model`, `aspect_ratio`, and `provider`
    
    **Why this matters:** Text-only generation loses the precise manga style, character proportions, and contrast balance of the Nous Girl. Uploading the official badge preserves the specific 1970s–80s cel-shaded ink style.
    
    **Prompting for reference workflows:**
    State what to **preserve** from the reference, then what to **add**:
    - "Keep the character's white over-ear headphones, white collared shirt, solid black hair with blunt bangs, neutral expression"
    - "Maintain the same high-contrast black ink on white manga style"
    - Then add the scene details, text, lighting, etc.
    
    ### Prompt Formula
    
    ```
    [STYLE: cyber-classical / Nous Research]
    + [SUBJECT DESCRIPTION]
    + [PALETTE: #00000E bg, #3847FF accent, #BDA6FF, #D6825A]
    + [TEXTURES: risograph grain, photocopy noise, CRT scan lines, paper fiber, ink smudge]
    + [LIGHTING: high-contrast chiaroscuro, dramatic spot, neon edge highlights]
    + [MOOD: intellectual, gritty, underground, calm/attentive]
    + [TYPOGRAPHY: heavy distressed display, Inter/IBM Plex Sans labels, JetBrains Mono code]
    ```
    
    ---
    
    ## Post-Processing
    
    Raw AI-generated images are too clean for the Nous aesthetic. **Post-processing is mandatory** after every generation — the raw generated image is never the final deliverable.
    
    ```bash
    python3 scripts/postprocess.py input.png output.png --mode imprint --intensity 0.7
    ```
    
    Run it as the final step after any generation method (text-only, img2img, multi-pass, or any provider). For mode selection (`imprint` / `nous` / `standard`) and intensity calibration per output type, load [references/post-processing.md](references/post-processing.md).
    
    ---
    
    ## API Workflow Notes
    
    | API | Approach |
    |-----|----------|
    | **DALL-E 3** | Text-only. Use Method 1–3 prompts with hex values. |
    | **OpenAI Variations / Edit** | Upload `assets/*.png` as image input. Prompt describes differences. |
    | **Midjourney** | `--sref <asset-url>` with `--iw 1.5–2.0`. Include palette hex values in prompt. |
    | **ComfyUI** | IPAdapter or Reference-Only ControlNet from assets. Denoise 0.6–0.7. Post-process with grain overlay. |
    | **Replicate / SD img2img** | Upload reference. Prompt strength 0.7–0.8. CFG 7. |
    
    ---
    
    ## What Is NOT On-Brand
    
    Avoid these common anti-patterns:
    
    | Anti-Pattern | Why It's Wrong |
    |-------------|----------------|
    | **Sad/melancholic expression** | Model defaults to sad for manga characters unless explicitly told "not sad, not crying" |
    | **Dark/black headphones** | Nous Girl wears **white** over-ear headphones in all canonical poses |
    | **Facial markings (teardrop, tattoos, scars)** | The character has clean, clear skin — no markings whatsoever |
    | **Wrong ethnicity (Asian instead of French)** | Model defaults to Asian features for anime style; explicitly state "French Caucasian" |
    | **Busy/cluttered compositions** | The brand is restrained — dark background, 1-2 accent colors, 2-3 text elements max |
    | **Smooth digital illustration** | The brand is **never** clean — every image needs grain, noise, or analog texture |
    | **Cartoon/anime with glossy rendering** | The manga style is stark black ink on white paper — no soft shading, no gradients |
    | **Corporate/sterile tech aesthetic** | The finish should feel like an underground research lab, not a SaaS landing page |
    | **Over-detailed backgrounds** | Let the subject breathe. Negative space is a feature. |
    
    For a complete list of known failure modes and mitigations, see [`references/pitfalls.md`](references/pitfalls.md).
    
    ---
    
    ## Brand Compliance Checklist
    
    - [ ] Background is near-black (#00000E) or very dark
    - [ ] Electric blue (#3847FF) is used as primary accent
    - [ ] At least one grunge texture visibly applied (grain, noise, scan lines, paper, ink)
    - [ ] High contrast — dramatic light/dark difference
    - [ ] Palette is restricted to the specified colors
    - [ ] If mascot appears: white headphones, manga style, neutral attentive expression, three-quarter profile
    - [ ] If text appears: heavy distressed display for titles, clean sans for labels, monospace for code
    - [ ] No flat/clean/corporate polish — finish is raw and tactile
    - [ ] Overall impression: intellectual, gritty, underground research lab
    
    Full color tables, the complete Nous Girl mascot specification with pose variants, typography roles, texture definitions, and art-style attributes live in [references/visual-system.md](references/visual-system.md); lane-specific prompt construction lives in [references/style-lanes.md](references/style-lanes.md).
    
    ## Available Scripts
    
    | Script | Purpose | Invocation |
    |---|---|---|
    | `scripts/generate-with-ref.py` | Reference-image-driven generation: reads the active image provider from `~/.hermes/config.yaml`, uploads the reference via the provider's image-input endpoint (square-cropped), and saves the result as JSON with output path. Run it for any Method 4 generation where brand fidelity matters — it preserves the manga style that text-only prompts lose. Use `--dry-run` first to preview provider and parameters. | `python3 scripts/generate-with-ref.py --prompt "..." --reference assets/nous-girl-official-badge.png --aspect landscape --quality medium` |
    | `scripts/postprocess.py` | Mandatory analog post-processing: applies grain/noise/scan-line modes (`standard`, `risograph`, `nous`, `imprint`) at a calibrated intensity. Run it as the final step on every generated image — raw AI output is never the deliverable. Load `references/post-processing.md` to pick mode and intensity. | `python3 scripts/postprocess.py input.png output.png --mode imprint --intensity 0.7` |
    
    ## Prerequisites
    
    - Python 3 for both scripts; `postprocess.py` additionally needs an imaging backend (Pillow) available.
    - For `generate-with-ref.py`: a configured Hermes image-generation provider in `~/.hermes/config.yaml` (or an explicit `--provider`) whose API accepts image inputs — text-only endpoints cannot do reference-driven generation.
    - The bundled reference images in `assets/`, which serve as img2img anchors; pass one via `--reference`.
    - An agent or client capable of image generation when working outside the scripts (per `compatibility`).
    
    ## Limitations
    
    - This skill encodes one specific brand system — the palette, mascot rules, textures, and compliance checklist here are not general design guidance and must not be applied to other brands (see When not to use).
    - The Nous Girl is not a logo substitute; official brand representation requires the wordmark/symbol, not generated mascot art.
    - Generated imagery approximates the style even with references: always run the Brand Compliance Checklist before delivering, and consult `references/pitfalls.md` when output drifts (sad expressions, dark headphones, glossy rendering).
    - Model/provider behavior changes over time — flag-specific details like endpoint names may drift from what the scripts assume; verify against your configured provider.
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related