@agents-at-scale/ark 0.1.31
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/README.md +95 -0
- package/dist/commands/cluster/get-ip.d.ts +2 -0
- package/dist/commands/cluster/get-ip.js +32 -0
- package/dist/commands/cluster/get-type.d.ts +2 -0
- package/dist/commands/cluster/get-type.js +26 -0
- package/dist/commands/cluster/index.d.ts +2 -0
- package/dist/commands/cluster/index.js +10 -0
- package/dist/commands/completion.d.ts +2 -0
- package/dist/commands/completion.js +108 -0
- package/dist/commands/config.d.ts +5 -0
- package/dist/commands/config.js +327 -0
- package/dist/commands/generate/config.d.ts +145 -0
- package/dist/commands/generate/config.js +253 -0
- package/dist/commands/generate/generators/agent.d.ts +2 -0
- package/dist/commands/generate/generators/agent.js +156 -0
- package/dist/commands/generate/generators/index.d.ts +6 -0
- package/dist/commands/generate/generators/index.js +6 -0
- package/dist/commands/generate/generators/marketplace.d.ts +2 -0
- package/dist/commands/generate/generators/marketplace.js +304 -0
- package/dist/commands/generate/generators/mcpserver.d.ts +25 -0
- package/dist/commands/generate/generators/mcpserver.js +350 -0
- package/dist/commands/generate/generators/project.d.ts +2 -0
- package/dist/commands/generate/generators/project.js +784 -0
- package/dist/commands/generate/generators/query.d.ts +2 -0
- package/dist/commands/generate/generators/query.js +213 -0
- package/dist/commands/generate/generators/team.d.ts +2 -0
- package/dist/commands/generate/generators/team.js +407 -0
- package/dist/commands/generate/index.d.ts +24 -0
- package/dist/commands/generate/index.js +357 -0
- package/dist/commands/generate/templateDiscovery.d.ts +30 -0
- package/dist/commands/generate/templateDiscovery.js +94 -0
- package/dist/commands/generate/templateEngine.d.ts +78 -0
- package/dist/commands/generate/templateEngine.js +368 -0
- package/dist/commands/generate/utils/nameUtils.d.ts +35 -0
- package/dist/commands/generate/utils/nameUtils.js +110 -0
- package/dist/commands/generate/utils/projectUtils.d.ts +28 -0
- package/dist/commands/generate/utils/projectUtils.js +133 -0
- package/dist/components/DashboardCLI.d.ts +3 -0
- package/dist/components/DashboardCLI.js +149 -0
- package/dist/components/GeneratorUI.d.ts +3 -0
- package/dist/components/GeneratorUI.js +167 -0
- package/dist/components/statusChecker.d.ts +48 -0
- package/dist/components/statusChecker.js +251 -0
- package/dist/config.d.ts +42 -0
- package/dist/config.js +243 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +67 -0
- package/dist/lib/arkClient.d.ts +32 -0
- package/dist/lib/arkClient.js +43 -0
- package/dist/lib/cluster.d.ts +8 -0
- package/dist/lib/cluster.js +134 -0
- package/dist/lib/config.d.ts +82 -0
- package/dist/lib/config.js +223 -0
- package/dist/lib/consts.d.ts +10 -0
- package/dist/lib/consts.js +15 -0
- package/dist/lib/errors.d.ts +56 -0
- package/dist/lib/errors.js +208 -0
- package/dist/lib/exec.d.ts +5 -0
- package/dist/lib/exec.js +20 -0
- package/dist/lib/gatewayManager.d.ts +24 -0
- package/dist/lib/gatewayManager.js +85 -0
- package/dist/lib/kubernetes.d.ts +28 -0
- package/dist/lib/kubernetes.js +122 -0
- package/dist/lib/progress.d.ts +128 -0
- package/dist/lib/progress.js +273 -0
- package/dist/lib/security.d.ts +37 -0
- package/dist/lib/security.js +295 -0
- package/dist/lib/types.d.ts +37 -0
- package/dist/lib/types.js +1 -0
- package/dist/lib/wrappers/git.d.ts +2 -0
- package/dist/lib/wrappers/git.js +43 -0
- package/dist/ui/MainMenu.d.ts +3 -0
- package/dist/ui/MainMenu.js +116 -0
- package/dist/ui/statusFormatter.d.ts +9 -0
- package/dist/ui/statusFormatter.js +47 -0
- package/package.json +62 -0
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import inquirer from 'inquirer';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import { fileURLToPath } from 'url';
|
|
6
|
+
import { createProjectGenerator, createAgentGenerator, createTeamGenerator, createQueryGenerator, createMcpServerGenerator, createMarketplaceGenerator, } from './generators/index.js';
|
|
7
|
+
import { normalizeAndValidateName, getNameValidationError, } from './utils/nameUtils.js';
|
|
8
|
+
import { ErrorHandler, ArkError, ErrorCode } from '../../lib/errors.js';
|
|
9
|
+
function getDefaultDestination() {
|
|
10
|
+
try {
|
|
11
|
+
// Get the path to this file
|
|
12
|
+
const currentFile = fileURLToPath(import.meta.url);
|
|
13
|
+
// Navigate up from tools/ark-cli/src/commands/generate/index.js to agents-at-scale/
|
|
14
|
+
const arkRoot = path.resolve(path.dirname(currentFile), '../../../../../');
|
|
15
|
+
// Get the parent directory of agents-at-scale
|
|
16
|
+
const parentDir = path.dirname(arkRoot);
|
|
17
|
+
return parentDir;
|
|
18
|
+
}
|
|
19
|
+
catch (_error) {
|
|
20
|
+
// Fallback to current working directory if we can't determine ark location
|
|
21
|
+
console.warn('Could not determine ark repository location, using current directory');
|
|
22
|
+
return process.cwd();
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export function createGenerateCommand() {
|
|
26
|
+
const generate = new Command('generate');
|
|
27
|
+
generate
|
|
28
|
+
.alias('g')
|
|
29
|
+
.description('Generate ARK resources from templates (projects, agents, teams, queries, mcp-servers, marketplace)')
|
|
30
|
+
.helpOption('-h, --help', 'Display help for the generate command')
|
|
31
|
+
.addHelpText('before', `
|
|
32
|
+
${chalk.blue('🎯 ARK Generator')}
|
|
33
|
+
Create new ARK resources quickly using interactive templates.
|
|
34
|
+
|
|
35
|
+
${chalk.cyan('Available generators:')}
|
|
36
|
+
• project - Complete ARK project with structure, CI/CD, and samples
|
|
37
|
+
• agent - Single AI agent definition
|
|
38
|
+
• team - Team of collaborative agents
|
|
39
|
+
• query - Query to test agents or teams
|
|
40
|
+
• mcp-server - MCP server with Kubernetes deployment
|
|
41
|
+
• marketplace - Central repository for sharing reusable components
|
|
42
|
+
`)
|
|
43
|
+
.addHelpText('after', `
|
|
44
|
+
${chalk.cyan('Examples:')}
|
|
45
|
+
${chalk.yellow('ark g project')} # Create project with prompts
|
|
46
|
+
${chalk.yellow('ark g agent')} # Create an agent
|
|
47
|
+
${chalk.yellow('ark g team')} # Create a team
|
|
48
|
+
${chalk.yellow('ark g query')} # Create a query
|
|
49
|
+
${chalk.yellow('ark g mcp-server')} # Create MCP server
|
|
50
|
+
${chalk.yellow('ark g marketplace')} # Create marketplace
|
|
51
|
+
|
|
52
|
+
${chalk.cyan('Usage:')}
|
|
53
|
+
Generators will prompt for required information when not provided via options.
|
|
54
|
+
|
|
55
|
+
${chalk.cyan('Getting started:')}
|
|
56
|
+
1. ${chalk.yellow('ark generate project my-first-project')} # Create project
|
|
57
|
+
2. ${chalk.yellow('cd my-first-project')} # Enter directory
|
|
58
|
+
3. ${chalk.yellow('source .env')} # Set environment
|
|
59
|
+
4. ${chalk.yellow('make quickstart')} # Deploy to cluster
|
|
60
|
+
`);
|
|
61
|
+
// Register generators
|
|
62
|
+
const generators = new Map();
|
|
63
|
+
// Register built-in generators
|
|
64
|
+
generators.set('project', createProjectGenerator());
|
|
65
|
+
generators.set('agent', createAgentGenerator());
|
|
66
|
+
generators.set('team', createTeamGenerator());
|
|
67
|
+
generators.set('query', createQueryGenerator());
|
|
68
|
+
generators.set('mcp-server', createMcpServerGenerator());
|
|
69
|
+
generators.set('marketplace', createMarketplaceGenerator());
|
|
70
|
+
// Add subcommands for each generator
|
|
71
|
+
for (const [type, generator] of generators) {
|
|
72
|
+
const subCommand = new Command(type);
|
|
73
|
+
// Enhanced descriptions and help text per generator type
|
|
74
|
+
const helpTexts = {
|
|
75
|
+
project: {
|
|
76
|
+
description: `${generator.description}
|
|
77
|
+
|
|
78
|
+
${chalk.cyan('Features:')}
|
|
79
|
+
• Complete project structure with Helm charts
|
|
80
|
+
• CI/CD pipeline with GitHub Actions
|
|
81
|
+
• Sample agents, teams, and queries
|
|
82
|
+
• Model configurations for major providers
|
|
83
|
+
• Security best practices and RBAC
|
|
84
|
+
• Comprehensive documentation`,
|
|
85
|
+
examples: `
|
|
86
|
+
${chalk.cyan('Examples:')}
|
|
87
|
+
${chalk.yellow(`ark g project`)}
|
|
88
|
+
${chalk.yellow(`ark generate project customer-service`)}
|
|
89
|
+
|
|
90
|
+
${chalk.cyan('Options:')}
|
|
91
|
+
--project-type Project type: 'empty' or 'with-samples' (default: with-samples)
|
|
92
|
+
--namespace Kubernetes namespace (default: project name)
|
|
93
|
+
--skip-models Skip model provider configuration
|
|
94
|
+
--skip-git Skip git repository initialization`,
|
|
95
|
+
},
|
|
96
|
+
agent: {
|
|
97
|
+
description: `${generator.description}
|
|
98
|
+
|
|
99
|
+
${chalk.cyan('Features:')}
|
|
100
|
+
• Creates agent YAML definition
|
|
101
|
+
• Validates project structure
|
|
102
|
+
• Optional query generation for testing
|
|
103
|
+
• Handles name conflicts gracefully`,
|
|
104
|
+
examples: `
|
|
105
|
+
${chalk.cyan('Examples:')}
|
|
106
|
+
${chalk.yellow(`ark generate agent customer-support`)}
|
|
107
|
+
${chalk.yellow(`ark g agent`)}
|
|
108
|
+
|
|
109
|
+
${chalk.cyan('Requirements:')}
|
|
110
|
+
• Must be run within an ARK project directory
|
|
111
|
+
• Project must have valid Chart.yaml and agents/ directory`,
|
|
112
|
+
},
|
|
113
|
+
team: {
|
|
114
|
+
description: `${generator.description}
|
|
115
|
+
|
|
116
|
+
${chalk.cyan('Features:')}
|
|
117
|
+
• Interactive agent selection
|
|
118
|
+
• Multiple collaboration strategies
|
|
119
|
+
• Can create new agents on-the-fly
|
|
120
|
+
• Optional query generation for testing`,
|
|
121
|
+
examples: `
|
|
122
|
+
${chalk.cyan('Examples:')}
|
|
123
|
+
${chalk.yellow(`ark generate team research-team`)}
|
|
124
|
+
${chalk.yellow(`ark g team support-escalation`)}
|
|
125
|
+
|
|
126
|
+
${chalk.cyan('Strategies:')}
|
|
127
|
+
• sequential - Agents work in order
|
|
128
|
+
• round-robin - Agents take turns
|
|
129
|
+
• graph - Custom workflow with dependencies
|
|
130
|
+
• selector - AI chooses the next agent`,
|
|
131
|
+
},
|
|
132
|
+
query: {
|
|
133
|
+
description: `${generator.description}
|
|
134
|
+
|
|
135
|
+
${chalk.cyan('Features:')}
|
|
136
|
+
• Interactive agent or team selection
|
|
137
|
+
• Customisable input message
|
|
138
|
+
• Automatic queries directory creation
|
|
139
|
+
• Validates project structure`,
|
|
140
|
+
examples: `
|
|
141
|
+
${chalk.cyan('Examples:')}
|
|
142
|
+
${chalk.yellow(`ark g query`)}
|
|
143
|
+
${chalk.yellow(`ark generate query user-interaction`)}
|
|
144
|
+
|
|
145
|
+
${chalk.cyan('Requirements:')}
|
|
146
|
+
• Must be run within an ARK project directory
|
|
147
|
+
• Target agent or team should exist (or will be created manually)`,
|
|
148
|
+
},
|
|
149
|
+
'mcp-server': {
|
|
150
|
+
description: `${generator.description}
|
|
151
|
+
|
|
152
|
+
${chalk.cyan('Features:')}
|
|
153
|
+
• Multi-technology support (Node.js, Deno, Go, Python)
|
|
154
|
+
• Complete Kubernetes deployment with Helm charts
|
|
155
|
+
• Docker containerization with mcp-proxy
|
|
156
|
+
• Authentication and configuration options
|
|
157
|
+
• Example agents and queries included
|
|
158
|
+
• Production-ready with security best practices`,
|
|
159
|
+
examples: `
|
|
160
|
+
${chalk.cyan('Examples:')}
|
|
161
|
+
${chalk.yellow(`ark g mcp-server`)}
|
|
162
|
+
${chalk.yellow(`ark generate mcp-server github-tools`)}
|
|
163
|
+
|
|
164
|
+
${chalk.cyan('Technologies:')}
|
|
165
|
+
• node - Node.js with NPM packages or local development
|
|
166
|
+
• deno - Deno with JSR packages or local development
|
|
167
|
+
• go - Go with go install packages or local development
|
|
168
|
+
• python - Python with pip packages or local development
|
|
169
|
+
|
|
170
|
+
${chalk.cyan('Features:')}
|
|
171
|
+
• Authentication support for secured APIs
|
|
172
|
+
• Custom configuration for flexible deployment
|
|
173
|
+
• Example agent and query generation
|
|
174
|
+
• Kubernetes-native with MCPServer CRD integration`,
|
|
175
|
+
},
|
|
176
|
+
marketplace: {
|
|
177
|
+
description: `${generator.description}
|
|
178
|
+
|
|
179
|
+
${chalk.cyan('Features:')}
|
|
180
|
+
• Central repository structure for component sharing
|
|
181
|
+
• Organized directories for all ARK component types
|
|
182
|
+
• Built-in contribution guidelines and documentation
|
|
183
|
+
• Git repository initialization with best practices
|
|
184
|
+
• Ready for CI/CD integration and automated validation
|
|
185
|
+
• Component templates for easy contribution`,
|
|
186
|
+
examples: `
|
|
187
|
+
${chalk.cyan('Examples:')}
|
|
188
|
+
${chalk.yellow(`ark g marketplace`)}
|
|
189
|
+
|
|
190
|
+
${chalk.cyan('Structure:')}
|
|
191
|
+
• agents/ - Reusable agent definitions
|
|
192
|
+
• teams/ - Multi-agent workflow configurations
|
|
193
|
+
• models/ - Model configurations by provider
|
|
194
|
+
• queries/ - Query templates and patterns
|
|
195
|
+
• tools/ - Tool definitions and implementations
|
|
196
|
+
• mcp-servers/ - MCP server configurations
|
|
197
|
+
• docs/ - Documentation and guides
|
|
198
|
+
|
|
199
|
+
${chalk.cyan('Use Cases:')}
|
|
200
|
+
• Team sharing of proven agent configurations
|
|
201
|
+
• Cross-project component libraries
|
|
202
|
+
• Community marketplace for ARK resources
|
|
203
|
+
• Internal organization component registry`,
|
|
204
|
+
},
|
|
205
|
+
};
|
|
206
|
+
const helpText = helpTexts[type];
|
|
207
|
+
subCommand
|
|
208
|
+
.description(helpText?.description || generator.description)
|
|
209
|
+
.argument('[name]', type === 'marketplace'
|
|
210
|
+
? 'Ignored - marketplace is always named "ark-marketplace"'
|
|
211
|
+
: `Name of the ${type} to generate`)
|
|
212
|
+
.option('-d, --destination <path>', type === 'project' || type === 'marketplace'
|
|
213
|
+
? 'Parent directory for the project (default: directory above ark)'
|
|
214
|
+
: 'Working directory (default: current directory)', type === 'project' || type === 'marketplace'
|
|
215
|
+
? getDefaultDestination()
|
|
216
|
+
: undefined)
|
|
217
|
+
.option('-i, --interactive', type === 'marketplace'
|
|
218
|
+
? 'Not supported for marketplace'
|
|
219
|
+
: 'Force additional configuration prompts (generators prompt by default when info is missing)', false);
|
|
220
|
+
if (helpText?.examples) {
|
|
221
|
+
subCommand.addHelpText('after', helpText.examples);
|
|
222
|
+
}
|
|
223
|
+
// Add project-specific options
|
|
224
|
+
if (type === 'project') {
|
|
225
|
+
subCommand
|
|
226
|
+
.option('-t, --project-type <type>', 'Project type (empty or with-samples)')
|
|
227
|
+
.option('-n, --namespace <namespace>', 'Kubernetes namespace (defaults to project name)')
|
|
228
|
+
.option('--skip-models', 'Skip model configuration', false)
|
|
229
|
+
.option('--skip-git', 'Skip git setup', false)
|
|
230
|
+
.option('--selected-models <models>', 'Selected model configuration (default, aigw, all, none)')
|
|
231
|
+
.option('--azure-api-key <key>', 'Azure OpenAI API key')
|
|
232
|
+
.option('--azure-base-url <url>', 'Azure OpenAI Base URL')
|
|
233
|
+
.option('--aigw-base-url <url>', 'AI Gateway Base URL')
|
|
234
|
+
.option('--git-user-name <name>', 'Git user name')
|
|
235
|
+
.option('--git-user-email <email>', 'Git user email')
|
|
236
|
+
.option('--git-create-commit', 'Create initial git commit', false);
|
|
237
|
+
}
|
|
238
|
+
subCommand.action(async (name, options) => {
|
|
239
|
+
await ErrorHandler.catchAndHandle(async () => {
|
|
240
|
+
let itemName = name;
|
|
241
|
+
let destination;
|
|
242
|
+
if (options.destination) {
|
|
243
|
+
destination = options.destination;
|
|
244
|
+
}
|
|
245
|
+
else if (type === 'project' || type === 'marketplace') {
|
|
246
|
+
destination = getDefaultDestination();
|
|
247
|
+
}
|
|
248
|
+
else {
|
|
249
|
+
destination = process.cwd();
|
|
250
|
+
}
|
|
251
|
+
// If no name provided or interactive mode, prompt for details
|
|
252
|
+
// Special case: marketplace always uses "ark-marketplace"
|
|
253
|
+
if (type === 'marketplace') {
|
|
254
|
+
itemName = 'ark-marketplace';
|
|
255
|
+
}
|
|
256
|
+
else if (!itemName || options.interactive) {
|
|
257
|
+
const answers = await inquirer.prompt([
|
|
258
|
+
{
|
|
259
|
+
type: 'input',
|
|
260
|
+
name: 'name',
|
|
261
|
+
message: `What name would you like to use for the ${type}?`,
|
|
262
|
+
default: itemName,
|
|
263
|
+
validate: (input) => {
|
|
264
|
+
const error = getNameValidationError(input);
|
|
265
|
+
return error || true;
|
|
266
|
+
},
|
|
267
|
+
filter: (input) => {
|
|
268
|
+
try {
|
|
269
|
+
const { name } = normalizeAndValidateName(input, type);
|
|
270
|
+
return name;
|
|
271
|
+
}
|
|
272
|
+
catch {
|
|
273
|
+
return input; // Let validate handle the error
|
|
274
|
+
}
|
|
275
|
+
},
|
|
276
|
+
},
|
|
277
|
+
{
|
|
278
|
+
type: 'input',
|
|
279
|
+
name: 'destination',
|
|
280
|
+
message: 'Where would you like to generate it?',
|
|
281
|
+
default: destination,
|
|
282
|
+
when: () => options.interactive && type === 'project',
|
|
283
|
+
},
|
|
284
|
+
]);
|
|
285
|
+
itemName = answers.name;
|
|
286
|
+
if (answers.destination) {
|
|
287
|
+
destination = answers.destination;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
// Validate the final name (skip for marketplace as it's fixed)
|
|
291
|
+
if (!itemName && type !== 'marketplace') {
|
|
292
|
+
throw new ArkError(`${type} name is required`, ErrorCode.INVALID_INPUT, undefined, [
|
|
293
|
+
`Provide a name for the ${type}`,
|
|
294
|
+
`Use: ark generate ${type} <name>`,
|
|
295
|
+
]);
|
|
296
|
+
}
|
|
297
|
+
// Normalize name if provided as argument (not already normalized via prompts)
|
|
298
|
+
if (name && !options.interactive && type !== 'marketplace') {
|
|
299
|
+
const { name: normalizedName, wasTransformed } = normalizeAndValidateName(itemName, type);
|
|
300
|
+
if (wasTransformed) {
|
|
301
|
+
console.log(chalk.yellow(`📝 Name normalized: "${itemName}" → "${normalizedName}"`));
|
|
302
|
+
itemName = normalizedName;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
console.log(chalk.blue(`\n🔧 Generating ${type}: ${itemName}`));
|
|
306
|
+
console.log(chalk.gray(`📁 Project directory: ${destination}\n`));
|
|
307
|
+
await generator.generate(itemName, destination, options);
|
|
308
|
+
console.log(chalk.green(`\n✅ Successfully generated ${type}: ${itemName}`));
|
|
309
|
+
}, `Generating ${type}`).catch((error) => ErrorHandler.handleAndExit(error));
|
|
310
|
+
});
|
|
311
|
+
generate.addCommand(subCommand);
|
|
312
|
+
}
|
|
313
|
+
// Add a list command to show available generators
|
|
314
|
+
const listCommand = new Command('list');
|
|
315
|
+
listCommand
|
|
316
|
+
.alias('ls')
|
|
317
|
+
.description('List available generators with detailed information')
|
|
318
|
+
.option('--detailed', 'Show detailed information for each generator', false)
|
|
319
|
+
.action((options) => {
|
|
320
|
+
console.log(chalk.blue('\n🎯 ARK Generators\n'));
|
|
321
|
+
for (const [type, generator] of generators) {
|
|
322
|
+
const icon = type === 'project'
|
|
323
|
+
? '📦'
|
|
324
|
+
: type === 'agent'
|
|
325
|
+
? '🤖'
|
|
326
|
+
: type === 'team'
|
|
327
|
+
? '👥'
|
|
328
|
+
: type === 'query'
|
|
329
|
+
? '🔍'
|
|
330
|
+
: '❓';
|
|
331
|
+
console.log(`${icon} ${chalk.green(type.padEnd(12))} ${chalk.gray(generator.description)}`);
|
|
332
|
+
if (options.detailed) {
|
|
333
|
+
const examples = {
|
|
334
|
+
project: 'ark g project my-ai-project',
|
|
335
|
+
agent: 'ark g agent customer-support',
|
|
336
|
+
team: 'ark g team research-team',
|
|
337
|
+
query: 'ark g query test-conversation',
|
|
338
|
+
};
|
|
339
|
+
console.log(chalk.gray(` Example: ${examples[type] || `ark g ${type} my-${type}`}\n`));
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
if (!options.detailed) {
|
|
343
|
+
console.log(chalk.gray('\n💡 Use --detailed for examples and more information'));
|
|
344
|
+
}
|
|
345
|
+
console.log(chalk.cyan('\n📖 Quick Start:'));
|
|
346
|
+
console.log(chalk.gray(' 1. ark generate project my-first-project'));
|
|
347
|
+
console.log(chalk.gray(' 2. cd my-first-project && source .env'));
|
|
348
|
+
console.log(chalk.gray(' 3. make quickstart'));
|
|
349
|
+
console.log(chalk.cyan('\n🔧 Usage:'));
|
|
350
|
+
console.log(chalk.gray(' ark generate <type> [name] [options]'));
|
|
351
|
+
console.log(chalk.gray(' ark g <type> [name] [options]'));
|
|
352
|
+
console.log(chalk.gray(' ark generate <type> --help'));
|
|
353
|
+
console.log();
|
|
354
|
+
});
|
|
355
|
+
generate.addCommand(listCommand);
|
|
356
|
+
return generate;
|
|
357
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export interface TemplateInfo {
|
|
2
|
+
name: string;
|
|
3
|
+
path: string;
|
|
4
|
+
description?: string;
|
|
5
|
+
type: 'project' | 'tool' | 'component';
|
|
6
|
+
}
|
|
7
|
+
export declare class TemplateDiscovery {
|
|
8
|
+
private templatesPath;
|
|
9
|
+
constructor();
|
|
10
|
+
/**
|
|
11
|
+
* Discover all available templates in the templates directory
|
|
12
|
+
*/
|
|
13
|
+
discoverTemplates(): Promise<TemplateInfo[]>;
|
|
14
|
+
/**
|
|
15
|
+
* Get the absolute path to a template
|
|
16
|
+
*/
|
|
17
|
+
getTemplatePath(templateName: string): string;
|
|
18
|
+
/**
|
|
19
|
+
* Check if a template exists
|
|
20
|
+
*/
|
|
21
|
+
templateExists(templateName: string): boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Analyze a template directory to extract metadata
|
|
24
|
+
*/
|
|
25
|
+
private analyzeTemplate;
|
|
26
|
+
/**
|
|
27
|
+
* Check if a template directory contains a specific file
|
|
28
|
+
*/
|
|
29
|
+
private hasFile;
|
|
30
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
export class TemplateDiscovery {
|
|
5
|
+
constructor() {
|
|
6
|
+
// Get the path to the templates directory relative to the ark CLI
|
|
7
|
+
// Navigate from tools/ark-cli/src/commands/generate/templateDiscovery.js to agents-at-scale/templates
|
|
8
|
+
const currentFile = fileURLToPath(import.meta.url);
|
|
9
|
+
const arkRoot = path.resolve(path.dirname(currentFile), '../../../../../');
|
|
10
|
+
this.templatesPath = path.join(arkRoot, 'templates');
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Discover all available templates in the templates directory
|
|
14
|
+
*/
|
|
15
|
+
async discoverTemplates() {
|
|
16
|
+
const templates = [];
|
|
17
|
+
try {
|
|
18
|
+
if (!fs.existsSync(this.templatesPath)) {
|
|
19
|
+
console.warn(`Templates directory not found at: ${this.templatesPath}`);
|
|
20
|
+
return templates;
|
|
21
|
+
}
|
|
22
|
+
const entries = fs.readdirSync(this.templatesPath, {
|
|
23
|
+
withFileTypes: true,
|
|
24
|
+
});
|
|
25
|
+
for (const entry of entries) {
|
|
26
|
+
if (entry.isDirectory()) {
|
|
27
|
+
const templatePath = path.join(this.templatesPath, entry.name);
|
|
28
|
+
const templateInfo = await this.analyzeTemplate(entry.name, templatePath);
|
|
29
|
+
if (templateInfo) {
|
|
30
|
+
templates.push(templateInfo);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
console.warn(`Failed to discover templates: ${error}`);
|
|
37
|
+
}
|
|
38
|
+
return templates;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Get the absolute path to a template
|
|
42
|
+
*/
|
|
43
|
+
getTemplatePath(templateName) {
|
|
44
|
+
return path.join(this.templatesPath, templateName);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Check if a template exists
|
|
48
|
+
*/
|
|
49
|
+
templateExists(templateName) {
|
|
50
|
+
const templatePath = this.getTemplatePath(templateName);
|
|
51
|
+
return (fs.existsSync(templatePath) && fs.statSync(templatePath).isDirectory());
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Analyze a template directory to extract metadata
|
|
55
|
+
*/
|
|
56
|
+
async analyzeTemplate(name, templatePath) {
|
|
57
|
+
try {
|
|
58
|
+
let description = '';
|
|
59
|
+
let type = 'component';
|
|
60
|
+
// Try to read description from README.md
|
|
61
|
+
const readmePath = path.join(templatePath, 'README.md');
|
|
62
|
+
if (fs.existsSync(readmePath)) {
|
|
63
|
+
const readmeContent = fs.readFileSync(readmePath, 'utf-8');
|
|
64
|
+
// Extract first line as description (remove # if present)
|
|
65
|
+
const firstLine = readmeContent.split('\n')[0];
|
|
66
|
+
description = firstLine.replace(/^#\s*/, '').trim();
|
|
67
|
+
}
|
|
68
|
+
// Determine template type based on content
|
|
69
|
+
if (this.hasFile(templatePath, 'Chart.yaml')) {
|
|
70
|
+
type = 'project';
|
|
71
|
+
}
|
|
72
|
+
else if (this.hasFile(templatePath, 'pyproject.toml') ||
|
|
73
|
+
this.hasFile(templatePath, 'main.py')) {
|
|
74
|
+
type = 'tool';
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
name,
|
|
78
|
+
path: templatePath,
|
|
79
|
+
description,
|
|
80
|
+
type,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
console.warn(`Failed to analyze template ${name}: ${error}`);
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Check if a template directory contains a specific file
|
|
90
|
+
*/
|
|
91
|
+
hasFile(templatePath, fileName) {
|
|
92
|
+
return fs.existsSync(path.join(templatePath, fileName));
|
|
93
|
+
}
|
|
94
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
export interface TemplateVariables {
|
|
2
|
+
[key: string]: string | number | boolean;
|
|
3
|
+
}
|
|
4
|
+
export interface TemplateOptions {
|
|
5
|
+
skipIfExists?: boolean;
|
|
6
|
+
createDirectories?: boolean;
|
|
7
|
+
exclude?: string[];
|
|
8
|
+
include?: string[];
|
|
9
|
+
}
|
|
10
|
+
export declare class TemplateEngine {
|
|
11
|
+
private variables;
|
|
12
|
+
/**
|
|
13
|
+
* Set template variables for substitution
|
|
14
|
+
*/
|
|
15
|
+
setVariables(variables: TemplateVariables): void;
|
|
16
|
+
/**
|
|
17
|
+
* Get current template variables
|
|
18
|
+
*/
|
|
19
|
+
getVariables(): TemplateVariables;
|
|
20
|
+
/**
|
|
21
|
+
* Process a template directory and copy it to destination
|
|
22
|
+
*/
|
|
23
|
+
processTemplate(templatePath: string, destinationPath: string, options?: TemplateOptions): Promise<void>;
|
|
24
|
+
/**
|
|
25
|
+
* Process a single template file
|
|
26
|
+
*/
|
|
27
|
+
processFile(templateFilePath: string, destinationFilePath: string, options?: {
|
|
28
|
+
skipIfExists?: boolean;
|
|
29
|
+
baseDir?: string;
|
|
30
|
+
}): Promise<void>;
|
|
31
|
+
/**
|
|
32
|
+
* Substitute template variables in content
|
|
33
|
+
*/
|
|
34
|
+
private substituteVariables;
|
|
35
|
+
/**
|
|
36
|
+
* Substitute variables in file/directory names
|
|
37
|
+
*/
|
|
38
|
+
private substituteVariablesInPath;
|
|
39
|
+
/**
|
|
40
|
+
* Derive destination filename from template name
|
|
41
|
+
*/
|
|
42
|
+
private deriveDestinationFilename;
|
|
43
|
+
/**
|
|
44
|
+
* Copy directory recursively with template processing
|
|
45
|
+
*/
|
|
46
|
+
private copyDirectory;
|
|
47
|
+
private shouldProcessEntry;
|
|
48
|
+
private processDirectory;
|
|
49
|
+
private processFileEntry;
|
|
50
|
+
/**
|
|
51
|
+
* Ensure a directory exists, creating it if necessary
|
|
52
|
+
*/
|
|
53
|
+
private ensureDirectory;
|
|
54
|
+
/**
|
|
55
|
+
* Safely ensure a directory exists with security validation
|
|
56
|
+
*/
|
|
57
|
+
private ensureDirectorySafe;
|
|
58
|
+
/**
|
|
59
|
+
* Check if a file should be excluded
|
|
60
|
+
*/
|
|
61
|
+
private shouldExclude;
|
|
62
|
+
/**
|
|
63
|
+
* Check if a file should be included
|
|
64
|
+
*/
|
|
65
|
+
private shouldInclude;
|
|
66
|
+
/**
|
|
67
|
+
* Check if a file is a text file that should have variable substitution
|
|
68
|
+
*/
|
|
69
|
+
private isTextFile;
|
|
70
|
+
/**
|
|
71
|
+
* Check if a file path represents important generated content
|
|
72
|
+
*/
|
|
73
|
+
private isImportantContent;
|
|
74
|
+
/**
|
|
75
|
+
* Get relative path from a full file path for display
|
|
76
|
+
*/
|
|
77
|
+
private getRelativePath;
|
|
78
|
+
}
|