create-struere 0.1.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 (2) hide show
  1. package/dist/index.js +420 -0
  2. package/package.json +39 -0
package/dist/index.js ADDED
@@ -0,0 +1,420 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import { program } from "commander";
5
+ import prompts from "prompts";
6
+
7
+ // src/create-project.ts
8
+ import { mkdir, writeFile } from "fs/promises";
9
+ import { join } from "path";
10
+ import chalk from "chalk";
11
+ import ora from "ora";
12
+
13
+ // src/templates/index.ts
14
+ function getTemplates(projectName) {
15
+ return {
16
+ "package.json": getPackageJson(projectName),
17
+ "tsconfig.json": getTsConfig(),
18
+ "struere.config.ts": getStruereConfig(),
19
+ "src/agent.ts": getAgentTs(projectName),
20
+ "src/context.ts": getContextTs(),
21
+ "src/tools.ts": getToolsTs(),
22
+ "src/workflows/.gitkeep": "",
23
+ "api/chat.ts": getVercelApiHandler(),
24
+ "tests/basic.test.yaml": getBasicTestYaml(),
25
+ ".env.example": getEnvExample(),
26
+ "README.md": getReadme(projectName),
27
+ ".gitignore": getGitignore()
28
+ };
29
+ }
30
+ function getPackageJson(name) {
31
+ return JSON.stringify({
32
+ name,
33
+ version: "0.1.0",
34
+ type: "module",
35
+ scripts: {
36
+ dev: "struere dev",
37
+ build: "struere build",
38
+ test: "struere test",
39
+ deploy: "struere deploy"
40
+ },
41
+ dependencies: {
42
+ "@struere/core": "^0.1.0",
43
+ "@struere/runtime": "^0.1.0"
44
+ },
45
+ devDependencies: {
46
+ "@struere/cli": "^0.1.0",
47
+ "bun-types": "^1.0.0",
48
+ typescript: "^5.3.0"
49
+ }
50
+ }, null, 2);
51
+ }
52
+ function getTsConfig() {
53
+ return JSON.stringify({
54
+ compilerOptions: {
55
+ target: "ES2022",
56
+ module: "ESNext",
57
+ moduleResolution: "bundler",
58
+ lib: ["ES2022"],
59
+ strict: true,
60
+ esModuleInterop: true,
61
+ skipLibCheck: true,
62
+ forceConsistentCasingInFileNames: true,
63
+ outDir: "dist",
64
+ rootDir: "src",
65
+ types: ["bun-types"]
66
+ },
67
+ include: ["src/**/*"],
68
+ exclude: ["node_modules", "dist"]
69
+ }, null, 2);
70
+ }
71
+ function getStruereConfig() {
72
+ return `import { defineConfig } from '@struere/core'
73
+
74
+ export default defineConfig({
75
+ port: 3000,
76
+ host: 'localhost',
77
+ cors: {
78
+ origins: ['http://localhost:3000'],
79
+ credentials: true,
80
+ },
81
+ logging: {
82
+ level: 'info',
83
+ format: 'pretty',
84
+ },
85
+ })
86
+ `;
87
+ }
88
+ function getAgentTs(name) {
89
+ const displayName = name.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
90
+ return `import { defineAgent } from '@struere/core'
91
+ import { context } from './context'
92
+ import { tools } from './tools'
93
+
94
+ export default defineAgent({
95
+ name: '${name}',
96
+ version: '0.1.0',
97
+ description: '${displayName} Agent',
98
+ model: {
99
+ provider: 'anthropic',
100
+ name: 'claude-sonnet-4-20250514',
101
+ temperature: 0.7,
102
+ maxTokens: 4096,
103
+ },
104
+ systemPrompt: \`You are ${displayName}, a helpful AI assistant.
105
+
106
+ Your capabilities:
107
+ - Answer questions accurately and helpfully
108
+ - Use available tools when appropriate
109
+ - Maintain conversation context
110
+
111
+ Always be concise, accurate, and helpful.\`,
112
+ tools,
113
+ context,
114
+ state: {
115
+ storage: 'memory',
116
+ ttl: 3600,
117
+ },
118
+ })
119
+ `;
120
+ }
121
+ function getContextTs() {
122
+ return `import { defineContext } from '@struere/core'
123
+
124
+ export const context = defineContext(async (request) => {
125
+ const { conversationId, userId, channel, state } = request
126
+
127
+ return {
128
+ additionalContext: \`
129
+ Current conversation: \${conversationId}
130
+ Channel: \${channel}
131
+ \`,
132
+ variables: {
133
+ userId,
134
+ timestamp: new Date().toISOString(),
135
+ },
136
+ }
137
+ })
138
+ `;
139
+ }
140
+ function getToolsTs() {
141
+ return `import { defineTools } from '@struere/core'
142
+
143
+ export const tools = defineTools([
144
+ {
145
+ name: 'get_current_time',
146
+ description: 'Get the current date and time',
147
+ parameters: {
148
+ type: 'object',
149
+ properties: {
150
+ timezone: {
151
+ type: 'string',
152
+ description: 'Timezone (e.g., "America/New_York", "UTC")',
153
+ },
154
+ },
155
+ },
156
+ handler: async (params) => {
157
+ const timezone = (params.timezone as string) || 'UTC'
158
+ const now = new Date()
159
+ return {
160
+ timestamp: now.toISOString(),
161
+ formatted: now.toLocaleString('en-US', { timeZone: timezone }),
162
+ timezone,
163
+ }
164
+ },
165
+ },
166
+ {
167
+ name: 'calculate',
168
+ description: 'Perform a mathematical calculation',
169
+ parameters: {
170
+ type: 'object',
171
+ properties: {
172
+ expression: {
173
+ type: 'string',
174
+ description: 'Mathematical expression to evaluate (e.g., "2 + 2")',
175
+ },
176
+ },
177
+ required: ['expression'],
178
+ },
179
+ handler: async (params) => {
180
+ const expression = params.expression as string
181
+ const sanitized = expression.replace(/[^0-9+-*/().\\s]/g, '')
182
+ try {
183
+ const result = new Function(\`return \${sanitized}\`)()
184
+ return { expression, result }
185
+ } catch {
186
+ return { expression, error: 'Invalid expression' }
187
+ }
188
+ },
189
+ },
190
+ ])
191
+ `;
192
+ }
193
+ function getBasicTestYaml() {
194
+ return `name: Basic conversation test
195
+ description: Verify the agent responds correctly to basic queries
196
+
197
+ conversation:
198
+ - role: user
199
+ content: Hello, what can you do?
200
+ - role: assistant
201
+ assertions:
202
+ - type: contains
203
+ value: help
204
+
205
+ - role: user
206
+ content: What time is it?
207
+ - role: assistant
208
+ assertions:
209
+ - type: toolCalled
210
+ value: get_current_time
211
+ `;
212
+ }
213
+ function getEnvExample() {
214
+ return `# Anthropic API Key (default provider)
215
+ ANTHROPIC_API_KEY=your_api_key_here
216
+
217
+ # Optional: OpenAI API Key (if using OpenAI models)
218
+ # OPENAI_API_KEY=your_openai_api_key
219
+
220
+ # Optional: Google AI API Key (if using Gemini models)
221
+ # GOOGLE_GENERATIVE_AI_API_KEY=your_google_api_key
222
+
223
+ # Optional: Custom API endpoint
224
+ # STRUERE_API_URL=https://api.struere.dev
225
+ `;
226
+ }
227
+ function getReadme(name) {
228
+ const displayName = name.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
229
+ return `# ${displayName}
230
+
231
+ An AI agent built with Struere.
232
+
233
+ ## Getting Started
234
+
235
+ 1. Install dependencies:
236
+ \`\`\`bash
237
+ bun install
238
+ \`\`\`
239
+
240
+ 2. Set up your environment:
241
+ \`\`\`bash
242
+ cp .env.example .env
243
+ # Edit .env and add your API keys
244
+ \`\`\`
245
+
246
+ 3. Start the development server:
247
+ \`\`\`bash
248
+ bun run dev
249
+ \`\`\`
250
+
251
+ 4. Open http://localhost:3000 to chat with your agent.
252
+
253
+ ## Project Structure
254
+
255
+ - \`src/agent.ts\` - Main agent definition (system prompt, model config)
256
+ - \`src/context.ts\` - Dynamic context injection
257
+ - \`src/tools.ts\` - Custom tools for the agent
258
+ - \`src/workflows/\` - Multi-step workflows (coming soon)
259
+ - \`api/chat.ts\` - Vercel Edge API handler for production
260
+ - \`tests/\` - Test conversations
261
+ - \`struere.config.ts\` - Framework configuration
262
+
263
+ ## Commands
264
+
265
+ - \`bun run dev\` - Start development server with hot reload
266
+ - \`bun run build\` - Build and validate the agent
267
+ - \`bun run test\` - Run test conversations
268
+ - \`bun run deploy\` - Deploy to Struere cloud
269
+
270
+ ## Deploy to Vercel
271
+
272
+ This project is ready for Vercel deployment:
273
+
274
+ 1. Push to GitHub
275
+ 2. Import in Vercel
276
+ 3. Add your \`ANTHROPIC_API_KEY\` to environment variables
277
+ 4. Deploy!
278
+
279
+ The \`api/chat.ts\` file provides a streaming chat endpoint at \`/api/chat\`.
280
+
281
+ ## API Usage
282
+
283
+ Send a POST request to \`/api/chat\`:
284
+
285
+ \`\`\`bash
286
+ curl -X POST https://your-app.vercel.app/api/chat \\
287
+ -H "Content-Type: application/json" \\
288
+ -d '{"message": "Hello!", "stream": true}'
289
+ \`\`\`
290
+
291
+ ## Documentation
292
+
293
+ Visit [struere.dev/docs](https://struere.dev/docs) for full documentation.
294
+ `;
295
+ }
296
+ function getGitignore() {
297
+ return `# Dependencies
298
+ node_modules/
299
+
300
+ # Build output
301
+ dist/
302
+
303
+ # Environment
304
+ .env
305
+ .env.local
306
+ .env.*.local
307
+
308
+ # IDE
309
+ .idea/
310
+ .vscode/
311
+ *.swp
312
+ *.swo
313
+
314
+ # OS
315
+ .DS_Store
316
+ Thumbs.db
317
+
318
+ # Logs
319
+ *.log
320
+ logs/
321
+
322
+ # Vercel
323
+ .vercel/
324
+ `;
325
+ }
326
+ function getVercelApiHandler() {
327
+ return `import agent from '../src/agent'
328
+ import { createVercelHandler } from '@struere/runtime/serverless/vercel'
329
+
330
+ export default createVercelHandler(agent, {
331
+ streaming: true,
332
+ corsOrigins: ['*'],
333
+ })
334
+
335
+ export const config = {
336
+ runtime: 'edge',
337
+ }
338
+ `;
339
+ }
340
+
341
+ // src/create-project.ts
342
+ async function createProject(name, options) {
343
+ const projectPath = join(process.cwd(), name);
344
+ const spinner = ora();
345
+ console.log();
346
+ console.log(chalk.bold(`Creating ${chalk.cyan(name)}...`));
347
+ console.log();
348
+ spinner.start("Creating project structure");
349
+ try {
350
+ await mkdir(projectPath, { recursive: true });
351
+ await mkdir(join(projectPath, "src"), { recursive: true });
352
+ await mkdir(join(projectPath, "src", "workflows"), { recursive: true });
353
+ await mkdir(join(projectPath, "api"), { recursive: true });
354
+ await mkdir(join(projectPath, "tests"), { recursive: true });
355
+ spinner.succeed("Project structure created");
356
+ spinner.start("Writing template files");
357
+ const templates = getTemplates(name);
358
+ for (const [filePath, content] of Object.entries(templates)) {
359
+ const fullPath = join(projectPath, filePath);
360
+ await writeFile(fullPath, content, "utf-8");
361
+ }
362
+ spinner.succeed("Template files written");
363
+ if (options.install) {
364
+ spinner.start("Installing dependencies");
365
+ const proc = Bun.spawn(["bun", "install"], {
366
+ cwd: projectPath,
367
+ stdout: "pipe",
368
+ stderr: "pipe"
369
+ });
370
+ await proc.exited;
371
+ if (proc.exitCode === 0) {
372
+ spinner.succeed("Dependencies installed");
373
+ } else {
374
+ spinner.warn("Failed to install dependencies. Run `bun install` manually.");
375
+ }
376
+ }
377
+ console.log();
378
+ console.log(chalk.green("Success!"), `Created ${chalk.cyan(name)} at ${chalk.gray(projectPath)}`);
379
+ console.log();
380
+ console.log("Next steps:");
381
+ console.log();
382
+ console.log(chalk.gray(" $"), chalk.cyan(`cd ${name}`));
383
+ if (!options.install) {
384
+ console.log(chalk.gray(" $"), chalk.cyan("bun install"));
385
+ }
386
+ console.log(chalk.gray(" $"), chalk.cyan("bun run dev"));
387
+ console.log();
388
+ console.log("Documentation:", chalk.blue("https://struere.dev/docs"));
389
+ console.log();
390
+ } catch (error) {
391
+ spinner.fail("Failed to create project");
392
+ console.error(error);
393
+ process.exit(1);
394
+ }
395
+ }
396
+
397
+ // src/index.ts
398
+ async function askProjectName() {
399
+ const response = await prompts({
400
+ type: "text",
401
+ name: "name",
402
+ message: "What is your agent name?",
403
+ validate: (value) => {
404
+ if (!value)
405
+ return "Agent name is required";
406
+ if (!/^[a-z0-9-]+$/.test(value)) {
407
+ return "Agent name must be lowercase alphanumeric with hyphens";
408
+ }
409
+ return true;
410
+ }
411
+ });
412
+ if (!response.name) {
413
+ process.exit(1);
414
+ }
415
+ return response.name;
416
+ }
417
+ program.name("create-struere").description("Create a new Struere project").argument("[name]", "Project name").option("-t, --template <template>", "Template to use", "default").option("--no-install", "Skip dependency installation").action(async (name, options) => {
418
+ const projectName = name || await askProjectName();
419
+ await createProject(projectName, options);
420
+ }).parse();
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "create-struere",
3
+ "version": "0.1.0",
4
+ "description": "Create new Struere projects with a single command",
5
+ "keywords": ["ai", "agents", "scaffolding", "create", "generator", "llm"],
6
+ "author": "struere",
7
+ "license": "MIT",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/struere/struere.git",
11
+ "directory": "packages/create-struere"
12
+ },
13
+ "homepage": "https://struere.dev",
14
+ "bugs": {
15
+ "url": "https://github.com/struere/struere/issues"
16
+ },
17
+ "type": "module",
18
+ "bin": {
19
+ "create-struere": "./dist/index.js"
20
+ },
21
+ "main": "./dist/index.js",
22
+ "files": ["dist"],
23
+ "scripts": {
24
+ "build": "bun build ./src/index.ts --outdir ./dist --target node --external commander --external prompts --external chalk --external ora && chmod +x ./dist/index.js",
25
+ "dev": "bun run ./src/index.ts",
26
+ "test": "bun test"
27
+ },
28
+ "dependencies": {
29
+ "chalk": "^5.3.0",
30
+ "commander": "^12.0.0",
31
+ "ora": "^8.0.0",
32
+ "prompts": "^2.4.2"
33
+ },
34
+ "devDependencies": {
35
+ "@types/prompts": "^2.4.9",
36
+ "bun-types": "^1.0.0",
37
+ "typescript": "^5.3.0"
38
+ }
39
+ }