DEV Community

Cover image for Rebuilding DEV’s Community Buddy to Actually Take Actions
SupermanSpace
SupermanSpace Subscriber

Posted on

Rebuilding DEV’s Community Buddy to Actually Take Actions

DEV recently added DEV Community Buddy BETA to the post editor, giving writers an AI assistant for Markdown, formatting, embeds, and community guidelines.

The idea is useful. The execution still feels like a chatbot placed beside the editor.

When I tested it, the assistant could explain how to complete a task, but it could not perform that task inside the writing workflow. It did not clearly show what part of the draft it could access, how long the conversation context would persist, or whether the session belonged to the current post. Opening the assistant also reduced the editor space and created a separate interface that competed with the writing experience.

That made me curious about a larger question:

What would DEV Community Buddy look like if it could understand the active draft and safely take actions inside the editor?

Since DEV runs on Forem, an open-source community platform, I downloaded the project and set it up locally. I then started integrating the open-source YourGPT Copilot SDK to turn the assistant from a passive question-and-answer panel into an editor-aware copilot.

The goal is not to replace DEV’s beta or dismiss an early experiment. It is to explore the next step: an assistant that can read the current writing context, rewrite selected text, insert Markdown, suggest tags, prepare cover-image prompts, and apply approved changes without forcing the writer to copy and paste everything manually.

This post is that exploration: how I built it, how others can reproduce it, and how the same pattern can extend beyond the editor (stats, reading lists, account settings). Credit to @ben , @jess and everyone who maintains DEV and Forem—the open platform and editor this depends on. If the product team reads this, treat it as a field report: keep what helps, ignore what does not. Actionable tools in the draft (and later across the product) are the direction I found useful to implement and document.

Demo


The problem I was trying to solve

DEV Community Buddy can already help with basic writing questions. It can explain Markdown, suggest wording, answer questions about DEV, and generate text inside the side panel.

The limitation is what happens after the answer appears.

I was not trying to build a better chatbot for brainstorming. I wanted the copilot to complete the small, repetitive actions that slow writers down while finishing a post:

  • “Fill four relevant tags” → the tag fields are populated
  • “Tighten this paragraph” → the selected paragraph is replaced
  • “Add a TL;DR” → a formatted section appears at the top
  • “Write an introduction for this title” → the title remains unchanged and the introduction is inserted into the body
  • “Outline the remaining sections” → Markdown is added at the current cursor position
  • “Convert this Mermaid diagram” → a PNG is generated and inserted into the post
  • “Create a cover image” → the image is generated and connected to the article
  • “Proofread this selection” → corrections are applied without replacing the rest of the draft
  • “Add citations” → relevant sources are found and inserted in the correct format

A side panel that only returns text still leaves the writer with the same workflow:

select → ask → copy → paste → repair formatting → return to writing

That is useful during ideation. It becomes friction when the draft is nearly complete.

The important context already exists inside the product:

  • Article title
  • Tags
  • Body content
  • Selected text
  • Cursor position
  • Focused field
  • Current draft
  • Authentication and CSRF state

The copilot should use that state instead of asking the author to repeatedly describe or paste it.

The design principle was simple:

The model decides what should change. Product-side handlers decide how that change is safely applied.

The model does not directly manipulate the page. It selects from a controlled set of tools such as updateTags, replaceSelection, insertMarkdown, or generateImage. Forem remains responsible for validating and applying each change.

This creates a clear separation of responsibility:

  • The model interprets the author’s intent
  • Client tools expose permitted product actions
  • Forem handlers update the real interface
  • Sensitive credentials remain on the server
  • The author reviews the result inside the existing workflow
  • Generated content does not need to be copied back manually

This is the difference between adding AI beside a product and making the product operable through AI.

The implementation follows the client-tool pattern provided by the YourGPT Copilot SDK.


How I Turned DEV Community Buddy Into an Action-Capable Copilot

In my version, DEV Community Buddy is no longer a detached assistant that only returns suggestions inside a side panel.

Using the YourGPT Copilot SDK, I connected Buddy to a controlled set of tools that can read the current article state and perform actions directly inside the DEV writing experience.

Each tool is bound to a specific part of the post, including the title, tags, body, selected text, and media.

Actions connected to the article editor

Request What Buddy does
Set or improve the title Updates the title field as plain text without adding a Markdown #
Fill the tags Selects up to four relevant tags through Forem’s existing chip interface
Write an introduction, outline, or section Inserts Markdown at the cursor, start, or end of the article
Rewrite the full article Replaces the body only when the request clearly applies to the complete draft
Rewrite selected text Replaces only the highlighted text
Make selected text shorter Condenses the selection without changing the surrounding content
Proofread selected text Fixes grammar, spelling, and clarity in place
Add a TL;DR Generates and inserts a formatted ## TL;DR section at the top
Generate a cover image Creates an article image and connects it to the publishing workflow
Convert Mermaid to PNG Renders a Mermaid diagram as an image and inserts it into the article
Add citations Finds relevant sources and inserts citations where they are needed
Continue writing Uses the existing draft and cursor position to extend the article naturally

The important difference is that every request maps to a real product action.

Buddy does not simply return a paragraph and leave the author to decide where it belongs. It understands whether the result should update the title, populate the tag fields, replace selected text, or insert Markdown at a particular position.

Editor behavior that mattered in practice

Behavior Why it matters
Replace only the selected text Prevents a small edit from accidentally changing the full draft
Insert at the cursor, start, or end Adds content where the author expects it
Add the TL;DR at the top Creates the correct article structure automatically
Remove a leading H1 from generated body content DEV already stores the title in a separate field
Preserve the selected range Keeps the correct text available after focus moves to the copilot
Use the real tag chip interface Preserves Forem’s existing validation and interaction patterns
Apply every action once Prevents duplicate text, repeated tags, or multiple insertions
Keep API keys off the client Ensures sensitive credentials remain on the server
Require controlled tools Limits the model to actions explicitly exposed by the product

These details may sound small, but they determine whether the copilot feels reliable.

Generating good text is only one part of the problem. The system must also know where that text belongs, what it is allowed to change, and how to apply the result without damaging the rest of the article.

Selection-aware editing

The fastest editing workflow does not begin inside the copilot panel.

The author highlights a sentence or paragraph and chooses an action from the contextual toolbar:

Rewrite · Shorter · Proofread · Add citation

For example:

Rewrite

> Ever wondered what actually happens inside a Transformer?
Enter fullscreen mode Exit fullscreen mode

Before the copilot opens, the editor stores the selected range. The model then returns an improved version, and the apply_selection_edit tool replaces that exact range.

The interaction works like this:

  1. The author highlights text
  2. The editor stores the selection range
  3. The author chooses an action
  4. The copilot receives the selected content
  5. The model generates the requested revision
  6. Forem replaces only the stored selection

Buddy does not need to be open before the author selects text.

The user also does not need to copy the response, return to the editor, find the original paragraph, paste the new version, and repair the formatting.

The change happens exactly where the writing already lives.

Draft-aware content insertion

Buddy also understands that different types of generated content belong in different places.

A title should update the title field.

Tags should become chips.

A TL;DR should appear near the top.

A new section should be inserted at the cursor or added to the end.

A full rewrite should replace the body only when the author clearly requests it.

This avoids one of the most common problems with generic AI writing tools: they generate useful content without understanding the structure of the product receiving it.

In my implementation, the model chooses the intended action, but Forem controls how that action is applied.

For example, the model can request:

{
  "tool":"insert_markdown",
  "position":"cursor",
  "content":"## How attention works\n\n..."
}
Enter fullscreen mode Exit fullscreen mode

Or:

{
  "tool":"update_tags",
  "tags": ["ai","machinelearning","javascript","tutorial"
  ]
}
Enter fullscreen mode Exit fullscreen mode

The client-side handler validates the request and updates the corresponding part of the editor.

Media generation inside the writing flow

I also wanted Buddy to handle tasks that normally require leaving the editor.

For Mermaid diagrams, the author can provide or select Mermaid syntax and ask Buddy to convert it into a PNG. The diagram is rendered, uploaded, and inserted back into the article as Markdown.

For article visuals, Buddy can generate a cover image or supporting image based on the title and draft context.

The goal is not merely to return an image prompt. The goal is to complete the usable workflow:

  1. Understand what image the article needs
  2. Generate or render the image
  3. Make the result available to the article
  4. Insert or assign it in the correct place

That is the difference between explaining how to add media and actually helping the author add it.

Automatic tags and TL;DR generation

Tags and summaries are small tasks, but they interrupt the final publishing flow.

Buddy can analyse the title and article body, select up to four relevant DEV tags, and populate the existing tag fields.

It can also generate a concise TL;DR and insert it at the top of the article using consistent Markdown:

## TL;DR

A concise summary of the article appears here.
Enter fullscreen mode Exit fullscreen mode

The author can still edit or remove the result, but the mechanical work is already complete.


Architecture

Three processes:

  1. Tools run against product state (DOM handlers for the form; later, APIs for member data). The model only emits tool names and arguments.
  2. Runtime uses official SSE (server docs), not raw plain text.
  3. Rails enforces session and CSRF. Model keys stay on the Node process.

Product-level extensions use the same spine. You change context and tools, not the overall shape.


How to split responsibilities

Long tool catalogs in the system prompt waste tokens and hurt tool choice. This split worked better:

Layer Role
systemPrompt Persona and product rules (e.g. title ≠ body H1)
tool descriptions Short when/how (one sentence when possible)
body() each request Live snapshot (article_state, later other *_state)
handlers Validation and mutations
Skills (optional) Process notes, not DOM writes
  • Attach draft state every turn; do not call “read draft” on every greeting.
  • Keep internal tool instructions out of the visible user message.

Then multi-part requests can become multiple tool calls in one turn (title + tags + body).


Setup on local Forem

Stack used: Forem + @yourgpt/copilot-sdk / @yourgpt/llm-sdk 2.5.x.

Prerequisites

  • Forem local environment (Ruby, Postgres, Redis, Node 20, Yarn)
  • Provider API key (Gemini used here)
  • Node runtime process alongside Rails

Packages

yarn add @yourgpt/copilot-sdk @yourgpt/llm-sdk openai
Enter fullscreen mode Exit fullscreen mode

Environment

GOOGLE_API_KEY="…"
COPILOT_MODEL="gemini-3.6-flash"
COPILOT_FALLBACK_MODELS="gemini-3.5-flash-lite,gemini-3.5-flash,gemini-flash-lite-latest"
COPILOT_RUNTIME_URL="http://127.0.0.1:3101/api/copilot/stream"
COPILOT_RUNTIME_PORT=3101
Enter fullscreen mode Exit fullscreen mode

Primary model: gemini-3.6-flash. Fallbacks help when the free tier returns 429.

Runtime

yarn copilot:runtime
curl -s http://127.0.0.1:3101/health
Enter fullscreen mode Exit fullscreen mode

Per request: parse body → restore Gemini thought signatures when needed → stream via createRuntime.

Rails

post "/api/copilot", to: "copilot#chat"
Enter fullscreen mode Exit fullscreen mode

Authenticate the user, proxy to COPILOT_RUNTIME_URL, stream text/event-stream, map failures to short messages. Do not show provider internals (e.g. thought_signature) in the UI.

Frontend

  • Mount node: #article-copilot-sdk-root
  • Pack: app/javascript/packs/copilotSdk.js
  • Keep CopilotProvider mounted when the dock is closed; only portal the panel when open. Selection actions need sendMessage without the dock.
<CopilotProvider
  runtimeUrl="/api/copilot"
  systemPrompt={SYSTEM_PROMPT}
  streaming
  maxIterations={12}
  headers={() => ({
    'X-CSRF-Token': csrfToken(),
    Accept: 'text/event-stream',
  })}
  body={() => ({ article_state: collectArticleState() })}
>
  <ArticleEditorTools />
  <SelectionToolbar onOpen={openDock} />
  {isOpen && createPortal(<BuddyPanel onClose={closeDock} />, document.body)}
</CopilotProvider>
Enter fullscreen mode Exit fullscreen mode

Tools

Prefer JSON Schema on inputSchema, short descriptions, and { success: true/false, … } from handlers.

useTools({
  set_article_title: {
    description: 'Set the post title field (plain text, not markdown).',
    inputSchema: {
      type: 'object',
      properties: { title: { type: 'string' } },
      required: ['title'],
      additionalProperties: false,
    },
    handler: async ({ title }) => {
      const clean = cleanArticleTitle(title);
      const ok = setNativeValue(titleEl, clean);
      return { success: ok, title: clean, _aiResponseMode: 'brief' };
    },
  },
  insert_into_body: {
    description:
      'Insert body markdown (start|end|cursor). No leading H1; title is separate.',
  },
  apply_selection_edit: {
    description:
      'Replace selected body text once (Rewrite/Shorter). New text only; do not call again.',
    aiResponseMode: 'none',
  },
});
Enter fullscreen mode Exit fullscreen mode
  • Strip a single leading # Title from body inserts so the title field is not duplicated.
  • On Rewrite/Shorter, store { start, end, text } before focus moves; clear after one successful apply to avoid retry loops.

CSS and build

@source "../../../node_modules/@yourgpt/copilot-sdk/dist/**/*.{js,ts,jsx,tsx}";
Enter fullscreen mode Exit fullscreen mode
yarn build:copilot-css && yarn build
Enter fullscreen mode Exit fullscreen mode

Checks

  1. /health reports the configured model
  2. Create Post shows the launcher when logged in
  3. Tags update as chips
  4. Title / tags / body update in the form, not only in chat
  5. Selection Rewrite works with the dock closed first
  6. Body does not start with a duplicate H1 title
  7. Gemini tool follow-ups do not return 400
Piece Path
UI app/javascript/article-form/components/CopilotSDK/index.jsx
Tools …/ArticleEditorTools.jsx
DOM …/editorDom.js
Selection …/SelectionToolbar.jsx
Tool UI …/ToolCards.jsx
Runtime services/copilot-runtime/server.mjs
Proxy app/controllers/copilot_controller.rb
Pack app/javascript/packs/copilotSdk.js

Docs: getting started · server · frontend tools · agentic loop · generative UI


Gemini 3 tool follow-ups

Gemini 3.x expects a thought signature on function calls for follow-up turns (docs, OpenAI compatibility):

"tool_calls": [{
  "id": "…",
  "type": "function",
  "function": { "name": "apply_selection_edit", "arguments": "{…}" },
  "extra_content": { "google": { "thought_signature": "…" } }
}]
Enter fullscreen mode Exit fullscreen mode

If history is rebuilt without extra_content, the next request often fails with 400. Cache signatures by tool-call id while streaming, re-attach them on the next request, and normalize field names (tool_calls, tool_call_id). Prefer fixing that over permanently downgrading the model to avoid the issue.

Multi-tool turns also hit rate limits more easily on free tiers. Surface a clear retry message; optional fallback models help.


Small product choices that improved the feel

  • Toolbar messages: action label + quoted selection only
  • One apply per selection edit
  • Title in the title field; body without a leading H1
  • Short confirmation after tools
  • Keep title/tags/body tools available without deferred search on multi-step turns
  • Provider mounted whenever selection tools need sendMessage
  • User-facing errors without provider jargon

Extending the same Copilots beyond writing

The editor integration is one vertical. The reusable part is the loop:

  1. Authenticated SSE
  2. Context on every request
  3. Tools that change real product state
  4. Short chat around those tools

Other surfaces can reuse runtime + proxy + CopilotProvider. You swap the context object and the tool set.

Editor                         Extension surfaces
──────                         ──────────────────
article_state                  stats_state | reading_state | account_state
set_article_title              summarize / list tools (read)
fill_tags                      pin_list / update_pref (write, often with confirm)
insert_into_body               navigate / open section
apply_selection_edit
Enter fullscreen mode Exit fullscreen mode

I did not fully implement stats, reading-list, or settings copilots. Below is a practical map so someone continuing from this work knows what to add.

Rules that still apply

Rule Reason
Put live data in body(), not a mandatory “read all” tool every turn Fewer wasted tool rounds
Tools only perform actions the member can already perform Matches product permissions
Keep tool descriptions short Better tool choice, smaller schemas
Keep user-visible messages free of internal instructions Clearer chat
Prefer one-shot mutations where possible Avoid loops
Confirm destructive or account-wide changes Especially settings
Never put secrets in model context Tokens, passwords, raw session ids

Stats

Goal: explain numbers the member can already see, and suggest a next writing step from real data—not invented metrics.

Context example:

body: () => ({
  surface: 'dashboard',
  stats_state: {
    range: '30d',
    totals: { views, reactions, comments, followers_delta },
    top_posts: [{ id, title, views, reactions, published_at }],
    series: [{ date, views }],
  },
})
Enter fullscreen mode Exit fullscreen mode

Example tools: get_stats_snapshot, list_top_posts, open_post_analytics, suggest_followup_topics (text only from provided posts), optional bridge to editor tools for an outline.

Example questions: why views changed; which posts to turn into a series; what to write next given top tags.

Constraint: if a figure is not in context or a tool result, say so. Do not invent analytics.

Reading list

Goal: build a short, personal queue from real posts (search/save/follow APIs), not hallucinated links.

Context example: followed tags, recent reads, saves, optional goals.

Example tools: search_posts, build_reading_list, pin_reading_item, save_for_later, follow_tag / mute_tag (with confirmation), export_reading_list.

Example questions: weekend list on a topic; next reads based on history; pin three and drop already-read items.

Constraint: every recommended URL/id should come from a tool or injected state.

Account and settings

Goal: explain settings and, where safe, apply changes the member already has UI for—without dumping secrets into the prompt.

Context example: profile completeness flags, notification toggles, 2FA on/off, session count—not passwords or raw tokens.

Example tools: open_settings_section, update_notification_pref (confirm), update_profile_field (confirm for public fields), list_sessions, revoke_session (confirm), explain_setting (read-only).

Example questions: reduce email noise; complete public profile; review sessions.

Constraint: prefer navigate + explain by default; mutate only with confirmation (needsApproval or equivalent). Stricter than the editor.

Implementation order for an extension

  1. Choose one surface.
  2. Define a *_state payload you can collect safely.
  3. Add a small tool pack (about 4–8 tools) with short descriptions.
  4. Mount CopilotProvider on that page; pass surface + state in body().
  5. Reuse the existing runtime and Rails proxy.
  6. Adjust the system prompt for that surface only.
  7. Test normal prompts and refusal cases (“delete my account” without a tool, etc.).
  8. Optionally connect back to the editor (e.g. stats → outline → insert_into_body).

Prompt for an AI coding agent

Paste this into Cursor, Claude Code, Codex, or similar when you want the agent to set up the same stack on a Forem (or similar) codebase:

You are implementing an editor-aware writing copilot on Forem using the YourGPT Copilot SDK
(@yourgpt/copilot-sdk + @yourgpt/llm-sdk ~2.5.x), matching this architecture:

ARCHITECTURE
- Browser: CopilotProvider (always mounted) + useTools handlers + SelectionToolbar
  + CopilotChat portaled in a right dock when open
- Rails: authenticated POST /api/copilot streams SSE to the Node runtime (CSRF + session)
- Node: services/copilot-runtime/server.mjs with createRuntime, Gemini 3.x primary model
  (COPILOT_MODEL=gemini-3.6-flash), thought_signature cache/restore on tool follow-ups,
  optional model fallbacks on 429
- Never put API keys in the client. Never stream text/plain to CopilotChat.

DOCS (prefer over memory)
- https://copilot-sdk.yourgpt.ai/docs/getting-started
- https://copilot-sdk.yourgpt.ai/docs/server
- https://copilot-sdk.yourgpt.ai/docs/tools/frontend-tools
- https://copilot-sdk.yourgpt.ai/docs/tools/agentic-loop
- https://copilot-sdk.yourgpt.ai/docs/generative-ui
- https://ai.google.dev/gemini-api/docs/thought-signatures

FILE MAP TO CREATE/UPDATE
app/javascript/article-form/components/CopilotSDK/
  index.jsx, SelectionToolbar.jsx, ArticleEditorTools.jsx, editorDom.js,
  ToolCards.jsx, toolSchemas.js
app/javascript/packs/copilotSdk.js
app/assets/stylesheets/copilot-sdk.css  (Tailwind @source SDK dist; FAB + dock + selection pill)
app/controllers/copilot_controller.rb
config/routes.rb → post "/api/copilot"
services/copilot-runtime/server.mjs
.env: GOOGLE_API_KEY, COPILOT_MODEL, COPILOT_RUNTIME_URL, COPILOT_FALLBACK_MODELS

UI REQUIREMENTS (match this UX)
- Floating launcher (avatar + BETA badge) bottom-right above article actions
- Right full-height dock (~400px) with official CopilotChat (csdk-theme-modern)
- Provider ALWAYS mounted; only dock portals when open; body class buddy-dock-open
- Selection toolbar on body highlight: Rewrite + Shorter only (no internal prompts in chat)
- User message format: "Rewrite\n\n> selected text" (blockquote). Tool policy in system
  prompt + tool descriptions, not in the bubble
- On toolbar click: setPendingBodySelection({start,end,text}), open dock, sendMessage
- apply_selection_edit once using pending range; clear pending; do not loop
- Live article_state (title, tags, body) in CopilotProvider body() every request
- Strip leading H1 from body inserts; title is a separate plain-text field
- Short friendly parseError messages; never show thought_signature to users
- toolRenderers for tags/cover/mermaid/TL;DR; ToolStep titles for simple tools
- local thread persistence key: dev-community-buddy-threads

TOOLS (inputSchema JSON Schema, short descriptions)
set_article_title, fill_tags, insert_into_body, replace_article_body,
apply_selection_edit (eager, one-shot), generate_tldr, generate_cover_image,
mermaid_to_png; optional get_article_draft (not on every greeting)

LAYERING
systemPrompt = persona + product rules only (no tool catalog)
tool descriptions = short decision boundaries
body() = live state
handlers = DOM mutations, return { success, ... }

VERIFY
yarn build:copilot-css && yarn build
yarn copilot:runtime → curl health
Logged-in Create Post: launcher, tags chips, multi-tool title+tags+body,
selection Rewrite with dock closed first, no body H1 title, no 400 on tool follow-up

If something fails, fix signatures / selection pending range / Provider mount first—
do not downgrade Gemini major solely to hide tool-follow-up 400s unless the user asks.
Enter fullscreen mode Exit fullscreen mode

FAQ

Is this the official DEV Community Buddy?

No. This is an independent fan-built experiment using Forem. It is not affiliated with, endorsed by, or deployed by DEV.to Community. I built it to explore a more efficient and action-capable version of Buddy could look like, especially one that can understand the article context, work directly with the editor, and help developers write and publish with less manual effort.

Why did you build it on Forem?

DEV Community at dev.to is built on Forem, its open-source community platform. After testing the first beta version of DEV Community Buddy, I found it useful for basic writing guidance, but it still behaved more like a chatbot than an integrated product copilot. I wanted to explore a more capable experience that could help developers write better posts with less manual work, so I ran the same Forem foundation behind dev.to locally and connected it to the YourGPT Copilot SDK. The SDK made it possible to turn Buddy into an action-capable copilot that understands the live article state and can safely interact with the real writing interface. Instead of only suggesting text, it can update the title, fill relevant tags, rewrite or proofread selected content, insert a TL;DR, generate images, convert Mermaid diagrams into PNGs, add citations, and place Markdown in the correct location. This allowed me to build a more contextual and efficient writing workflow inside the actual product, rather than a separate AI demo that could not operate the editor.

What part of the project is actually implemented?

The editor copilot is implemented. It can work with the title, tags, article body, selected text, cursor position, TL;DR generation, Mermaid rendering, and media-related actions.

The stats, personalised reading list, account, and settings copilots are proposed extensions of the same architecture. They are not presented as completed features.

What is the YourGPT Copilot SDK?

The YourGPT Copilot SDK is an open-source framework for building AI copilots that can understand product context, use tools, and take actions inside an application. It provides the client components and runtime infrastructure for streaming conversations, frontend and backend tool execution, live application context, multi-step agent workflows, and generative UI. Rather than adding a standalone chatbot, it helps developers build a agentic copilot that is embedded into the product experience and can safely operate within the actions and permissions the application exposes.

Why use the YourGPT Copilot SDK instead of calling a model API directly?

A direct LLM API can generate text, but it does not automatically make an application agentic. The YourGPT Copilot SDK adds the orchestration layer required to build an action-capable AI copilot agent inside a product. It streams responses, passes live application context such as the current article title, tags, body, cursor position, and selected text, connects the model to controlled frontend tools, supports multi-step tool execution, and renders tool progress directly in the interface. This allows the copilot to do more than suggest content. It can safely update fields, replace selected text, insert Markdown, generate media, and complete approved actions on the user’s behalf. The core value of the SDK is not access to a particular AI model, but the infrastructure for turning model output into contextual, permissioned product actions.

Can I use a different model?

Yes. The architecture is model-independent. Gemini was used for this implementation, but another supported provider can be configured through the runtime.

Why did you use Gemini for this build?

Gemini was the primary model used while developing and testing the integration. It supported the tool-calling and image gen workflow needed for editor actions.

Why use frontend tools for editor actions?

The relevant state already exists in the browser, including the current selection, cursor position, unsaved body content, and focused field.

Frontend tools can operate on that live state without repeatedly saving the draft or sending unnecessary requests to the backend. Server-side tools are still more appropriate for protected data, external services, uploads, and account-level actions.

How does the copilot know what is in the current draft?

The application passes a structured article-state object with each request. It can include the title, tags, body, selected text, cursor position, and active surface.

This gives the AI current product context without requiring the author to paste the article into the conversation.

Can the copilot modify anything in the application?

Yes, Copilot can take action on an application by using the exposed tools.

Can this architecture work outside the DEV editor?

Yes. The same runtime, authenticated proxy, provider, and tool pattern can support other product surfaces.

For example:

  • Analytics explanations using stats_state
  • Personalised reading lists using real post-search tools
  • Navigation and account assistance
  • Notification preference management
  • Profile updates with confirmation

The main changes are the context object, available tools, and permission rules.

Is the YourGPT Copilot SDK limited to React applications?

The client components used in this implementation are React-based, but the broader architecture is not limited to a particular backend. The runtime and tool pattern can be integrated with applications using Rails, Node, or other server environments.

The exact frontend integration depends on how the host application exposes state and actions.

Can this be used in production?

Absolutely. Both Forem and the YourGPT Copilot SDK are open source, so you can clone the repositories, fork them, modify the code, self-host the complete stack, and adapt this implementation for your own product. The YourGPT Copilot SDK is available under the permissive MIT License, while Forem is released under the GNU Affero General Public License v3.0


Closing

I wanted to build a copilot that understands a draft as live product state, not just text inside a chat window. It should know the title, tags, body, cursor position, and selected text, choose from a controlled set of tools, apply the requested change in the correct place, and stop once the editor has been updated.

That is what I built on a local Forem instance. The process also exposed the less visible problems behind an action-capable copilot: preserving selections after focus changes, preventing repeated tool execution, avoiding duplicate H1 titles, handling provider-specific tool follow-ups, and recovering cleanly from rate limits.

The same architecture can extend beyond writing. By changing the context and available tools, the copilot could explain article performance, curate a personalised reading list, navigate product settings, or complete other approved actions across the platform.

Forem’s open-source foundation made the experiment possible. The YourGPT Copilot SDK provided the agentic application layer for streaming, context, tool execution, and product actions. I have included the architecture, implementation notes, checks, and extension paths so other developers can reproduce the editor integration, improve it, or adapt the same pattern to their own products.

The larger lesson is simple: adding a chat panel gives users answers. Connecting an AI copilot to real product state and controlled tools gives them outcomes.

Top comments (0)