@tellet/create 0.10.0 → 0.12.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/dist/index.js CHANGED
@@ -1,422 +1,9 @@
1
1
  #!/usr/bin/env node
2
- import * as p from "@clack/prompts";
3
- import chalk from "chalk";
4
- import { generateAgents } from "./ai/generate.js";
5
- import { scaffoldProject } from "./scaffold/project.js";
6
- import { setupSupabase } from "./setup/supabase.js";
7
- import { installDependencies } from "./setup/deps.js";
8
- async function main() {
9
- console.clear();
10
- console.log();
11
- console.log(chalk.bold(` ${chalk.white("tel")}${chalk.yellow("let")} ${chalk.dim("— Build Your AI Company")}`));
12
- console.log();
13
- p.intro(chalk.bgHex("#8b5cf6").white(" @tellet/create "));
14
- // Step 0: New or Connect
15
- const modeChoice = await p.select({
16
- message: "What would you like to do?",
17
- options: [
18
- {
19
- value: "new",
20
- label: "New",
21
- hint: "Build a new AI company from scratch",
22
- },
23
- {
24
- value: "connect",
25
- label: "Connect",
26
- hint: "Add AI agents to your existing business",
27
- },
28
- ],
29
- });
30
- if (p.isCancel(modeChoice)) {
31
- p.cancel("Setup cancelled.");
32
- process.exit(0);
33
- }
34
- const mode = modeChoice;
35
- // Step 1: Company info
36
- const company = await p.group({
37
- name: () => p.text({
38
- message: "What's your company name?",
39
- placeholder: "Sunny Coffee",
40
- validate: (v) => (!v ? "Company name is required" : undefined),
41
- }),
42
- description: () => p.text({
43
- message: "Describe your business (what you do, who your customers are, what help you need):",
44
- placeholder: "We sell specialty coffee subscriptions. Customers are coffee enthusiasts aged 25-45...",
45
- validate: (v) => !v || v.length < 20
46
- ? "Please provide at least a few sentences"
47
- : undefined,
48
- }),
49
- }, {
50
- onCancel: () => {
51
- p.cancel("Setup cancelled.");
52
- process.exit(0);
53
- },
54
- });
55
- // Step 2: Deployment tier
56
- const tierChoice = await p.select({
57
- message: "Deployment mode:",
58
- options: [
59
- {
60
- value: "quickstart",
61
- label: "Quick Start",
62
- hint: "Vercel + Supabase — free, instant deploy",
63
- },
64
- {
65
- value: "cloud",
66
- label: "Cloud",
67
- hint: "Railway / Render / Fly.io — Docker, $5-20/mo",
68
- },
69
- {
70
- value: "enterprise",
71
- label: "Enterprise",
72
- hint: "AWS CDK — auto-provision Lambda + RDS + CloudFront",
73
- },
74
- ],
75
- });
76
- if (p.isCancel(tierChoice)) {
77
- p.cancel("Setup cancelled.");
78
- process.exit(0);
79
- }
80
- const tier = tierChoice;
81
- // Step 3: Choose AI provider
82
- const providerChoice = await p.select({
83
- message: "Choose your AI provider:",
84
- options: [
85
- {
86
- value: "anthropic",
87
- label: "Anthropic (Claude)",
88
- hint: "recommended — also powers the Orchestrator",
89
- },
90
- {
91
- value: "openai",
92
- label: "OpenAI (GPT)",
93
- hint: "uses gpt-4.1 for generation",
94
- },
95
- ],
96
- });
97
- if (p.isCancel(providerChoice)) {
98
- p.cancel("Setup cancelled.");
99
- process.exit(0);
100
- }
101
- const provider = providerChoice;
102
- // Step 4: Get API key
103
- const envKey = provider === "anthropic"
104
- ? process.env.ANTHROPIC_API_KEY
105
- : process.env.OPENAI_API_KEY;
106
- const envName = provider === "anthropic" ? "ANTHROPIC_API_KEY" : "OPENAI_API_KEY";
107
- const keyPrefix = provider === "anthropic" ? "sk-ant-" : "sk-";
108
- const keyPlaceholder = provider === "anthropic" ? "sk-ant-..." : "sk-...";
109
- let apiKey = envKey || "";
110
- if (!apiKey) {
111
- const keyInput = await p.text({
112
- message: `Your ${provider === "anthropic" ? "Anthropic" : "OpenAI"} API key:`,
113
- placeholder: keyPlaceholder,
114
- validate: (v) => !v || !v.startsWith(keyPrefix)
115
- ? `Please enter a valid key (starts with ${keyPrefix})`
116
- : undefined,
117
- });
118
- if (p.isCancel(keyInput)) {
119
- p.cancel("Setup cancelled.");
120
- process.exit(0);
121
- }
122
- apiKey = keyInput;
123
- process.env[envName] = apiKey;
124
- }
125
- else {
126
- p.log.info(chalk.dim(`Using ${envName} from environment.`));
127
- }
128
- // Orchestrator always needs Anthropic
129
- let anthropicKey = "";
130
- if (provider === "anthropic") {
131
- anthropicKey = apiKey;
132
- }
133
- else {
134
- const existingKey = process.env.ANTHROPIC_API_KEY || "";
135
- if (existingKey) {
136
- anthropicKey = existingKey;
137
- p.log.info(chalk.dim("Using ANTHROPIC_API_KEY for Orchestrator."));
138
- }
139
- else {
140
- p.log.info(chalk.dim("The Orchestrator requires an Anthropic API key (Claude tool use)."));
141
- const orchKeyInput = await p.text({
142
- message: "Anthropic API key for Orchestrator:",
143
- placeholder: "sk-ant-...",
144
- validate: (v) => !v || !v.startsWith("sk-ant-")
145
- ? "Please enter a valid Anthropic key (starts with sk-ant-)"
146
- : undefined,
147
- });
148
- if (p.isCancel(orchKeyInput)) {
149
- p.cancel("Setup cancelled.");
150
- process.exit(0);
151
- }
152
- anthropicKey = orchKeyInput;
153
- }
154
- }
155
- // Step 5: Generate agents + site content
156
- const s = p.spinner();
157
- s.start("Generating your AI team and website...");
158
- let agents;
159
- try {
160
- agents = await generateAgents(company.name, company.description, provider);
161
- s.stop("Your AI team is ready!");
162
- }
163
- catch (err) {
164
- s.stop("Failed to generate agents.");
165
- p.log.error(err instanceof Error ? err.message : "Check your API key");
166
- p.cancel("Setup failed.");
167
- process.exit(1);
168
- }
169
- // Step 6: Show agents + site preview
170
- p.log.info(chalk.bold("Meet your team:"));
171
- console.log();
172
- for (const agent of agents.agents) {
173
- console.log(` ${chalk.hex("#8b5cf6").bold(agent.name)} ${chalk.dim(`(${agent.role})`)}`);
174
- console.log(` ${chalk.dim(agent.description)}`);
175
- console.log();
176
- }
177
- if (agents.site && mode === "new") {
178
- p.log.info(chalk.bold("Your website:"));
179
- console.log(` ${chalk.hex("#f59e0b")(agents.site.tagline)}`);
180
- console.log(` ${chalk.dim(agents.site.subtitle)}`);
181
- console.log();
182
- }
183
- const confirm = await p.confirm({
184
- message: "Looks good?",
185
- initialValue: true,
186
- });
187
- if (p.isCancel(confirm) || !confirm) {
188
- p.cancel("Setup cancelled.");
189
- process.exit(0);
190
- }
191
- // Step 7a: Connect mode — website URL for KB
192
- let websiteUrl = "";
193
- if (mode === "connect") {
194
- const urlInput = await p.text({
195
- message: "Your existing website URL (for Knowledge Base crawling):",
196
- placeholder: "https://my-business.com",
197
- });
198
- if (!p.isCancel(urlInput) && urlInput) {
199
- websiteUrl = urlInput;
200
- }
201
- }
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
205
- let supabaseUrl = "";
206
- let supabaseKey = "";
207
- let useAutomatedSupabase = false;
208
- if (tier === "quickstart") {
209
- // We'll handle Supabase setup after scaffold for CLI automation
210
- // For now, just mark that we need it
211
- useAutomatedSupabase = true;
212
- }
213
- else if (tier === "cloud") {
214
- p.log.info(chalk.dim("Cloud mode: PostgreSQL runs in Docker. No Supabase needed."));
215
- }
216
- else {
217
- p.log.info(`${chalk.bold("AWS Enterprise setup")}\n` +
218
- ` ${chalk.dim("Requires:")} AWS CLI configured + CDK bootstrapped\n` +
219
- ` ${chalk.dim("Cost:")} ~$5-15/mo (Lambda + RDS free tier)`);
220
- }
221
- // Step 8: Scaffold project
222
- s.start("Creating your project...");
223
- const slug = company.name
224
- .toLowerCase()
225
- .replace(/[^a-z0-9]+/g, "-")
226
- .replace(/^-|-$/g, "");
227
- try {
228
- const projectDir = await scaffoldProject({
229
- company: {
230
- name: company.name,
231
- description: company.description,
232
- industry: agents.industry,
233
- },
234
- agents: agents.agents,
235
- site: agents.site,
236
- provider,
237
- tier,
238
- mode,
239
- websiteUrl,
240
- infra: {
241
- anthropicKey,
242
- openaiKey: provider === "openai" ? apiKey : undefined,
243
- supabaseUrl: supabaseUrl || "PLACEHOLDER",
244
- supabaseKey: supabaseKey || "PLACEHOLDER",
245
- },
246
- });
247
- s.stop("Project created!");
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
- // ─────────────────────────────────────────────────────────
348
- const widgetSnippet = mode === "connect"
349
- ? [
350
- ``,
351
- `${chalk.bold("Embed in your existing site:")}`,
352
- `${chalk.cyan('<script src="https://YOUR_URL/widget.js"')}`,
353
- `${chalk.cyan(' data-agent="' + agents.agents[0].id + '"')}`,
354
- `${chalk.cyan(' data-api="https://YOUR_URL"></script>')}`,
355
- ]
356
- : [];
357
- const didInstall = !p.isCancel(installConfirm) && installConfirm === true;
358
- const devCmd = didInstall ? "npm run dev" : "npm install && npm run dev";
359
- if (tier === "quickstart") {
360
- p.note([
361
- `cd ${slug}`,
362
- `${devCmd} ${chalk.dim("→ http://localhost:3000")}`,
363
- ``,
364
- `Dashboard: ${chalk.dim("/dashboard")}`,
365
- `Orchestrator: ${chalk.dim("floating button in dashboard")}`,
366
- `Agents: ${chalk.dim(`${agents.agents.length} active`)}`,
367
- ...widgetSnippet,
368
- ].join("\n"), mode === "connect" ? "Your AI agents are ready" : "Your AI company is ready");
369
- p.outro(`Deploy: ${chalk.cyan("vercel deploy")}`);
370
- }
371
- else if (tier === "cloud") {
372
- p.note([
373
- `cd ${slug}`,
374
- ``,
375
- `${chalk.bold("Local development:")}`,
376
- `docker compose up ${chalk.dim("→ http://localhost:3000")}`,
377
- ``,
378
- `${chalk.bold("Deploy to Railway:")}`,
379
- `railway login`,
380
- `railway init`,
381
- `railway add --plugin postgresql`,
382
- `railway up`,
383
- ``,
384
- `Dashboard: ${chalk.dim("/dashboard")}`,
385
- `Orchestrator: ${chalk.dim("floating button in dashboard")}`,
386
- `Agents: ${chalk.dim(`${agents.agents.length} active`)}`,
387
- ].join("\n"), "Your AI company is ready");
388
- p.outro(`Or deploy to ${chalk.cyan("Render")}, ${chalk.cyan("Fly.io")}, or any Docker host`);
389
- }
390
- else {
391
- p.note([
392
- `cd ${slug}`,
393
- ``,
394
- `${chalk.bold("Local development:")}`,
395
- `docker compose up ${chalk.dim("→ http://localhost:3000")}`,
396
- ``,
397
- `${chalk.bold("Deploy to AWS:")}`,
398
- `cd infra`,
399
- `npm install`,
400
- `npx cdk bootstrap ${chalk.dim("(first time only)")}`,
401
- `npx cdk deploy ${chalk.dim("→ CloudFront URL in output")}`,
402
- ``,
403
- `${chalk.bold("Update API keys:")}`,
404
- `aws secretsmanager put-secret-value \\`,
405
- ` --secret-id ${slug}/api-keys \\`,
406
- ` --secret-string '{"ANTHROPIC_API_KEY":"sk-ant-..."}'`,
407
- ``,
408
- `Dashboard: ${chalk.dim("/dashboard")}`,
409
- `Orchestrator: ${chalk.dim("floating button in dashboard")}`,
410
- `Agents: ${chalk.dim(`${agents.agents.length} active`)}`,
411
- `Est. cost: ${chalk.dim("$5-15/mo")}`,
412
- ].join("\n"), "Your AI company is ready (AWS)");
413
- p.outro(`Run ${chalk.cyan("cd infra && npx cdk deploy")} to provision AWS infrastructure`);
414
- }
415
- }
416
- catch (err) {
417
- s.stop("Failed to create project.");
418
- p.log.error(err instanceof Error ? err.message : String(err));
419
- process.exit(1);
420
- }
421
- }
422
- main().catch(console.error);
2
+ // Entry point for both `npx @tellet/create` and `tellet <command>`
3
+ // Delegates to cli.ts which handles routing.
4
+ import { run } from "./cli.js";
5
+ const args = process.argv.slice(2);
6
+ run(args).catch((err) => {
7
+ console.error(err instanceof Error ? err.message : String(err));
8
+ process.exit(1);
9
+ });
@@ -0,0 +1 @@
1
+ export declare function startMcpServer(): Promise<void>;
@@ -0,0 +1,25 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3
+ import fs from "fs-extra";
4
+ import path from "path";
5
+ import { fileURLToPath } from "url";
6
+ import { registerTools } from "./tools.js";
7
+ async function getVersion() {
8
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
9
+ const pkg = await fs.readJSON(path.resolve(__dirname, "..", "..", "package.json"));
10
+ return pkg.version;
11
+ }
12
+ export async function startMcpServer() {
13
+ const version = await getVersion();
14
+ const server = new McpServer({
15
+ name: "tellet",
16
+ version,
17
+ }, {
18
+ instructions: "Tellet MCP server — manage AI agent projects. " +
19
+ "Use these tools inside a tellet project directory (one containing tellet.json). " +
20
+ "Tools: project_status, agent_list, agent_add, agent_remove, config_read, dev_start, deploy_info.",
21
+ });
22
+ registerTools(server);
23
+ const transport = new StdioServerTransport();
24
+ await server.connect(transport);
25
+ }
@@ -0,0 +1,2 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerTools(server: McpServer): void;