@agentforge/cli 0.11.4 → 0.11.6

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/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Command } from 'commander';
2
2
  import chalk4 from 'chalk';
3
- import path6 from 'path';
3
+ import path12 from 'path';
4
4
  import ora from 'ora';
5
5
  import inquirer from 'inquirer';
6
6
  import fs from 'fs-extra';
@@ -250,7 +250,7 @@ async function promptToolSetup(defaults = {}) {
250
250
  };
251
251
  }
252
252
  var __filename$1 = fileURLToPath(import.meta.url);
253
- var __dirname$1 = path6.dirname(__filename$1);
253
+ var __dirname$1 = path12.dirname(__filename$1);
254
254
  async function ensureDir(dir) {
255
255
  await fs.ensureDir(dir);
256
256
  }
@@ -268,9 +268,9 @@ async function copyTemplate(templatePath, targetPath, replacements = {}) {
268
268
  throw new Error(`No files found in template: ${templatePath}`);
269
269
  }
270
270
  for (const file of files) {
271
- const sourcePath = path6.join(templatePath, file);
272
- const destPath = path6.join(targetPath, file);
273
- await fs.ensureDir(path6.dirname(destPath));
271
+ const sourcePath = path12.join(templatePath, file);
272
+ const destPath = path12.join(targetPath, file);
273
+ await fs.ensureDir(path12.dirname(destPath));
274
274
  let content = await fs.readFile(sourcePath, "utf-8");
275
275
  for (const [key, value] of Object.entries(replacements)) {
276
276
  content = content.replace(new RegExp(`{{${key}}}`, "g"), value);
@@ -294,11 +294,11 @@ async function readFile(filePath) {
294
294
  return fs.readFile(filePath, "utf-8");
295
295
  }
296
296
  async function writeFile(filePath, content) {
297
- await fs.ensureDir(path6.dirname(filePath));
297
+ await fs.ensureDir(path12.dirname(filePath));
298
298
  await fs.writeFile(filePath, content);
299
299
  }
300
300
  function getTemplatePath(template) {
301
- return path6.join(__dirname$1, "..", "templates", template);
301
+ return path12.join(__dirname$1, "..", "templates", template);
302
302
  }
303
303
  async function isEmptyDir(dir) {
304
304
  if (!await pathExists(dir)) {
@@ -308,13 +308,13 @@ async function isEmptyDir(dir) {
308
308
  return files.length === 0;
309
309
  }
310
310
  async function detectPackageManager(cwd = process.cwd()) {
311
- if (await fs.pathExists(path6.join(cwd, "pnpm-lock.yaml"))) {
311
+ if (await fs.pathExists(path12.join(cwd, "pnpm-lock.yaml"))) {
312
312
  return "pnpm";
313
313
  }
314
- if (await fs.pathExists(path6.join(cwd, "yarn.lock"))) {
314
+ if (await fs.pathExists(path12.join(cwd, "yarn.lock"))) {
315
315
  return "yarn";
316
316
  }
317
- if (await fs.pathExists(path6.join(cwd, "package-lock.json"))) {
317
+ if (await fs.pathExists(path12.join(cwd, "package-lock.json"))) {
318
318
  return "npm";
319
319
  }
320
320
  try {
@@ -422,7 +422,7 @@ pnpm-debug.log*
422
422
  .temp/
423
423
  .tmp/
424
424
  `;
425
- await fs.writeFile(path6.join(cwd, ".gitignore"), gitignore);
425
+ await fs.writeFile(path12.join(cwd, ".gitignore"), gitignore);
426
426
  }
427
427
  async function createInitialCommit(cwd) {
428
428
  await 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 = path6.join(process.cwd(), projectName);
440
+ const targetPath = path12.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);
@@ -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 = path6.join(targetPath, "package.json");
470
+ const packageJsonPath = path12.join(targetPath, "package.json");
471
471
  const packageJson = await readJson(packageJsonPath);
472
472
  packageJson.name = answers.projectName;
473
473
  if (answers.author) {
@@ -641,8 +641,8 @@ async function agentCreateCommand(name, options) {
641
641
  logger.info(`Pattern: ${chalk4.cyan(answers.pattern)}`);
642
642
  logger.newLine();
643
643
  const cwd = process.cwd();
644
- const agentDir = path6.join(cwd, "src", "agents");
645
- const agentFile = path6.join(agentDir, `${answers.name}.ts`);
644
+ const agentDir = path12.join(cwd, "src", "agents");
645
+ const agentFile = path12.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,8 +650,8 @@ 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 = path6.join(cwd, "tests", "agents");
654
- const testFile = path6.join(testDir, `${answers.name}.test.ts`);
653
+ const testDir = path12.join(cwd, "tests", "agents");
654
+ const testFile = path12.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);
@@ -810,7 +810,7 @@ async function agentCreateReusableCommand(name, options) {
810
810
  logger.info(`Description: ${chalk4.gray(answers.description)}`);
811
811
  logger.newLine();
812
812
  const cwd = process.cwd();
813
- const agentDir = path6.join(cwd, answers.name);
813
+ const agentDir = path12.join(cwd, answers.name);
814
814
  const agentNameKebab = answers.name;
815
815
  const agentNamePascal = kebabToPascal(agentNameKebab);
816
816
  const agentNameCamel = kebabToCamel(agentNameKebab);
@@ -828,12 +828,12 @@ async function agentCreateReusableCommand(name, options) {
828
828
  await copyTemplate(templatePath, agentDir, replacements);
829
829
  logger.succeedSpinner("Agent structure created");
830
830
  logger.startSpinner("Organizing files...");
831
- const fs4 = await import('fs-extra');
832
- const srcDir = path6.join(agentDir, "src");
833
- await fs4.ensureDir(srcDir);
834
- await fs4.move(path6.join(agentDir, "index.ts"), path6.join(srcDir, "index.ts"));
835
- await fs4.move(path6.join(agentDir, "prompt-loader.ts"), path6.join(srcDir, "prompt-loader.ts"));
836
- await fs4.move(path6.join(agentDir, "index.test.ts"), path6.join(srcDir, "index.test.ts"));
831
+ const fs5 = await import('fs-extra');
832
+ const srcDir = path12.join(agentDir, "src");
833
+ await fs5.ensureDir(srcDir);
834
+ await fs5.move(path12.join(agentDir, "index.ts"), path12.join(srcDir, "index.ts"));
835
+ await fs5.move(path12.join(agentDir, "prompt-loader.ts"), path12.join(srcDir, "prompt-loader.ts"));
836
+ await fs5.move(path12.join(agentDir, "index.test.ts"), path12.join(srcDir, "index.test.ts"));
837
837
  logger.succeedSpinner("Files organized");
838
838
  logger.newLine();
839
839
  logger.success(chalk4.bold.green("\u2728 Reusable agent created successfully!"));
@@ -866,7 +866,7 @@ async function agentListCommand(options) {
866
866
  try {
867
867
  logger.header("\u{1F4CB} List Agents");
868
868
  const cwd = process.cwd();
869
- const agentDir = path6.join(cwd, "src", "agents");
869
+ const agentDir = path12.join(cwd, "src", "agents");
870
870
  const agentFiles = await findFiles("*.ts", agentDir);
871
871
  if (agentFiles.length === 0) {
872
872
  logger.warn("No agents found");
@@ -876,8 +876,8 @@ async function agentListCommand(options) {
876
876
  logger.info(`Found ${chalk4.cyan(agentFiles.length)} agent(s):
877
877
  `);
878
878
  for (const file of agentFiles) {
879
- const agentName = path6.basename(file, ".ts");
880
- const agentPath = path6.join(agentDir, file);
879
+ const agentName = path12.basename(file, ".ts");
880
+ const agentPath = path12.join(agentDir, file);
881
881
  if (options.verbose) {
882
882
  const content = await readFile(agentPath);
883
883
  const pattern = extractPattern(content);
@@ -921,7 +921,7 @@ async function agentTestCommand(name, options) {
921
921
  try {
922
922
  logger.header("\u{1F9EA} Test Agent");
923
923
  const cwd = process.cwd();
924
- const testFile = path6.join(cwd, "tests", "agents", `${name}.test.ts`);
924
+ const testFile = path12.join(cwd, "tests", "agents", `${name}.test.ts`);
925
925
  if (!await pathExists(testFile)) {
926
926
  logger.error(`Test file not found: ${testFile}`);
927
927
  logger.info(`Create tests with: ${chalk4.cyan(`agentforge agent:create ${name} --test`)}`);
@@ -1072,8 +1072,8 @@ function capitalize2(str) {
1072
1072
  return str.charAt(0).toUpperCase() + str.slice(1);
1073
1073
  }
1074
1074
  async function createSingleFileTool(cwd, answers) {
1075
- const toolDir = path6.join(cwd, "src", "tools");
1076
- const toolFile = path6.join(toolDir, `${answers.name}.ts`);
1075
+ const toolDir = path12.join(cwd, "src", "tools");
1076
+ const toolFile = path12.join(toolDir, `${answers.name}.ts`);
1077
1077
  logger.startSpinner("Creating tool file...");
1078
1078
  await ensureDir(toolDir);
1079
1079
  const toolContent = generateToolContent(answers.name, answers.category, answers.description);
@@ -1081,8 +1081,8 @@ async function createSingleFileTool(cwd, answers) {
1081
1081
  logger.succeedSpinner("Tool file created");
1082
1082
  if (answers.generateTests) {
1083
1083
  logger.startSpinner("Creating test file...");
1084
- const testDir = path6.join(cwd, "tests", "tools");
1085
- const testFile = path6.join(testDir, `${answers.name}.test.ts`);
1084
+ const testDir = path12.join(cwd, "tests", "tools");
1085
+ const testFile = path12.join(testDir, `${answers.name}.test.ts`);
1086
1086
  await ensureDir(testDir);
1087
1087
  const testContent = generateTestContent2(answers.name);
1088
1088
  await writeFile(testFile, testContent);
@@ -1090,7 +1090,7 @@ async function createSingleFileTool(cwd, answers) {
1090
1090
  }
1091
1091
  }
1092
1092
  async function createMultiFileTool(cwd, answers) {
1093
- const toolDir = path6.join(cwd, "src", "tools", answers.name);
1093
+ const toolDir = path12.join(cwd, "src", "tools", answers.name);
1094
1094
  logger.startSpinner("Creating tool directory structure...");
1095
1095
  await ensureDir(toolDir);
1096
1096
  const templatePath = getTemplatePath("tool-multi");
@@ -1105,9 +1105,9 @@ async function createMultiFileTool(cwd, answers) {
1105
1105
  logger.succeedSpinner("Tool directory structure created");
1106
1106
  if (!answers.generateTests) {
1107
1107
  logger.startSpinner("Cleaning up test files...");
1108
- const fs4 = await import('fs-extra');
1109
- const testDir = path6.join(toolDir, "__tests__");
1110
- await fs4.remove(testDir);
1108
+ const fs5 = await import('fs-extra');
1109
+ const testDir = path12.join(toolDir, "__tests__");
1110
+ await fs5.remove(testDir);
1111
1111
  logger.succeedSpinner("Test files removed");
1112
1112
  }
1113
1113
  }
@@ -1115,7 +1115,7 @@ async function toolListCommand(options) {
1115
1115
  try {
1116
1116
  logger.header("\u{1F4CB} List Tools");
1117
1117
  const cwd = process.cwd();
1118
- const toolDir = path6.join(cwd, "src", "tools");
1118
+ const toolDir = path12.join(cwd, "src", "tools");
1119
1119
  const toolFiles = await findFiles("*.ts", toolDir);
1120
1120
  if (toolFiles.length === 0) {
1121
1121
  logger.warn("No tools found");
@@ -1126,7 +1126,7 @@ async function toolListCommand(options) {
1126
1126
  if (options.category) {
1127
1127
  filteredTools = [];
1128
1128
  for (const file of toolFiles) {
1129
- const toolPath = path6.join(toolDir, file);
1129
+ const toolPath = path12.join(toolDir, file);
1130
1130
  const content = await readFile(toolPath);
1131
1131
  const category = extractCategory(content);
1132
1132
  if (category === options.category) {
@@ -1141,8 +1141,8 @@ async function toolListCommand(options) {
1141
1141
  logger.info(`Found ${chalk4.cyan(filteredTools.length)} tool(s):
1142
1142
  `);
1143
1143
  for (const file of filteredTools) {
1144
- const toolName = path6.basename(file, ".ts");
1145
- const toolPath = path6.join(toolDir, file);
1144
+ const toolName = path12.basename(file, ".ts");
1145
+ const toolPath = path12.join(toolDir, file);
1146
1146
  if (options.verbose) {
1147
1147
  const content = await readFile(toolPath);
1148
1148
  const category = extractCategory(content);
@@ -1181,7 +1181,7 @@ async function toolTestCommand(name, options) {
1181
1181
  try {
1182
1182
  logger.header("\u{1F9EA} Test Tool");
1183
1183
  const cwd = process.cwd();
1184
- const testFile = path6.join(cwd, "tests", "tools", `${name}.test.ts`);
1184
+ const testFile = path12.join(cwd, "tests", "tools", `${name}.test.ts`);
1185
1185
  if (!await pathExists(testFile)) {
1186
1186
  logger.error(`Test file not found: ${testFile}`);
1187
1187
  logger.info(`Create tests with: ${chalk4.cyan(`agentforge tool:create ${name} --test`)}`);
@@ -1213,28 +1213,36 @@ async function toolPublishCommand(name, options) {
1213
1213
  logger.warn("Dry run mode - no actual publishing will occur");
1214
1214
  logger.newLine();
1215
1215
  }
1216
- const cwd = process.cwd();
1217
- const packageManager = await detectPackageManager(cwd);
1218
- logger.startSpinner("Running tests...");
1219
- try {
1220
- await runScript(cwd, "test", packageManager);
1221
- logger.succeedSpinner("Tests passed");
1222
- } catch (error) {
1223
- logger.failSpinner("Tests failed");
1224
- logger.error("Cannot publish tool with failing tests");
1225
- process.exit(1);
1216
+ const { toolPath, hasTestScript, hasBuildScript } = await resolveToolPath(name);
1217
+ const packageManager = await detectPackageManager(toolPath);
1218
+ if (hasTestScript) {
1219
+ logger.startSpinner("Running tests...");
1220
+ try {
1221
+ await runScript(toolPath, "test", packageManager);
1222
+ logger.succeedSpinner("Tests passed");
1223
+ } catch (error) {
1224
+ logger.failSpinner("Tests failed");
1225
+ logger.error("Cannot publish tool with failing tests");
1226
+ process.exit(1);
1227
+ }
1228
+ } else {
1229
+ logger.info("\u26A0\uFE0F Skipping tests (no test script found)");
1226
1230
  }
1227
- logger.startSpinner("Building tool...");
1228
- try {
1229
- await runScript(cwd, "build", packageManager);
1230
- logger.succeedSpinner("Build completed");
1231
- } catch (error) {
1232
- logger.failSpinner("Build failed");
1233
- process.exit(1);
1231
+ if (hasBuildScript) {
1232
+ logger.startSpinner("Building tool...");
1233
+ try {
1234
+ await runScript(toolPath, "build", packageManager);
1235
+ logger.succeedSpinner("Build completed");
1236
+ } catch (error) {
1237
+ logger.failSpinner("Build failed");
1238
+ process.exit(1);
1239
+ }
1240
+ } else {
1241
+ logger.info("\u26A0\uFE0F Skipping build (no build script found)");
1234
1242
  }
1235
1243
  logger.startSpinner(options.dryRun ? "Running dry-run publish..." : "Publishing to npm...");
1236
1244
  try {
1237
- await publishPackage(cwd, {
1245
+ await publishPackage(toolPath, {
1238
1246
  tag: options.tag,
1239
1247
  access: "public",
1240
1248
  dryRun: options.dryRun
@@ -1277,6 +1285,95 @@ async function toolPublishCommand(name, options) {
1277
1285
  process.exit(1);
1278
1286
  }
1279
1287
  }
1288
+ async function resolveToolPath(name) {
1289
+ const cwd = process.cwd();
1290
+ const isScopedPackage = /^@[^/]+\/[^/]+$/.test(name);
1291
+ const isPath = !isScopedPackage && (name.includes("/") || name.includes("\\"));
1292
+ if (isPath) {
1293
+ const absolutePath = path12.isAbsolute(name) ? name : path12.resolve(cwd, name);
1294
+ return await validateToolPath(absolutePath, name);
1295
+ }
1296
+ const cwdPackageJsonPath = path12.join(cwd, "package.json");
1297
+ if (await fs.pathExists(cwdPackageJsonPath)) {
1298
+ const cwdPackageJson = await fs.readJson(cwdPackageJsonPath);
1299
+ if (cwdPackageJson.name === name || cwdPackageJson.name === `@agentforge/${name}`) {
1300
+ return await validateToolPath(cwd, name);
1301
+ }
1302
+ }
1303
+ const unscopedName = isScopedPackage ? name.split("/")[1] : name;
1304
+ const possiblePaths = [
1305
+ path12.join(cwd, "tools", name),
1306
+ path12.join(cwd, "packages", name),
1307
+ path12.join(cwd, name)
1308
+ ];
1309
+ if (isScopedPackage) {
1310
+ possiblePaths.push(
1311
+ path12.join(cwd, "tools", unscopedName),
1312
+ path12.join(cwd, "packages", unscopedName),
1313
+ path12.join(cwd, unscopedName)
1314
+ );
1315
+ }
1316
+ for (const possiblePath of possiblePaths) {
1317
+ if (await fs.pathExists(possiblePath)) {
1318
+ const packageJsonPath = path12.join(possiblePath, "package.json");
1319
+ if (await fs.pathExists(packageJsonPath)) {
1320
+ return await validateToolPath(possiblePath, name);
1321
+ }
1322
+ }
1323
+ }
1324
+ logger.error(`Could not find tool package: ${chalk4.cyan(name)}`);
1325
+ logger.newLine();
1326
+ logger.info("Tried the following locations:");
1327
+ logger.list([
1328
+ `Current directory (${cwd})`,
1329
+ ...possiblePaths.map((p) => path12.relative(cwd, p) || ".")
1330
+ ]);
1331
+ logger.newLine();
1332
+ logger.info("Make sure you are either:");
1333
+ logger.list([
1334
+ "In the tool package directory with matching package.json name",
1335
+ "Providing a path to the tool package directory",
1336
+ "Have the tool in a standard location (./tools/<name>, ./packages/<name>)"
1337
+ ]);
1338
+ process.exit(1);
1339
+ }
1340
+ async function validateToolPath(toolPath, expectedName) {
1341
+ if (!await fs.pathExists(toolPath)) {
1342
+ logger.error(`Tool directory not found: ${toolPath}`);
1343
+ process.exit(1);
1344
+ }
1345
+ const packageJsonPath = path12.join(toolPath, "package.json");
1346
+ if (!await fs.pathExists(packageJsonPath)) {
1347
+ logger.error(`package.json not found in: ${toolPath}`);
1348
+ logger.info("Tool packages must have a package.json file");
1349
+ process.exit(1);
1350
+ }
1351
+ let packageJson;
1352
+ try {
1353
+ packageJson = await fs.readJson(packageJsonPath);
1354
+ } catch (error) {
1355
+ logger.error(`Failed to read package.json: ${error.message}`);
1356
+ process.exit(1);
1357
+ }
1358
+ if (!packageJson.name) {
1359
+ logger.error('package.json must have a "name" field');
1360
+ process.exit(1);
1361
+ }
1362
+ const packageName = packageJson.name;
1363
+ const nameMatches = packageName === expectedName || packageName === `@agentforge/${expectedName}` || packageName.endsWith(`/${expectedName}`);
1364
+ if (!nameMatches) {
1365
+ logger.warn(`Package name mismatch: expected ${chalk4.cyan(expectedName)}, found ${chalk4.cyan(packageName)}`);
1366
+ logger.info(`Publishing package: ${chalk4.cyan(packageName)}`);
1367
+ logger.newLine();
1368
+ }
1369
+ const hasTestScript = !!packageJson.scripts?.test;
1370
+ const hasBuildScript = !!packageJson.scripts?.build;
1371
+ return {
1372
+ toolPath,
1373
+ hasTestScript,
1374
+ hasBuildScript
1375
+ };
1376
+ }
1280
1377
 
1281
1378
  // src/index.ts
1282
1379
  var program = new Command();