@tellet/create 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/README.md +195 -0
  2. package/dist/ai/generate.d.ts +33 -0
  3. package/dist/ai/generate.js +108 -0
  4. package/dist/index.d.ts +2 -0
  5. package/dist/index.js +337 -0
  6. package/dist/scaffold/project.d.ts +44 -0
  7. package/dist/scaffold/project.js +318 -0
  8. package/package.json +48 -0
  9. package/template/Dockerfile +35 -0
  10. package/template/app/(dashboard)/agents/page.tsx +14 -0
  11. package/template/app/(dashboard)/conversations/[id]/page.tsx +103 -0
  12. package/template/app/(dashboard)/conversations/page.tsx +50 -0
  13. package/template/app/(dashboard)/dashboard/page.tsx +102 -0
  14. package/template/app/(dashboard)/layout.tsx +15 -0
  15. package/template/app/(dashboard)/settings/page.tsx +46 -0
  16. package/template/app/(site)/layout.tsx +3 -0
  17. package/template/app/(site)/page.tsx +25 -0
  18. package/template/app/api/chat/route.ts +129 -0
  19. package/template/app/api/cron/route.ts +29 -0
  20. package/template/app/api/orchestrator/route.ts +139 -0
  21. package/template/app/globals.css +30 -0
  22. package/template/app/layout.tsx +18 -0
  23. package/template/components/chat/ChatWidget.tsx +109 -0
  24. package/template/components/chat/Markdown.tsx +136 -0
  25. package/template/components/dashboard/AgentChat.tsx +192 -0
  26. package/template/components/dashboard/AgentsListClient.tsx +86 -0
  27. package/template/components/dashboard/DashboardAgentGrid.tsx +73 -0
  28. package/template/components/dashboard/OrchestratorChat.tsx +251 -0
  29. package/template/components/dashboard/Sidebar.tsx +44 -0
  30. package/template/components/dashboard/StatsCards.tsx +40 -0
  31. package/template/components/dashboard/Welcome.tsx +139 -0
  32. package/template/components/sections/Agents.tsx +67 -0
  33. package/template/components/sections/CTA.tsx +46 -0
  34. package/template/components/sections/FAQ.tsx +81 -0
  35. package/template/components/sections/Features.tsx +51 -0
  36. package/template/components/sections/Footer.tsx +22 -0
  37. package/template/components/sections/Hero.tsx +86 -0
  38. package/template/components/sections/Icons.tsx +29 -0
  39. package/template/components/ui/Button.tsx +26 -0
  40. package/template/docker-compose.yml +32 -0
  41. package/template/infra/bin/app.ts +16 -0
  42. package/template/infra/cdk.json +6 -0
  43. package/template/infra/lib/tellet-stack.ts +216 -0
  44. package/template/infra/package.json +20 -0
  45. package/template/infra/tsconfig.json +16 -0
  46. package/template/lib/db.ts +37 -0
  47. package/template/lib/engine/default.ts +227 -0
  48. package/template/lib/engine/index.ts +17 -0
  49. package/template/lib/mcp/client.ts +97 -0
  50. package/template/lib/mcp/knowledge.ts +84 -0
  51. package/template/lib/mcp/registry.ts +106 -0
  52. package/template/lib/orchestrator/executor.ts +202 -0
  53. package/template/lib/orchestrator/tools.ts +245 -0
  54. package/template/lib/providers/anthropic.ts +41 -0
  55. package/template/lib/providers/index.ts +36 -0
  56. package/template/lib/providers/openai.ts +46 -0
  57. package/template/lib/scheduler.ts +115 -0
  58. package/template/lib/supabase.ts +30 -0
  59. package/template/lib/tellet.ts +45 -0
  60. package/template/lib/utils.ts +6 -0
  61. package/template/next.config.ts +7 -0
  62. package/template/public/widget.js +172 -0
  63. package/template/railway.toml +9 -0
  64. package/template/tsconfig.json +21 -0
@@ -0,0 +1,136 @@
1
+ import { type ReactNode } from "react";
2
+
3
+ function parseLine(line: string): ReactNode {
4
+ // Bold
5
+ let parts = line.split(/\*\*(.+?)\*\*/g);
6
+ if (parts.length > 1) {
7
+ return parts.map((part, i) =>
8
+ i % 2 === 1 ? <strong key={i}>{part}</strong> : part
9
+ );
10
+ }
11
+
12
+ // Inline code
13
+ parts = line.split(/`(.+?)`/g);
14
+ if (parts.length > 1) {
15
+ return parts.map((part, i) =>
16
+ i % 2 === 1 ? (
17
+ <code
18
+ key={i}
19
+ className="bg-bg-tertiary text-accent px-1 py-0.5 rounded text-xs"
20
+ >
21
+ {part}
22
+ </code>
23
+ ) : (
24
+ part
25
+ )
26
+ );
27
+ }
28
+
29
+ return line;
30
+ }
31
+
32
+ export function Markdown({ content }: { content: string }) {
33
+ const lines = content.split("\n");
34
+ const elements: ReactNode[] = [];
35
+ let i = 0;
36
+
37
+ while (i < lines.length) {
38
+ const line = lines[i];
39
+
40
+ // Code block
41
+ if (line.startsWith("```")) {
42
+ const lang = line.slice(3).trim();
43
+ const codeLines: string[] = [];
44
+ i++;
45
+ while (i < lines.length && !lines[i].startsWith("```")) {
46
+ codeLines.push(lines[i]);
47
+ i++;
48
+ }
49
+ i++; // skip closing ```
50
+ elements.push(
51
+ <pre
52
+ key={elements.length}
53
+ className="bg-bg-tertiary border border-border rounded-lg p-3 my-2 overflow-x-auto"
54
+ >
55
+ <code className="text-xs text-text-primary">{codeLines.join("\n")}</code>
56
+ </pre>
57
+ );
58
+ continue;
59
+ }
60
+
61
+ // Heading
62
+ if (line.startsWith("### ")) {
63
+ elements.push(
64
+ <p key={elements.length} className="font-semibold text-sm mt-2 mb-1">
65
+ {line.slice(4)}
66
+ </p>
67
+ );
68
+ i++;
69
+ continue;
70
+ }
71
+
72
+ if (line.startsWith("## ")) {
73
+ elements.push(
74
+ <p key={elements.length} className="font-semibold mt-2 mb-1">
75
+ {line.slice(3)}
76
+ </p>
77
+ );
78
+ i++;
79
+ continue;
80
+ }
81
+
82
+ // Bullet list
83
+ if (line.match(/^[-*] /)) {
84
+ const items: string[] = [];
85
+ while (i < lines.length && lines[i].match(/^[-*] /)) {
86
+ items.push(lines[i].replace(/^[-*] /, ""));
87
+ i++;
88
+ }
89
+ elements.push(
90
+ <ul key={elements.length} className="list-disc list-inside space-y-0.5 my-1">
91
+ {items.map((item, j) => (
92
+ <li key={j} className="text-sm">
93
+ {parseLine(item)}
94
+ </li>
95
+ ))}
96
+ </ul>
97
+ );
98
+ continue;
99
+ }
100
+
101
+ // Numbered list
102
+ if (line.match(/^\d+\. /)) {
103
+ const items: string[] = [];
104
+ while (i < lines.length && lines[i].match(/^\d+\. /)) {
105
+ items.push(lines[i].replace(/^\d+\. /, ""));
106
+ i++;
107
+ }
108
+ elements.push(
109
+ <ol key={elements.length} className="list-decimal list-inside space-y-0.5 my-1">
110
+ {items.map((item, j) => (
111
+ <li key={j} className="text-sm">
112
+ {parseLine(item)}
113
+ </li>
114
+ ))}
115
+ </ol>
116
+ );
117
+ continue;
118
+ }
119
+
120
+ // Empty line
121
+ if (line.trim() === "") {
122
+ i++;
123
+ continue;
124
+ }
125
+
126
+ // Regular paragraph
127
+ elements.push(
128
+ <p key={elements.length} className="text-sm">
129
+ {parseLine(line)}
130
+ </p>
131
+ );
132
+ i++;
133
+ }
134
+
135
+ return <div className="space-y-1">{elements}</div>;
136
+ }
@@ -0,0 +1,192 @@
1
+ "use client";
2
+
3
+ import { useState, useRef, useEffect } from "react";
4
+ import { motion, AnimatePresence } from "framer-motion";
5
+ import { cn } from "@/lib/utils";
6
+ import { Markdown } from "@/components/chat/Markdown";
7
+
8
+ interface ChatMessage {
9
+ role: "user" | "assistant";
10
+ content: string;
11
+ }
12
+
13
+ interface AgentChatProps {
14
+ agent: { id: string; name: string; role: string };
15
+ onClose: () => void;
16
+ }
17
+
18
+ export function AgentChat({ agent, onClose }: AgentChatProps) {
19
+ const [input, setInput] = useState("");
20
+ const [messages, setMessages] = useState<ChatMessage[]>([]);
21
+ const [streaming, setStreaming] = useState(false);
22
+ const [conversationId, setConversationId] = useState<string | null>(null);
23
+ const endRef = useRef<HTMLDivElement>(null);
24
+
25
+ useEffect(() => {
26
+ endRef.current?.scrollIntoView({ behavior: "smooth" });
27
+ }, [messages]);
28
+
29
+ const send = async () => {
30
+ const text = input.trim();
31
+ if (!text || streaming) return;
32
+ setInput("");
33
+ setMessages((p) => [...p, { role: "user", content: text }]);
34
+ setStreaming(true);
35
+ setMessages((p) => [...p, { role: "assistant", content: "" }]);
36
+
37
+ try {
38
+ const res = await fetch("/api/chat", {
39
+ method: "POST",
40
+ headers: { "Content-Type": "application/json" },
41
+ body: JSON.stringify({
42
+ message: text,
43
+ agent_id: agent.id,
44
+ conversation_id: conversationId,
45
+ }),
46
+ });
47
+ const reader = res.body?.getReader();
48
+ const decoder = new TextDecoder();
49
+ if (!reader) return;
50
+
51
+ while (true) {
52
+ const { done, value } = await reader.read();
53
+ if (done) break;
54
+ for (const line of decoder
55
+ .decode(value)
56
+ .split("\n")
57
+ .filter((l) => l.startsWith("data: "))) {
58
+ try {
59
+ const data = JSON.parse(line.slice(6));
60
+ if (data.text) {
61
+ setMessages((p) => {
62
+ const u = [...p];
63
+ const last = u[u.length - 1];
64
+ if (last.role === "assistant")
65
+ u[u.length - 1] = { ...last, content: last.content + data.text };
66
+ return u;
67
+ });
68
+ }
69
+ if (data.conversation_id) setConversationId(data.conversation_id);
70
+ } catch {}
71
+ }
72
+ }
73
+ } catch {
74
+ setMessages((p) => {
75
+ const u = [...p];
76
+ u[u.length - 1] = { role: "assistant", content: "Something went wrong." };
77
+ return u;
78
+ });
79
+ } finally {
80
+ setStreaming(false);
81
+ }
82
+ };
83
+
84
+ const roleLabel = agent.role.replace("_", " ");
85
+
86
+ return (
87
+ <AnimatePresence>
88
+ <motion.div
89
+ className="fixed inset-0 bg-black/30 z-40"
90
+ initial={{ opacity: 0 }}
91
+ animate={{ opacity: 1 }}
92
+ exit={{ opacity: 0 }}
93
+ onClick={onClose}
94
+ />
95
+ <motion.div
96
+ className="fixed top-0 right-0 h-full w-full max-w-[440px] z-50 border-l border-border bg-bg-primary shadow-2xl flex flex-col"
97
+ initial={{ x: "100%" }}
98
+ animate={{ x: 0 }}
99
+ exit={{ x: "100%" }}
100
+ transition={{ type: "spring", damping: 25, stiffness: 200 }}
101
+ >
102
+ {/* Header */}
103
+ <div className="flex items-center justify-between px-5 py-4 border-b border-border">
104
+ <div className="flex items-center gap-3">
105
+ <div className="w-8 h-8 rounded-full bg-accent/10 text-accent text-xs font-bold flex items-center justify-center">
106
+ {agent.name[0]}
107
+ </div>
108
+ <div>
109
+ <p className="text-sm font-semibold">{agent.name}</p>
110
+ <p className="text-[11px] text-text-tertiary capitalize">{roleLabel}</p>
111
+ </div>
112
+ </div>
113
+ <button
114
+ onClick={onClose}
115
+ className="text-text-tertiary hover:text-text-secondary transition-colors cursor-pointer"
116
+ >
117
+ <svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
118
+ <path strokeLinecap="round" strokeLinejoin="round" d="M6 18 18 6M6 6l12 12" />
119
+ </svg>
120
+ </button>
121
+ </div>
122
+
123
+ {/* Messages */}
124
+ <div className="flex-1 overflow-y-auto px-5 py-4 space-y-3">
125
+ {messages.length === 0 && (
126
+ <div className="text-center py-12">
127
+ <div className="w-12 h-12 rounded-full bg-accent/10 text-accent mx-auto mb-3 flex items-center justify-center text-lg font-bold">
128
+ {agent.name[0]}
129
+ </div>
130
+ <p className="text-sm text-text-secondary">
131
+ Chat with {agent.name}
132
+ </p>
133
+ <p className="text-xs text-text-tertiary mt-1 capitalize">
134
+ {roleLabel}
135
+ </p>
136
+ </div>
137
+ )}
138
+ {messages.map((m, i) => (
139
+ <div
140
+ key={i}
141
+ className={cn("flex", m.role === "user" ? "justify-end" : "justify-start")}
142
+ >
143
+ <div
144
+ className={cn(
145
+ "rounded-xl px-3 py-2 max-w-[85%] text-sm leading-relaxed",
146
+ m.role === "user"
147
+ ? "bg-accent text-white"
148
+ : "bg-bg-secondary text-text-primary border border-border"
149
+ )}
150
+ >
151
+ {m.content ? <Markdown content={m.content} /> : (
152
+ <span className="inline-flex gap-1">
153
+ <span className="w-1.5 h-1.5 rounded-full bg-text-tertiary animate-pulse" />
154
+ <span className="w-1.5 h-1.5 rounded-full bg-text-tertiary animate-pulse [animation-delay:150ms]" />
155
+ <span className="w-1.5 h-1.5 rounded-full bg-text-tertiary animate-pulse [animation-delay:300ms]" />
156
+ </span>
157
+ )}
158
+ </div>
159
+ </div>
160
+ ))}
161
+ <div ref={endRef} />
162
+ </div>
163
+
164
+ {/* Input */}
165
+ <form
166
+ onSubmit={(e) => {
167
+ e.preventDefault();
168
+ send();
169
+ }}
170
+ className="px-4 py-3 border-t border-border flex gap-2"
171
+ >
172
+ <input
173
+ value={input}
174
+ onChange={(e) => setInput(e.target.value)}
175
+ placeholder="Type a message..."
176
+ disabled={streaming}
177
+ className="flex-1 rounded-lg bg-bg-secondary border border-border px-3 py-2 text-sm text-text-primary placeholder:text-text-tertiary focus:outline-none focus:border-accent disabled:opacity-50"
178
+ />
179
+ <button
180
+ type="submit"
181
+ disabled={streaming || !input.trim()}
182
+ className="rounded-lg bg-accent px-3 py-2 text-white text-sm hover:bg-accent-hover disabled:opacity-50 cursor-pointer transition-colors"
183
+ >
184
+ <svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
185
+ <path strokeLinecap="round" strokeLinejoin="round" d="M6 12 3.269 3.125A59.769 59.769 0 0 1 21.485 12 59.768 59.768 0 0 1 3.27 20.875L5.999 12Zm0 0h7.5" />
186
+ </svg>
187
+ </button>
188
+ </form>
189
+ </motion.div>
190
+ </AnimatePresence>
191
+ );
192
+ }
@@ -0,0 +1,86 @@
1
+ "use client";
2
+
3
+ import { useState } from "react";
4
+ import { AgentChat } from "./AgentChat";
5
+
6
+ interface Agent {
7
+ id: string;
8
+ name: string;
9
+ role: string;
10
+ model: string;
11
+ status: string;
12
+ system_prompt: string;
13
+ }
14
+
15
+ export function AgentsListClient({ agents }: { agents: Agent[] }) {
16
+ const [chatAgent, setChatAgent] = useState<Agent | null>(null);
17
+
18
+ if (!agents || agents.length === 0) {
19
+ return (
20
+ <div className="rounded-lg border border-dashed border-border bg-bg-secondary/20 p-8 text-center">
21
+ <p className="text-text-secondary text-sm">No agents configured.</p>
22
+ <p className="text-text-tertiary text-xs mt-1">
23
+ Check your tellet.json and run the database migration.
24
+ </p>
25
+ </div>
26
+ );
27
+ }
28
+
29
+ return (
30
+ <>
31
+ <div className="grid gap-4">
32
+ {agents.map((agent) => (
33
+ <div
34
+ key={agent.id}
35
+ className="rounded-xl border border-border bg-bg-secondary/50 p-5"
36
+ >
37
+ <div className="flex items-center gap-4">
38
+ <div className="w-10 h-10 rounded-full bg-accent/10 text-accent text-sm font-bold flex items-center justify-center">
39
+ {agent.name[0]}
40
+ </div>
41
+ <div className="flex-1">
42
+ <p className="font-semibold">{agent.name}</p>
43
+ <p className="text-sm text-text-secondary capitalize">
44
+ {agent.role.replace("_", " ")} &middot; {agent.model}
45
+ </p>
46
+ </div>
47
+ <div className="flex items-center gap-3">
48
+ <div className="flex items-center gap-2">
49
+ <span
50
+ className={`w-2 h-2 rounded-full ${
51
+ agent.status === "active" ? "bg-green-400" : "bg-text-tertiary"
52
+ }`}
53
+ />
54
+ <span className="text-xs text-text-secondary capitalize">
55
+ {agent.status}
56
+ </span>
57
+ </div>
58
+ <button
59
+ onClick={() => setChatAgent(agent)}
60
+ className="text-xs text-text-secondary border border-border rounded-lg px-3 py-1.5 hover:border-accent hover:text-accent transition-colors cursor-pointer"
61
+ >
62
+ Chat now
63
+ </button>
64
+ </div>
65
+ </div>
66
+ <div className="mt-4 rounded-lg bg-bg-primary border border-border p-3">
67
+ <p className="text-xs text-text-tertiary uppercase tracking-wider mb-1">
68
+ System Prompt
69
+ </p>
70
+ <p className="text-sm text-text-secondary leading-relaxed line-clamp-3">
71
+ {agent.system_prompt}
72
+ </p>
73
+ </div>
74
+ </div>
75
+ ))}
76
+ </div>
77
+
78
+ {chatAgent && (
79
+ <AgentChat
80
+ agent={chatAgent}
81
+ onClose={() => setChatAgent(null)}
82
+ />
83
+ )}
84
+ </>
85
+ );
86
+ }
@@ -0,0 +1,73 @@
1
+ "use client";
2
+
3
+ import { useState } from "react";
4
+ import { AgentChat } from "./AgentChat";
5
+
6
+ interface Agent {
7
+ id: string;
8
+ name: string;
9
+ role: string;
10
+ status: string;
11
+ }
12
+
13
+ const roleColors: Record<string, string> = {
14
+ customer_support: "bg-green-500/10 text-green-400 border-green-500/20",
15
+ marketing: "bg-blue-500/10 text-blue-400 border-blue-500/20",
16
+ sales: "bg-amber-500/10 text-amber-400 border-amber-500/20",
17
+ operations: "bg-purple-500/10 text-purple-400 border-purple-500/20",
18
+ development: "bg-cyan-500/10 text-cyan-400 border-cyan-500/20",
19
+ analytics: "bg-rose-500/10 text-rose-400 border-rose-500/20",
20
+ };
21
+
22
+ export function DashboardAgentGrid({ agents }: { agents: Agent[] }) {
23
+ const [chatAgent, setChatAgent] = useState<Agent | null>(null);
24
+
25
+ return (
26
+ <>
27
+ <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
28
+ {agents.map((agent) => (
29
+ <div
30
+ key={agent.id}
31
+ className="rounded-xl border border-border bg-bg-secondary/50 p-5 transition-colors hover:border-border-hover"
32
+ >
33
+ <div className="flex items-center justify-between mb-3">
34
+ <span className="text-lg font-semibold">{agent.name}</span>
35
+ <span
36
+ className={`inline-flex items-center rounded-full border px-2 py-0.5 text-[11px] font-medium ${
37
+ roleColors[agent.role] || "bg-bg-tertiary text-text-secondary border-border"
38
+ }`}
39
+ >
40
+ {agent.role.replace("_", " ")}
41
+ </span>
42
+ </div>
43
+ <div className="flex items-center justify-between">
44
+ <div className="flex items-center gap-2">
45
+ <span
46
+ className={`w-2 h-2 rounded-full ${
47
+ agent.status === "active" ? "bg-green-400" : "bg-text-tertiary"
48
+ }`}
49
+ />
50
+ <span className="text-xs text-text-secondary capitalize">
51
+ {agent.status}
52
+ </span>
53
+ </div>
54
+ <button
55
+ onClick={() => setChatAgent(agent)}
56
+ className="text-xs text-text-secondary border border-border rounded-lg px-3 py-1.5 hover:border-accent hover:text-accent transition-colors cursor-pointer"
57
+ >
58
+ Chat
59
+ </button>
60
+ </div>
61
+ </div>
62
+ ))}
63
+ </div>
64
+
65
+ {chatAgent && (
66
+ <AgentChat
67
+ agent={chatAgent}
68
+ onClose={() => setChatAgent(null)}
69
+ />
70
+ )}
71
+ </>
72
+ );
73
+ }