@agentforge/cli 0.5.1 → 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.
package/README.md CHANGED
@@ -135,6 +135,57 @@ agentforge agent:create myAgent --pattern plan-execute
135
135
  - `-p, --pattern <pattern>` - Agent pattern (react, plan-execute, reflection, multi-agent) [default: react]
136
136
  - `--no-test` - Skip test generation
137
137
 
138
+ #### `agent:create-reusable <name>`
139
+
140
+ Create a new reusable agent using the production template.
141
+
142
+ This command scaffolds a complete reusable agent with:
143
+ - Factory function pattern
144
+ - External prompt templates (`.md` files)
145
+ - Tool injection support
146
+ - Feature flags
147
+ - Configuration validation with Zod
148
+ - Comprehensive test suite
149
+ - Full documentation
150
+
151
+ ```bash
152
+ agentforge agent:create-reusable customer-support
153
+
154
+ # With options
155
+ agentforge agent:create-reusable data-analyst --description "Analyze data and generate insights" --author "Your Name"
156
+ ```
157
+
158
+ **Options:**
159
+ - `-d, --description <description>` - Agent description
160
+ - `-a, --author <author>` - Author name
161
+
162
+ **What Gets Created:**
163
+ ```
164
+ customer-support/
165
+ ├── src/
166
+ │ ├── index.ts # Agent factory function
167
+ │ ├── index.test.ts # Comprehensive tests
168
+ │ └── prompt-loader.ts # Prompt template utility
169
+ ├── prompts/
170
+ │ └── system.md # External prompt template
171
+ ├── package.json
172
+ ├── tsconfig.json
173
+ ├── vitest.config.ts
174
+ └── README.md
175
+ ```
176
+
177
+ **Next Steps After Creation:**
178
+ 1. `cd customer-support`
179
+ 2. `pnpm install`
180
+ 3. Edit `prompts/system.md` to customize the prompt
181
+ 4. Edit `src/index.ts` to add tools and configuration
182
+ 5. `pnpm test` to run tests
183
+ 6. `pnpm build` to build
184
+
185
+ **See Also:**
186
+ - [Reusable Agents Guide](../../docs-site/guide/advanced/reusable-agents.md)
187
+ - [Example Reusable Agents](../../examples/reusable-agents/)
188
+
138
189
  #### `agent:list`
139
190
 
140
191
  List all agents.
package/dist/index.cjs CHANGED
@@ -1,8 +1,8 @@
1
1
  'use strict';
2
2
 
3
3
  var commander = require('commander');
4
- var chalk7 = require('chalk');
5
- var path = require('path');
4
+ var chalk4 = require('chalk');
5
+ var path6 = require('path');
6
6
  var ora = require('ora');
7
7
  var inquirer = require('inquirer');
8
8
  var fs = require('fs-extra');
@@ -13,8 +13,8 @@ var execa = require('execa');
13
13
  var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
14
14
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
15
15
 
16
- var chalk7__default = /*#__PURE__*/_interopDefault(chalk7);
17
- var path__default = /*#__PURE__*/_interopDefault(path);
16
+ var chalk4__default = /*#__PURE__*/_interopDefault(chalk4);
17
+ var path6__default = /*#__PURE__*/_interopDefault(path6);
18
18
  var ora__default = /*#__PURE__*/_interopDefault(ora);
19
19
  var inquirer__default = /*#__PURE__*/_interopDefault(inquirer);
20
20
  var fs__default = /*#__PURE__*/_interopDefault(fs);
@@ -23,20 +23,20 @@ var fs__default = /*#__PURE__*/_interopDefault(fs);
23
23
  var Logger = class {
24
24
  spinner = null;
25
25
  info(message) {
26
- console.log(chalk7__default.default.blue("\u2139"), message);
26
+ console.log(chalk4__default.default.blue("\u2139"), message);
27
27
  }
28
28
  success(message) {
29
- console.log(chalk7__default.default.green("\u2714"), message);
29
+ console.log(chalk4__default.default.green("\u2714"), message);
30
30
  }
31
31
  warn(message) {
32
- console.log(chalk7__default.default.yellow("\u26A0"), message);
32
+ console.log(chalk4__default.default.yellow("\u26A0"), message);
33
33
  }
34
34
  error(message) {
35
- console.log(chalk7__default.default.red("\u2716"), message);
35
+ console.log(chalk4__default.default.red("\u2716"), message);
36
36
  }
37
37
  debug(message) {
38
38
  if (process.env.DEBUG) {
39
- console.log(chalk7__default.default.gray("\u{1F41B}"), message);
39
+ console.log(chalk4__default.default.gray("\u{1F41B}"), message);
40
40
  }
41
41
  }
42
42
  startSpinner(message) {
@@ -69,19 +69,19 @@ var Logger = class {
69
69
  console.log();
70
70
  }
71
71
  divider() {
72
- console.log(chalk7__default.default.gray("\u2500".repeat(50)));
72
+ console.log(chalk4__default.default.gray("\u2500".repeat(50)));
73
73
  }
74
74
  header(message) {
75
75
  this.newLine();
76
- console.log(chalk7__default.default.bold.cyan(message));
76
+ console.log(chalk4__default.default.bold.cyan(message));
77
77
  this.divider();
78
78
  }
79
79
  code(code) {
80
- console.log(chalk7__default.default.gray(" " + code));
80
+ console.log(chalk4__default.default.gray(" " + code));
81
81
  }
82
82
  list(items) {
83
83
  items.forEach((item) => {
84
- console.log(chalk7__default.default.gray(" \u2022"), item);
84
+ console.log(chalk4__default.default.gray(" \u2022"), item);
85
85
  });
86
86
  }
87
87
  };
@@ -261,7 +261,7 @@ async function promptToolSetup(defaults = {}) {
261
261
  };
262
262
  }
263
263
  var __filename$1 = url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
264
- var __dirname$1 = path__default.default.dirname(__filename$1);
264
+ var __dirname$1 = path6__default.default.dirname(__filename$1);
265
265
  async function ensureDir(dir) {
266
266
  await fs__default.default.ensureDir(dir);
267
267
  }
@@ -279,9 +279,9 @@ async function copyTemplate(templatePath, targetPath, replacements = {}) {
279
279
  throw new Error(`No files found in template: ${templatePath}`);
280
280
  }
281
281
  for (const file of files) {
282
- const sourcePath = path__default.default.join(templatePath, file);
283
- const destPath = path__default.default.join(targetPath, file);
284
- await fs__default.default.ensureDir(path__default.default.dirname(destPath));
282
+ const sourcePath = path6__default.default.join(templatePath, file);
283
+ const destPath = path6__default.default.join(targetPath, file);
284
+ await fs__default.default.ensureDir(path6__default.default.dirname(destPath));
285
285
  let content = await fs__default.default.readFile(sourcePath, "utf-8");
286
286
  for (const [key, value] of Object.entries(replacements)) {
287
287
  content = content.replace(new RegExp(`{{${key}}}`, "g"), value);
@@ -305,11 +305,11 @@ async function readFile(filePath) {
305
305
  return fs__default.default.readFile(filePath, "utf-8");
306
306
  }
307
307
  async function writeFile(filePath, content) {
308
- await fs__default.default.ensureDir(path__default.default.dirname(filePath));
308
+ await fs__default.default.ensureDir(path6__default.default.dirname(filePath));
309
309
  await fs__default.default.writeFile(filePath, content);
310
310
  }
311
311
  function getTemplatePath(template) {
312
- return path__default.default.join(__dirname$1, "..", "templates", template);
312
+ return path6__default.default.join(__dirname$1, "..", "templates", template);
313
313
  }
314
314
  async function isEmptyDir(dir) {
315
315
  if (!await pathExists(dir)) {
@@ -319,13 +319,13 @@ async function isEmptyDir(dir) {
319
319
  return files.length === 0;
320
320
  }
321
321
  async function detectPackageManager(cwd = process.cwd()) {
322
- if (await fs__default.default.pathExists(path__default.default.join(cwd, "pnpm-lock.yaml"))) {
322
+ if (await fs__default.default.pathExists(path6__default.default.join(cwd, "pnpm-lock.yaml"))) {
323
323
  return "pnpm";
324
324
  }
325
- if (await fs__default.default.pathExists(path__default.default.join(cwd, "yarn.lock"))) {
325
+ if (await fs__default.default.pathExists(path6__default.default.join(cwd, "yarn.lock"))) {
326
326
  return "yarn";
327
327
  }
328
- if (await fs__default.default.pathExists(path__default.default.join(cwd, "package-lock.json"))) {
328
+ if (await fs__default.default.pathExists(path6__default.default.join(cwd, "package-lock.json"))) {
329
329
  return "npm";
330
330
  }
331
331
  try {
@@ -422,7 +422,7 @@ pnpm-debug.log*
422
422
  .temp/
423
423
  .tmp/
424
424
  `;
425
- await fs__default.default.writeFile(path__default.default.join(cwd, ".gitignore"), gitignore);
425
+ await fs__default.default.writeFile(path6__default.default.join(cwd, ".gitignore"), gitignore);
426
426
  }
427
427
  async function createInitialCommit(cwd) {
428
428
  await execa.execa("git", ["add", "."], { cwd });
@@ -437,7 +437,7 @@ async function createCommand(projectName, options) {
437
437
  logger.error("Project name is required");
438
438
  process.exit(1);
439
439
  }
440
- const targetPath = path__default.default.join(process.cwd(), projectName);
440
+ const targetPath = path6__default.default.join(process.cwd(), projectName);
441
441
  if (!await isEmptyDir(targetPath)) {
442
442
  logger.error(`Directory ${projectName} already exists and is not empty`);
443
443
  process.exit(1);
@@ -450,9 +450,9 @@ async function createCommand(projectName, options) {
450
450
  initGit: options.git
451
451
  });
452
452
  logger.newLine();
453
- logger.info(`Creating project: ${chalk7__default.default.cyan(answers.projectName)}`);
454
- logger.info(`Template: ${chalk7__default.default.cyan(answers.template)}`);
455
- logger.info(`Package manager: ${chalk7__default.default.cyan(answers.packageManager)}`);
453
+ logger.info(`Creating project: ${chalk4__default.default.cyan(answers.projectName)}`);
454
+ logger.info(`Template: ${chalk4__default.default.cyan(answers.template)}`);
455
+ logger.info(`Package manager: ${chalk4__default.default.cyan(answers.packageManager)}`);
456
456
  logger.newLine();
457
457
  logger.startSpinner("Creating project directory...");
458
458
  await ensureDir(targetPath);
@@ -467,7 +467,7 @@ async function createCommand(projectName, options) {
467
467
  });
468
468
  logger.succeedSpinner("Template files copied");
469
469
  logger.startSpinner("Updating package.json...");
470
- const packageJsonPath = path__default.default.join(targetPath, "package.json");
470
+ const packageJsonPath = path6__default.default.join(targetPath, "package.json");
471
471
  const packageJson = await readJson(packageJsonPath);
472
472
  packageJson.name = answers.projectName;
473
473
  if (answers.author) {
@@ -500,7 +500,7 @@ async function createCommand(projectName, options) {
500
500
  }
501
501
  }
502
502
  logger.newLine();
503
- logger.success(chalk7__default.default.bold.green("\u2728 Project created successfully!"));
503
+ logger.success(chalk4__default.default.bold.green("\u2728 Project created successfully!"));
504
504
  logger.newLine();
505
505
  logger.header("\u{1F4DD} Next Steps");
506
506
  logger.list([
@@ -637,12 +637,12 @@ async function agentCreateCommand(name, options) {
637
637
  generateTests: options.test
638
638
  });
639
639
  logger.newLine();
640
- logger.info(`Creating agent: ${chalk7__default.default.cyan(answers.name)}`);
641
- logger.info(`Pattern: ${chalk7__default.default.cyan(answers.pattern)}`);
640
+ logger.info(`Creating agent: ${chalk4__default.default.cyan(answers.name)}`);
641
+ logger.info(`Pattern: ${chalk4__default.default.cyan(answers.pattern)}`);
642
642
  logger.newLine();
643
643
  const cwd = process.cwd();
644
- const agentDir = path__default.default.join(cwd, "src", "agents");
645
- const agentFile = path__default.default.join(agentDir, `${answers.name}.ts`);
644
+ const agentDir = path6__default.default.join(cwd, "src", "agents");
645
+ const agentFile = path6__default.default.join(agentDir, `${answers.name}.ts`);
646
646
  logger.startSpinner("Creating agent file...");
647
647
  await ensureDir(agentDir);
648
648
  const agentContent = generateAgentContent(answers.name, answers.pattern, answers.description);
@@ -650,20 +650,20 @@ async function agentCreateCommand(name, options) {
650
650
  logger.succeedSpinner("Agent file created");
651
651
  if (answers.generateTests) {
652
652
  logger.startSpinner("Creating test file...");
653
- const testDir = path__default.default.join(cwd, "tests", "agents");
654
- const testFile = path__default.default.join(testDir, `${answers.name}.test.ts`);
653
+ const testDir = path6__default.default.join(cwd, "tests", "agents");
654
+ const testFile = path6__default.default.join(testDir, `${answers.name}.test.ts`);
655
655
  await ensureDir(testDir);
656
656
  const testContent = generateTestContent(answers.name, answers.pattern);
657
657
  await writeFile(testFile, testContent);
658
658
  logger.succeedSpinner("Test file created");
659
659
  }
660
660
  logger.newLine();
661
- logger.success(chalk7__default.default.bold.green("\u2728 Agent created successfully!"));
661
+ logger.success(chalk4__default.default.bold.green("\u2728 Agent created successfully!"));
662
662
  logger.newLine();
663
663
  logger.header("\u{1F4DD} Next Steps");
664
664
  logger.list([
665
- `Edit ${chalk7__default.default.cyan(`src/agents/${answers.name}.ts`)} to customize your agent`,
666
- answers.generateTests ? `Run ${chalk7__default.default.cyan(`pnpm test tests/agents/${answers.name}.test.ts`)} to test your agent` : ""
665
+ `Edit ${chalk4__default.default.cyan(`src/agents/${answers.name}.ts`)} to customize your agent`,
666
+ answers.generateTests ? `Run ${chalk4__default.default.cyan(`pnpm test tests/agents/${answers.name}.test.ts`)} to test your agent` : ""
667
667
  ].filter(Boolean));
668
668
  } catch (error) {
669
669
  logger.error(`Failed to create agent: ${error.message}`);
@@ -775,42 +775,129 @@ describe('${capitalize(name)} ${pattern === "multi-agent" ? "System" : "Agent"}'
775
775
  function capitalize(str) {
776
776
  return str.charAt(0).toUpperCase() + str.slice(1);
777
777
  }
778
+ async function agentCreateReusableCommand(name, options) {
779
+ try {
780
+ logger.header("\u{1F4E6} Create Reusable Agent");
781
+ const answers = await inquirer__default.default.prompt([
782
+ {
783
+ type: "input",
784
+ name: "name",
785
+ message: "Agent name (e.g., customer-support):",
786
+ default: name,
787
+ validate: (input) => {
788
+ if (!input) return "Agent name is required";
789
+ if (!/^[a-z][a-z0-9-]*$/.test(input)) {
790
+ return "Agent name must be lowercase with hyphens (e.g., customer-support)";
791
+ }
792
+ return true;
793
+ }
794
+ },
795
+ {
796
+ type: "input",
797
+ name: "description",
798
+ message: "Agent description:",
799
+ default: options.description || `A reusable ${name} agent`
800
+ },
801
+ {
802
+ type: "input",
803
+ name: "author",
804
+ message: "Author name:",
805
+ default: options.author || ""
806
+ }
807
+ ]);
808
+ logger.newLine();
809
+ logger.info(`Creating reusable agent: ${chalk4__default.default.cyan(answers.name)}`);
810
+ logger.info(`Description: ${chalk4__default.default.gray(answers.description)}`);
811
+ logger.newLine();
812
+ const cwd = process.cwd();
813
+ const agentDir = path6__default.default.join(cwd, answers.name);
814
+ const agentNameKebab = answers.name;
815
+ const agentNamePascal = kebabToPascal(agentNameKebab);
816
+ const agentNameCamel = kebabToCamel(agentNameKebab);
817
+ const packageName = `@agentforge/${agentNameKebab}`;
818
+ const replacements = {
819
+ AGENT_NAME_KEBAB: agentNameKebab,
820
+ AGENT_NAME_PASCAL: agentNamePascal,
821
+ AGENT_NAME_CAMEL: agentNameCamel,
822
+ AGENT_DESCRIPTION: answers.description,
823
+ PACKAGE_NAME: packageName,
824
+ AUTHOR: answers.author || "Your Name"
825
+ };
826
+ logger.startSpinner("Creating agent structure...");
827
+ const templatePath = getTemplatePath("reusable-agent");
828
+ await copyTemplate(templatePath, agentDir, replacements);
829
+ logger.succeedSpinner("Agent structure created");
830
+ logger.startSpinner("Organizing files...");
831
+ const fs4 = await import('fs-extra');
832
+ const srcDir = path6__default.default.join(agentDir, "src");
833
+ await fs4.ensureDir(srcDir);
834
+ await fs4.move(path6__default.default.join(agentDir, "index.ts"), path6__default.default.join(srcDir, "index.ts"));
835
+ await fs4.move(path6__default.default.join(agentDir, "prompt-loader.ts"), path6__default.default.join(srcDir, "prompt-loader.ts"));
836
+ await fs4.move(path6__default.default.join(agentDir, "index.test.ts"), path6__default.default.join(srcDir, "index.test.ts"));
837
+ logger.succeedSpinner("Files organized");
838
+ logger.newLine();
839
+ logger.success(chalk4__default.default.bold.green("\u2728 Reusable agent created successfully!"));
840
+ logger.newLine();
841
+ logger.header("\u{1F4DD} Next Steps");
842
+ const nextSteps = [
843
+ `cd ${chalk4__default.default.cyan(answers.name)}`,
844
+ `Install dependencies: ${chalk4__default.default.cyan("pnpm install")}`,
845
+ `Edit ${chalk4__default.default.cyan("prompts/system.md")} to customize the agent prompt`,
846
+ `Edit ${chalk4__default.default.cyan("src/index.ts")} to add tools and configuration`,
847
+ `Run tests: ${chalk4__default.default.cyan("pnpm test")}`,
848
+ `Build: ${chalk4__default.default.cyan("pnpm build")}`
849
+ ];
850
+ logger.list(nextSteps);
851
+ logger.newLine();
852
+ logger.info(chalk4__default.default.gray("\u{1F4A1} Tip: See examples/reusable-agents/ for reference implementations"));
853
+ } catch (error) {
854
+ logger.error(`Failed to create reusable agent: ${error.message}`);
855
+ process.exit(1);
856
+ }
857
+ }
858
+ function kebabToPascal(str) {
859
+ return str.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
860
+ }
861
+ function kebabToCamel(str) {
862
+ const pascal = kebabToPascal(str);
863
+ return pascal.charAt(0).toLowerCase() + pascal.slice(1);
864
+ }
778
865
  async function agentListCommand(options) {
779
866
  try {
780
867
  logger.header("\u{1F4CB} List Agents");
781
868
  const cwd = process.cwd();
782
- const agentDir = path__default.default.join(cwd, "src", "agents");
869
+ const agentDir = path6__default.default.join(cwd, "src", "agents");
783
870
  const agentFiles = await findFiles("*.ts", agentDir);
784
871
  if (agentFiles.length === 0) {
785
872
  logger.warn("No agents found");
786
- logger.info(`Create an agent with: ${chalk7__default.default.cyan("agentforge agent:create <name>")}`);
873
+ logger.info(`Create an agent with: ${chalk4__default.default.cyan("agentforge agent:create <name>")}`);
787
874
  return;
788
875
  }
789
- logger.info(`Found ${chalk7__default.default.cyan(agentFiles.length)} agent(s):
876
+ logger.info(`Found ${chalk4__default.default.cyan(agentFiles.length)} agent(s):
790
877
  `);
791
878
  for (const file of agentFiles) {
792
- const agentName = path__default.default.basename(file, ".ts");
793
- const agentPath = path__default.default.join(agentDir, file);
879
+ const agentName = path6__default.default.basename(file, ".ts");
880
+ const agentPath = path6__default.default.join(agentDir, file);
794
881
  if (options.verbose) {
795
882
  const content = await readFile(agentPath);
796
883
  const pattern = extractPattern(content);
797
884
  const description = extractDescription(content);
798
- logger.info(chalk7__default.default.bold.cyan(` ${agentName}`));
885
+ logger.info(chalk4__default.default.bold.cyan(` ${agentName}`));
799
886
  if (pattern) {
800
887
  logger.info(` Pattern: ${pattern}`);
801
888
  }
802
889
  if (description) {
803
890
  logger.info(` Description: ${description}`);
804
891
  }
805
- logger.info(` Path: ${chalk7__default.default.gray(agentPath)}`);
892
+ logger.info(` Path: ${chalk4__default.default.gray(agentPath)}`);
806
893
  logger.newLine();
807
894
  } else {
808
- logger.info(` \u2022 ${chalk7__default.default.cyan(agentName)}`);
895
+ logger.info(` \u2022 ${chalk4__default.default.cyan(agentName)}`);
809
896
  }
810
897
  }
811
898
  if (!options.verbose) {
812
899
  logger.newLine();
813
- logger.info(`Use ${chalk7__default.default.cyan("--verbose")} for more details`);
900
+ logger.info(`Use ${chalk4__default.default.cyan("--verbose")} for more details`);
814
901
  }
815
902
  } catch (error) {
816
903
  logger.error(`Failed to list agents: ${error.message}`);
@@ -834,13 +921,13 @@ async function agentTestCommand(name, options) {
834
921
  try {
835
922
  logger.header("\u{1F9EA} Test Agent");
836
923
  const cwd = process.cwd();
837
- const testFile = path__default.default.join(cwd, "tests", "agents", `${name}.test.ts`);
924
+ const testFile = path6__default.default.join(cwd, "tests", "agents", `${name}.test.ts`);
838
925
  if (!await pathExists(testFile)) {
839
926
  logger.error(`Test file not found: ${testFile}`);
840
- logger.info(`Create tests with: ${chalk7__default.default.cyan(`agentforge agent:create ${name} --test`)}`);
927
+ logger.info(`Create tests with: ${chalk4__default.default.cyan(`agentforge agent:create ${name} --test`)}`);
841
928
  process.exit(1);
842
929
  }
843
- logger.info(`Testing agent: ${chalk7__default.default.cyan(name)}`);
930
+ logger.info(`Testing agent: ${chalk4__default.default.cyan(name)}`);
844
931
  logger.info(`Watch mode: ${options.watch ? "Yes" : "No"}`);
845
932
  logger.newLine();
846
933
  const packageManager = await detectPackageManager(cwd);
@@ -858,8 +945,8 @@ async function agentTestCommand(name, options) {
858
945
  async function agentDeployCommand(name, options) {
859
946
  try {
860
947
  logger.header("\u{1F680} Deploy Agent");
861
- logger.info(`Agent: ${chalk7__default.default.cyan(name)}`);
862
- logger.info(`Environment: ${chalk7__default.default.cyan(options.environment || "production")}`);
948
+ logger.info(`Agent: ${chalk4__default.default.cyan(name)}`);
949
+ logger.info(`Environment: ${chalk4__default.default.cyan(options.environment || "production")}`);
863
950
  logger.info(`Dry run: ${options.dryRun ? "Yes" : "No"}`);
864
951
  logger.newLine();
865
952
  if (options.dryRun) {
@@ -875,7 +962,7 @@ async function agentDeployCommand(name, options) {
875
962
  logger.succeedSpinner("Agent deployed successfully");
876
963
  }
877
964
  logger.newLine();
878
- logger.success(chalk7__default.default.bold.green("\u2728 Deployment completed!"));
965
+ logger.success(chalk4__default.default.bold.green("\u2728 Deployment completed!"));
879
966
  logger.newLine();
880
967
  logger.info("Note: Actual deployment implementation coming soon");
881
968
  logger.info("For now, please use the deployment templates in the templates/deployment directory");
@@ -896,9 +983,9 @@ async function toolCreateCommand(name, options) {
896
983
  generateTests: options.test
897
984
  });
898
985
  logger.newLine();
899
- logger.info(`Creating tool: ${chalk7__default.default.cyan(answers.name)}`);
900
- logger.info(`Category: ${chalk7__default.default.cyan(answers.category)}`);
901
- logger.info(`Structure: ${chalk7__default.default.cyan(answers.structure)}`);
986
+ logger.info(`Creating tool: ${chalk4__default.default.cyan(answers.name)}`);
987
+ logger.info(`Category: ${chalk4__default.default.cyan(answers.category)}`);
988
+ logger.info(`Structure: ${chalk4__default.default.cyan(answers.structure)}`);
902
989
  logger.newLine();
903
990
  const cwd = process.cwd();
904
991
  if (answers.structure === "multi") {
@@ -907,18 +994,18 @@ async function toolCreateCommand(name, options) {
907
994
  await createSingleFileTool(cwd, answers);
908
995
  }
909
996
  logger.newLine();
910
- logger.success(chalk7__default.default.bold.green("\u2728 Tool created successfully!"));
997
+ logger.success(chalk4__default.default.bold.green("\u2728 Tool created successfully!"));
911
998
  logger.newLine();
912
999
  logger.header("\u{1F4DD} Next Steps");
913
1000
  const nextSteps = answers.structure === "multi" ? [
914
- `Edit ${chalk7__default.default.cyan(`src/tools/${answers.name}/index.ts`)} to implement your tool`,
915
- `Add providers in ${chalk7__default.default.cyan(`src/tools/${answers.name}/providers/`)}`,
916
- `Define types in ${chalk7__default.default.cyan(`src/tools/${answers.name}/types.ts`)}`,
917
- answers.generateTests ? `Run ${chalk7__default.default.cyan(`pnpm test ${answers.name}`)} to test your tool` : "",
1001
+ `Edit ${chalk4__default.default.cyan(`src/tools/${answers.name}/index.ts`)} to implement your tool`,
1002
+ `Add providers in ${chalk4__default.default.cyan(`src/tools/${answers.name}/providers/`)}`,
1003
+ `Define types in ${chalk4__default.default.cyan(`src/tools/${answers.name}/types.ts`)}`,
1004
+ answers.generateTests ? `Run ${chalk4__default.default.cyan(`pnpm test ${answers.name}`)} to test your tool` : "",
918
1005
  `Register the tool in your agent's tool registry`
919
1006
  ] : [
920
- `Edit ${chalk7__default.default.cyan(`src/tools/${answers.name}.ts`)} to implement your tool`,
921
- answers.generateTests ? `Run ${chalk7__default.default.cyan(`pnpm test tests/tools/${answers.name}.test.ts`)} to test your tool` : "",
1007
+ `Edit ${chalk4__default.default.cyan(`src/tools/${answers.name}.ts`)} to implement your tool`,
1008
+ answers.generateTests ? `Run ${chalk4__default.default.cyan(`pnpm test tests/tools/${answers.name}.test.ts`)} to test your tool` : "",
922
1009
  `Register the tool in your agent's tool registry`
923
1010
  ];
924
1011
  logger.list(nextSteps.filter(Boolean));
@@ -978,8 +1065,8 @@ function capitalize2(str) {
978
1065
  return str.charAt(0).toUpperCase() + str.slice(1);
979
1066
  }
980
1067
  async function createSingleFileTool(cwd, answers) {
981
- const toolDir = path__default.default.join(cwd, "src", "tools");
982
- const toolFile = path__default.default.join(toolDir, `${answers.name}.ts`);
1068
+ const toolDir = path6__default.default.join(cwd, "src", "tools");
1069
+ const toolFile = path6__default.default.join(toolDir, `${answers.name}.ts`);
983
1070
  logger.startSpinner("Creating tool file...");
984
1071
  await ensureDir(toolDir);
985
1072
  const toolContent = generateToolContent(answers.name, answers.category, answers.description);
@@ -987,8 +1074,8 @@ async function createSingleFileTool(cwd, answers) {
987
1074
  logger.succeedSpinner("Tool file created");
988
1075
  if (answers.generateTests) {
989
1076
  logger.startSpinner("Creating test file...");
990
- const testDir = path__default.default.join(cwd, "tests", "tools");
991
- const testFile = path__default.default.join(testDir, `${answers.name}.test.ts`);
1077
+ const testDir = path6__default.default.join(cwd, "tests", "tools");
1078
+ const testFile = path6__default.default.join(testDir, `${answers.name}.test.ts`);
992
1079
  await ensureDir(testDir);
993
1080
  const testContent = generateTestContent2(answers.name);
994
1081
  await writeFile(testFile, testContent);
@@ -996,7 +1083,7 @@ async function createSingleFileTool(cwd, answers) {
996
1083
  }
997
1084
  }
998
1085
  async function createMultiFileTool(cwd, answers) {
999
- const toolDir = path__default.default.join(cwd, "src", "tools", answers.name);
1086
+ const toolDir = path6__default.default.join(cwd, "src", "tools", answers.name);
1000
1087
  logger.startSpinner("Creating tool directory structure...");
1001
1088
  await ensureDir(toolDir);
1002
1089
  const templatePath = getTemplatePath("tool-multi");
@@ -1012,7 +1099,7 @@ async function createMultiFileTool(cwd, answers) {
1012
1099
  if (!answers.generateTests) {
1013
1100
  logger.startSpinner("Cleaning up test files...");
1014
1101
  const fs4 = await import('fs-extra');
1015
- const testDir = path__default.default.join(toolDir, "__tests__");
1102
+ const testDir = path6__default.default.join(toolDir, "__tests__");
1016
1103
  await fs4.remove(testDir);
1017
1104
  logger.succeedSpinner("Test files removed");
1018
1105
  }
@@ -1021,18 +1108,18 @@ async function toolListCommand(options) {
1021
1108
  try {
1022
1109
  logger.header("\u{1F4CB} List Tools");
1023
1110
  const cwd = process.cwd();
1024
- const toolDir = path__default.default.join(cwd, "src", "tools");
1111
+ const toolDir = path6__default.default.join(cwd, "src", "tools");
1025
1112
  const toolFiles = await findFiles("*.ts", toolDir);
1026
1113
  if (toolFiles.length === 0) {
1027
1114
  logger.warn("No tools found");
1028
- logger.info(`Create a tool with: ${chalk7__default.default.cyan("agentforge tool:create <name>")}`);
1115
+ logger.info(`Create a tool with: ${chalk4__default.default.cyan("agentforge tool:create <name>")}`);
1029
1116
  return;
1030
1117
  }
1031
1118
  let filteredTools = toolFiles;
1032
1119
  if (options.category) {
1033
1120
  filteredTools = [];
1034
1121
  for (const file of toolFiles) {
1035
- const toolPath = path__default.default.join(toolDir, file);
1122
+ const toolPath = path6__default.default.join(toolDir, file);
1036
1123
  const content = await readFile(toolPath);
1037
1124
  const category = extractCategory(content);
1038
1125
  if (category === options.category) {
@@ -1044,31 +1131,31 @@ async function toolListCommand(options) {
1044
1131
  logger.warn(`No tools found in category: ${options.category}`);
1045
1132
  return;
1046
1133
  }
1047
- logger.info(`Found ${chalk7__default.default.cyan(filteredTools.length)} tool(s):
1134
+ logger.info(`Found ${chalk4__default.default.cyan(filteredTools.length)} tool(s):
1048
1135
  `);
1049
1136
  for (const file of filteredTools) {
1050
- const toolName = path__default.default.basename(file, ".ts");
1051
- const toolPath = path__default.default.join(toolDir, file);
1137
+ const toolName = path6__default.default.basename(file, ".ts");
1138
+ const toolPath = path6__default.default.join(toolDir, file);
1052
1139
  if (options.verbose) {
1053
1140
  const content = await readFile(toolPath);
1054
1141
  const category = extractCategory(content);
1055
1142
  const description = extractDescription2(content);
1056
- logger.info(chalk7__default.default.bold.cyan(` ${toolName}`));
1143
+ logger.info(chalk4__default.default.bold.cyan(` ${toolName}`));
1057
1144
  if (category) {
1058
1145
  logger.info(` Category: ${category}`);
1059
1146
  }
1060
1147
  if (description) {
1061
1148
  logger.info(` Description: ${description}`);
1062
1149
  }
1063
- logger.info(` Path: ${chalk7__default.default.gray(toolPath)}`);
1150
+ logger.info(` Path: ${chalk4__default.default.gray(toolPath)}`);
1064
1151
  logger.newLine();
1065
1152
  } else {
1066
- logger.info(` \u2022 ${chalk7__default.default.cyan(toolName)}`);
1153
+ logger.info(` \u2022 ${chalk4__default.default.cyan(toolName)}`);
1067
1154
  }
1068
1155
  }
1069
1156
  if (!options.verbose) {
1070
1157
  logger.newLine();
1071
- logger.info(`Use ${chalk7__default.default.cyan("--verbose")} for more details`);
1158
+ logger.info(`Use ${chalk4__default.default.cyan("--verbose")} for more details`);
1072
1159
  }
1073
1160
  } catch (error) {
1074
1161
  logger.error(`Failed to list tools: ${error.message}`);
@@ -1087,13 +1174,13 @@ async function toolTestCommand(name, options) {
1087
1174
  try {
1088
1175
  logger.header("\u{1F9EA} Test Tool");
1089
1176
  const cwd = process.cwd();
1090
- const testFile = path__default.default.join(cwd, "tests", "tools", `${name}.test.ts`);
1177
+ const testFile = path6__default.default.join(cwd, "tests", "tools", `${name}.test.ts`);
1091
1178
  if (!await pathExists(testFile)) {
1092
1179
  logger.error(`Test file not found: ${testFile}`);
1093
- logger.info(`Create tests with: ${chalk7__default.default.cyan(`agentforge tool:create ${name} --test`)}`);
1180
+ logger.info(`Create tests with: ${chalk4__default.default.cyan(`agentforge tool:create ${name} --test`)}`);
1094
1181
  process.exit(1);
1095
1182
  }
1096
- logger.info(`Testing tool: ${chalk7__default.default.cyan(name)}`);
1183
+ logger.info(`Testing tool: ${chalk4__default.default.cyan(name)}`);
1097
1184
  logger.info(`Watch mode: ${options.watch ? "Yes" : "No"}`);
1098
1185
  logger.newLine();
1099
1186
  const packageManager = await detectPackageManager(cwd);
@@ -1111,8 +1198,8 @@ async function toolTestCommand(name, options) {
1111
1198
  async function toolPublishCommand(name, options) {
1112
1199
  try {
1113
1200
  logger.header("\u{1F4E6} Publish Tool");
1114
- logger.info(`Tool: ${chalk7__default.default.cyan(name)}`);
1115
- logger.info(`Tag: ${chalk7__default.default.cyan(options.tag || "latest")}`);
1201
+ logger.info(`Tool: ${chalk4__default.default.cyan(name)}`);
1202
+ logger.info(`Tag: ${chalk4__default.default.cyan(options.tag || "latest")}`);
1116
1203
  logger.info(`Dry run: ${options.dryRun ? "Yes" : "No"}`);
1117
1204
  logger.newLine();
1118
1205
  if (options.dryRun) {
@@ -1144,7 +1231,7 @@ async function toolPublishCommand(name, options) {
1144
1231
  logger.succeedSpinner("Published to npm");
1145
1232
  }
1146
1233
  logger.newLine();
1147
- logger.success(chalk7__default.default.bold.green("\u2728 Tool published successfully!"));
1234
+ logger.success(chalk4__default.default.bold.green("\u2728 Tool published successfully!"));
1148
1235
  logger.newLine();
1149
1236
  logger.info("Note: Actual npm publishing implementation coming soon");
1150
1237
  logger.info("For now, please use npm publish manually");
@@ -1165,6 +1252,7 @@ program.command("test").description("Run tests with coverage").option("-w, --wat
1165
1252
  program.command("lint").description("Lint and format code").option("--fix", "Auto-fix issues").option("--no-format", "Skip formatting").action(lintCommand);
1166
1253
  var agent = program.command("agent").description("Manage agents");
1167
1254
  agent.command("create <name>").description("Create a new agent").option("-p, --pattern <pattern>", "Agent pattern (react, plan-execute, reflection, multi-agent)", "react").option("--no-test", "Skip test generation").action(agentCreateCommand);
1255
+ agent.command("create-reusable <name>").description("Create a new reusable agent (production template)").option("-d, --description <description>", "Agent description").option("-a, --author <author>", "Author name").action(agentCreateReusableCommand);
1168
1256
  agent.command("list").description("List all agents").option("-v, --verbose", "Show detailed information").action(agentListCommand);
1169
1257
  agent.command("test <name>").description("Test a specific agent").option("-w, --watch", "Watch mode").action(agentTestCommand);
1170
1258
  agent.command("deploy <name>").description("Deploy an agent").option("-e, --environment <env>", "Deployment environment", "production").option("--dry-run", "Dry run without actual deployment").action(agentDeployCommand);
@@ -1179,7 +1267,7 @@ async function run() {
1179
1267
  await program.parseAsync(process.argv);
1180
1268
  } catch (error) {
1181
1269
  if (error.code !== "commander.help" && error.code !== "commander.version") {
1182
- console.error(chalk7__default.default.red("Error:"), error.message);
1270
+ console.error(chalk4__default.default.red("Error:"), error.message);
1183
1271
  process.exit(1);
1184
1272
  }
1185
1273
  }