@sanity/agent-directives
v0.0.10
Published
Shared directive system for Sanity Agent
Maintainers
Readme
@sanity/agent-directives
Shared directive system for Sanity Agent.
Installation
pnpm add @sanity/agent-directivesReact Usage
// kit.ts
import { createDirectiveKit } from "@sanity/agent-directives/react";
export const { defineDirective } = createDirectiveKit<Application>({
useApplication: (source) => useThreadApplication(source),
});
// directives/Document.tsx
export const DocumentDirective = defineDirective(
"document",
({ id, application }) => {
return <DocumentCard id={id} application={application} />;
},
);
// directives/Changes.tsx - no application needed
export const ChangesDirective = defineDirective(
"changes",
({ createdCount, updatedCount }) => (
<ChangesCard created={createdCount} updated={updatedCount} />
),
{ requiresContext: false },
);
// AgentMessage.tsx
import { remarkAgentDirectives } from "@sanity/agent-directives/react";
import ReactMarkdown from "react-markdown";
const components = {
Document: DocumentDirective,
Changes: ChangesDirective,
// directive names are converted to PascalCase
};
function AgentMessage({ content }: { content: string }) {
return (
<ReactMarkdown
remarkPlugins={[remarkAgentDirectives]}
components={components}
>
{content}
</ReactMarkdown>
);
}Server-side Usage
// kit.ts
import {
createDirectiveKit,
createRemarkRenderer,
} from "@sanity/agent-directives/string";
interface MyContext {
accessToken: string;
applications: Application[];
}
interface MyOutput {
text?: string;
blocks?: Block[];
}
export const { defineDirective, renderDirective } = createDirectiveKit<
MyContext,
MyOutput
>();
export const remarkRenderDirectives = createRemarkRenderer<MyContext, Block>(
renderDirective,
);
// directives/document.ts
defineDirective("document", async ({ props, context }) => {
const doc = await fetchDocument(props.id, context.accessToken);
return { blocks: [linkButton(doc.title, url)] };
});
// directives/changes.ts - simple text output
defineDirective("changes", ({ props }) => {
return {
text: `Created: ${props.createdCount}, Updated: ${props.updatedCount}`,
};
});
// markdown.ts
import remarkDirective from "remark-directive";
async function toMarkdown(content: string, context: MyContext) {
const collector = { blocks: [] };
const processor = unified()
.use(remarkParse)
.use(remarkDirective)
.use(remarkRenderDirectives, context, collector)
.use(remarkStringify);
const file = await processor.process(content);
return { text: String(file), blocks: collector.blocks };
}Formatters
For generating directive strings (agent backend):
import { createDocumentDirective } from "@sanity/agent-directives/formatters";
createDocumentDirective({ id: "doc-123", type: "article" });
// => '::document{id="doc-123" type="article"}'Adding a New Directive
Edit src/schemas.ts:
// 1. Add schema
export const MyDirectiveSchema = z.object({
id: z.string(),
title: z.string().optional(),
});
// 2. Add to DirectiveSchemas
export const DirectiveSchemas = {
// ...existing
my: MyDirectiveSchema,
} as const;
// 3. Add to DIRECTIVE_NAMES
export const DIRECTIVE_NAMES = {
// ...existing
my: "my",
} as const;
// 4. Add type
export type MyDirectiveProps = z.infer<typeof MyDirectiveSchema>;
// 5. Add to DirectivePropsMap
export interface DirectivePropsMap {
// ...existing
my: MyDirectiveProps;
}Then define the render function in your client using defineDirective('my', ...).
