@tacuchi/agent-factory 0.2.0 → 0.4.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.
@@ -38,6 +38,8 @@ program
38
38
  .option('-s, --scope <path>', 'Repository path (for stack detection)')
39
39
  .option('-o, --output <path>', 'Output directory (default: current dir)')
40
40
  .option('-t, --target <target>', 'Target: claude, codex, all', 'all')
41
+ .option('--stack <csv>', 'Override detected stack (comma-separated)')
42
+ .option('--dry-run', 'Preview generated agent without writing files')
41
43
  .option('--tools <tools>', 'Comma-separated tools: Read,Write,Edit,Bash')
42
44
  .option('--specialists <list>', 'CSV list of specialist agent names (for coordinator role)')
43
45
  .option('--repo-count <n>', 'Number of repos in workspace (for coordinator role)', parseInt)
package/package.json CHANGED
@@ -1,45 +1 @@
1
- {
2
- "name": "@tacuchi/agent-factory",
3
- "version": "0.2.0",
4
- "description": "CLI to create AI agents for Claude Code, Codex, Gemini CLI and more",
5
- "bin": {
6
- "agent-factory": "bin/agent-factory.js"
7
- },
8
- "files": [
9
- "bin/",
10
- "src/",
11
- "templates/",
12
- "README.md",
13
- "LICENSE"
14
- ],
15
- "engines": {
16
- "node": ">=16"
17
- },
18
- "scripts": {
19
- "test": "node --test tests/",
20
- "start": "node bin/agent-factory.js"
21
- },
22
- "keywords": [
23
- "claude-code",
24
- "agents",
25
- "codex",
26
- "gemini",
27
- "ai",
28
- "cli",
29
- "agent-factory"
30
- ],
31
- "author": "tacuchi",
32
- "license": "MIT",
33
- "repository": {
34
- "type": "git",
35
- "url": "https://github.com/tacuchi/agent-factory"
36
- },
37
- "dependencies": {
38
- "chalk": "^4.1.2",
39
- "commander": "^11.1.0",
40
- "fs-extra": "^11.2.0",
41
- "inquirer": "^8.2.6",
42
- "js-yaml": "^4.1.0",
43
- "ora": "^5.4.1"
44
- }
45
- }
1
+ {"name":"@tacuchi/agent-factory","version":"0.4.0","description":"CLI to create AI agents for Claude Code, Codex, Gemini CLI and more","bin":{"agent-factory":"bin/agent-factory.js"},"files":["bin/","src/","templates/","README.md","LICENSE"],"engines":{"node":">=16"},"scripts":{"test":"node --test tests/*.test.js","start":"node bin/agent-factory.js"},"keywords":["claude-code","agents","codex","gemini","ai","cli","agent-factory"],"author":"tacuchi","license":"MIT","repository":{"type":"git","url":"https://github.com/tacuchi/agent-factory"},"dependencies":{"chalk":"^4.1.2","commander":"^11.1.0","fs-extra":"^11.2.0","inquirer":"^8.2.6","js-yaml":"^4.1.0","ora":"^5.4.1"}}
@@ -10,7 +10,7 @@ async function runCreate(options = {}) {
10
10
  const isInteractive = !options.name;
11
11
  const config = isInteractive ? await askCreateOptions() : normalizeFlags(options);
12
12
 
13
- const { name, role, model, scope, output, target, tools, specialists, repoCount, description, instructions } = config;
13
+ const { name, role, model, scope, output, target, tools, specialists, repoCount, description, instructions, stack, dryRun } = config;
14
14
 
15
15
  const spin = spinner('Generating agent...').start();
16
16
 
@@ -21,13 +21,27 @@ async function runCreate(options = {}) {
21
21
  spin.succeed('Custom agent generated');
22
22
  } else {
23
23
  let stackResult = { primaryTech: 'Generic', framework: '', verifyCommands: '', stackParts: [], stackCsv: 'Generic' };
24
- if (scope) {
24
+ if (stack) {
25
+ const parts = stack.split(',').map(s => s.trim()).filter(Boolean);
26
+ stackResult = {
27
+ primaryTech: parts[0] || 'Generic',
28
+ framework: parts[1] || '',
29
+ verifyCommands: '',
30
+ stackParts: parts,
31
+ stackCsv: parts.join(', '),
32
+ };
33
+ } else if (scope) {
25
34
  stackResult = await detect(scope);
26
35
  }
27
36
 
37
+ const techLabel = stackResult.framework
38
+ ? `${stackResult.framework} (${stackResult.primaryTech})`
39
+ : stackResult.primaryTech;
40
+
28
41
  const templateData = {
29
42
  name,
30
43
  primary_tech: stackResult.primaryTech,
44
+ tech_label: techLabel,
31
45
  framework: stackResult.framework,
32
46
  scope: scope || '.',
33
47
  stack_list: stackResult.stackParts.length > 0
@@ -38,8 +52,6 @@ async function runCreate(options = {}) {
38
52
  specialist_list: formatSpecialistList(specialists),
39
53
  N: repoCount ? String(repoCount) : '',
40
54
  repos_word: repoCount === 1 ? 'repositorio' : 'repositorios',
41
- skills_section: '',
42
- mcp_section: '',
43
55
  };
44
56
 
45
57
  const templateFile = `${role}.md.tmpl`;
@@ -58,6 +70,13 @@ async function runCreate(options = {}) {
58
70
  if (target === 'claude' || target === 'all') targetDirs.push('.claude/agents/');
59
71
  if (target === 'codex' || target === 'all') targetDirs.push('.agents/');
60
72
 
73
+ if (dryRun) {
74
+ log.info('--- DRY RUN: Agent preview ---');
75
+ console.log(body);
76
+ log.info('--- End preview ---');
77
+ return;
78
+ }
79
+
61
80
  if (!options.yes && isInteractive) {
62
81
  const confirmed = await confirmGeneration(name, targetDirs);
63
82
  if (!confirmed) {
@@ -113,6 +132,8 @@ function normalizeFlags(options) {
113
132
  repoCount: options.repoCount || 0,
114
133
  description: options.description || '',
115
134
  instructions: options.instructions || '',
135
+ stack: options.stack || '',
136
+ dryRun: options.dryRun || false,
116
137
  };
117
138
  }
118
139
 
@@ -138,4 +159,4 @@ async function resolveCustomBody(instructions, name, description) {
138
159
  return `# ${name}\n\n${instructions}\n`;
139
160
  }
140
161
 
141
- module.exports = { runCreate };
162
+ module.exports = { runCreate, normalizeFlags, formatSpecialistList };
@@ -37,7 +37,12 @@ function buildSkillsFormat(name, description, body) {
37
37
  return `---\nname: ${name}\ndescription: "${description}"\n---\n\n${body}\n`;
38
38
  }
39
39
 
40
- async function writeAgent({ name, role, model, tools, body, outputDir, target, description: customDesc }) {
40
+ function ensureAgentSuffix(name) {
41
+ return name.endsWith('-agent') ? name : `${name}-agent`;
42
+ }
43
+
44
+ async function writeAgent({ name: rawName, role, model, tools, body, outputDir, target, description: customDesc }) {
45
+ const name = ensureAgentSuffix(rawName);
41
46
  const results = { claude: null, codex: null, skills: null };
42
47
  const description = customDesc || body.split('\n').find((l) => l.trim() && !l.startsWith('#'))?.trim() || name;
43
48
 
@@ -73,4 +78,4 @@ async function writeAgent({ name, role, model, tools, body, outputDir, target, d
73
78
  return results;
74
79
  }
75
80
 
76
- module.exports = { writeAgent, buildClaudeFormat, buildCodexFormat, buildSkillsFormat, buildDescription, TOOLS_BY_ROLE };
81
+ module.exports = { writeAgent, buildClaudeFormat, buildCodexFormat, buildSkillsFormat, buildDescription, ensureAgentSuffix, TOOLS_BY_ROLE };
@@ -112,6 +112,20 @@ async function detect(repoPath) {
112
112
  verifyCommands = 'npm run build, npm test';
113
113
  stackParts.push('Node.js');
114
114
  }
115
+
116
+ // Fallback: Node.js CLI (has bin field) or plain JS
117
+ if (!primaryTech && pkg) {
118
+ if (pkg.bin) {
119
+ primaryTech = 'Node.js';
120
+ framework = 'CLI';
121
+ verifyCommands = 'npm test';
122
+ stackParts.push('Node.js', 'CLI');
123
+ } else {
124
+ primaryTech = 'JavaScript';
125
+ verifyCommands = 'npm test';
126
+ stackParts.push('JavaScript');
127
+ }
128
+ }
115
129
  }
116
130
  }
117
131
 
@@ -267,6 +281,26 @@ async function detect(repoPath) {
267
281
  }
268
282
  }
269
283
 
284
+ // --- Shell / Bash ---
285
+ if (!primaryTech) {
286
+ const shFiles = await findFiles(abs, ['.sh'], 1);
287
+ if (shFiles.length > 0) {
288
+ primaryTech = 'Bash';
289
+ framework = 'Shell';
290
+ verifyCommands = 'bash -n *.sh, shellcheck *.sh';
291
+ stackParts.push('Bash', 'Shell');
292
+ }
293
+ }
294
+
295
+ // --- Makefile (standalone) ---
296
+ if (!primaryTech) {
297
+ if (await fs.pathExists(path.join(abs, 'Makefile'))) {
298
+ primaryTech = 'Make';
299
+ verifyCommands = 'make';
300
+ stackParts.push('Make');
301
+ }
302
+ }
303
+
270
304
  // --- Supplementary ---
271
305
  if (await fs.pathExists(path.join(abs, 'tsconfig.json'))) {
272
306
  if (!stackParts.includes('TypeScript') && primaryTech !== 'TypeScript') {
@@ -32,5 +32,3 @@ Cada especialista opera exclusivamente dentro de su repo asignado. Para ejecutar
32
32
  ## Agentes globales (gestionados por el usuario)
33
33
 
34
34
  Si el usuario ha creado agentes globales/personales en el workspace (por ejemplo, agentes transversales de arquitectura, estilo o revision de codigo), puedes invocarlos para obtener analisis cross-repo. Estos agentes son gestionados directamente por el usuario y pueden no estar presentes en todos los workspaces.
35
- {{skills_section}}
36
- {{mcp_section}}
@@ -1,6 +1,6 @@
1
1
  # {{name}}
2
2
 
3
- Especialista {{primary_tech}} — scope exclusivo: `{{scope}}`
3
+ Especialista {{tech_label}} — scope exclusivo: `{{scope}}`
4
4
 
5
5
  ## Stack
6
6