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.
- package/dist/index.js +420 -0
- 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
|
+
}
|