-
Notifications
You must be signed in to change notification settings - Fork 4.3k
feat(root): add agent template to novu init CLI scaffolding fixes NV-7371
#10734
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
ChmaraX
merged 10 commits into
next
from
nv-7371-add-agent-template-to-novu-init-cli-scaffolding
Apr 15, 2026
Merged
Changes from 9 commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
4a1d4c7
feat(root): add agent template to `novu init` CLI scaffolding fixes N…
ChmaraX 351bc55
fix(root): bump eslint to ^9 to match eslint-config-next@16 peer dep
ChmaraX 1dea3f6
Revert "fix(root): bump eslint to ^9 to match eslint-config-next@16 p…
ChmaraX c876e1c
fix(root): bump eslint to ^9 to match eslint-config-next@16 peer dep
ChmaraX 50d0ebc
fix(root): use correct Button id prop from chat SDK
ChmaraX 6159b3c
fix(root): replace Studio reference with sync in agent template docs
ChmaraX 87d1a35
chore(root): simplify agent template getting started steps
ChmaraX 1ba8e28
feat(root): use JSX syntax for agent Card components
ChmaraX e0468f5
fix: address CodeRabbit review comments on agent template
ChmaraX 085ede6
fix: address PR review comments
ChmaraX File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
69 changes: 69 additions & 0 deletions
69
packages/novu/src/commands/init/templates/app-agent/ts/README-template.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| # Novu Agent | ||
|
|
||
| A conversational AI agent powered by [Novu](https://novu.co) and [Next.js](https://nextjs.org). | ||
|
|
||
| ## Getting Started | ||
|
|
||
| 1. Start the development server: | ||
|
|
||
| ```bash | ||
| npm run dev | ||
| ``` | ||
|
|
||
| 2. Connect a chat platform in the [Novu Dashboard](https://dashboard.novu.co). | ||
|
|
||
| 3. Replace the demo handler in `app/novu/agents/support-agent.tsx` with your LLM call. | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
|
|
||
| Your agent is served at `/api/novu` and handles incoming messages via the Novu Bridge protocol. | ||
|
|
||
| ## Project Structure | ||
|
|
||
| ```text | ||
| app/ | ||
| api/novu/route.ts → Bridge endpoint serving your agent | ||
| novu/agents/ | ||
| index.ts → Agent exports | ||
| support-agent.tsx → Your agent handler (edit this!) | ||
| page.tsx → Landing page | ||
| ``` | ||
|
|
||
| ## Agent API | ||
|
|
||
| Your agent handler receives a context object with: | ||
|
|
||
| | Method / Property | Description | | ||
| |---|---| | ||
| | `ctx.message` | The inbound message (text, author, timestamp) | | ||
| | `ctx.conversation` | Current conversation state and metadata | | ||
| | `ctx.history` | Recent conversation history | | ||
| | `ctx.subscriber` | Resolved subscriber info | | ||
| | `ctx.platform` | Source platform (slack, teams, whatsapp) | | ||
| | `ctx.reply(content)` | Send a reply (text, Markdown, or Card) | | ||
| | `ctx.metadata.set(k, v)` | Set conversation metadata | | ||
| | `ctx.resolve(summary?)` | Mark conversation as resolved | | ||
| | `ctx.trigger(workflowId)` | Trigger a Novu workflow | | ||
|
|
||
| ## Wiring Up Your LLM | ||
|
|
||
| Replace the demo handler in `app/novu/agents/support-agent.tsx` with your LLM call: | ||
|
|
||
| ```typescript | ||
| onMessage: async (ctx) => { | ||
| const response = await openai.chat.completions.create({ | ||
| model: 'gpt-4', | ||
| messages: [ | ||
| { role: 'system', content: 'You are a helpful support agent.' }, | ||
| ...ctx.history.map((h) => ({ role: h.role, content: h.content })), | ||
| { role: 'user', content: ctx.message?.text ?? '' }, | ||
| ], | ||
| }); | ||
|
|
||
| await ctx.reply(response.choices[0].message.content ?? ''); | ||
| }, | ||
| ``` | ||
|
|
||
| ## Learn More | ||
|
|
||
| - [Novu Agent Docs](https://docs.novu.co/agents) | ||
| - [Novu Framework SDK](https://docs.novu.co/framework) | ||
| - [Next.js Documentation](https://nextjs.org/docs) | ||
6 changes: 6 additions & 0 deletions
6
packages/novu/src/commands/init/templates/app-agent/ts/app/api/novu/route.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| import { serve } from '@novu/framework/next'; | ||
| import { supportAgent } from '../../novu/agents'; | ||
|
|
||
| export const { GET, POST, OPTIONS } = serve({ | ||
| agents: [supportAgent], | ||
| }); |
19 changes: 19 additions & 0 deletions
19
packages/novu/src/commands/init/templates/app-agent/ts/app/globals.css
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| *, | ||
| *::before, | ||
| *::after { | ||
| box-sizing: border-box; | ||
| margin: 0; | ||
| padding: 0; | ||
| } | ||
|
|
||
| body { | ||
| background: #fafafa; | ||
| color: #171717; | ||
| } | ||
|
|
||
| @media (prefers-color-scheme: dark) { | ||
| body { | ||
| background: #0a0a0a; | ||
| color: #fafafa; | ||
| } | ||
| } |
20 changes: 20 additions & 0 deletions
20
packages/novu/src/commands/init/templates/app-agent/ts/app/layout.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| import type { Metadata } from 'next'; | ||
| import './globals.css'; | ||
|
|
||
| export const metadata: Metadata = { | ||
| title: 'Novu Agent', | ||
| description: 'Conversational AI agent powered by Novu', | ||
| }; | ||
|
|
||
| export default function RootLayout({ | ||
| children, | ||
| }: Readonly<{ | ||
| children: React.ReactNode; | ||
| }>) { | ||
|
|
||
| return ( | ||
| <html lang="en"> | ||
| <body>{children}</body> | ||
| </html> | ||
| ); | ||
| } |
1 change: 1 addition & 0 deletions
1
packages/novu/src/commands/init/templates/app-agent/ts/app/novu/agents/index.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| export { supportAgent } from './support-agent'; |
58 changes: 58 additions & 0 deletions
58
packages/novu/src/commands/init/templates/app-agent/ts/app/novu/agents/support-agent.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,58 @@ | ||
| /** @jsxImportSource @novu/framework */ | ||
| import { agent, Card, CardText, Actions, Button } from '@novu/framework'; | ||
|
|
||
| export const supportAgent = agent('support-agent', { | ||
| onMessage: async (ctx) => { | ||
| const text = (ctx.message?.text ?? '').toLowerCase(); | ||
| const isFirstMessage = ctx.conversation.messageCount <= 1; | ||
|
|
||
| if (isFirstMessage) { | ||
| ctx.metadata.set('topic', 'unknown'); | ||
| await ctx.reply( | ||
| <Card title="Hi, I'm Support Agent"> | ||
| <CardText>How can I help you today?</CardText> | ||
| <Actions> | ||
| <Button id="topic-billing" label="Billing question" value="billing" /> | ||
| <Button id="topic-technical" label="Technical issue" value="technical" /> | ||
| <Button id="topic-other" label="Something else" value="other" /> | ||
| </Actions> | ||
| </Card> | ||
| ); | ||
|
|
||
| return; | ||
| } | ||
|
|
||
| if (text.includes('resolve') || text.includes('thanks')) { | ||
| ctx.resolve(`Resolved by user: ${text}`); | ||
| await ctx.reply('Glad I could help! Marking this resolved.'); | ||
|
|
||
| return; | ||
| } | ||
|
|
||
| // Replace this block with your LLM call (OpenAI, Anthropic, etc.) | ||
| ctx.metadata.set('lastMessage', text); | ||
| await ctx.reply({ | ||
| markdown: | ||
| `**Got it.** You said: "${ctx.message?.text}"\n\n` + | ||
| `_This is a demo agent. Replace this handler with your LLM call._\n\n` + | ||
| `**Conversation so far:** ${ctx.history.length} messages | ` + | ||
| `**Topic:** ${ctx.conversation.metadata?.topic ?? 'unknown'}`, | ||
| }); | ||
| }, | ||
|
|
||
| onAction: async (ctx) => { | ||
| const { actionId, value } = ctx.action!; | ||
| if (actionId.startsWith('topic-') && value) { | ||
| ctx.metadata.set('topic', value); | ||
| await ctx.reply({ | ||
| markdown: `Topic set to **${value}**. Describe your issue and I'll help.`, | ||
| }); | ||
| } | ||
| }, | ||
|
|
||
| onResolve: async (ctx) => { | ||
| ctx.metadata.set('resolvedAt', new Date().toISOString()); | ||
| // Trigger a follow-up workflow when a conversation is resolved: | ||
| // ctx.trigger('follow-up-survey', { to: ctx.subscriber?.subscriberId }); | ||
| }, | ||
| }); |
118 changes: 118 additions & 0 deletions
118
packages/novu/src/commands/init/templates/app-agent/ts/app/page.module.css
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,118 @@ | ||
| .main { | ||
| display: flex; | ||
| justify-content: center; | ||
| align-items: center; | ||
| min-height: 100vh; | ||
| padding: 2rem; | ||
| font-family: | ||
| -apple-system, | ||
| BlinkMacSystemFont, | ||
| 'Segoe UI', | ||
| Roboto, | ||
| sans-serif; | ||
| } | ||
|
|
||
| .container { | ||
| max-width: 640px; | ||
| width: 100%; | ||
| } | ||
|
|
||
| .title { | ||
| font-size: 2.5rem; | ||
| font-weight: 700; | ||
| margin-bottom: 0.5rem; | ||
| color: #171717; | ||
| } | ||
|
|
||
| .description { | ||
| font-size: 1.1rem; | ||
| color: #525252; | ||
| margin-bottom: 2rem; | ||
| } | ||
|
|
||
| .code { | ||
| background: #f5f5f5; | ||
| padding: 0.2em 0.4em; | ||
| border-radius: 4px; | ||
| font-size: 0.9em; | ||
| font-family: monospace; | ||
| } | ||
|
|
||
| .steps, | ||
| .features { | ||
| margin-bottom: 2rem; | ||
| } | ||
|
|
||
| .steps h2, | ||
| .features h2 { | ||
| font-size: 1.2rem; | ||
| font-weight: 600; | ||
| margin-bottom: 0.75rem; | ||
| color: #171717; | ||
| } | ||
|
|
||
| .steps ol { | ||
| padding-left: 1.5rem; | ||
| line-height: 1.8; | ||
| color: #525252; | ||
| } | ||
|
|
||
| .features ul { | ||
| padding-left: 1.5rem; | ||
| line-height: 1.8; | ||
| color: #525252; | ||
| } | ||
|
|
||
| .steps a { | ||
| color: #2563eb; | ||
| text-decoration: none; | ||
| } | ||
|
|
||
| .steps a:hover { | ||
| text-decoration: underline; | ||
| } | ||
|
|
||
| .links { | ||
| display: flex; | ||
| gap: 1.5rem; | ||
| padding-top: 1rem; | ||
| border-top: 1px solid #e5e5e5; | ||
| } | ||
|
|
||
| .links a { | ||
| color: #2563eb; | ||
| text-decoration: none; | ||
| font-weight: 500; | ||
| } | ||
|
|
||
| .links a:hover { | ||
| text-decoration: underline; | ||
| } | ||
|
|
||
| @media (prefers-color-scheme: dark) { | ||
| .title { | ||
| color: #fafafa; | ||
| } | ||
|
|
||
| .description { | ||
| color: #a3a3a3; | ||
| } | ||
|
|
||
| .code { | ||
| background: #262626; | ||
| } | ||
|
|
||
| .steps h2, | ||
| .features h2 { | ||
| color: #fafafa; | ||
| } | ||
|
|
||
| .steps ol, | ||
| .features ul { | ||
| color: #a3a3a3; | ||
| } | ||
|
|
||
| .links { | ||
| border-top-color: #404040; | ||
| } | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.