agents.dev 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +71 -0
- package/definitions/backend-architect.yaml +12 -0
- package/package.json +30 -0
- package/src/index.ts +140 -0
- package/src/transformers/gemini.ts +31 -0
- package/src/transformers/kilo.ts +21 -0
- package/src/transformers/opencode.ts +21 -0
- package/src/transformers/roo.ts +23 -0
- package/src/types.ts +12 -0
- package/test-inquirer.ts +19 -0
- package/tsconfig.json +15 -0
package/README.md
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Agents.dev
|
|
2
|
+
|
|
3
|
+
**Agents.dev** is a CLI tool that acts as a "Source of Truth" for your AI Development Agents.
|
|
4
|
+
Inspired by tools like *Specify*, it allows you to define your agent personas in a single, agnostic YAML format and "compile" them into configuration files for various AI coding assistants.
|
|
5
|
+
|
|
6
|
+
## 🚀 Supported Targets
|
|
7
|
+
|
|
8
|
+
The CLI currently installs agents directly into the configuration paths for:
|
|
9
|
+
|
|
10
|
+
- **Gemini CLI**: `.gemini/commands/dev/`
|
|
11
|
+
- **Roo Code**: `.roo/commands/`
|
|
12
|
+
- **Kilo Code**: `.kilocode/workflows/`
|
|
13
|
+
- **OpenCode**: `.opencode/command/`
|
|
14
|
+
|
|
15
|
+
## 📦 Installation
|
|
16
|
+
|
|
17
|
+
This project is designed to be run as a dev dependency or a local tool within your repository.
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## 🛠️ Usage
|
|
24
|
+
|
|
25
|
+
### 1. Define your Agents
|
|
26
|
+
Create YAML files in the `definitions/` directory.
|
|
27
|
+
|
|
28
|
+
**Example:** `definitions/backend-architect.yaml`
|
|
29
|
+
```yaml
|
|
30
|
+
name: Backend Architect
|
|
31
|
+
role: Senior Backend Engineer
|
|
32
|
+
emoji: 🏗️
|
|
33
|
+
systemPrompt: |
|
|
34
|
+
You are a Senior Backend Engineer.
|
|
35
|
+
You specialize in Scalable Node.js APIs.
|
|
36
|
+
rules:
|
|
37
|
+
- Always write unit tests.
|
|
38
|
+
- Use snake_case for database columns.
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### 2. Build/Install
|
|
42
|
+
Run the start command to convert your YAMLs into target configurations.
|
|
43
|
+
|
|
44
|
+
**Interactive Mode:**
|
|
45
|
+
```bash
|
|
46
|
+
npm start
|
|
47
|
+
```
|
|
48
|
+
*Select which targets you want to update using the interactive menu.*
|
|
49
|
+
|
|
50
|
+
**CLI Mode (CI/CD friendly):**
|
|
51
|
+
```bash
|
|
52
|
+
npm start -- --target gemini,roo
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## ✅ Validation
|
|
56
|
+
The CLI includes Zod validation to ensure your definitions are complete.
|
|
57
|
+
Required fields:
|
|
58
|
+
- `name`
|
|
59
|
+
- `role`
|
|
60
|
+
- `systemPrompt` (min 10 characters)
|
|
61
|
+
|
|
62
|
+
## 📂 Project Structure
|
|
63
|
+
```text
|
|
64
|
+
.
|
|
65
|
+
├── definitions/ # Your Source of Truth (YAML)
|
|
66
|
+
├── src/ # CLI Source Code
|
|
67
|
+
├── .gemini/ # Generated Gemini Commands
|
|
68
|
+
├── .roo/ # Generated Roo Contexts
|
|
69
|
+
├── .kilocode/ # Generated Kilo Workflows
|
|
70
|
+
└── .opencode/ # Generated OpenCode Commands
|
|
71
|
+
```
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
name: Backend Architect
|
|
2
|
+
role: Senior Backend Engineer specializes in scalable APIs
|
|
3
|
+
emoji: 🏗️
|
|
4
|
+
systemPrompt: |
|
|
5
|
+
You are a Senior Backend Engineer. Your goal is to design and implement scalable, robust, and secure backend systems.
|
|
6
|
+
You prefer Node.js with TypeScript or Python with FastAPI.
|
|
7
|
+
You always consider performance implications.
|
|
8
|
+
rules:
|
|
9
|
+
- Follow RESTful API design principles.
|
|
10
|
+
- Use environment variables for secrets.
|
|
11
|
+
- Write unit tests for all business logic.
|
|
12
|
+
- Document API endpoints.
|
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "agents.dev",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
|
8
|
+
"start": "npx ts-node src/index.ts build"
|
|
9
|
+
},
|
|
10
|
+
"keywords": [],
|
|
11
|
+
"author": "",
|
|
12
|
+
"license": "ISC",
|
|
13
|
+
"type": "commonjs",
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"@types/inquirer": "^9.0.9",
|
|
16
|
+
"chalk": "^5.6.2",
|
|
17
|
+
"commander": "^14.0.2",
|
|
18
|
+
"fs-extra": "^11.3.2",
|
|
19
|
+
"inquirer": "^8.2.7",
|
|
20
|
+
"user": "^0.0.0",
|
|
21
|
+
"yaml": "^2.8.2",
|
|
22
|
+
"zod": "^4.1.13"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@types/fs-extra": "^11.0.4",
|
|
26
|
+
"@types/node": "^25.0.1",
|
|
27
|
+
"ts-node": "^10.9.2",
|
|
28
|
+
"typescript": "^5.9.3"
|
|
29
|
+
}
|
|
30
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import fs from 'fs-extra';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import YAML from 'yaml';
|
|
5
|
+
import chalk from 'chalk';
|
|
6
|
+
import inquirer from 'inquirer';
|
|
7
|
+
import { AgentDefinition, AgentSchema } from './types';
|
|
8
|
+
import { toGeminiSystemPrompt } from './transformers/gemini';
|
|
9
|
+
import { toRooRules } from './transformers/roo';
|
|
10
|
+
import { toKiloConfig } from './transformers/kilo';
|
|
11
|
+
import { toOpenCodeCommand } from './transformers/opencode';
|
|
12
|
+
|
|
13
|
+
const program = new Command();
|
|
14
|
+
|
|
15
|
+
program
|
|
16
|
+
.name('agents')
|
|
17
|
+
.description('Agent Installer CLI')
|
|
18
|
+
.version('1.0.0');
|
|
19
|
+
|
|
20
|
+
program
|
|
21
|
+
.command('build')
|
|
22
|
+
.description('Build agent definitions into target formats')
|
|
23
|
+
.option('-o, --out <dir>', 'Output directory', '.')
|
|
24
|
+
.option('-t, --target <targets>', 'Comma-separated target formats (gemini, roo, kilo, opencode)')
|
|
25
|
+
.action(async (options) => {
|
|
26
|
+
const definitionsDir = path.join(process.cwd(), 'definitions');
|
|
27
|
+
const outDir = path.resolve(options.out);
|
|
28
|
+
|
|
29
|
+
// Determine targets
|
|
30
|
+
let selectedTargets: string[] = [];
|
|
31
|
+
if (options.target) {
|
|
32
|
+
selectedTargets = options.target.split(',').map((t: string) => t.trim().toLowerCase());
|
|
33
|
+
} else {
|
|
34
|
+
const answers = await inquirer.prompt([
|
|
35
|
+
{
|
|
36
|
+
type: 'checkbox',
|
|
37
|
+
name: 'targets',
|
|
38
|
+
message: 'Select target formats to build:',
|
|
39
|
+
choices: [
|
|
40
|
+
{ name: 'Gemini CLI', value: 'gemini', checked: true },
|
|
41
|
+
{ name: 'Roo Code', value: 'roo', checked: false },
|
|
42
|
+
{ name: 'Kilo Code', value: 'kilo', checked: false },
|
|
43
|
+
{ name: 'OpenCode', value: 'opencode', checked: false }
|
|
44
|
+
],
|
|
45
|
+
validate: (answer) => {
|
|
46
|
+
if (answer.length < 1) {
|
|
47
|
+
return 'You must choose at least one target.';
|
|
48
|
+
}
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
]);
|
|
53
|
+
selectedTargets = answers.targets;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (selectedTargets.length === 0) {
|
|
57
|
+
console.log(chalk.yellow('No targets selected. Exiting.'));
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
console.log(chalk.blue(`Building agents from ${definitionsDir} to ${outDir}...`));
|
|
62
|
+
console.log(chalk.blue(`Targets: ${selectedTargets.join(', ')}`));
|
|
63
|
+
|
|
64
|
+
try {
|
|
65
|
+
if (outDir !== process.cwd()) {
|
|
66
|
+
await fs.ensureDir(outDir);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const files = await fs.readdir(definitionsDir);
|
|
70
|
+
|
|
71
|
+
for (const file of files) {
|
|
72
|
+
if (!file.endsWith('.yaml') && !file.endsWith('.yml')) continue;
|
|
73
|
+
|
|
74
|
+
const content = await fs.readFile(path.join(definitionsDir, file), 'utf-8');
|
|
75
|
+
const rawAgent = YAML.parse(content);
|
|
76
|
+
const parseResult = AgentSchema.safeParse(rawAgent);
|
|
77
|
+
|
|
78
|
+
if (!parseResult.success) {
|
|
79
|
+
console.error(chalk.red(`Validation failed for ${file}:`));
|
|
80
|
+
parseResult.error.issues.forEach((err) => {
|
|
81
|
+
console.error(chalk.red(` - ${err.path.join('.')}: ${err.message}`));
|
|
82
|
+
});
|
|
83
|
+
continue; // Skip this agent
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const agent = parseResult.data;
|
|
87
|
+
const slug = file.replace(/\.(yaml|yml)$/, '');
|
|
88
|
+
|
|
89
|
+
console.log(chalk.green(`Processing agent: ${agent.name} (${slug})`));
|
|
90
|
+
|
|
91
|
+
// specific outputs
|
|
92
|
+
const allTargets = [
|
|
93
|
+
{
|
|
94
|
+
id: 'gemini',
|
|
95
|
+
subDir: '.gemini/commands/dev',
|
|
96
|
+
ext: 'md',
|
|
97
|
+
content: toGeminiSystemPrompt(agent),
|
|
98
|
+
name: `${slug}.md`
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
id: 'roo',
|
|
102
|
+
subDir: '.roo/commands',
|
|
103
|
+
ext: 'md',
|
|
104
|
+
content: toRooRules(agent),
|
|
105
|
+
name: `${slug}.md`
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
id: 'kilo',
|
|
109
|
+
subDir: '.kilocode/workflows',
|
|
110
|
+
ext: 'md',
|
|
111
|
+
content: toKiloConfig(agent),
|
|
112
|
+
name: `${slug}.md`
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
id: 'opencode',
|
|
116
|
+
subDir: '.opencode/command',
|
|
117
|
+
ext: 'md',
|
|
118
|
+
content: toOpenCodeCommand(agent),
|
|
119
|
+
name: `${slug}.md`
|
|
120
|
+
}
|
|
121
|
+
];
|
|
122
|
+
|
|
123
|
+
const targetsToBuild = allTargets.filter(t => selectedTargets.includes(t.id));
|
|
124
|
+
|
|
125
|
+
for (const target of targetsToBuild) {
|
|
126
|
+
const targetDir = path.join(outDir, target.subDir);
|
|
127
|
+
await fs.ensureDir(targetDir);
|
|
128
|
+
await fs.writeFile(path.join(targetDir, target.name), target.content);
|
|
129
|
+
console.log(chalk.gray(` -> ${target.subDir}/${target.name}`));
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
console.log(chalk.bold.green('Build complete!'));
|
|
133
|
+
|
|
134
|
+
} catch (err) {
|
|
135
|
+
console.error(chalk.red('Build failed:'), err);
|
|
136
|
+
process.exit(1);
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
program.parse(process.argv);
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { AgentDefinition } from '../types';
|
|
2
|
+
|
|
3
|
+
export function toGeminiSystemPrompt(agent: AgentDefinition): string {
|
|
4
|
+
const parts = [];
|
|
5
|
+
|
|
6
|
+
// Identity
|
|
7
|
+
parts.push(`# Identity\n`);
|
|
8
|
+
parts.push(`You are **${agent.name}** ${agent.emoji || ''}`);
|
|
9
|
+
parts.push(`Role: ${agent.role}\n`);
|
|
10
|
+
|
|
11
|
+
// System Prompt
|
|
12
|
+
parts.push(`# Core Instructions\n`);
|
|
13
|
+
parts.push(agent.systemPrompt.trim());
|
|
14
|
+
parts.push('\n');
|
|
15
|
+
|
|
16
|
+
// Rules
|
|
17
|
+
if (agent.rules && agent.rules.length > 0) {
|
|
18
|
+
parts.push(`# Rules & Guidelines\n`);
|
|
19
|
+
agent.rules.forEach(rule => parts.push(`- ${rule}`));
|
|
20
|
+
parts.push('\n');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Tools (if any)
|
|
24
|
+
if (agent.tools && agent.tools.length > 0) {
|
|
25
|
+
parts.push(`# Preferred Tools\n`);
|
|
26
|
+
agent.tools.forEach(tool => parts.push(`- ${tool}`));
|
|
27
|
+
parts.push('\n');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return parts.join('\n');
|
|
31
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { AgentDefinition } from '../types';
|
|
2
|
+
|
|
3
|
+
export function toKiloConfig(agent: AgentDefinition): string {
|
|
4
|
+
// Kilo Code generic config/prompt
|
|
5
|
+
const parts = [];
|
|
6
|
+
|
|
7
|
+
parts.push(`<!--- Kilo Code Agent Config --->`);
|
|
8
|
+
parts.push(`# ${agent.name} ${agent.emoji || ''}`);
|
|
9
|
+
parts.push(`**Role**: ${agent.role}\n`);
|
|
10
|
+
|
|
11
|
+
parts.push(`## Instructions`);
|
|
12
|
+
parts.push(agent.systemPrompt.trim());
|
|
13
|
+
parts.push('\n');
|
|
14
|
+
|
|
15
|
+
if (agent.rules && agent.rules.length > 0) {
|
|
16
|
+
parts.push(`## Constraints`);
|
|
17
|
+
agent.rules.forEach(rule => parts.push(`- ${rule}`));
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return parts.join('\n');
|
|
21
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { AgentDefinition } from '../types';
|
|
2
|
+
|
|
3
|
+
export function toOpenCodeCommand(agent: AgentDefinition): string {
|
|
4
|
+
// OpenCode generic config/prompt
|
|
5
|
+
const parts = [];
|
|
6
|
+
|
|
7
|
+
parts.push(`<!--- OpenCode Agent Config --->`);
|
|
8
|
+
parts.push(`# ${agent.name} ${agent.emoji || ''}`);
|
|
9
|
+
parts.push(`**Role**: ${agent.role}\n`);
|
|
10
|
+
|
|
11
|
+
parts.push(`## Instructions`);
|
|
12
|
+
parts.push(agent.systemPrompt.trim());
|
|
13
|
+
parts.push('\n');
|
|
14
|
+
|
|
15
|
+
if (agent.rules && agent.rules.length > 0) {
|
|
16
|
+
parts.push(`## Constraints`);
|
|
17
|
+
agent.rules.forEach(rule => parts.push(`- ${rule}`));
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return parts.join('\n');
|
|
21
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { AgentDefinition } from '../types';
|
|
2
|
+
|
|
3
|
+
export function toRooRules(agent: AgentDefinition): string {
|
|
4
|
+
// Roo Code / Cline usually expects a clear set of rules and role definition.
|
|
5
|
+
const parts = [];
|
|
6
|
+
|
|
7
|
+
parts.push(`# ${agent.name} (${agent.role})`);
|
|
8
|
+
parts.push(`\n${agent.systemPrompt.trim()}\n`);
|
|
9
|
+
|
|
10
|
+
if (agent.rules && agent.rules.length > 0) {
|
|
11
|
+
parts.push(`## Analytical Rules & Guidelines`);
|
|
12
|
+
agent.rules.forEach(rule => parts.push(`- ${rule}`));
|
|
13
|
+
parts.push('\n');
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Tools might be handled differently in Roo, but listing them as preferences is safe
|
|
17
|
+
if (agent.tools && agent.tools.length > 0) {
|
|
18
|
+
parts.push(`## Tool Usage Preferences`);
|
|
19
|
+
agent.tools.forEach(tool => parts.push(`- Prefer using ${tool} when applicable.`));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return parts.join('\n');
|
|
23
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
export const AgentSchema = z.object({
|
|
4
|
+
name: z.string().min(1, "Name is required"),
|
|
5
|
+
role: z.string().min(1, "Role is required"),
|
|
6
|
+
emoji: z.string().optional(),
|
|
7
|
+
systemPrompt: z.string().min(10, "System prompt must be at least 10 characters"),
|
|
8
|
+
rules: z.array(z.string()).optional(),
|
|
9
|
+
tools: z.array(z.string()).optional()
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
export type AgentDefinition = z.infer<typeof AgentSchema>;
|
package/test-inquirer.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import inquirer from 'inquirer';
|
|
2
|
+
|
|
3
|
+
(async () => {
|
|
4
|
+
console.log('Testing inquirer checkbox...');
|
|
5
|
+
const answers = await inquirer.prompt([
|
|
6
|
+
{
|
|
7
|
+
type: 'checkbox',
|
|
8
|
+
name: 'targets',
|
|
9
|
+
message: 'Select target formats:',
|
|
10
|
+
choices: [
|
|
11
|
+
{ name: 'Gemini CLI', value: 'gemini', checked: true },
|
|
12
|
+
{ name: 'Roo Code', value: 'roo', checked: true },
|
|
13
|
+
{ name: 'Kilo Code', value: 'kilo', checked: true },
|
|
14
|
+
{ name: 'OpenCode', value: 'opencode', checked: true }
|
|
15
|
+
]
|
|
16
|
+
}
|
|
17
|
+
]);
|
|
18
|
+
console.log('Selected:', answers.targets);
|
|
19
|
+
})();
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "es2020",
|
|
4
|
+
"module": "commonjs",
|
|
5
|
+
"strict": true,
|
|
6
|
+
"esModuleInterop": true,
|
|
7
|
+
"skipLibCheck": true,
|
|
8
|
+
"forceConsistentCasingInFileNames": true,
|
|
9
|
+
"outDir": "./dist",
|
|
10
|
+
"rootDir": "./src",
|
|
11
|
+
"resolveJsonModule": true
|
|
12
|
+
},
|
|
13
|
+
"include": ["src/**/*"],
|
|
14
|
+
"exclude": ["node_modules"]
|
|
15
|
+
}
|