@porchestra/cli 1.0.0 → 1.0.2

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 (39) hide show
  1. package/bin/porchestra.js +1 -1
  2. package/dist/index.d.ts +1 -0
  3. package/dist/index.js +1666 -0
  4. package/dist/index.js.map +1 -0
  5. package/package.json +11 -1
  6. package/src/agents/testPrompt/ast.json +0 -71
  7. package/src/agents/testPrompt/config.ts +0 -18
  8. package/src/agents/testPrompt/index.ts +0 -64
  9. package/src/agents/testPrompt/schemas.ts +0 -45
  10. package/src/agents/testPrompt/tools.ts +0 -88
  11. package/src/commands/agents.ts +0 -173
  12. package/src/commands/config.ts +0 -97
  13. package/src/commands/explore.ts +0 -160
  14. package/src/commands/login.ts +0 -101
  15. package/src/commands/logout.ts +0 -52
  16. package/src/commands/pull.ts +0 -220
  17. package/src/commands/status.ts +0 -78
  18. package/src/commands/whoami.ts +0 -56
  19. package/src/core/api/client.ts +0 -133
  20. package/src/core/auth/auth-service.ts +0 -176
  21. package/src/core/auth/token-manager.ts +0 -47
  22. package/src/core/config/config-manager.ts +0 -107
  23. package/src/core/config/config-schema.ts +0 -56
  24. package/src/core/config/project-tracker.ts +0 -158
  25. package/src/core/generators/code-generator.ts +0 -329
  26. package/src/core/generators/schema-generator.ts +0 -59
  27. package/src/index.ts +0 -85
  28. package/src/types/index.ts +0 -214
  29. package/src/utils/date.ts +0 -23
  30. package/src/utils/errors.ts +0 -38
  31. package/src/utils/logger.ts +0 -11
  32. package/src/utils/path-utils.ts +0 -47
  33. package/tests/unit/config-manager.test.ts +0 -74
  34. package/tests/unit/config-schema.test.ts +0 -61
  35. package/tests/unit/path-utils.test.ts +0 -53
  36. package/tests/unit/schema-generator.test.ts +0 -82
  37. package/tsconfig.json +0 -30
  38. package/tsup.config.ts +0 -19
  39. package/vitest.config.ts +0 -20
@@ -1,47 +0,0 @@
1
- import { ConfigManager } from '../config/config-manager.js';
2
-
3
- export class TokenManager {
4
- constructor(private configManager: ConfigManager) {}
5
-
6
- async getToken(): Promise<string | undefined> {
7
- const config = await this.configManager.get();
8
- return config.auth?.token;
9
- }
10
-
11
- async getTokenId(): Promise<string | undefined> {
12
- const config = await this.configManager.get();
13
- return config.auth?.tokenId;
14
- }
15
-
16
- async getExpiresAt(): Promise<string | undefined> {
17
- const config = await this.configManager.get();
18
- return config.auth?.expiresAt;
19
- }
20
-
21
- async isAuthenticated(): Promise<boolean> {
22
- const token = await this.getToken();
23
- if (!token) return false;
24
-
25
- const expiresAt = await this.getExpiresAt();
26
- if (!expiresAt) return false;
27
-
28
- const expiryDate = new Date(expiresAt);
29
- return expiryDate > new Date();
30
- }
31
-
32
- async getDaysUntilExpiry(): Promise<number | null> {
33
- const expiresAt = await this.getExpiresAt();
34
- if (!expiresAt) return null;
35
-
36
- const expiryDate = new Date(expiresAt);
37
- const now = new Date();
38
- const diffMs = expiryDate.getTime() - now.getTime();
39
- return Math.ceil(diffMs / (1000 * 60 * 60 * 24));
40
- }
41
-
42
- async isExpiringSoon(days = 7): Promise<boolean> {
43
- const daysUntilExpiry = await this.getDaysUntilExpiry();
44
- if (daysUntilExpiry === null) return false;
45
- return daysUntilExpiry <= days;
46
- }
47
- }
@@ -1,107 +0,0 @@
1
- import { promises as fs } from 'fs';
2
- import path from 'path';
3
- import os from 'os';
4
- import { z } from 'zod';
5
- import { CliConfig, CliConfigSchema } from './config-schema.js';
6
-
7
- const DEFAULT_CONFIG_DIR = path.join(os.homedir(), '.porchestra');
8
- const CONFIG_FILE = 'config.json';
9
-
10
- export class ConfigManager {
11
- private configPath: string;
12
- private config: CliConfig | null = null;
13
-
14
- constructor() {
15
- const configDir = process.env.PORCHESTRA_CONFIG_DIR || DEFAULT_CONFIG_DIR;
16
- this.configPath = path.join(configDir, CONFIG_FILE);
17
- }
18
-
19
- async load(): Promise<CliConfig> {
20
- try {
21
- const data = await fs.readFile(this.configPath, 'utf-8');
22
- const parsed = JSON.parse(data);
23
-
24
- // STRICT VALIDATION - throws on error
25
- const validated = CliConfigSchema.parse(parsed);
26
- this.config = validated;
27
- return validated;
28
- } catch (error) {
29
- if ((error as NodeJS.ErrnoException).code === 'ENOENT') {
30
- // Return default config
31
- const defaultConfig = { version: '1.0.0' } as const;
32
- this.config = CliConfigSchema.parse(defaultConfig);
33
- return this.config;
34
- }
35
-
36
- // THROW on validation errors
37
- if (error instanceof z.ZodError) {
38
- console.error('❌ Config validation failed:');
39
- error.errors.forEach((err) => {
40
- console.error(` - ${err.path.join('.')}: ${err.message}`);
41
- });
42
- throw new Error(`Invalid config file at ${this.configPath}. Please run 'porchestra config reset' to fix.`);
43
- }
44
-
45
- throw error;
46
- }
47
- }
48
-
49
- async save(config: CliConfig): Promise<void> {
50
- try {
51
- // STRICT VALIDATION before save
52
- const validated = CliConfigSchema.parse(config);
53
-
54
- // Ensure directory exists
55
- const configDir = path.dirname(this.configPath);
56
- await fs.mkdir(configDir, { recursive: true });
57
-
58
- // Atomic write (write to temp, then rename)
59
- const tempPath = `${this.configPath}.tmp`;
60
- await fs.writeFile(
61
- tempPath,
62
- JSON.stringify(validated, null, 2),
63
- 'utf-8'
64
- );
65
- await fs.rename(tempPath, this.configPath);
66
-
67
- this.config = validated;
68
- } catch (error) {
69
- if (error instanceof z.ZodError) {
70
- console.error('❌ Config validation failed on save:');
71
- error.errors.forEach((err) => {
72
- console.error(` - ${err.path.join('.')}: ${err.message}`);
73
- });
74
- throw new Error('Failed to save config: validation error');
75
- }
76
- throw error;
77
- }
78
- }
79
-
80
- async get(): Promise<CliConfig> {
81
- if (!this.config) {
82
- return this.load();
83
- }
84
- return this.config;
85
- }
86
-
87
- async update(updater: (config: CliConfig) => CliConfig): Promise<void> {
88
- const config = await this.get();
89
- const updated = updater({ ...config }); // Create new object
90
- await this.save(updated);
91
- }
92
-
93
- async clear(): Promise<void> {
94
- try {
95
- await fs.unlink(this.configPath);
96
- } catch (error) {
97
- if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {
98
- throw error;
99
- }
100
- }
101
- this.config = null;
102
- }
103
-
104
- getConfigPath(): string {
105
- return this.configPath;
106
- }
107
- }
@@ -1,56 +0,0 @@
1
- import { z } from 'zod';
2
-
3
- // Simplified datetime validation - just accepts any string
4
- const DateTimeStringSchema = z.string().optional();
5
-
6
- export const TrackedAgentSchema = z.object({
7
- agentId: z.string().uuid(),
8
- agentName: z.string(),
9
- agentSlug: z.string(),
10
- folderPath: z.string(),
11
- selectedAt: DateTimeStringSchema,
12
- lastPulledAt: DateTimeStringSchema.optional(),
13
- lastPulledVersion: z.string().optional(),
14
- });
15
-
16
- export const TrackedProjectSchema = z.object({
17
- projectId: z.string().uuid(),
18
- projectName: z.string(),
19
- projectSlug: z.string(),
20
- selectedAt: DateTimeStringSchema,
21
- agents: z.array(TrackedAgentSchema),
22
- lastPulledAt: DateTimeStringSchema.optional(),
23
- });
24
-
25
- export const CliConfigSchema = z.object({
26
- auth: z.object({
27
- token: z.string().optional(),
28
- tokenId: z.string().uuid().optional(),
29
- expiresAt: DateTimeStringSchema.optional(),
30
- deviceName: z.string().optional(),
31
- }).optional(),
32
-
33
- api: z.object({
34
- baseUrl: z.string().url().default('https://api.porchestra.io/v1'),
35
- skipTlsVerify: z.boolean().default(false),
36
- }).default({}),
37
-
38
- trackedProjects: z.array(TrackedProjectSchema).default([]),
39
-
40
- output: z.object({
41
- baseDir: z.string().default('./src/agents'),
42
- createIndexFiles: z.boolean().default(true),
43
- }).default({}),
44
-
45
- cli: z.object({
46
- lastLoginAt: DateTimeStringSchema.optional(),
47
- lastVersionCheck: DateTimeStringSchema.optional(),
48
- latestKnownVersion: z.string().optional(),
49
- }).optional(),
50
-
51
- version: z.literal('1.0.0'),
52
- });
53
-
54
- export type TrackedAgent = z.infer<typeof TrackedAgentSchema>;
55
- export type TrackedProject = z.infer<typeof TrackedProjectSchema>;
56
- export type CliConfig = z.infer<typeof CliConfigSchema>;
@@ -1,158 +0,0 @@
1
- import { ConfigManager } from './config-manager.js';
2
- import { TrackedProject, TrackedAgent } from './config-schema.js';
3
-
4
- export interface ProjectBrief {
5
- id: string;
6
- name: string;
7
- slug: string;
8
- }
9
-
10
- export interface AgentBrief {
11
- id: string;
12
- name: string;
13
- slug: string;
14
- folderPath: string;
15
- }
16
-
17
- export class ProjectTracker {
18
- constructor(private configManager: ConfigManager) {}
19
-
20
- async getTrackedProjects(): Promise<TrackedProject[]> {
21
- const config = await this.configManager.get();
22
- return config.trackedProjects || [];
23
- }
24
-
25
- async getTrackedAgents(projectId: string): Promise<TrackedAgent[]> {
26
- const config = await this.configManager.get();
27
- const project = config.trackedProjects?.find(p => p.projectId === projectId);
28
- return project?.agents || [];
29
- }
30
-
31
- async listTrackedAgents(): Promise<Array<{ project: TrackedProject; agent: TrackedAgent }>> {
32
- const projects = await this.getTrackedProjects();
33
- return projects.flatMap(project =>
34
- project.agents.map(agent => ({ project, agent }))
35
- );
36
- }
37
-
38
- async trackProject(project: ProjectBrief, agents: AgentBrief[]): Promise<void> {
39
- await this.configManager.update((config) => {
40
- const existingIndex = config.trackedProjects.findIndex(
41
- p => p.projectId === project.id
42
- );
43
-
44
- const trackedAgents: TrackedAgent[] = agents.map(agent => ({
45
- agentId: agent.id,
46
- agentName: agent.name,
47
- agentSlug: agent.slug,
48
- folderPath: agent.folderPath,
49
- selectedAt: new Date().toISOString(),
50
- }));
51
-
52
- const trackedProject: TrackedProject = {
53
- projectId: project.id,
54
- projectName: project.name,
55
- projectSlug: project.slug,
56
- selectedAt: new Date().toISOString(),
57
- agents: trackedAgents,
58
- };
59
-
60
- const newProjects = [...config.trackedProjects];
61
- if (existingIndex >= 0) {
62
- newProjects[existingIndex] = trackedProject;
63
- } else {
64
- newProjects.push(trackedProject);
65
- }
66
-
67
- return { ...config, trackedProjects: newProjects };
68
- });
69
- }
70
-
71
- async untrackProject(projectId: string): Promise<void> {
72
- await this.configManager.update((config) => ({
73
- ...config,
74
- trackedProjects: config.trackedProjects.filter(
75
- p => p.projectId !== projectId
76
- ),
77
- }));
78
- }
79
-
80
- async untrackAgent(projectId: string, agentId: string): Promise<boolean> {
81
- let removed = false;
82
-
83
- await this.configManager.update((config) => {
84
- const projectIndex = config.trackedProjects.findIndex(
85
- p => p.projectId === projectId
86
- );
87
-
88
- if (projectIndex === -1) return config;
89
-
90
- const project = config.trackedProjects[projectIndex];
91
- const remainingAgents = project.agents.filter(
92
- agent => agent.agentId !== agentId
93
- );
94
-
95
- if (remainingAgents.length === project.agents.length) {
96
- return config;
97
- }
98
-
99
- removed = true;
100
-
101
- const newProjects = [...config.trackedProjects];
102
-
103
- if (remainingAgents.length === 0) {
104
- newProjects.splice(projectIndex, 1);
105
- } else {
106
- newProjects[projectIndex] = { ...project, agents: remainingAgents };
107
- }
108
-
109
- return { ...config, trackedProjects: newProjects };
110
- });
111
-
112
- return removed;
113
- }
114
-
115
- async updateLastPulled(
116
- projectId: string,
117
- agentId: string,
118
- version: string
119
- ): Promise<void> {
120
- await this.configManager.update((config) => {
121
- const projectIndex = config.trackedProjects.findIndex(
122
- p => p.projectId === projectId
123
- );
124
- if (projectIndex === -1) return config;
125
-
126
- const project = config.trackedProjects[projectIndex];
127
- const agentIndex = project.agents.findIndex(
128
- a => a.agentId === agentId
129
- );
130
- if (agentIndex === -1) return config;
131
-
132
- const now = new Date().toISOString();
133
- const newAgents = [...project.agents];
134
- newAgents[agentIndex] = {
135
- ...newAgents[agentIndex],
136
- lastPulledAt: now,
137
- lastPulledVersion: version,
138
- };
139
-
140
- const newProjects = [...config.trackedProjects];
141
- newProjects[projectIndex] = {
142
- ...project,
143
- agents: newAgents,
144
- lastPulledAt: now,
145
- };
146
-
147
- return { ...config, trackedProjects: newProjects };
148
- });
149
- }
150
-
151
- async getSummary(): Promise<{ projectCount: number; agentCount: number }> {
152
- const projects = await this.getTrackedProjects();
153
- return {
154
- projectCount: projects.length,
155
- agentCount: projects.reduce((sum, p) => sum + p.agents.length, 0),
156
- };
157
- }
158
- }
@@ -1,329 +0,0 @@
1
- import fs from 'fs/promises';
2
- import path from 'path';
3
- import Handlebars from 'handlebars';
4
- import { calculateAgentOutputPath } from '../../utils/path-utils.js';
5
- import { AgentToolsResponse } from '../../types/index.js';
6
- import { logger } from '../../utils/logger.js';
7
-
8
- interface GenerateOptions {
9
- agent: AgentToolsResponse['agent'];
10
- tools: AgentToolsResponse['tools'];
11
- components: AgentToolsResponse['components'];
12
- outputDir: string;
13
- forceOverwrite: boolean;
14
- }
15
-
16
- interface ToolData {
17
- name: string;
18
- description: string;
19
- parameters: object;
20
- returns: object | null;
21
- isBuiltin?: boolean;
22
- builtinType?: string;
23
- }
24
-
25
- interface TemplateData {
26
- agentName: string;
27
- version: string;
28
- generatedAt: string;
29
- tools: ToolData[];
30
- variableSchema: object | null;
31
- inputSchema: object | null;
32
- responseSchema: object | null;
33
- responseJsonSchema: object;
34
- }
35
-
36
- // Register Handlebars helpers
37
- Handlebars.registerHelper('pascalCase', function(str: string) {
38
- return str
39
- .replace(/[-_\s](.)/g, (_, char) => char.toUpperCase())
40
- .replace(/^(.)/, (_, char) => char.toUpperCase());
41
- });
42
-
43
- Handlebars.registerHelper('camelCase', function(str: string) {
44
- return str
45
- .replace(/[-_\s](.)/g, (_, char) => char.toUpperCase())
46
- .replace(/^(.)/, (_, char) => char.toLowerCase());
47
- });
48
-
49
- Handlebars.registerHelper('zodType', function(schema: any) {
50
- return jsonSchemaToZodType(schema);
51
- });
52
-
53
- Handlebars.registerHelper('json', function(value: any) {
54
- return JSON.stringify(value ?? {}, null, 2);
55
- });
56
-
57
- function jsonSchemaToZodType(schema: any): string {
58
- if (!schema) return 'z.any()';
59
-
60
- switch (schema.type) {
61
- case 'string': {
62
- let zod = 'z.string()';
63
- if (schema.minLength) zod += `.min(${schema.minLength})`;
64
- if (schema.maxLength) zod += `.max(${schema.maxLength})`;
65
- if (schema.pattern) zod += `.regex(/${schema.pattern}/)`;
66
- if (schema.enum) zod = `z.enum([${schema.enum.map((e: string) => `'${e}'`).join(', ')}])`;
67
- if (schema.nullable) zod += '.nullable()';
68
- return zod;
69
- }
70
- case 'number':
71
- case 'integer': {
72
- let zod = schema.type === 'integer' ? 'z.number().int()' : 'z.number()';
73
- if (schema.minimum !== undefined) zod += `.min(${schema.minimum})`;
74
- if (schema.maximum !== undefined) zod += `.max(${schema.maximum})`;
75
- if (schema.nullable) zod += '.nullable()';
76
- return zod;
77
- }
78
- case 'boolean': {
79
- let zod = 'z.boolean()';
80
- if (schema.nullable) zod += '.nullable()';
81
- return zod;
82
- }
83
- case 'array': {
84
- const itemType = jsonSchemaToZodType(schema.items);
85
- let zod = `z.array(${itemType})`;
86
- if (schema.minItems) zod += `.min(${schema.minItems})`;
87
- if (schema.maxItems) zod += `.max(${schema.maxItems})`;
88
- if (schema.nullable) zod += '.nullable()';
89
- return zod;
90
- }
91
- case 'object': {
92
- if (!schema.properties) return 'z.record(z.any())';
93
-
94
- const required = new Set(schema.required || []);
95
- const props = Object.entries(schema.properties).map(([key, prop]: [string, any]) => {
96
- const propType = jsonSchemaToZodType(prop);
97
- const isRequired = required.has(key);
98
- const formattedKey = /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key) ? key : JSON.stringify(key);
99
- return ` ${formattedKey}: ${isRequired ? propType : `${propType}.optional()`}`;
100
- });
101
-
102
- let zod = `z.object({\n${props.join(',\n')}\n})`;
103
- if (schema.additionalProperties === false) zod += '.strict()';
104
- if (schema.nullable) zod += '.nullable()';
105
- return zod;
106
- }
107
- default:
108
- return 'z.any()';
109
- }
110
- }
111
-
112
- export class CodeGenerator {
113
- private schemasTemplate: Handlebars.TemplateDelegate;
114
- private toolsTemplate: Handlebars.TemplateDelegate;
115
- private indexTemplate: Handlebars.TemplateDelegate;
116
-
117
- constructor() {
118
- this.schemasTemplate = Handlebars.compile(schemasTemplateSource);
119
- this.toolsTemplate = Handlebars.compile(toolsTemplateSource);
120
- this.indexTemplate = Handlebars.compile(indexTemplateSource);
121
- }
122
-
123
- async generate(options: GenerateOptions): Promise<void> {
124
- const { agent, tools, components, outputDir } = options;
125
-
126
- await fs.mkdir(outputDir, { recursive: true });
127
-
128
- const responseJsonSchema = (components?.responseSchema?.content as object) || {};
129
-
130
- const templateData: TemplateData = {
131
- agentName: agent?.name || 'Unknown Agent',
132
- version: agent?.version || 'unknown',
133
- generatedAt: new Date().toISOString(),
134
- tools: (tools || [])
135
- .filter(t => t && !t.isBuiltin && typeof t.name === 'string' && t.name.length > 0)
136
- .map(t => ({
137
- name: t.name,
138
- description: t.description || '',
139
- parameters: t.parameters || {},
140
- returns: t.returns ?? null,
141
- isBuiltin: t.isBuiltin,
142
- builtinType: t.builtinType,
143
- })),
144
- variableSchema: (components?.variableSchema?.content as object) || null,
145
- inputSchema: (components?.inputSchema?.content as object) || null,
146
- responseSchema: (components?.responseSchema?.content as object) || null,
147
- responseJsonSchema,
148
- };
149
-
150
- // Generate ast.json (always overwrite)
151
- const astPath = path.join(outputDir, 'ast.json');
152
- const astContent = JSON.stringify(components?.ast?.content ?? {}, null, 2);
153
- await fs.writeFile(astPath, astContent, 'utf-8');
154
-
155
- // Generate config.ts (always overwrite)
156
- const configPath = path.join(outputDir, 'config.ts');
157
- const configContent = `/**
158
- * Auto-generated model config for ${templateData.agentName}
159
- * Generated: ${templateData.generatedAt}
160
- * Version: ${templateData.version}
161
- * DO NOT MODIFY - This file is auto-generated by Porchestra CLI
162
- */
163
-
164
- export const ModelConfig = ${JSON.stringify(components?.modelConfig?.content ?? {}, null, 2)} as const;
165
- `;
166
- await fs.writeFile(configPath, configContent, 'utf-8');
167
-
168
- // Generate schemas.ts (always overwrite)
169
- const schemasPath = path.join(outputDir, 'schemas.ts');
170
- const schemasContent = this.schemasTemplate(templateData);
171
- await fs.writeFile(schemasPath, schemasContent, 'utf-8');
172
-
173
- // Generate tools.ts (always overwrite)
174
- const toolsPath = path.join(outputDir, 'tools.ts');
175
- const toolsContent = this.toolsTemplate(templateData);
176
- await fs.writeFile(toolsPath, toolsContent, 'utf-8');
177
-
178
- // Generate index.ts (always overwrite)
179
- const indexPath = path.join(outputDir, 'index.ts');
180
- const indexContent = this.indexTemplate(templateData);
181
- await fs.writeFile(indexPath, indexContent, 'utf-8');
182
- }
183
-
184
- calculateOutputPath(baseDir: string, folderPath: string, agentName: string): string {
185
- return calculateAgentOutputPath(baseDir, folderPath, agentName);
186
- }
187
- }
188
-
189
- // Template sources
190
- const schemasTemplateSource = `/**
191
- * Auto-generated Zod schemas for {{agentName}}
192
- * Generated: {{generatedAt}}
193
- * Version: {{version}}
194
- * DO NOT MODIFY - This file is auto-generated by Porchestra CLI
195
- */
196
-
197
- import { z } from 'zod';
198
-
199
- // 1. System Prompt Variables
200
- export const VariableSchema = {{{zodType variableSchema}}};
201
- export type AgentVariables = z.infer<typeof VariableSchema>;
202
-
203
- // 2. User Input
204
- export const InputSchema = {{{zodType inputSchema}}};
205
- export type AgentInput = z.infer<typeof InputSchema>;
206
-
207
- // 3. Model Response
208
- export const ResponseSchema = {{{zodType responseSchema}}};
209
- export type AgentResponse = z.infer<typeof ResponseSchema>;
210
-
211
- // 4. Raw JSON Schema (for LLM Provider 'response_format')
212
- export const ResponseJsonSchema = {{{json responseJsonSchema}}} as const;
213
- `;
214
-
215
- const toolsTemplateSource = `/**
216
- * Auto-generated tools for {{agentName}}
217
- * Generated: {{generatedAt}}
218
- * Version: {{version}}
219
- * DO NOT MODIFY - This file is auto-generated by Porchestra CLI
220
- */
221
-
222
- import { z } from 'zod';
223
-
224
- {{#each tools}}
225
- // Schema for {{name}}
226
- export const {{pascalCase name}}Params = {{{zodType parameters}}};
227
- export type {{pascalCase name}}ParamsType = z.infer<typeof {{pascalCase name}}Params>;
228
-
229
- {{/each}}
230
- // The Interface the user MUST implement
231
- export interface ToolImplementation {
232
- {{#each tools}}
233
- {{camelCase name}}: (args: {{pascalCase name}}ParamsType) => Promise<unknown>;
234
- {{/each}}
235
- }
236
-
237
- // Raw Tool Definitions for provider SDKs
238
- export const ToolDefinitions = [
239
- {{#each tools}}
240
- {
241
- name: '{{name}}',
242
- description: {{{json description}}},
243
- parameters: {{{json parameters}}},
244
- },
245
- {{/each}}
246
- ] as const;
247
-
248
- // Dispatcher with validation
249
- export async function dispatch(
250
- name: string,
251
- args: unknown,
252
- impl: ToolImplementation
253
- ): Promise<unknown> {
254
- switch (name) {
255
- {{#each tools}}
256
- case '{{name}}':
257
- return await impl.{{camelCase name}}({{pascalCase name}}Params.parse(args));
258
- {{/each}}
259
- default:
260
- throw new Error(\`Unknown tool: \${name}\`);
261
- }
262
- }
263
- `;
264
-
265
- const indexTemplateSource = `/**
266
- * Auto-generated Agent class for {{agentName}}
267
- * Generated: {{generatedAt}}
268
- * Version: {{version}}
269
- * DO NOT MODIFY - This file is auto-generated by Porchestra CLI
270
- */
271
-
272
- import { PorchestraRuntime } from '@porchestra/core';
273
- import rawAst from './ast.json';
274
- import { ModelConfig } from './config.js';
275
- import * as Schemas from './schemas.js';
276
- import * as Tools from './tools.js';
277
-
278
- export class {{pascalCase agentName}}Agent {
279
- /**
280
- * @param tools - The implementation of the tool logic. Required if the agent uses tools.
281
- */
282
- constructor(private readonly tools?: Tools.ToolImplementation) {}
283
-
284
- /**
285
- * Renders the System Prompt.
286
- * Throws ZodError if variables are invalid.
287
- */
288
- public renderPrompt(variables: Schemas.AgentVariables): string {
289
- const safeVars = Schemas.VariableSchema.parse(variables);
290
- return PorchestraRuntime.render(rawAst as any, safeVars);
291
- }
292
-
293
- /**
294
- * Routes an LLM Tool Call to your implementation.
295
- * Validates arguments automatically.
296
- */
297
- public async handleTool(name: string, args: unknown): Promise<unknown> {
298
- if (!this.tools) {
299
- throw new Error('No tool implementation provided to Agent constructor.');
300
- }
301
- return Tools.dispatch(name, args, this.tools);
302
- }
303
-
304
- /** Validates User Input (e.g. from API Request) */
305
- public static validateInput(data: unknown): Schemas.AgentInput {
306
- return Schemas.InputSchema.parse(data);
307
- }
308
-
309
- /** Validates LLM Output (Post-generation check) */
310
- public static validateResponse(data: unknown): Schemas.AgentResponse {
311
- return Schemas.ResponseSchema.parse(data);
312
- }
313
-
314
- /** Get Provider Configuration */
315
- public static get config() {
316
- return ModelConfig;
317
- }
318
-
319
- /** Get Tool Definitions for the LLM Provider */
320
- public static get toolDefinitions() {
321
- return Tools.ToolDefinitions;
322
- }
323
-
324
- /** Get JSON Schema for Structured Outputs */
325
- public static get responseFormat() {
326
- return Schemas.ResponseJsonSchema;
327
- }
328
- }
329
- `;