agents.dev 1.0.1 → 1.0.3

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.
@@ -13,65 +13,66 @@ const gemini_1 = require("../transformers/gemini");
13
13
  const roo_1 = require("../transformers/roo");
14
14
  const kilo_1 = require("../transformers/kilo");
15
15
  const opencode_1 = require("../transformers/opencode");
16
+ const markdown_1 = require("../parsers/markdown");
16
17
  async function generateAgents(definitionsDir, outDir, selectedTargets) {
17
18
  console.log(chalk_1.default.blue(`\nšŸ“¦ Building agents from ${definitionsDir}...`));
18
19
  try {
19
20
  if (outDir !== process.cwd()) {
20
21
  await fs_extra_1.default.ensureDir(outDir);
21
22
  }
22
- const files = await fs_extra_1.default.readdir(definitionsDir);
23
+ // Check if input exists
24
+ if (!await fs_extra_1.default.pathExists(definitionsDir)) {
25
+ console.error(chalk_1.default.red(`Input not found: ${definitionsDir}`));
26
+ return;
27
+ }
28
+ let files = [];
29
+ let baseDir = definitionsDir;
30
+ const stats = await fs_extra_1.default.stat(definitionsDir);
31
+ if (stats.isFile()) {
32
+ baseDir = path_1.default.dirname(definitionsDir);
33
+ files = [path_1.default.basename(definitionsDir)];
34
+ }
35
+ else {
36
+ files = await fs_extra_1.default.readdir(definitionsDir);
37
+ }
23
38
  for (const file of files) {
24
- if (!file.endsWith('.yaml') && !file.endsWith('.yml'))
25
- continue;
26
- const content = await fs_extra_1.default.readFile(path_1.default.join(definitionsDir, file), 'utf-8');
27
- const rawAgent = yaml_1.default.parse(content);
28
- const parseResult = types_1.AgentSchema.safeParse(rawAgent);
29
- if (!parseResult.success) {
30
- console.error(chalk_1.default.red(`āŒ Validation failed for ${file}:`));
31
- parseResult.error.issues.forEach((err) => {
32
- console.error(chalk_1.default.red(` - ${err.path.join('.')}: ${err.message}`));
33
- });
34
- continue; // Skip this agent
39
+ const filePath = path_1.default.join(baseDir, file);
40
+ // 1. Handle YAML
41
+ if (file.endsWith('.yaml') || file.endsWith('.yml')) {
42
+ const content = await fs_extra_1.default.readFile(filePath, 'utf-8');
43
+ try {
44
+ const rawAgent = yaml_1.default.parse(content);
45
+ const parseResult = types_1.AgentSchema.safeParse(rawAgent);
46
+ if (!parseResult.success) {
47
+ logValidationErrors(file, parseResult.error);
48
+ continue;
49
+ }
50
+ // Use filename as slug for YAML (backward compatibility)
51
+ const slug = file.replace(/\.(yaml|yml)$/, '');
52
+ await buildAgentArtifacts(parseResult.data, slug, outDir, selectedTargets);
53
+ }
54
+ catch (e) {
55
+ console.error(chalk_1.default.red(`Error parsing YAML ${file}: ${e.message}`));
56
+ }
35
57
  }
36
- const agent = parseResult.data;
37
- const slug = file.replace(/\.(yaml|yml)$/, '');
38
- // specific outputs
39
- const allTargets = [
40
- {
41
- id: 'gemini',
42
- subDir: '.gemini/commands',
43
- ext: 'toml',
44
- content: (0, gemini_1.toGeminiSystemPrompt)(agent),
45
- name: `${slug}.toml`
46
- },
47
- {
48
- id: 'roo',
49
- subDir: '.roo/commands',
50
- ext: 'md',
51
- content: (0, roo_1.toRooRules)(agent),
52
- name: `${slug}.md`
53
- },
54
- {
55
- id: 'kilo',
56
- subDir: '.kilocode/workflows',
57
- ext: 'md',
58
- content: (0, kilo_1.toKiloConfig)(agent),
59
- name: `${slug}.md`
60
- },
61
- {
62
- id: 'opencode',
63
- subDir: '.opencode/command',
64
- ext: 'md',
65
- content: (0, opencode_1.toOpenCodeCommand)(agent),
66
- name: `${slug}.md`
58
+ // 2. Handle Markdown
59
+ else if (file.endsWith('.md')) {
60
+ const content = await fs_extra_1.default.readFile(filePath, 'utf-8');
61
+ const agents = (0, markdown_1.parseMarkdownAgents)(content);
62
+ if (agents.length === 0) {
63
+ console.warn(chalk_1.default.yellow(`āš ļø No agents found in ${file}`));
64
+ continue;
65
+ }
66
+ for (const agent of agents) {
67
+ const parseResult = types_1.AgentSchema.safeParse(agent);
68
+ if (!parseResult.success) {
69
+ logValidationErrors(`${file} -> ${agent.name}`, parseResult.error);
70
+ continue;
71
+ }
72
+ // Use agent name as slug for Markdown
73
+ const slug = toSlug(agent.name);
74
+ await buildAgentArtifacts(agent, slug, outDir, selectedTargets);
67
75
  }
68
- ];
69
- const targetsToBuild = allTargets.filter(t => selectedTargets.includes(t.id));
70
- for (const target of targetsToBuild) {
71
- const targetDir = path_1.default.join(outDir, target.subDir);
72
- await fs_extra_1.default.ensureDir(targetDir);
73
- await fs_extra_1.default.writeFile(path_1.default.join(targetDir, target.name), target.content);
74
- console.log(chalk_1.default.gray(` -> ${target.subDir}/${target.name}`));
75
76
  }
76
77
  }
77
78
  console.log(chalk_1.default.bold.green('\nšŸŽ‰ Build complete! Agents are ready to use.'));
@@ -81,3 +82,54 @@ async function generateAgents(definitionsDir, outDir, selectedTargets) {
81
82
  process.exit(1);
82
83
  }
83
84
  }
85
+ async function buildAgentArtifacts(agent, slug, outDir, selectedTargets) {
86
+ const allTargets = [
87
+ {
88
+ id: 'gemini',
89
+ subDir: '.gemini/commands',
90
+ ext: 'toml',
91
+ content: (0, gemini_1.toGeminiSystemPrompt)(agent),
92
+ name: `${slug}.toml`
93
+ },
94
+ {
95
+ id: 'roo',
96
+ subDir: '.roo/commands',
97
+ ext: 'md',
98
+ content: (0, roo_1.toRooRules)(agent),
99
+ name: `${slug}.md`
100
+ },
101
+ {
102
+ id: 'kilo',
103
+ subDir: '.kilocode/workflows',
104
+ ext: 'md',
105
+ content: (0, kilo_1.toKiloConfig)(agent),
106
+ name: `${slug}.md`
107
+ },
108
+ {
109
+ id: 'opencode',
110
+ subDir: '.opencode/command',
111
+ ext: 'md',
112
+ content: (0, opencode_1.toOpenCodeCommand)(agent),
113
+ name: `${slug}.md`
114
+ }
115
+ ];
116
+ const targetsToBuild = allTargets.filter(t => selectedTargets.includes(t.id));
117
+ for (const target of targetsToBuild) {
118
+ const targetDir = path_1.default.join(outDir, target.subDir);
119
+ await fs_extra_1.default.ensureDir(targetDir);
120
+ await fs_extra_1.default.writeFile(path_1.default.join(targetDir, target.name), target.content);
121
+ console.log(chalk_1.default.gray(` -> ${target.subDir}/${target.name}`));
122
+ }
123
+ }
124
+ function logValidationErrors(source, error) {
125
+ console.error(chalk_1.default.red(`āŒ Validation failed for ${source}:`));
126
+ error.issues.forEach((err) => {
127
+ console.error(chalk_1.default.red(` - ${err.path.join('.')}: ${err.message}`));
128
+ });
129
+ }
130
+ function toSlug(name) {
131
+ return name.toLowerCase()
132
+ .replace(/[^\w\s-]/g, '') // remove non-word chars
133
+ .replace(/[\s_-]+/g, '-') // collapse whitespace/underscores to hyphens
134
+ .replace(/^-+|-+$/g, ''); // trim hyphens
135
+ }
package/dist/index.js CHANGED
@@ -17,6 +17,7 @@ program
17
17
  .description('Agent Installer CLI')
18
18
  .version('1.0.0')
19
19
  .option('-o, --out <dir>', 'Output directory', '.')
20
+ .option('-i, --input <path>', 'Input file or directory (default: definitions/ or agents.md)')
20
21
  .option('-t, --target <targets>', 'Comma-separated target formats (gemini, roo, kilo, opencode)')
21
22
  .action(async (options) => {
22
23
  console.log(chalk_1.default.bold.blue('\nšŸš€ Agents.dev Installer Wizard\n'));
@@ -42,7 +43,15 @@ program
42
43
  console.log(chalk_1.default.gray('āœ… "docs/" directory already exists.\n'));
43
44
  }
44
45
  // --- STEP 2: BUILD AGENTS ---
45
- const definitionsDir = path_1.default.join(process.cwd(), 'definitions');
46
+ let inputPath = options.input ? path_1.default.resolve(options.input) : path_1.default.join(process.cwd(), 'definitions');
47
+ // If default definitions dir doesn't exist and no input specified, try agents.md
48
+ if (!options.input && !await fs_extra_1.default.pathExists(inputPath)) {
49
+ const localAgentsMd = path_1.default.join(process.cwd(), 'agents.md');
50
+ if (await fs_extra_1.default.pathExists(localAgentsMd)) {
51
+ inputPath = localAgentsMd;
52
+ console.log(chalk_1.default.gray(`ā„¹ļø Using 'agents.md' found in root.`));
53
+ }
54
+ }
46
55
  const outDir = path_1.default.resolve(options.out);
47
56
  // Determine targets
48
57
  let selectedTargets = [];
@@ -75,6 +84,6 @@ program
75
84
  console.log(chalk_1.default.yellow('No targets selected. Exiting.'));
76
85
  return;
77
86
  }
78
- await (0, agents_1.generateAgents)(definitionsDir, outDir, selectedTargets);
87
+ await (0, agents_1.generateAgents)(inputPath, outDir, selectedTargets);
79
88
  });
80
89
  program.parse(process.argv);
@@ -0,0 +1,86 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseMarkdownAgents = parseMarkdownAgents;
4
+ function parseMarkdownAgents(content) {
5
+ const agents = [];
6
+ // Normalize line endings
7
+ const normalized = content.replace(/\r\n/g, '\n');
8
+ // Split by H1 headers (# Name)
9
+ // We use a lookahead to keep the delimiter or just split and ignore the first empty part if it starts with #
10
+ const parts = normalized.split(/^# /gm);
11
+ for (const part of parts) {
12
+ if (!part.trim())
13
+ continue;
14
+ const lines = part.split('\n');
15
+ const headerLine = lines[0].trim();
16
+ // Extract emoji if present at the end
17
+ // E.g. "Project Manager šŸ‘©ā€šŸ’¼" -> name: "Project Manager", emoji: "šŸ‘©ā€šŸ’¼"
18
+ // Simple emoji regex or just take the last char if it looks like an emoji?
19
+ // Let's generic split by space and check if last part is emoji-like or just treat whole as name
20
+ // A simplified approach: regex for emoji is complex, let's just grab the whole string as name for now,
21
+ // or try to extract the last non-word character if it looks like a symbol.
22
+ // Spec said: "# Agent Name [Emoji]"
23
+ const emojiMatch = headerLine.match(/^(.*?)\s+([^\w\s\d]+)$/);
24
+ let name = headerLine;
25
+ let emoji = '';
26
+ if (emojiMatch) {
27
+ name = emojiMatch[1].trim();
28
+ emoji = emojiMatch[2].trim();
29
+ }
30
+ const remainingLines = lines.slice(1);
31
+ const sectionContent = remainingLines.join('\n');
32
+ // Parse sections
33
+ // > Role
34
+ // ## System Prompt
35
+ // ## Rules
36
+ // ## Tools
37
+ let role = '';
38
+ let systemPrompt = '';
39
+ const rules = [];
40
+ const tools = [];
41
+ // Extract Role (Blockquote)
42
+ const roleMatch = sectionContent.match(/^\s*>\s*(.+)$/m);
43
+ if (roleMatch) {
44
+ role = roleMatch[1].trim();
45
+ }
46
+ // specific sections
47
+ const sections = sectionContent.split(/^## /gm);
48
+ for (const section of sections) {
49
+ const trimmed = section.trim();
50
+ if (!trimmed)
51
+ continue;
52
+ const sectionLines = trimmed.split('\n');
53
+ const sectionTitle = sectionLines[0].trim().toLowerCase();
54
+ const sectionBody = sectionLines.slice(1).join('\n').trim();
55
+ if (sectionTitle.includes('system prompt') || sectionTitle.includes('instructions')) {
56
+ systemPrompt = sectionBody;
57
+ }
58
+ else if (sectionTitle.includes('rules')) {
59
+ // Extract bullets
60
+ const bullets = sectionBody.split('\n')
61
+ .map(l => l.trim())
62
+ .filter(l => l.startsWith('- ') || l.startsWith('* '))
63
+ .map(l => l.substring(2).trim());
64
+ rules.push(...bullets);
65
+ }
66
+ else if (sectionTitle.includes('tools')) {
67
+ const bullets = sectionBody.split('\n')
68
+ .map(l => l.trim())
69
+ .filter(l => l.startsWith('- ') || l.startsWith('* '))
70
+ .map(l => l.substring(2).trim());
71
+ tools.push(...bullets);
72
+ }
73
+ }
74
+ if (name) {
75
+ agents.push({
76
+ name,
77
+ role,
78
+ emoji,
79
+ systemPrompt,
80
+ rules,
81
+ tools
82
+ });
83
+ }
84
+ }
85
+ return agents;
86
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agents.dev",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -15,6 +15,7 @@
15
15
  "test": "echo \"Error: no test specified\" && exit 1",
16
16
  "start": "npx ts-node src/index.ts",
17
17
  "build": "tsc",
18
+ "prepack": "npm run build",
18
19
  "prepublishOnly": "npm run build"
19
20
  },
20
21
  "keywords": [