@tellet/create 0.8.3 → 0.10.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.
package/README.md CHANGED
@@ -1,188 +1,154 @@
1
- # @tellet/create
1
+ # Tell it. Let it.
2
2
 
3
- > The open-source platform for running an Agentic Company.
3
+ > AI agents that run your business email, delegate, schedule, embed.
4
4
 
5
5
  ```bash
6
6
  npx @tellet/create
7
7
  ```
8
8
 
9
- tellet is a management platform for AI-powered companies. One command generates your AI agent team, website, dashboard, Knowledge Base, and Orchestrator ready to deploy anywhere.
9
+ Or start instantly at [tellet.com](https://tellet.com)no setup required.
10
10
 
11
- ## What you get
11
+ ---
12
12
 
13
- - **AI Agent Team** 3-5 agents auto-generated for your business (CS, marketing, sales, ops)
14
- - **Orchestrator** — Manage your entire company through conversation
15
- - **Knowledge Base** — pgvector-powered, agents reference it for accurate answers
16
- - **Dashboard** — Stats, agent chat, conversations, onboarding
17
- - **Tool Marketplace** — Stripe, Email, GitHub, Slack, Notion via MCP
18
- - **Embeddable Widget** — One script tag to add AI chat to any website
19
- - **3-Tier Deployment** — Free (Vercel) / Cloud (Railway) / Enterprise (AWS)
13
+ tellet generates an AI agent team for your business. Agents don't just chat — they send emails, delegate tasks to each other, run scheduled automations, and serve customers through an embeddable widget.
20
14
 
21
- ## How it works
15
+ ## What agents can do
22
16
 
23
- ```
24
- npx @tellet/create
17
+ - **Email customers** — Send follow-ups, confirmations, and outreach via Resend
18
+ - **Delegate to each other** — Support agent routes billing questions to sales automatically
19
+ - **Run on schedule** — Daily reports, weekly summaries, automated follow-ups via cron
20
+ - **Search knowledge** — Reference your product info, policies, and FAQ for accurate answers
21
+ - **Serve customers anywhere** — Embeddable widget for any website, one script tag
25
22
 
26
- ? New or Connect? → New business or add AI to existing
27
- ? Deployment? → Quick Start / Cloud / Enterprise
28
- ? AI Provider? → Anthropic / OpenAI
29
- ? Company name? → Sunny Coffee
30
- ? Describe your business → We sell specialty coffee...
23
+ ## Two ways to start
31
24
 
32
- Generating your AI team and website...
25
+ ### Hosted (tellet.com) recommended
33
26
 
34
- Your team:
35
- Barista (customer_support)
36
- Roaster (marketing)
37
- Grinder (sales)
27
+ 1. Sign up at [tellet.com](https://tellet.com)
28
+ 2. Describe your business in one sentence
29
+ 3. AI generates your agent team with email, delegation, and scheduling
30
+ 4. Embed the widget on your site, agents go to work
38
31
 
39
- Your website:
40
- "Coffee worth waking up for"
41
-
42
- ✓ Project created!
43
- ```
44
-
45
- ## Quick start
32
+ ### Self-hosted (CLI)
46
33
 
47
34
  ```bash
48
- # Quick Start (Vercel + Supabase, free)
49
35
  npx @tellet/create
50
36
  cd your-company
51
37
  npm install && npm run dev
52
-
53
- # Cloud (Docker + Railway, $5/mo)
54
- npx @tellet/create # choose "Cloud"
55
- cd your-company
56
- docker compose up # local dev
57
- railway up # deploy
58
-
59
- # Enterprise (AWS CDK, $5-15/mo)
60
- npx @tellet/create # choose "Enterprise"
61
- cd your-company/infra
62
- npm install && npx cdk deploy
63
38
  ```
64
39
 
65
- ## Architecture
40
+ ## How it works
66
41
 
67
42
  ```
68
- ┌─ tellet Platform ─────────────────────────┐
69
- │ Owner ↔ Orchestrator │
70
- │ ↕ │
71
- │ Agent Team (CS · Marketing · Sales · Ops) │
72
- │ ↕ │
73
- │ MCP Bridge Layer │
74
- │ (KB, Stripe, DB, Email, Custom API...) │
75
- └────────────────────────────────────────────┘
76
- ```
43
+ You: "I run a coffee shop called Sunny Coffee"
77
44
 
78
- ## Project structure
45
+ Generating your AI team...
79
46
 
80
- ```
81
- your-company/
82
- ├── agents/ # AI agent definitions (auto-generated)
83
- ├── app/
84
- │ ├── (site)/ # Public website with chat widget
85
- │ ├── (dashboard)/ # Management dashboard + Orchestrator
86
- │ └── api/
87
- │ ├── chat/ # Streaming chat API (tool use)
88
- │ ├── orchestrator/ # Orchestrator API (tool use loop)
89
- │ └── cron/ # Scheduled agent tasks
90
- ├── components/
91
- │ ├── chat/ # ChatWidget, Markdown
92
- │ ├── dashboard/ # Sidebar, Stats, AgentChat, Orchestrator
93
- │ └── sections/ # Landing page sections
94
- ├── lib/
95
- │ ├── engine/ # Agent runtime with tool use agentic loop
96
- │ ├── providers/ # LLM providers (Anthropic, OpenAI)
97
- │ ├── mcp/ # MCP client, Knowledge Base, tool registry
98
- │ ├── orchestrator/ # Orchestrator tools + executor
99
- │ └── scheduler.ts # Cron/heartbeat agent scheduler
100
- ├── public/widget.js # Embeddable chat widget
101
- ├── tellet.json # Configuration (single source of truth)
102
- ├── Dockerfile # Docker deployment (Cloud/Enterprise)
103
- ├── docker-compose.yml # Local dev with PostgreSQL + pgvector
104
- ├── railway.toml # Railway auto-deploy
105
- └── infra/ # AWS CDK (Enterprise only)
106
- ```
47
+ Your team:
48
+ Barista (customer_support) — emails, knowledge, delegation
49
+ Roaster (marketing) — emails, knowledge, delegation
50
+ Grinder (sales) — emails, knowledge, delegation
107
51
 
108
- ## Orchestrator
52
+ Orchestrator tools:
53
+ ✓ schedule_task — cron-based agent automation
54
+ ✓ list_scheduled_tasks — view all schedules
55
+ ✓ cancel_scheduled_task
109
56
 
110
- The Orchestrator is your AI company manager. Talk to it from the dashboard:
57
+ Your AI company is live!
58
+ ```
111
59
 
112
- - "Show my stats" — conversations, messages, costs
113
- - "Update the website tagline" — modifies site content
114
- - "Add Stripe to my agents" — installs tools from marketplace
115
- - "Schedule marketing to post daily at 9am" — sets up cron tasks
116
- - "Add our refund policy to the Knowledge Base" — agents reference it
60
+ ## Architecture
117
61
 
118
- ## Tool Marketplace
62
+ ```
63
+ ┌─ tellet Platform ──────────────────────────────────┐
64
+ │ │
65
+ │ Owner ↔ Orchestrator (schedule, manage, configure) │
66
+ │ ↕ │
67
+ │ Agent Team (CS · Marketing · Sales · Ops) │
68
+ │ ↕ delegate_to_agent ↕ │
69
+ │ Actions: email · search · schedule │
70
+ │ ↕ │
71
+ │ Channels: dashboard · widget · cron │
72
+ │ │
73
+ └─────────────────────────────────────────────────────┘
74
+ ```
119
75
 
120
- Connect tools via the Orchestrator or `tellet.json`:
76
+ ## Agent capabilities by role
121
77
 
122
- | Tool | Package | Use case |
123
- |------|---------|----------|
124
- | Stripe | `@stripe/mcp` | Payments, invoices, subscriptions |
125
- | Email | `resend-mcp` | Send emails, campaigns |
126
- | GitHub | `@modelcontextprotocol/server-github` | Issues, PRs, repos |
127
- | Slack | `@anthropic-ai/mcp-server-slack` | Messages, channels |
128
- | Notion | `@anthropic-ai/mcp-server-notion` | Docs, databases |
78
+ | Role | search_knowledge | send_email | delegate_to_agent |
79
+ |------|:---:|:---:|:---:|
80
+ | customer_support | Y | Y | Y |
81
+ | sales | Y | Y | Y |
82
+ | marketing | Y | Y | Y |
83
+ | operations | Y | Y | Y |
84
+ | analytics | Y | - | Y |
129
85
 
130
- 19,000+ MCP servers available via the [MCP Registry](https://registry.modelcontextprotocol.io/).
86
+ ## Orchestrator commands
131
87
 
132
- ## Deployment options
88
+ The Orchestrator manages your AI company through conversation:
133
89
 
134
- | Tier | Provider | Cost | Best for |
135
- |------|----------|------|----------|
136
- | Quick Start | Vercel + Supabase | $0 | Prototyping, new business |
137
- | Cloud | Railway / Render / Fly.io | $5-20/mo | Production, startups |
138
- | Enterprise | AWS CDK (Lambda + RDS) | $5-15/mo | Scale, existing AWS |
90
+ - "Schedule marketing to send a weekly summary every Monday 9am"
91
+ - "Show all scheduled tasks"
92
+ - "Add our refund policy to the Knowledge Base"
93
+ - "Update the support agent's system prompt"
94
+ - "Show my stats"
139
95
 
140
- ## Connect mode
96
+ ## Embeddable widget
141
97
 
142
- Already have a business? Use Connect mode:
98
+ Add AI chat to any website:
143
99
 
144
- ```bash
145
- npx @tellet/create # choose "Connect"
100
+ ```html
101
+ <script src="https://tellet.com/widget.js"></script>
102
+ <script>
103
+ Tellet.init({ companyId: "your-company-id" });
104
+ </script>
146
105
  ```
147
106
 
148
- - Skips site generation, keeps dashboard + API
149
- - Embeddable widget for your existing site:
107
+ Features: dark/light theme, streaming responses, session persistence, mobile responsive.
150
108
 
151
- ```html
152
- <script src="https://your-tellet.com/widget.js"
153
- data-agent="support"
154
- data-api="https://your-tellet.com"></script>
155
- ```
109
+ ## Project structure (self-hosted)
156
110
 
157
- ## Configuration
158
-
159
- All configuration lives in `tellet.json`:
160
-
161
- ```json
162
- {
163
- "company": { "name": "Sunny Coffee", "industry": "Food & Beverage" },
164
- "mode": "new",
165
- "llm": { "provider": "anthropic", "defaultModel": "claude-sonnet-4-6" },
166
- "agents": [
167
- { "id": "barista", "role": "customer_support", "tools": ["search_knowledge"] },
168
- { "id": "roaster", "role": "marketing", "tools": ["email"] }
169
- ],
170
- "tools": {
171
- "search_knowledge": { "type": "builtin" },
172
- "email": { "type": "mcp", "package": "resend-mcp" }
173
- },
174
- "site": { "tagline": "Coffee worth waking up for", "..." : "..." }
175
- }
111
+ ```
112
+ your-company/
113
+ ├─�� app/
114
+ │ ├── (site)/ # Public website with chat widget
115
+ │ ├── (dashboard)/ # Management dashboard + Orchestrator
116
+ │ └── api/
117
+ │ ├── chat/ # Agent chat (SSE, tool use loop)
118
+ │ ├── orchestrator/ # Orchestrator (scheduling, management)
119
+ │ ├── cron/ # Scheduled task execution
120
+ │ └── widget/ # Public widget API (CORS, no auth)
121
+ ├── lib/
122
+ │ ├── engine/ # Agent runtime with agentic loop
123
+ │ ├── actions/ # Role-based tools (email, delegate, search)
124
+ │ ├── scheduling/ # Cron parser + task executor
125
+ │ ├── orchestrator/ # Orchestrator tools + executor
126
+ │ ├── providers/ # LLM providers (Anthropic, OpenAI)
127
+ │ └── mcp/ # Knowledge base, tool registry
128
+ ├── public/widget.js # Embeddable chat widget
129
+ ├── supabase/migrations/ # Database schema
130
+ └── vercel.json # Cron configuration
176
131
  ```
177
132
 
178
133
  ## Tech stack
179
134
 
180
135
  - [Next.js 16](https://nextjs.org/) — App framework
181
- - [PostgreSQL + pgvector](https://github.com/pgvector/pgvector) — Database + vector search
182
- - [MCP](https://modelcontextprotocol.io/) — Tool integration protocol
136
+ - [Supabase](https://supabase.com/) — Auth, database, pgvector, RLS
183
137
  - [Anthropic Claude](https://anthropic.com/) / [OpenAI](https://openai.com/) — AI models
138
+ - [Resend](https://resend.com/) — Email delivery
139
+ - [Vercel Cron](https://vercel.com/docs/cron-jobs) — Scheduled task execution
184
140
  - [Tailwind CSS 4](https://tailwindcss.com/) — Styling
185
- - [Framer Motion](https://www.framer.com/motion/) — Animations
141
+
142
+ ## Environment variables
143
+
144
+ | Variable | Required | Description |
145
+ |----------|:---:|-------------|
146
+ | `NEXT_PUBLIC_SUPABASE_URL` | Y | Supabase project URL |
147
+ | `NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY` | Y | Supabase publishable key |
148
+ | `SUPABASE_SERVICE_ROLE_KEY` | Y | Supabase service role key |
149
+ | `ANTHROPIC_API_KEY` | Y | Claude API key |
150
+ | `RESEND_API_KEY` | - | Resend API key (for email) |
151
+ | `CRON_SECRET` | - | Secret for cron endpoint auth |
186
152
 
187
153
  ## License
188
154
 
@@ -190,6 +156,6 @@ MIT
190
156
 
191
157
  ## Links
192
158
 
193
- - [tellet.com](https://tellet.com) — Website
159
+ - [tellet.com](https://tellet.com) — Hosted platform (free)
194
160
  - [GitHub](https://github.com/agentic-company/tellet) — Source code
195
- - [npm](https://www.npmjs.com/package/@tellet/create) — Package
161
+ - [npm](https://www.npmjs.com/package/@tellet/create) — CLI package
package/dist/index.js CHANGED
@@ -3,6 +3,8 @@ import * as p from "@clack/prompts";
3
3
  import chalk from "chalk";
4
4
  import { generateAgents } from "./ai/generate.js";
5
5
  import { scaffoldProject } from "./scaffold/project.js";
6
+ import { setupSupabase } from "./setup/supabase.js";
7
+ import { installDependencies } from "./setup/deps.js";
6
8
  async function main() {
7
9
  console.clear();
8
10
  console.log();
@@ -198,33 +200,15 @@ async function main() {
198
200
  }
199
201
  }
200
202
  // Step 7b: Infrastructure setup (tier-dependent)
203
+ // For quickstart: we'll collect Supabase info now, but may override
204
+ // with automated setup after scaffolding
201
205
  let supabaseUrl = "";
202
206
  let supabaseKey = "";
207
+ let useAutomatedSupabase = false;
203
208
  if (tier === "quickstart") {
204
- p.log.info(`${chalk.bold("Supabase setup")} ${chalk.dim("(free tier works fine)")}\n` +
205
- ` ${chalk.dim("1.")} Create a project at ${chalk.cyan("https://supabase.com/dashboard/new")}\n` +
206
- ` ${chalk.dim("2.")} Go to Settings → API to find your URL and keys`);
207
- const supabase = await p.group({
208
- url: () => p.text({
209
- message: "Your Supabase project URL:",
210
- placeholder: "https://xxx.supabase.co",
211
- validate: (v) => !v || !v.includes("supabase")
212
- ? "Please enter a valid Supabase URL"
213
- : undefined,
214
- }),
215
- key: () => p.text({
216
- message: "Your Supabase publishable key (anon/public):",
217
- placeholder: "sb_publishable_...",
218
- validate: (v) => (!v ? "Key is required" : undefined),
219
- }),
220
- }, {
221
- onCancel: () => {
222
- p.cancel("Setup cancelled.");
223
- process.exit(0);
224
- },
225
- });
226
- supabaseUrl = supabase.url;
227
- supabaseKey = supabase.key;
209
+ // We'll handle Supabase setup after scaffold for CLI automation
210
+ // For now, just mark that we need it
211
+ useAutomatedSupabase = true;
228
212
  }
229
213
  else if (tier === "cloud") {
230
214
  p.log.info(chalk.dim("Cloud mode: PostgreSQL runs in Docker. No Supabase needed."));
@@ -236,6 +220,10 @@ async function main() {
236
220
  }
237
221
  // Step 8: Scaffold project
238
222
  s.start("Creating your project...");
223
+ const slug = company.name
224
+ .toLowerCase()
225
+ .replace(/[^a-z0-9]+/g, "-")
226
+ .replace(/^-|-$/g, "");
239
227
  try {
240
228
  const projectDir = await scaffoldProject({
241
229
  company: {
@@ -252,15 +240,111 @@ async function main() {
252
240
  infra: {
253
241
  anthropicKey,
254
242
  openaiKey: provider === "openai" ? apiKey : undefined,
255
- supabaseUrl,
256
- supabaseKey,
243
+ supabaseUrl: supabaseUrl || "PLACEHOLDER",
244
+ supabaseKey: supabaseKey || "PLACEHOLDER",
257
245
  },
258
246
  });
259
247
  s.stop("Project created!");
260
- const slug = company.name
261
- .toLowerCase()
262
- .replace(/[^a-z0-9]+/g, "-")
263
- .replace(/^-|-$/g, "");
248
+ // ─────────────────────────────────────────────────────────
249
+ // Post-scaffold automation
250
+ // ─────────────────────────────────────────────────────────
251
+ // Step 9: Supabase setup (quickstart only)
252
+ if (tier === "quickstart" && useAutomatedSupabase) {
253
+ const result = await setupSupabase(projectDir);
254
+ if (result.method === "local" && result.credentials) {
255
+ // Auto-detected credentials from local Supabase
256
+ supabaseUrl = result.credentials.url;
257
+ supabaseKey = result.credentials.anonKey;
258
+ // Rewrite .env.local with real credentials
259
+ const { writeEnvFile } = await import("./setup/env.js");
260
+ await writeEnvFile(projectDir, {
261
+ anthropicKey,
262
+ openaiKey: provider === "openai" ? apiKey : undefined,
263
+ supabaseUrl,
264
+ supabaseKey,
265
+ serviceRoleKey: result.credentials.serviceRoleKey,
266
+ });
267
+ p.log.success(chalk.green("Supabase configured automatically!") +
268
+ chalk.dim(` → ${supabaseUrl}`));
269
+ }
270
+ else if (result.method === "remote") {
271
+ // Remote linked — user needs to provide URL/key for .env
272
+ p.log.info(chalk.dim("Project linked. Get your URL and key from Settings → API."));
273
+ const supabase = await p.group({
274
+ url: () => p.text({
275
+ message: "Your Supabase project URL:",
276
+ placeholder: "https://xxx.supabase.co",
277
+ validate: (v) => !v || !v.includes("supabase")
278
+ ? "Please enter a valid Supabase URL"
279
+ : undefined,
280
+ }),
281
+ key: () => p.text({
282
+ message: "Your Supabase publishable key (anon/public):",
283
+ placeholder: "sb_publishable_...",
284
+ validate: (v) => (!v ? "Key is required" : undefined),
285
+ }),
286
+ }, {
287
+ onCancel: () => {
288
+ p.cancel("Setup cancelled.");
289
+ process.exit(0);
290
+ },
291
+ });
292
+ supabaseUrl = supabase.url;
293
+ supabaseKey = supabase.key;
294
+ const { writeEnvFile } = await import("./setup/env.js");
295
+ await writeEnvFile(projectDir, {
296
+ anthropicKey,
297
+ openaiKey: provider === "openai" ? apiKey : undefined,
298
+ supabaseUrl,
299
+ supabaseKey,
300
+ });
301
+ }
302
+ else {
303
+ // Manual fallback — same as before
304
+ p.log.info(`${chalk.bold("Supabase setup")} ${chalk.dim("(free tier works fine)")}\n` +
305
+ ` ${chalk.dim("1.")} Create a project at ${chalk.cyan("https://supabase.com/dashboard/new")}\n` +
306
+ ` ${chalk.dim("2.")} Go to Settings → API to find your URL and keys`);
307
+ const supabase = await p.group({
308
+ url: () => p.text({
309
+ message: "Your Supabase project URL:",
310
+ placeholder: "https://xxx.supabase.co",
311
+ validate: (v) => !v || !v.includes("supabase")
312
+ ? "Please enter a valid Supabase URL"
313
+ : undefined,
314
+ }),
315
+ key: () => p.text({
316
+ message: "Your Supabase publishable key (anon/public):",
317
+ placeholder: "sb_publishable_...",
318
+ validate: (v) => (!v ? "Key is required" : undefined),
319
+ }),
320
+ }, {
321
+ onCancel: () => {
322
+ p.cancel("Setup cancelled.");
323
+ process.exit(0);
324
+ },
325
+ });
326
+ supabaseUrl = supabase.url;
327
+ supabaseKey = supabase.key;
328
+ const { writeEnvFile } = await import("./setup/env.js");
329
+ await writeEnvFile(projectDir, {
330
+ anthropicKey,
331
+ openaiKey: provider === "openai" ? apiKey : undefined,
332
+ supabaseUrl,
333
+ supabaseKey,
334
+ });
335
+ }
336
+ }
337
+ // Step 10: Install dependencies
338
+ const installConfirm = await p.confirm({
339
+ message: "Install dependencies now? (npm install)",
340
+ initialValue: true,
341
+ });
342
+ if (!p.isCancel(installConfirm) && installConfirm) {
343
+ await installDependencies(projectDir);
344
+ }
345
+ // ─────────────────────────────────────────────────────────
346
+ // Final output
347
+ // ─────────────────────────────────────────────────────────
264
348
  const widgetSnippet = mode === "connect"
265
349
  ? [
266
350
  ``,
@@ -270,11 +354,12 @@ async function main() {
270
354
  `${chalk.cyan(' data-api="https://YOUR_URL"></script>')}`,
271
355
  ]
272
356
  : [];
357
+ const didInstall = !p.isCancel(installConfirm) && installConfirm === true;
358
+ const devCmd = didInstall ? "npm run dev" : "npm install && npm run dev";
273
359
  if (tier === "quickstart") {
274
360
  p.note([
275
361
  `cd ${slug}`,
276
- `npm install`,
277
- `npm run dev ${chalk.dim("→ http://localhost:3000")}`,
362
+ `${devCmd} ${chalk.dim("→ http://localhost:3000")}`,
278
363
  ``,
279
364
  `Dashboard: ${chalk.dim("/dashboard")}`,
280
365
  `Orchestrator: ${chalk.dim("floating button in dashboard")}`,
@@ -181,7 +181,7 @@ export default defineAgent({
181
181
  }
182
182
  function generateMigration(agents, company) {
183
183
  const seedValues = agents
184
- .map((a) => `('${a.id}', '${a.name}', '${a.role}', ${pgEscape(a.systemPrompt)}, '${a.model}', 'active', '{}')`)
184
+ .map((a) => `(${pgEscape(a.id)}, ${pgEscape(a.name)}, ${pgEscape(a.role)}, ${pgEscape(a.systemPrompt)}, ${pgEscape(a.model)}, 'active', '{}')`)
185
185
  .join(",\n");
186
186
  return `-- tellet schema
187
187
 
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Run `npm install` in the project directory with live output.
3
+ */
4
+ export declare function installDependencies(projectDir: string): Promise<boolean>;
@@ -0,0 +1,24 @@
1
+ import { spawnSync } from "child_process";
2
+ import * as p from "@clack/prompts";
3
+ /**
4
+ * Run `npm install` in the project directory with live output.
5
+ */
6
+ export async function installDependencies(projectDir) {
7
+ const s = p.spinner();
8
+ s.start("Installing dependencies...");
9
+ const result = spawnSync("npm", ["install"], {
10
+ cwd: projectDir,
11
+ stdio: "pipe",
12
+ timeout: 300_000,
13
+ });
14
+ if (result.status !== 0) {
15
+ s.stop("Failed to install dependencies.");
16
+ const stderr = result.stderr?.toString().trim();
17
+ if (stderr) {
18
+ p.log.error(stderr.slice(0, 500));
19
+ }
20
+ return false;
21
+ }
22
+ s.stop("Dependencies installed!");
23
+ return true;
24
+ }
@@ -0,0 +1,12 @@
1
+ interface EnvOptions {
2
+ anthropicKey: string;
3
+ openaiKey?: string;
4
+ supabaseUrl: string;
5
+ supabaseKey: string;
6
+ serviceRoleKey?: string;
7
+ }
8
+ /**
9
+ * Write (or overwrite) the .env.local file with Supabase + API credentials.
10
+ */
11
+ export declare function writeEnvFile(projectDir: string, options: EnvOptions): Promise<void>;
12
+ export {};
@@ -0,0 +1,26 @@
1
+ import fs from "fs-extra";
2
+ import path from "path";
3
+ /** Escape a value for .env files — wrap in quotes if it contains special chars. */
4
+ function envQuote(v) {
5
+ if (/[\s#"'\\$`!]/.test(v) || v.includes("\n")) {
6
+ return `"${v.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n")}"`;
7
+ }
8
+ return v;
9
+ }
10
+ /**
11
+ * Write (or overwrite) the .env.local file with Supabase + API credentials.
12
+ */
13
+ export async function writeEnvFile(projectDir, options) {
14
+ const lines = [
15
+ `# Orchestrator (always Anthropic)`,
16
+ `ANTHROPIC_API_KEY=${envQuote(options.anthropicKey)}`,
17
+ ];
18
+ if (options.openaiKey) {
19
+ lines.push(`OPENAI_API_KEY=${envQuote(options.openaiKey)}`);
20
+ }
21
+ lines.push(``, `# Supabase`, `NEXT_PUBLIC_SUPABASE_URL=${envQuote(options.supabaseUrl)}`, `NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY=${envQuote(options.supabaseKey)}`);
22
+ if (options.serviceRoleKey) {
23
+ lines.push(`SUPABASE_SERVICE_ROLE_KEY=${envQuote(options.serviceRoleKey)}`);
24
+ }
25
+ await fs.writeFile(path.join(projectDir, ".env.local"), lines.join("\n") + "\n");
26
+ }
@@ -0,0 +1,36 @@
1
+ /** Check if the Supabase CLI is installed and accessible. */
2
+ export declare function hasSupabaseCLI(): boolean;
3
+ /** Check if Docker is running (required for `supabase start`). */
4
+ export declare function hasDocker(): boolean;
5
+ export interface SupabaseCredentials {
6
+ url: string;
7
+ anonKey: string;
8
+ serviceRoleKey?: string;
9
+ }
10
+ /**
11
+ * Initialize Supabase in the project directory.
12
+ * Creates supabase/config.toml if not already present.
13
+ */
14
+ export declare function supabaseInit(projectDir: string): boolean;
15
+ /**
16
+ * Start local Supabase (Docker containers).
17
+ * Returns credentials on success, null on failure.
18
+ */
19
+ export declare function supabaseStartLocal(projectDir: string): Promise<SupabaseCredentials | null>;
20
+ /**
21
+ * Link to a remote Supabase project.
22
+ * Prompts for project ref, runs `supabase link`.
23
+ */
24
+ export declare function supabaseLinkRemote(projectDir: string): Promise<boolean>;
25
+ /**
26
+ * Push migrations to the database.
27
+ */
28
+ export declare function supabasePushMigrations(projectDir: string): Promise<boolean>;
29
+ /**
30
+ * Full Supabase setup flow for quickstart tier.
31
+ * Returns credentials or null if user chose manual setup.
32
+ */
33
+ export declare function setupSupabase(projectDir: string): Promise<{
34
+ credentials: SupabaseCredentials | null;
35
+ method: "local" | "remote" | "manual";
36
+ }>;
@@ -0,0 +1,207 @@
1
+ import { execFileSync, spawnSync } from "child_process";
2
+ import * as p from "@clack/prompts";
3
+ import chalk from "chalk";
4
+ /** Check if the Supabase CLI is installed and accessible. */
5
+ export function hasSupabaseCLI() {
6
+ try {
7
+ execFileSync("supabase", ["--version"], { stdio: "ignore" });
8
+ return true;
9
+ }
10
+ catch {
11
+ return false;
12
+ }
13
+ }
14
+ /** Check if Docker is running (required for `supabase start`). */
15
+ export function hasDocker() {
16
+ try {
17
+ execFileSync("docker", ["info"], { stdio: "ignore", timeout: 5000 });
18
+ return true;
19
+ }
20
+ catch {
21
+ return false;
22
+ }
23
+ }
24
+ /**
25
+ * Parse `supabase status` output to extract local dev credentials.
26
+ * The output format is like:
27
+ * API URL: http://127.0.0.1:54321
28
+ * anon key: eyJ...
29
+ * service_role key: eyJ...
30
+ */
31
+ function parseSupabaseStatus(output) {
32
+ const urlMatch = output.match(/API URL:\s*(http\S+)/);
33
+ const anonMatch = output.match(/anon key:\s*(\S+)/);
34
+ const serviceMatch = output.match(/service_role key:\s*(\S+)/);
35
+ if (!urlMatch || !anonMatch)
36
+ return null;
37
+ return {
38
+ url: urlMatch[1],
39
+ anonKey: anonMatch[1],
40
+ serviceRoleKey: serviceMatch?.[1],
41
+ };
42
+ }
43
+ /**
44
+ * Run supabase CLI command in a directory, returning stdout.
45
+ */
46
+ function runSupabase(args, cwd) {
47
+ return execFileSync("supabase", args, {
48
+ cwd,
49
+ encoding: "utf-8",
50
+ timeout: 120_000,
51
+ });
52
+ }
53
+ /**
54
+ * Run supabase CLI with live stdio (visible to user).
55
+ * Returns the exit code.
56
+ */
57
+ function runSupabaseLive(args, cwd) {
58
+ const result = spawnSync("supabase", args, {
59
+ cwd,
60
+ stdio: "inherit",
61
+ timeout: 300_000,
62
+ });
63
+ return result.status ?? 1;
64
+ }
65
+ /**
66
+ * Initialize Supabase in the project directory.
67
+ * Creates supabase/config.toml if not already present.
68
+ */
69
+ export function supabaseInit(projectDir) {
70
+ try {
71
+ runSupabase(["init", "--with-intellij-settings=false", "--with-vscode-settings=false"], projectDir);
72
+ return true;
73
+ }
74
+ catch {
75
+ return false;
76
+ }
77
+ }
78
+ /**
79
+ * Start local Supabase (Docker containers).
80
+ * Returns credentials on success, null on failure.
81
+ */
82
+ export async function supabaseStartLocal(projectDir) {
83
+ p.log.info(chalk.dim("Starting local Supabase (this may take a minute on first run)..."));
84
+ const code = runSupabaseLive(["start"], projectDir);
85
+ if (code !== 0) {
86
+ p.log.error("Failed to start local Supabase.");
87
+ return null;
88
+ }
89
+ p.log.success("Local Supabase is running!");
90
+ // Extract credentials from status
91
+ try {
92
+ const output = runSupabase(["status"], projectDir);
93
+ return parseSupabaseStatus(output);
94
+ }
95
+ catch {
96
+ return null;
97
+ }
98
+ }
99
+ /**
100
+ * Link to a remote Supabase project.
101
+ * Prompts for project ref, runs `supabase link`.
102
+ */
103
+ export async function supabaseLinkRemote(projectDir) {
104
+ const refInput = await p.text({
105
+ message: "Supabase project ref (from Dashboard → Settings → General):",
106
+ placeholder: "abcdefghijklmnopqrst",
107
+ validate: (v) => !v || v.length < 10
108
+ ? "Please enter a valid project ref"
109
+ : undefined,
110
+ });
111
+ if (p.isCancel(refInput))
112
+ return false;
113
+ p.log.info(chalk.dim("Linking to Supabase project (follow the prompts below)..."));
114
+ const code = runSupabaseLive(["link", "--project-ref", refInput], projectDir);
115
+ if (code !== 0) {
116
+ p.log.error("Failed to link project.");
117
+ return false;
118
+ }
119
+ p.log.success("Linked to Supabase project!");
120
+ return true;
121
+ }
122
+ /**
123
+ * Push migrations to the database.
124
+ */
125
+ export async function supabasePushMigrations(projectDir) {
126
+ p.log.info(chalk.dim("Applying database migrations..."));
127
+ const code = runSupabaseLive(["db", "push"], projectDir);
128
+ if (code !== 0) {
129
+ p.log.error("Migration push failed.");
130
+ return false;
131
+ }
132
+ p.log.success("Migrations applied!");
133
+ return true;
134
+ }
135
+ /**
136
+ * Full Supabase setup flow for quickstart tier.
137
+ * Returns credentials or null if user chose manual setup.
138
+ */
139
+ export async function setupSupabase(projectDir) {
140
+ const cliAvailable = hasSupabaseCLI();
141
+ const dockerAvailable = hasDocker();
142
+ if (!cliAvailable) {
143
+ p.log.info(chalk.dim("Supabase CLI not found. Install it for automated setup:\n" +
144
+ " brew install supabase/tap/supabase\n" +
145
+ " — or —\n" +
146
+ " npx supabase --version"));
147
+ return { credentials: null, method: "manual" };
148
+ }
149
+ // Build options based on what's available
150
+ const options = [];
151
+ if (dockerAvailable) {
152
+ options.push({
153
+ value: "local",
154
+ label: "Local development",
155
+ hint: "supabase start — runs Postgres + Auth locally via Docker",
156
+ });
157
+ }
158
+ options.push({
159
+ value: "remote",
160
+ label: "Remote project",
161
+ hint: "Link to an existing Supabase project",
162
+ }, {
163
+ value: "manual",
164
+ label: "Manual setup",
165
+ hint: "I'll enter the URL and key myself",
166
+ });
167
+ if (!dockerAvailable) {
168
+ p.log.info(chalk.dim("Docker not detected — local Supabase requires Docker Desktop."));
169
+ }
170
+ const choice = await p.select({
171
+ message: "Supabase setup:",
172
+ options,
173
+ });
174
+ if (p.isCancel(choice)) {
175
+ return { credentials: null, method: "manual" };
176
+ }
177
+ const method = choice;
178
+ if (method === "manual") {
179
+ return { credentials: null, method: "manual" };
180
+ }
181
+ // Initialize supabase in project
182
+ const inited = supabaseInit(projectDir);
183
+ if (!inited) {
184
+ p.log.error("Failed to initialize Supabase in the project directory.");
185
+ return { credentials: null, method: "manual" };
186
+ }
187
+ if (method === "local") {
188
+ const creds = await supabaseStartLocal(projectDir);
189
+ if (creds) {
190
+ // Apply migrations to local DB
191
+ await supabasePushMigrations(projectDir);
192
+ }
193
+ return { credentials: creds, method: "local" };
194
+ }
195
+ // Remote
196
+ const linked = await supabaseLinkRemote(projectDir);
197
+ if (linked) {
198
+ const pushConfirm = await p.confirm({
199
+ message: "Apply database migrations now?",
200
+ initialValue: true,
201
+ });
202
+ if (!p.isCancel(pushConfirm) && pushConfirm) {
203
+ await supabasePushMigrations(projectDir);
204
+ }
205
+ }
206
+ return { credentials: null, method: "remote" };
207
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tellet/create",
3
- "version": "0.8.3",
4
- "description": "The open-source platform for running an Agentic Company",
3
+ "version": "0.10.0",
4
+ "description": "AI agents that run your business email, delegate, schedule, embed. One command.",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "create": "dist/index.js"
@@ -28,10 +28,16 @@
28
28
  "no-code"
29
29
  ],
30
30
  "license": "MIT",
31
+ "author": "tellet <hello@tellet.com>",
32
+ "homepage": "https://tellet.com",
33
+ "bugs": "https://github.com/agentic-company/tellet/issues",
31
34
  "repository": {
32
35
  "type": "git",
33
36
  "url": "https://github.com/agentic-company/tellet"
34
37
  },
38
+ "engines": {
39
+ "node": ">=18.0.0"
40
+ },
35
41
  "dependencies": {
36
42
  "@anthropic-ai/sdk": "^0.80.0",
37
43
  "@clack/prompts": "^1.1.0",