@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,59 +0,0 @@
1
- import { z } from 'zod';
2
-
3
- export function jsonSchemaToZod(schema: any): z.ZodTypeAny {
4
- switch (schema.type) {
5
- case 'string': {
6
- let stringSchema = z.string();
7
- if (schema.minLength) stringSchema = stringSchema.min(schema.minLength);
8
- if (schema.maxLength) stringSchema = stringSchema.max(schema.maxLength);
9
- if (schema.pattern) stringSchema = stringSchema.regex(new RegExp(schema.pattern));
10
- if (schema.enum) return z.enum(schema.enum);
11
- return schema.nullable ? stringSchema.nullable() : stringSchema;
12
- }
13
-
14
- case 'number':
15
- case 'integer': {
16
- let numberSchema = schema.type === 'integer' ? z.number().int() : z.number();
17
- if (schema.minimum !== undefined) numberSchema = numberSchema.min(schema.minimum);
18
- if (schema.maximum !== undefined) numberSchema = numberSchema.max(schema.maximum);
19
- return schema.nullable ? numberSchema.nullable() : numberSchema;
20
- }
21
-
22
- case 'boolean': {
23
- const boolSchema = z.boolean();
24
- return schema.nullable ? boolSchema.nullable() : boolSchema;
25
- }
26
-
27
- case 'array': {
28
- const itemSchema = jsonSchemaToZod(schema.items);
29
- let arraySchema = z.array(itemSchema);
30
- if (schema.minItems) arraySchema = arraySchema.min(schema.minItems);
31
- if (schema.maxItems) arraySchema = arraySchema.max(schema.maxItems);
32
- return schema.nullable ? arraySchema.nullable() : arraySchema;
33
- }
34
-
35
- case 'object': {
36
- const shape: Record<string, z.ZodTypeAny> = {};
37
- const required = new Set(schema.required || []);
38
-
39
- for (const [key, propSchema] of Object.entries(schema.properties || {})) {
40
- let fieldSchema = jsonSchemaToZod(propSchema as any);
41
- if (!required.has(key)) {
42
- fieldSchema = fieldSchema.optional();
43
- }
44
- shape[key] = fieldSchema;
45
- }
46
-
47
- let objectSchema = z.object(shape);
48
- // Note: strict() is not applied to keep the schema flexible
49
- return schema.nullable ? objectSchema.nullable() : objectSchema;
50
- }
51
-
52
- default:
53
- if (schema.anyOf || schema.oneOf) {
54
- const schemas = (schema.anyOf || schema.oneOf).map(jsonSchemaToZod);
55
- return z.union([schemas[0], schemas[1], ...schemas.slice(2)]);
56
- }
57
- return z.any();
58
- }
59
- }
package/src/index.ts DELETED
@@ -1,85 +0,0 @@
1
- #!/usr/bin/env node
2
- import { Command } from 'commander';
3
- import pc from 'picocolors';
4
- import { ConfigManager } from './core/config/config-manager.js';
5
- import { AuthService } from './core/auth/auth-service.js';
6
- import { TokenManager } from './core/auth/token-manager.js';
7
- import { ApiClient } from './core/api/client.js';
8
- import { CodeGenerator } from './core/generators/code-generator.js';
9
- import { createLoginCommand } from './commands/login.js';
10
- import { createLogoutCommand } from './commands/logout.js';
11
- import { createWhoamiCommand } from './commands/whoami.js';
12
- import { createExploreCommand } from './commands/explore.js';
13
- import { createPullCommand } from './commands/pull.js';
14
- import { createConfigCommand } from './commands/config.js';
15
- import { createStatusCommand } from './commands/status.js';
16
- import { createAgentsCommand } from './commands/agents.js';
17
- import { PorchestraError } from './utils/errors.js';
18
-
19
- const packageJson = { version: '1.0.0' };
20
-
21
- // Global error handlers
22
- process.on('unhandledRejection', (error) => {
23
- console.error(pc.red('\n✗ Unexpected error:'));
24
- console.error(error);
25
- process.exit(1);
26
- });
27
-
28
- process.on('uncaughtException', (error) => {
29
- console.error(pc.red('\n✗ Fatal error:'));
30
- console.error(error);
31
- process.exit(1);
32
- });
33
-
34
- async function main() {
35
- // Initialize core services
36
- const configManager = new ConfigManager();
37
- const authService = new AuthService(configManager);
38
- // TokenManager is available for future token operations
39
- new TokenManager(configManager);
40
- const apiClient = new ApiClient(configManager);
41
- const codeGenerator = new CodeGenerator();
42
-
43
- // Check token expiration and refresh if needed
44
- await authService.checkAndRefreshTokenIfNeeded();
45
-
46
- // Create CLI program
47
- const program = new Command()
48
- .name('porchestra')
49
- .description('CLI for Porchestra - Generate LLM tool handlers')
50
- .version(packageJson.version)
51
- .configureOutput({
52
- writeErr: (str) => process.stderr.write(str),
53
- outputError: (str, write) => write(pc.red(str))
54
- });
55
-
56
- // Add global options
57
- program
58
- .option('--api-url <url>', 'Override API URL')
59
- .option('--config-dir <dir>', 'Override config directory');
60
-
61
- // Register commands
62
- program.addCommand(createLoginCommand(configManager, authService));
63
- program.addCommand(createLogoutCommand(configManager, authService));
64
- program.addCommand(createWhoamiCommand(configManager, authService));
65
- program.addCommand(createExploreCommand(configManager, apiClient));
66
- program.addCommand(createPullCommand(configManager, apiClient, codeGenerator));
67
- program.addCommand(createConfigCommand(configManager));
68
- program.addCommand(createStatusCommand(configManager));
69
- program.addCommand(createAgentsCommand(configManager));
70
-
71
- // Parse arguments
72
- await program.parseAsync(process.argv);
73
- }
74
-
75
- main().catch((error) => {
76
- if (error instanceof PorchestraError) {
77
- console.error(pc.red(`\n✗ ${error.message}`));
78
- if (process.env.DEBUG) {
79
- console.error(pc.gray(error.stack));
80
- }
81
- } else {
82
- console.error(pc.red(`\n✗ Unexpected error: ${(error as Error).message}`));
83
- }
84
- process.exit(1);
85
- });
@@ -1,214 +0,0 @@
1
- // Authentication Types
2
- export interface ApiResponse<T> {
3
- statusCode: number;
4
- message: string;
5
- data: T;
6
- timestamp: string;
7
- path: string;
8
- }
9
-
10
- export interface CliTokenRequest {
11
- deviceName?: string;
12
- deviceInfo?: {
13
- os: string;
14
- version: string;
15
- cliVersion: string;
16
- };
17
- }
18
-
19
- export interface CliTokenResponse {
20
- token: string;
21
- tokenId: string;
22
- expiresAt: string;
23
- issuedAt: string;
24
- deviceName: string;
25
- }
26
-
27
- export interface CliTokenRefreshRequest {
28
- tokenId: string;
29
- currentToken: string;
30
- }
31
-
32
- export interface CliTokenRefreshResponse {
33
- token: string;
34
- tokenId: string;
35
- expiresAt: string;
36
- refreshedAt: string;
37
- }
38
-
39
- export interface CliTokenRevokeRequest {
40
- revokeAll?: boolean;
41
- }
42
-
43
- export interface CliTokenRevokeResponse {
44
- revoked: boolean;
45
- revokedCount?: number;
46
- revokedAt: string;
47
- }
48
-
49
- export interface CliTokensListResponse {
50
- tokens: Array<{
51
- tokenId: string;
52
- deviceName: string;
53
- deviceInfo: {
54
- os: string;
55
- version: string;
56
- cliVersion: string;
57
- };
58
- issuedAt: string;
59
- expiresAt: string;
60
- lastUsedAt: string | null;
61
- lastUsedIp: string | null;
62
- isCurrent: boolean;
63
- }>;
64
- total: number;
65
- maxAllowed: number;
66
- }
67
-
68
- // Project Types
69
- export interface ProjectsBriefResponse {
70
- projects: Array<{
71
- id: string;
72
- name: string;
73
- slug: string;
74
- description: string | null;
75
- agentCount: number;
76
- environments: Array<{
77
- name: 'PRODUCTION' | 'STAGING' | 'DEVELOPMENT';
78
- hasPublishedVersion: boolean;
79
- }>;
80
- lastModifiedAt: string;
81
- }>;
82
- total: number;
83
- }
84
-
85
- export interface AgentsListQuery {
86
- environment?: 'PRODUCTION' | 'STAGING' | 'DEVELOPMENT';
87
- }
88
-
89
- export interface AgentsListResponse {
90
- project: {
91
- id: string;
92
- name: string;
93
- slug: string;
94
- };
95
- environment: 'PRODUCTION' | 'STAGING' | 'DEVELOPMENT';
96
- agents: Array<{
97
- id: string;
98
- name: string;
99
- slug: string;
100
- folderPath: string;
101
- description: string | null;
102
- version: string;
103
- toolCount: number;
104
- lastUpdatedAt: string;
105
- isPublished: boolean;
106
- }>;
107
- versionResolution: {
108
- source: 'PRODUCTION' | 'STAGING' | 'DEVELOPMENT';
109
- note: string;
110
- };
111
- }
112
-
113
- export interface AgentDetailQuery {
114
- environment?: 'PRODUCTION' | 'STAGING' | 'DEVELOPMENT';
115
- }
116
-
117
- export interface AgentDetailResponse {
118
- agent: {
119
- id: string;
120
- name: string;
121
- slug: string;
122
- folderPath: string;
123
- description: string | null;
124
- instructions: string | null;
125
- model: string;
126
- temperature: number;
127
- version: string;
128
- versionId: string;
129
- environment: 'PRODUCTION' | 'STAGING' | 'DEVELOPMENT';
130
- createdAt: string;
131
- updatedAt: string;
132
- publishedAt: string | null;
133
- };
134
- versionResolution: {
135
- source: 'PRODUCTION' | 'STAGING' | 'DEVELOPMENT';
136
- note: string;
137
- };
138
- }
139
-
140
- // Tools Types
141
- export interface AgentToolsQuery {
142
- environment?: 'PRODUCTION' | 'STAGING' | 'DEVELOPMENT';
143
- }
144
-
145
- export interface AgentToolsResponse {
146
- agent: {
147
- id: string;
148
- name: string;
149
- folderPath: string;
150
- version: string;
151
- };
152
- tools?: Array<{
153
- id: string;
154
- name: string;
155
- description: string;
156
- parameters: object;
157
- returns: object | null;
158
- isBuiltin: boolean;
159
- builtinType?: string;
160
- }>;
161
- // Legacy/alternate API shape where tools are provided under `toolset`
162
- toolset?: Array<{
163
- name: string;
164
- description?: string;
165
- parameters?: object;
166
- returns?: object | null;
167
- isBuiltin?: boolean;
168
- builtinType?: string;
169
- id?: string;
170
- }>;
171
- components: {
172
- ast: { id: string; content: unknown } | null;
173
- modelConfig: { id: string; content: unknown } | null;
174
- inputSchema: { id: string; content: unknown } | null;
175
- variableSchema: { id: string; content: unknown } | null;
176
- responseSchema: { id: string; content: unknown } | null;
177
- toolConfig: { id: string; content: unknown } | null;
178
- };
179
- total: number;
180
- }
181
-
182
- // User Types
183
- export interface UserInfo {
184
- email: string;
185
- name: string;
186
- organization: string;
187
- }
188
-
189
- // CLI Config Types
190
- export interface CliConfigResponse {
191
- api: {
192
- version: string;
193
- minCliVersion: string;
194
- recommendedCliVersion: string;
195
- deprecatedCliVersions: string[];
196
- };
197
- features: {
198
- codeGeneration: boolean;
199
- multiAgent: boolean;
200
- onPremise: boolean;
201
- customPaths: boolean;
202
- };
203
- limits: {
204
- maxTokensPerUser: number;
205
- tokenExpiryDays: number;
206
- exportCacheHours: number;
207
- maxToolsPerAgent: number;
208
- };
209
- endpoints: {
210
- webApp: string;
211
- documentation: string;
212
- support: string;
213
- };
214
- }
package/src/utils/date.ts DELETED
@@ -1,23 +0,0 @@
1
- export function formatDistanceToNow(date: Date): string {
2
- const now = new Date();
3
- const diffMs = now.getTime() - date.getTime();
4
- const diffSecs = Math.floor(diffMs / 1000);
5
- const diffMins = Math.floor(diffSecs / 60);
6
- const diffHours = Math.floor(diffMins / 60);
7
- const diffDays = Math.floor(diffHours / 24);
8
-
9
- if (diffSecs < 60) return 'just now';
10
- if (diffMins < 60) return `${diffMins}m ago`;
11
- if (diffHours < 24) return `${diffHours}h ago`;
12
- if (diffDays < 30) return `${diffDays}d ago`;
13
- return date.toISOString().split('T')[0];
14
- }
15
-
16
- export function formatDate(dateStr: string): string {
17
- const date = new Date(dateStr);
18
- return date.toLocaleDateString('en-US', {
19
- year: 'numeric',
20
- month: 'short',
21
- day: 'numeric',
22
- });
23
- }
@@ -1,38 +0,0 @@
1
- export class PorchestraError extends Error {
2
- constructor(message: string, public code: string) {
3
- super(message);
4
- this.name = 'PorchestraError';
5
- }
6
- }
7
-
8
- export class AuthenticationError extends PorchestraError {
9
- constructor(message: string, public retryable = false) {
10
- super(message, 'AUTH_ERROR');
11
- this.name = 'AuthenticationError';
12
- }
13
- }
14
-
15
- export class NetworkError extends PorchestraError {
16
- constructor(
17
- message: string,
18
- public statusCode?: number,
19
- public retryable = true
20
- ) {
21
- super(message, 'NETWORK_ERROR');
22
- this.name = 'NetworkError';
23
- }
24
- }
25
-
26
- export class ValidationError extends PorchestraError {
27
- constructor(message: string, public field?: string) {
28
- super(message, 'VALIDATION_ERROR');
29
- this.name = 'ValidationError';
30
- }
31
- }
32
-
33
- export class FileSystemError extends PorchestraError {
34
- constructor(message: string, public path: string) {
35
- super(message, 'FS_ERROR');
36
- this.name = 'FileSystemError';
37
- }
38
- }
@@ -1,11 +0,0 @@
1
- import pc from 'picocolors';
2
-
3
- export const logger = {
4
- info: (msg: string) => console.log(msg),
5
- success: (msg: string) => console.log(pc.green(msg)),
6
- error: (msg: string) => console.error(pc.red(msg)),
7
- warn: (msg: string) => console.log(pc.yellow(msg)),
8
- gray: (msg: string) => console.log(pc.gray(msg)),
9
- bold: (msg: string) => console.log(pc.bold(msg)),
10
- cyan: (msg: string) => console.log(pc.cyan(msg)),
11
- };
@@ -1,47 +0,0 @@
1
- import path from 'path';
2
- import { promises as fs } from 'fs';
3
-
4
- export function normalizeFolderPath(folderPath: string): string {
5
- let normalized = folderPath.replace(/^\//, '');
6
- normalized = normalized.replace(/\/+/g, '/');
7
- normalized = normalized
8
- .split('/')
9
- .map(segment => sanitizePathSegment(segment))
10
- .join('/');
11
- normalized = normalized.replace(/\/$/, '');
12
- return normalized;
13
- }
14
-
15
- function sanitizePathSegment(segment: string): string {
16
- return segment
17
- .replace(/[^a-zA-Z0-9-_]/g, '-')
18
- .replace(/^-+|-+$/g, '')
19
- .replace(/-+/g, '-');
20
- }
21
-
22
- export function calculateOutputPath(
23
- baseDir: string,
24
- folderPath: string,
25
- filename?: string
26
- ): string {
27
- const normalized = normalizeFolderPath(folderPath);
28
- const fullPath = path.join(baseDir, normalized);
29
- return filename ? path.join(fullPath, filename) : fullPath;
30
- }
31
-
32
- export function calculateAgentOutputPath(
33
- baseDir: string,
34
- folderPath: string,
35
- agentName: string,
36
- filename?: string
37
- ): string {
38
- const normalizedFolder = normalizeFolderPath(folderPath);
39
- const normalizedAgent = sanitizePathSegment(agentName);
40
- const fullPath = path.join(baseDir, normalizedFolder, normalizedAgent);
41
- return filename ? path.join(fullPath, filename) : fullPath;
42
- }
43
-
44
- export async function ensureDirectory(filePath: string): Promise<void> {
45
- const dir = path.dirname(filePath);
46
- await fs.mkdir(dir, { recursive: true });
47
- }
@@ -1,74 +0,0 @@
1
- import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
- import { promises as fs } from 'fs';
3
- import path from 'path';
4
- import os from 'os';
5
- import { ConfigManager } from '../../src/core/config/config-manager.js';
6
-
7
- describe('ConfigManager', () => {
8
- let tempDir: string;
9
- let configManager: ConfigManager;
10
-
11
- beforeEach(async () => {
12
- tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'porchestra-test-'));
13
- process.env.PORCHESTRA_CONFIG_DIR = tempDir;
14
- configManager = new ConfigManager();
15
- });
16
-
17
- afterEach(async () => {
18
- await fs.rm(tempDir, { recursive: true, force: true });
19
- delete process.env.PORCHESTRA_CONFIG_DIR;
20
- });
21
-
22
- it('should create default config when none exists', async () => {
23
- const config = await configManager.load();
24
- expect(config.version).toBe('1.0.0');
25
- expect(config.trackedProjects).toEqual([]);
26
- expect(config.api?.baseUrl).toBe('https://api.porchestra.io/v1');
27
- });
28
-
29
- it('should save and load config', async () => {
30
- const config = await configManager.load();
31
- config.auth = {
32
- token: 'test-token',
33
- tokenId: '550e8400-e29b-41d4-a716-446655440000',
34
- expiresAt: '2025-12-31T23:59:59Z',
35
- deviceName: 'Test Device'
36
- };
37
-
38
- await configManager.save(config);
39
-
40
- // Create new manager to test loading from disk
41
- const newManager = new ConfigManager();
42
- const loaded = await newManager.load();
43
-
44
- expect(loaded.auth?.token).toBe('test-token');
45
- expect(loaded.auth?.deviceName).toBe('Test Device');
46
- });
47
-
48
- it('should update config with updater function', async () => {
49
- await configManager.load();
50
-
51
- await configManager.update((cfg) => ({
52
- ...cfg,
53
- api: {
54
- ...cfg.api,
55
- baseUrl: 'https://custom.api.com/v1'
56
- }
57
- }));
58
-
59
- const config = await configManager.get();
60
- expect(config.api?.baseUrl).toBe('https://custom.api.com/v1');
61
- });
62
-
63
- it('should clear config', async () => {
64
- const config = await configManager.load();
65
- config.auth = { token: 'test' };
66
- await configManager.save(config);
67
-
68
- await configManager.clear();
69
-
70
- // After clear, should return default config
71
- const newConfig = await configManager.load();
72
- expect(newConfig.auth).toBeUndefined();
73
- });
74
- });
@@ -1,61 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { CliConfigSchema, TrackedProjectSchema } from '../../src/core/config/config-schema.js';
3
-
4
- describe('config-schema', () => {
5
- it('should validate a valid config', () => {
6
- const validConfig = {
7
- version: '1.0.0',
8
- auth: {
9
- token: 'test-token',
10
- tokenId: '550e8400-e29b-41d4-a716-446655440000',
11
- expiresAt: '2025-12-31T23:59:59Z',
12
- deviceName: 'Test Device'
13
- },
14
- api: {
15
- baseUrl: 'https://api.porchestra.io/v1',
16
- skipTlsVerify: false
17
- },
18
- trackedProjects: [],
19
- output: {
20
- baseDir: './src/agents',
21
- createIndexFiles: true
22
- }
23
- };
24
-
25
- const result = CliConfigSchema.safeParse(validConfig);
26
- expect(result.success).toBe(true);
27
- });
28
-
29
- it('should apply defaults for missing fields', () => {
30
- const minimalConfig = {
31
- version: '1.0.0'
32
- };
33
-
34
- const result = CliConfigSchema.parse(minimalConfig);
35
- expect(result.api?.baseUrl).toBe('https://api.porchestra.io/v1');
36
- expect(result.output?.baseDir).toBe('./src/agents');
37
- expect(result.trackedProjects).toEqual([]);
38
- });
39
-
40
- it('should reject invalid version', () => {
41
- const invalidConfig = {
42
- version: '2.0.0'
43
- };
44
-
45
- const result = CliConfigSchema.safeParse(invalidConfig);
46
- expect(result.success).toBe(false);
47
- });
48
-
49
- it('should validate tracked project schema', () => {
50
- const validProject = {
51
- projectId: '550e8400-e29b-41d4-a716-446655440000',
52
- projectName: 'Test Project',
53
- projectSlug: 'test-project',
54
- selectedAt: '2025-01-30T12:00:00Z',
55
- agents: []
56
- };
57
-
58
- const result = TrackedProjectSchema.safeParse(validProject);
59
- expect(result.success).toBe(true);
60
- });
61
- });
@@ -1,53 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import {
3
- normalizeFolderPath,
4
- calculateOutputPath,
5
- } from '../../src/utils/path-utils.js';
6
-
7
- describe('path-utils', () => {
8
- describe('normalizeFolderPath', () => {
9
- it('should remove leading slash', () => {
10
- expect(normalizeFolderPath('/main/x-agent/')).toBe('main/x-agent');
11
- });
12
-
13
- it('should handle paths without leading slash', () => {
14
- expect(normalizeFolderPath('y-agent')).toBe('y-agent');
15
- });
16
-
17
- it('should replace multiple slashes', () => {
18
- expect(normalizeFolderPath('//legacy//agents//')).toBe('/legacy/agents');
19
- });
20
-
21
- it('should sanitize special characters', () => {
22
- expect(normalizeFolderPath('My Agent (v2)')).toBe('My-Agent-v2');
23
- });
24
-
25
- it('should handle empty path', () => {
26
- expect(normalizeFolderPath('/')).toBe('');
27
- });
28
-
29
- it('should prevent directory traversal', () => {
30
- expect(normalizeFolderPath('../malicious')).toBe('/malicious');
31
- });
32
- });
33
-
34
- describe('calculateOutputPath', () => {
35
- it('should calculate correct output path', () => {
36
- const result = calculateOutputPath(
37
- './src/agents',
38
- 'main/x-agent',
39
- 'tool-schemas.ts'
40
- );
41
- expect(result).toMatch(/src[\\/]agents[\\/]main[\\/]x-agent[\\/]tool-schemas\.ts/);
42
- });
43
-
44
- it('should handle empty folder path', () => {
45
- const result = calculateOutputPath(
46
- './src/agents',
47
- '/',
48
- 'tool-schemas.ts'
49
- );
50
- expect(result).toMatch(/src[\\/]agents[\\/]tool-schemas\.ts$/);
51
- });
52
- });
53
- });