@nightowlsdev/agent-writer
Pre-built agentsContent + 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-writerKey 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
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-agentsWhat 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.
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.
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-marketer — The strategy the writer executes (strategy → execution).
- agent-designer — Copy for the approved design direction (the writer supplies the words).
- agent-kit — importCuratedSkills + the grant helpers behind the six craft skills.
- skills — materializeSkillStore injects the writer's stored craft grants at runtime.
- adopt-prebuilt-agents — The four-step host-wiring journey.