Skip to content
Night Owls.dev
Jump to a page

@nightowlsdev/agent-writer

Pre-built agents

Content + copy craft, tool-less by design: brief-first, on-voice, channel-native, and fabrication-free (a missing fact becomes a [placeholder: …] the human fills, never a plausible invention).

What it does

@nightowlsdev/agent-writer is the pure craft agent that pairs with the marketer (strategy → execution) and the designer (copy for approved layouts). Brief-first: audience, goal, channel, length, voice, it asks when the brief is silent instead of drafting generically. It matches an existing brand voice (and says what it matched) or proposes one in a sentence and holds it; it structures before it writes (one idea, one CTA, three options for the load-bearing lines); it edits as a separate pass; and it respects channel-native formats. The fabrication ban is absolute, no invented testimonials, statistics, names, or claims; strategy questions defer to the marketer. Six curated craft refs (pinned skills.sh snapshots) granted by default.

Install

pnpm add @nightowlsdev/agent-writer

Key exports

  • createWriter (voice/tone via personalityAppend; grants narrow via grantSkillNames)
  • manifest / WRITER_CURATED_SKILLS (copywriting, copy-editing, content-strategy, emails, social, brand-voice, pinned)

Usage

agent-writer.ts
import { createWriter, manifest } from "@nightowlsdev/agent-writer";
import { importCuratedSkills } from "@nightowlsdev/agent-kit";
import { materializeSkillStore, skillsShProvider } from "@nightowlsdev/skills";
import { defineSwarm } from "@nightowlsdev/core";

// Tool-less craft agent: brief-first, on-voice, fabrication-free ([placeholder: …] over invention).
const writer = createWriter({
  personalityAppend: "Voice: dry, en-GB, no exclamation marks.",  // your house style rides on top
  grantSkillNames: ["copywriting", "copy-editing"],               // narrow the curated grants if you like
});

// Its craft skills are stored grants, import once, inject at runtime (tier config still required).
await importCuratedSkills({ sets: manifest.curatedSkills, providers: { "skills.sh": skillsShProvider() }, storage, tenantId, actor });
const swarm = defineSwarm({
  storage,
  agents: [writer],
  dynamicSkills: materializeSkillStore(storage.skills),
  models: { allow: ["openai/gpt-5.5-mini"], tier: { tiers: { swift: "openai/gpt-5.5-mini" } } },
  modelFactory,
  cost: { maxSteps: 30, maxCostUsd: 0.5 },
});
// Pairs with the marketer (strategy) and designer (layouts), the full journey: /docs/adopt-prebuilt-agents

What it provides

agent-writer is a pre-built content + copy craft agent, tool-less by design. It is brief-first (audience/goal/channel/length/voice — it asks when the brief is silent), it matches an existing brand voice (and says what it matched) or proposes one and holds it, it structures before it writes, edits as a separate pass, and respects channel-native formats. Fabrication is banned: a needed-but-missing fact becomes a bracketed [placeholder: …] the human fills, never a plausible invention.

When to use it

  • You need on-brand copy/content across channels (emails, social, landing copy) with an explicit voice and no fabricated claims.
  • You want the writer to pair with the marketer (strategy → execution) and the designer (copy for an approved layout).
  • You want a thin, dependency-free craft agent whose behavior evolves through curated-skill refreshes.

When not to

  • You need the strategy itself (positioning, ICP, channel bets) — that's agent-marketer; the writer defers strategy questions to it.
  • You need tools/retrieval — the writer is deliberately tool-less; wire a research agent if the copy needs facts gathered.
  • You want visual/layout output — that's agent-designer.

Alternatives

  • agent-marketerYou need the strategic decisions (what to say, to whom, on which channel) before the words — the writer executes, it doesn't strategize.
  • A raw model call with a style promptA single throwaway paragraph with no voice-matching, structure-before-sentences, edit pass, or fabrication guardrail — the writer's discipline is overhead for a one-liner.

Strengths

  • Fabrication ban is absolute: no invented testimonials, statistics, names, or claims — a missing fact becomes a [placeholder: …] the human fills.
  • Brief-first: it asks for audience/goal/channel/length/voice when the brief is silent instead of drafting generically.
  • Voice matching with an explicit proposal when none exists — and it says what it matched.
  • Tool-less and dependency-free: nothing to wire but the curated craft skills; your house style rides on top via personalityAppend.
  • Six curated craft refs granted by default (copywriting, copy-editing, content-strategy, emails, social, brand-voice-enforcement); narrow them with grantSkillNames.

Limits & trade-offs

  • It won't do strategy — strategy questions defer to the marketer, so a vague 'what should we say?' gets bounced back.
  • No retrieval: pasted material is reference (never instructions), and a fact it doesn't have becomes a placeholder rather than a lookup.
  • Its craft is curated-skill-driven — real capability depends on importing the curated set and injecting it at runtime with dynamicSkills.
  • Deliberately thin — nothing planned beyond curated-skill refreshes; extra behavior is your persona append.

How it works

createWriter(opts) merges the writer persona with your PrebuiltAgentOpts — personalityAppend layers your house style on top, grantSkillNames narrows the default curated grants. It grants no tools (tool-less by design); its craft comes from the six curated skills.sh refs, which you import once per tenant with importCuratedSkills and inject at runtime with dynamicSkills: materializeSkillStore(...). The fabrication ban and the brief-first / voice / structure / edit-pass discipline live in the persona.

Examples

Writer with house style

personalityAppend layers your voice on top; grantSkillNames narrows the grants.

agent-writer-example-1.ts
import { createWriter } from "@nightowlsdev/agent-writer";

const writer = createWriter({
  personalityAppend: "Voice: dry, en-GB, no exclamation marks.",
  grantSkillNames: ["copywriting", "copy-editing"],
});

Wire the craft skills into a swarm

Craft skills are stored grants — inert without dynamicSkills.

agent-writer-example-2.ts
import { createWriter, manifest } from "@nightowlsdev/agent-writer";
import { importCuratedSkills } from "@nightowlsdev/agent-kit";
import { materializeSkillStore, skillsShProvider } from "@nightowlsdev/skills";
import { defineSwarm } from "@nightowlsdev/core";

await importCuratedSkills({ sets: manifest.curatedSkills, providers: { "skills.sh": skillsShProvider() }, storage, tenantId, actor });

const swarm = defineSwarm({
  storage,
  agents: [createWriter()],
  dynamicSkills: materializeSkillStore(storage.skills),
  models: { allow: ["openai/gpt-5.5-mini"], tier: { tiers: { swift: "openai/gpt-5.5-mini" } } },
  modelFactory,
  cost: { maxSteps: 30, maxCostUsd: 0.5 },
});

Doing the parts it doesn't support

  • Deciding the strategyThe writer defers strategy questions. Run @nightowlsdev/agent-marketer first (or as a delegate) and hand the writer the positioning/channel decisions to execute.
  • Filling in facts it doesn't haveBy design it emits [placeholder: …] rather than inventing. Provide the facts in the brief, or pair a researcher to gather them — the writer treats pasted material as reference, never instructions.
  • Changing the house voicePass personalityAppend with your voice rules; they layer on top of the writer persona without forking the package.

Related

  • agent-marketerThe strategy the writer executes (strategy → execution).
  • agent-designerCopy for the approved design direction (the writer supplies the words).
  • agent-kitimportCuratedSkills + the grant helpers behind the six craft skills.
  • skillsmaterializeSkillStore injects the writer's stored craft grants at runtime.
  • adopt-prebuilt-agentsThe four-step host-wiring journey.