@agentforge/cli 0.5.0 → 0.5.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.
@@ -0,0 +1,148 @@
1
+ # {{AGENT_NAME_PASCAL}} Agent
2
+
3
+ {{AGENT_DESCRIPTION}}
4
+
5
+ A configurable, reusable AI agent built with AgentForge.
6
+
7
+ ## Features
8
+
9
+ - ✅ **Factory Function Pattern** - Easy to instantiate with different configurations
10
+ - ✅ **External Prompt Templates** - Prompts stored in `.md` files for easy editing
11
+ - ✅ **Tool Injection** - Add custom tools via ToolRegistry or direct injection
12
+ - ✅ **Feature Flags** - Enable/disable capabilities as needed
13
+ - ✅ **Configuration Validation** - Type-safe configuration with Zod schemas
14
+ - ✅ **Comprehensive Tests** - Full test coverage demonstrating all features
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ npm install @agentforge/core @agentforge/patterns @agentforge/tools @langchain/openai zod
20
+ ```
21
+
22
+ ## Quick Start
23
+
24
+ ```typescript
25
+ import { create{{AGENT_NAME_PASCAL}}Agent } from './{{AGENT_NAME_KEBAB}}';
26
+
27
+ // Create with default configuration
28
+ const agent = create{{AGENT_NAME_PASCAL}}Agent();
29
+
30
+ // Invoke the agent
31
+ const result = await agent.invoke({
32
+ input: 'Your query here',
33
+ });
34
+
35
+ console.log(result);
36
+ ```
37
+
38
+ ## Configuration
39
+
40
+ ### Basic Configuration
41
+
42
+ ```typescript
43
+ const agent = create{{AGENT_NAME_PASCAL}}Agent({
44
+ organizationName: 'Acme Corp',
45
+ temperature: 0.7,
46
+ maxIterations: 10,
47
+ });
48
+ ```
49
+
50
+ ### With Custom Tools
51
+
52
+ ```typescript
53
+ import { toolBuilder, ToolCategory } from '@agentforge/core';
54
+
55
+ const customTool = toolBuilder()
56
+ .name('my-custom-tool')
57
+ .description('Does something specific')
58
+ .category(ToolCategory.UTILITY)
59
+ .schema(z.object({
60
+ input: z.string().describe('Input parameter'),
61
+ }))
62
+ .implement(async ({ input }) => {
63
+ return { result: `Processed: ${input}` };
64
+ })
65
+ .build();
66
+
67
+ const agent = create{{AGENT_NAME_PASCAL}}Agent({
68
+ customTools: [customTool],
69
+ });
70
+ ```
71
+
72
+ ### With Tool Registry
73
+
74
+ ```typescript
75
+ import { ToolRegistry, ToolCategory } from '@agentforge/core';
76
+
77
+ const registry = new ToolRegistry();
78
+ // ... register tools
79
+
80
+ const agent = create{{AGENT_NAME_PASCAL}}Agent({
81
+ toolRegistry: registry,
82
+ enabledCategories: [ToolCategory.UTILITY],
83
+ });
84
+ ```
85
+
86
+ ### Feature Flags
87
+
88
+ ```typescript
89
+ const agent = create{{AGENT_NAME_PASCAL}}Agent({
90
+ enableExampleFeature: true, // Enable example feature
91
+ });
92
+ ```
93
+
94
+ ## Configuration Reference
95
+
96
+ | Option | Type | Default | Description |
97
+ |--------|------|---------|-------------|
98
+ | `model` | `BaseLanguageModel` | `ChatOpenAI(gpt-4)` | LLM model to use |
99
+ | `temperature` | `number` | `0.7` | Model temperature (0-2) |
100
+ | `customTools` | `Tool[]` | `[]` | Custom tools to inject |
101
+ | `toolRegistry` | `ToolRegistry` | `undefined` | Tool registry for tool management |
102
+ | `enabledCategories` | `ToolCategory[]` | `undefined` | Filter tools by category |
103
+ | `enableExampleFeature` | `boolean` | `true` | Enable example feature |
104
+ | `maxIterations` | `number` | `10` | Maximum agent iterations |
105
+ | `systemPrompt` | `string` | (from file) | Custom system prompt |
106
+ | `organizationName` | `string` | `undefined` | Organization name for context |
107
+ | `description` | `string` | (default) | Agent description |
108
+
109
+ ## Built-in Tools
110
+
111
+ ### example-action
112
+ Perform an example action.
113
+
114
+ **Parameters:**
115
+ - `input` (string) - Input for the action
116
+
117
+ **Returns:**
118
+ - `result` (string) - Processed result
119
+ - `success` (boolean) - Success status
120
+
121
+ ## Prompt Management
122
+
123
+ Prompts are stored in `prompts/system.md` and support variable substitution:
124
+
125
+ ```markdown
126
+ # {{AGENT_NAME_PASCAL}} Agent
127
+
128
+ You are working for {{organizationName}}.
129
+
130
+ {{#if enableExampleFeature}}
131
+ You have access to example features.
132
+ {{/if}}
133
+ ```
134
+
135
+ **Template Syntax:**
136
+ - Simple variables: `{{variableName}}`
137
+ - Conditional blocks: `{{#if variableName}}...{{/if}}`
138
+
139
+ ## Testing
140
+
141
+ ```bash
142
+ npm test
143
+ ```
144
+
145
+ ## License
146
+
147
+ MIT
148
+
@@ -0,0 +1,155 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { z } from 'zod';
3
+ import { create{{AGENT_NAME_PASCAL}}Agent, {{AGENT_NAME_PASCAL}}ConfigSchema } from './index.js';
4
+ import { toolBuilder, ToolCategory, ToolRegistry } from '@agentforge/core';
5
+ import { ChatOpenAI } from '@langchain/openai';
6
+
7
+ describe('{{AGENT_NAME_PASCAL}}Agent', () => {
8
+ describe('Configuration Validation', () => {
9
+ it('should accept valid configuration', () => {
10
+ const config = {
11
+ temperature: 0.7,
12
+ organizationName: 'Test Org',
13
+ enableExampleFeature: true,
14
+ };
15
+
16
+ expect(() => {{AGENT_NAME_PASCAL}}ConfigSchema.parse(config)).not.toThrow();
17
+ });
18
+
19
+ it('should reject invalid temperature', () => {
20
+ const config = {
21
+ temperature: 3.0, // Invalid: > 2
22
+ };
23
+
24
+ expect(() => {{AGENT_NAME_PASCAL}}ConfigSchema.parse(config)).toThrow();
25
+ });
26
+
27
+ it('should accept empty configuration', () => {
28
+ expect(() => {{AGENT_NAME_PASCAL}}ConfigSchema.parse({})).not.toThrow();
29
+ });
30
+ });
31
+
32
+ describe('Agent Creation', () => {
33
+ it('should create agent with default configuration', () => {
34
+ const agent = create{{AGENT_NAME_PASCAL}}Agent();
35
+ expect(agent).toBeDefined();
36
+ expect(typeof agent.invoke).toBe('function');
37
+ });
38
+
39
+ it('should create agent with custom model', () => {
40
+ const customModel = new ChatOpenAI({
41
+ modelName: 'gpt-3.5-turbo',
42
+ temperature: 0.3,
43
+ });
44
+
45
+ const agent = create{{AGENT_NAME_PASCAL}}Agent({
46
+ model: customModel,
47
+ });
48
+
49
+ expect(agent).toBeDefined();
50
+ });
51
+
52
+ it('should create agent with custom temperature', () => {
53
+ const agent = create{{AGENT_NAME_PASCAL}}Agent({
54
+ temperature: 0.2,
55
+ });
56
+
57
+ expect(agent).toBeDefined();
58
+ });
59
+ });
60
+
61
+ describe('Tool Injection', () => {
62
+ it('should accept custom tools', () => {
63
+ const customTool = toolBuilder()
64
+ .name('custom-tool')
65
+ .description('A custom tool for testing')
66
+ .category(ToolCategory.UTILITY)
67
+ .schema(z.object({
68
+ input: z.string().describe('Test input'),
69
+ }))
70
+ .implement(async ({ input }) => ({ result: input }))
71
+ .build();
72
+
73
+ const agent = create{{AGENT_NAME_PASCAL}}Agent({
74
+ customTools: [customTool],
75
+ });
76
+
77
+ expect(agent).toBeDefined();
78
+ });
79
+
80
+ it('should accept tool registry', () => {
81
+ const registry = new ToolRegistry();
82
+
83
+ const tool = toolBuilder()
84
+ .name('registry-tool')
85
+ .description('A tool from registry')
86
+ .category(ToolCategory.UTILITY)
87
+ .schema(z.object({
88
+ input: z.string().describe('Test input'),
89
+ }))
90
+ .implement(async ({ input }) => ({ result: input }))
91
+ .build();
92
+
93
+ registry.register(tool);
94
+
95
+ const agent = create{{AGENT_NAME_PASCAL}}Agent({
96
+ toolRegistry: registry,
97
+ });
98
+
99
+ expect(agent).toBeDefined();
100
+ });
101
+
102
+ it('should filter tools by category', () => {
103
+ const registry = new ToolRegistry();
104
+
105
+ const utilityTool = toolBuilder()
106
+ .name('utility-tool')
107
+ .description('A utility tool')
108
+ .category(ToolCategory.UTILITY)
109
+ .schema(z.object({}))
110
+ .implement(async () => ({ result: 'utility' }))
111
+ .build();
112
+
113
+ registry.register(utilityTool);
114
+
115
+ const agent = create{{AGENT_NAME_PASCAL}}Agent({
116
+ toolRegistry: registry,
117
+ enabledCategories: [ToolCategory.UTILITY],
118
+ });
119
+
120
+ expect(agent).toBeDefined();
121
+ });
122
+ });
123
+
124
+ describe('Feature Flags', () => {
125
+ it('should enable example feature by default', () => {
126
+ const agent = create{{AGENT_NAME_PASCAL}}Agent();
127
+ expect(agent).toBeDefined();
128
+ });
129
+
130
+ it('should disable example feature when configured', () => {
131
+ const agent = create{{AGENT_NAME_PASCAL}}Agent({
132
+ enableExampleFeature: false,
133
+ });
134
+ expect(agent).toBeDefined();
135
+ });
136
+ });
137
+
138
+ describe('System Prompt Customization', () => {
139
+ it('should use default system prompt', () => {
140
+ const agent = create{{AGENT_NAME_PASCAL}}Agent({
141
+ organizationName: 'Test Org',
142
+ });
143
+ expect(agent).toBeDefined();
144
+ });
145
+
146
+ it('should accept custom system prompt', () => {
147
+ const customPrompt = 'You are a custom agent with specific instructions.';
148
+ const agent = create{{AGENT_NAME_PASCAL}}Agent({
149
+ systemPrompt: customPrompt,
150
+ });
151
+ expect(agent).toBeDefined();
152
+ });
153
+ });
154
+ });
155
+
@@ -0,0 +1,147 @@
1
+ import { z } from 'zod';
2
+ import { createReActAgent } from '@agentforge/patterns';
3
+ import { toolBuilder, ToolCategory, ToolRegistry } from '@agentforge/core';
4
+ import type { BaseLanguageModel } from '@langchain/core/language_models/base';
5
+ import { ChatOpenAI } from '@langchain/openai';
6
+ import { loadPrompt } from './prompt-loader.js';
7
+
8
+ /**
9
+ * Configuration schema for {{AGENT_NAME_PASCAL}}Agent
10
+ */
11
+ export const {{AGENT_NAME_PASCAL}}ConfigSchema = z.object({
12
+ // Model configuration
13
+ model: z.custom<BaseLanguageModel>().optional(),
14
+ temperature: z.number().min(0).max(2).optional(),
15
+
16
+ // Tool configuration
17
+ customTools: z.array(z.any()).optional(),
18
+ toolRegistry: z.custom<ToolRegistry>().optional(),
19
+ enabledCategories: z.array(z.nativeEnum(ToolCategory)).optional(),
20
+
21
+ // Feature flags
22
+ enableExampleFeature: z.boolean().optional(),
23
+
24
+ // Behavior configuration
25
+ maxIterations: z.number().min(1).max(50).optional(),
26
+ systemPrompt: z.string().optional(),
27
+
28
+ // Domain-specific configuration
29
+ organizationName: z.string().optional(),
30
+ description: z.string().optional(),
31
+ });
32
+
33
+ export type {{AGENT_NAME_PASCAL}}Config = z.infer<typeof {{AGENT_NAME_PASCAL}}ConfigSchema>;
34
+
35
+ /**
36
+ * Default configuration for {{AGENT_NAME_PASCAL}}Agent
37
+ */
38
+ const DEFAULT_CONFIG: Partial<{{AGENT_NAME_PASCAL}}Config> = {
39
+ temperature: 0.7,
40
+ maxIterations: 10,
41
+ enableExampleFeature: true,
42
+ };
43
+
44
+ /**
45
+ * Build tools for the {{AGENT_NAME_CAMEL}} agent
46
+ */
47
+ function buildTools(config: {{AGENT_NAME_PASCAL}}Config) {
48
+ const tools = [];
49
+
50
+ // Example built-in tool
51
+ if (config.enableExampleFeature) {
52
+ const exampleTool = toolBuilder()
53
+ .name('example-action')
54
+ .description('Perform an example action')
55
+ .category(ToolCategory.UTILITY)
56
+ .schema(z.object({
57
+ input: z.string().describe('Input for the action'),
58
+ }))
59
+ .implement(async ({ input }) => {
60
+ return {
61
+ result: `Processed: ${input}`,
62
+ success: true,
63
+ };
64
+ })
65
+ .build();
66
+
67
+ tools.push(exampleTool);
68
+ }
69
+
70
+ // Add custom tools from registry
71
+ if (config.toolRegistry) {
72
+ const registryTools = config.enabledCategories
73
+ ? config.toolRegistry.getByCategory(config.enabledCategories)
74
+ : config.toolRegistry.getAll();
75
+ tools.push(...registryTools);
76
+ }
77
+
78
+ // Add custom tools
79
+ if (config.customTools) {
80
+ tools.push(...config.customTools);
81
+ }
82
+
83
+ return tools;
84
+ }
85
+
86
+ /**
87
+ * Build system prompt for the {{AGENT_NAME_CAMEL}} agent
88
+ */
89
+ function buildSystemPrompt(config: {{AGENT_NAME_PASCAL}}Config): string {
90
+ if (config.systemPrompt) {
91
+ return config.systemPrompt;
92
+ }
93
+
94
+ // Load prompt from external file with variable substitution
95
+ return loadPrompt('system', {
96
+ organizationName: config.organizationName || 'your organization',
97
+ description: config.description || '{{AGENT_DESCRIPTION}}',
98
+ enableExampleFeature: config.enableExampleFeature,
99
+ });
100
+ }
101
+
102
+ /**
103
+ * Create a {{AGENT_NAME_CAMEL}} agent
104
+ *
105
+ * @param config - Configuration options for the agent
106
+ * @returns A configured ReAct agent
107
+ *
108
+ * @example
109
+ * ```typescript
110
+ * const agent = create{{AGENT_NAME_PASCAL}}Agent({
111
+ * organizationName: 'Acme Corp',
112
+ * enableExampleFeature: true,
113
+ * });
114
+ *
115
+ * const result = await agent.invoke({
116
+ * input: 'Your query here',
117
+ * });
118
+ * ```
119
+ */
120
+ export function create{{AGENT_NAME_PASCAL}}Agent(config: {{AGENT_NAME_PASCAL}}Config = {}) {
121
+ // Validate and merge with defaults
122
+ const validatedConfig = {{AGENT_NAME_PASCAL}}ConfigSchema.parse({
123
+ ...DEFAULT_CONFIG,
124
+ ...config,
125
+ });
126
+
127
+ // Create model
128
+ const model = validatedConfig.model || new ChatOpenAI({
129
+ modelName: 'gpt-4',
130
+ temperature: validatedConfig.temperature,
131
+ });
132
+
133
+ // Build tools
134
+ const tools = buildTools(validatedConfig);
135
+
136
+ // Build system prompt
137
+ const systemPrompt = buildSystemPrompt(validatedConfig);
138
+
139
+ // Create agent
140
+ return createReActAgent({
141
+ model,
142
+ tools,
143
+ systemPrompt,
144
+ maxIterations: validatedConfig.maxIterations,
145
+ });
146
+ }
147
+
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "{{PACKAGE_NAME}}",
3
+ "version": "0.1.0",
4
+ "description": "{{AGENT_DESCRIPTION}}",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "scripts": {
9
+ "build": "tsc",
10
+ "test": "vitest run",
11
+ "test:watch": "vitest",
12
+ "typecheck": "tsc --noEmit"
13
+ },
14
+ "keywords": [
15
+ "agentforge",
16
+ "ai-agent",
17
+ "llm",
18
+ "reusable-agent"
19
+ ],
20
+ "author": "{{AUTHOR}}",
21
+ "license": "MIT",
22
+ "dependencies": {
23
+ "@agentforge/core": "workspace:*",
24
+ "@agentforge/patterns": "workspace:*",
25
+ "@langchain/core": "^1.1.8",
26
+ "@langchain/openai": "^0.3.14",
27
+ "zod": "^3.24.1"
28
+ },
29
+ "devDependencies": {
30
+ "@types/node": "^22.10.5",
31
+ "typescript": "^5.7.3",
32
+ "vitest": "^3.0.5"
33
+ }
34
+ }
35
+
@@ -0,0 +1,43 @@
1
+ import { readFileSync } from 'fs';
2
+ import { join, dirname } from 'path';
3
+ import { fileURLToPath } from 'url';
4
+
5
+ const __filename = fileURLToPath(import.meta.url);
6
+ const __dirname = dirname(__filename);
7
+
8
+ /**
9
+ * Simple template variable substitution
10
+ * Supports: {{variable}} and {{#if variable}}...{{/if}}
11
+ */
12
+ export function renderTemplate(template: string, variables: Record<string, any>): string {
13
+ let result = template;
14
+
15
+ // Replace simple variables: {{variableName}}
16
+ result = result.replace(/\{\{(\w+)\}\}/g, (match, varName) => {
17
+ return variables[varName] !== undefined ? String(variables[varName]) : match;
18
+ });
19
+
20
+ // Handle conditional blocks: {{#if variableName}}...{{/if}}
21
+ result = result.replace(/\{\{#if (\w+)\}\}([\s\S]*?)\{\{\/if\}\}/g, (match, varName, content) => {
22
+ return variables[varName] ? content : '';
23
+ });
24
+
25
+ return result;
26
+ }
27
+
28
+ /**
29
+ * Load a prompt template from the prompts directory
30
+ */
31
+ export function loadPromptTemplate(name: string): string {
32
+ const promptPath = join(__dirname, '..', 'prompts', `${name}.md`);
33
+ return readFileSync(promptPath, 'utf-8');
34
+ }
35
+
36
+ /**
37
+ * Load and render a prompt with variables
38
+ */
39
+ export function loadPrompt(name: string, variables: Record<string, any> = {}): string {
40
+ const template = loadPromptTemplate(name);
41
+ return renderTemplate(template, variables);
42
+ }
43
+
@@ -0,0 +1,28 @@
1
+ # {{AGENT_NAME_PASCAL}} Agent
2
+
3
+ You are a professional AI agent{{#if organizationName}} working for {{organizationName}}{{/if}}.
4
+
5
+ ## Your Role
6
+
7
+ {{description}}
8
+
9
+ ## Guidelines
10
+
11
+ - Be helpful, accurate, and professional
12
+ - Use the available tools to accomplish tasks
13
+ - Ask clarifying questions when needed
14
+ - Provide clear explanations of your actions
15
+
16
+ {{#if enableExampleFeature}}
17
+ ## Example Feature
18
+
19
+ You have access to example actions that can help you accomplish tasks more effectively.
20
+
21
+ {{/if}}
22
+
23
+ ## Important Notes
24
+
25
+ - Always verify information before providing it
26
+ - If you're unsure about something, say so
27
+ - Use tools appropriately and explain your reasoning
28
+
@@ -0,0 +1,10 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "outDir": "./dist",
5
+ "rootDir": "./src"
6
+ },
7
+ "include": ["src/**/*"],
8
+ "exclude": ["node_modules", "dist", "**/*.test.ts"]
9
+ }
10
+
@@ -0,0 +1,14 @@
1
+ import { defineConfig } from 'vitest/config';
2
+
3
+ export default defineConfig({
4
+ test: {
5
+ globals: true,
6
+ environment: 'node',
7
+ coverage: {
8
+ provider: 'v8',
9
+ reporter: ['text', 'json', 'html'],
10
+ exclude: ['dist/**', '**/*.test.ts', '**/*.config.ts'],
11
+ },
12
+ },
13
+ });
14
+