@mutagent/cli 0.1.16 → 0.1.18

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/bin/cli.js CHANGED
@@ -235,7 +235,7 @@ function handleError(error, isJson) {
235
235
  }
236
236
  const message = error instanceof Error ? error.message : "Unknown error";
237
237
  if (isJson) {
238
- console.log(JSON.stringify({ error: { code: "UNKNOWN_ERROR", message } }, null, 2));
238
+ console.log(JSON.stringify({ success: false, error: message, code: "UNKNOWN_ERROR" }, null, 2));
239
239
  } else {
240
240
  console.error(`Error: ${message}`);
241
241
  }
@@ -256,11 +256,10 @@ var init_errors = __esm(() => {
256
256
  }
257
257
  toJSON() {
258
258
  return {
259
- error: {
260
- code: this.code,
261
- message: this.message,
262
- suggestion: this.suggestion
263
- }
259
+ success: false,
260
+ error: this.message,
261
+ code: this.code,
262
+ suggestedAction: this.suggestion
264
263
  };
265
264
  }
266
265
  };
@@ -300,7 +299,7 @@ var init_errors = __esm(() => {
300
299
  `);
301
300
  WorkspaceContextError = class WorkspaceContextError extends MutagentError {
302
301
  constructor(message) {
303
- super("WORKSPACE_CONTEXT_MISSING", message ?? "Workspace context is required but not configured", "Run: mutagent config set workspace <workspace-id>", 3);
302
+ super("WORKSPACE_REQUIRED", message ?? "Workspace context is required but not configured", "Run: mutagent config set workspace <workspace-id>", 3);
304
303
  }
305
304
  };
306
305
  ValidationError = class ValidationError extends MutagentError {
@@ -577,6 +576,13 @@ class SDKClientWrapper {
577
576
  this.handleError(error);
578
577
  }
579
578
  }
579
+ async deleteEvaluation(evaluationId) {
580
+ try {
581
+ await this.request(`/api/prompts/evaluations/${evaluationId}`, { method: "DELETE" });
582
+ } catch (error) {
583
+ this.handleError(error);
584
+ }
585
+ }
580
586
  async getEvaluationResults(runId) {
581
587
  try {
582
588
  return await this.request(`/api/prompts/evaluations/${runId}/result`);
@@ -592,7 +598,7 @@ class SDKClientWrapper {
592
598
  datasetId: parseInt(datasetId, 10),
593
599
  config: {
594
600
  maxIterations: config?.maxIterations ?? 1,
595
- targetScore: config?.targetScore,
601
+ targetScore: config?.targetScore ?? 0.8,
596
602
  patience: config?.patience,
597
603
  model: config?.model,
598
604
  ...config?.evalModel ? { tuningParams: { evaluationModel: config.evalModel } } : {}
@@ -931,8 +937,8 @@ var init_sdk_client = __esm(() => {
931
937
  });
932
938
 
933
939
  // src/bin/cli.ts
934
- import { Command as Command14 } from "commander";
935
- import chalk18 from "chalk";
940
+ import { Command as Command15 } from "commander";
941
+ import chalk19 from "chalk";
936
942
  import { readFileSync as readFileSync12 } from "fs";
937
943
  import { join as join7, dirname } from "path";
938
944
  import { fileURLToPath } from "url";
@@ -1047,9 +1053,14 @@ ${String(data.length)} result(s)`));
1047
1053
  console.log(chalk.green(`✓ ${message}`));
1048
1054
  }
1049
1055
  }
1050
- error(message) {
1056
+ error(message, code, suggestedAction) {
1051
1057
  if (this.format === "json") {
1052
- console.log(JSON.stringify({ success: false, error: message }, null, 2));
1058
+ const result = { success: false, error: message };
1059
+ if (code)
1060
+ result.code = code;
1061
+ if (suggestedAction)
1062
+ result.suggestedAction = suggestedAction;
1063
+ console.log(JSON.stringify(result, null, 2));
1053
1064
  } else {
1054
1065
  console.log(chalk.red(`✗ ${message}`));
1055
1066
  }
@@ -1067,6 +1078,24 @@ ${String(data.length)} result(s)`));
1067
1078
  }
1068
1079
  }
1069
1080
  }
1081
+ function createSpinner(text, isJson) {
1082
+ if (isJson) {
1083
+ const noop = () => {
1084
+ return;
1085
+ };
1086
+ return {
1087
+ start: noop,
1088
+ stop: noop,
1089
+ succeed: noop,
1090
+ fail: noop
1091
+ };
1092
+ }
1093
+ const start = async () => {
1094
+ const { default: ora } = await import("ora");
1095
+ return ora(text).start();
1096
+ };
1097
+ return { start };
1098
+ }
1070
1099
 
1071
1100
  // src/commands/auth.ts
1072
1101
  init_errors();
@@ -2563,9 +2592,17 @@ async function runGuidedEvalCreator(promptId) {
2563
2592
  }
2564
2593
  }]);
2565
2594
  let targetField;
2566
- if (allFields.length > 0) {
2595
+ const usedParams = new Set(criteria.map((c) => c.evaluationParameter));
2596
+ const availableFields = allFields.filter((f) => {
2597
+ const param = f.startsWith("output.") ? f.slice("output.".length) : f.startsWith("input.") ? f.slice("input.".length) : f;
2598
+ return !usedParams.has(param);
2599
+ });
2600
+ if (availableFields.length === 0 && allFields.length > 0) {
2601
+ console.log(chalk5.yellow(" All detected schema fields are already used by existing criteria."));
2602
+ }
2603
+ if (availableFields.length > 0) {
2567
2604
  const fieldChoices = [
2568
- ...allFields.map((f) => ({ name: f, value: f })),
2605
+ ...availableFields.map((f) => ({ name: f, value: f })),
2569
2606
  { name: "(custom field name)", value: "__custom__" }
2570
2607
  ];
2571
2608
  const { field } = await inquirer3.prompt([{
@@ -2623,6 +2660,10 @@ async function runGuidedEvalCreator(promptId) {
2623
2660
  scoringRubric = rubric;
2624
2661
  }
2625
2662
  const evaluationParameter = targetField.startsWith("output.") ? targetField.slice("output.".length) : targetField.startsWith("input.") ? targetField.slice("input.".length) : targetField;
2663
+ if (criteria.some((c) => c.evaluationParameter === evaluationParameter)) {
2664
+ console.log(chalk5.red(` Error: "${evaluationParameter}" is already used by another criterion. Each criterion must target a unique output field.`));
2665
+ continue;
2666
+ }
2626
2667
  criteria.push({
2627
2668
  name: criterionName.trim(),
2628
2669
  description: scoringRubric,
@@ -2806,7 +2847,7 @@ function updateMutationContext(updater) {
2806
2847
  } catch {}
2807
2848
  }
2808
2849
  var PREREQUISITES_TEXT = `
2809
- ${chalk7.yellow("Prerequisites:")}
2850
+ ${chalk7.red("Prerequisites (required):")}
2810
2851
  1. Evaluation criteria defined ${chalk7.dim("(via dashboard or evaluation create)")}
2811
2852
  2. Dataset uploaded ${chalk7.dim("mutagent prompts dataset list <prompt-id>")}
2812
2853
  ${chalk7.dim("Note: LLM provider config is only required when the server uses external providers (USE_EXT_PROVIDERS=true)")}`;
@@ -2990,7 +3031,7 @@ Examples:
2990
3031
  Subcommands:
2991
3032
  list, get, create, update, delete
2992
3033
  dataset list|add|remove
2993
- evaluation list|create|results
3034
+ evaluation list|create|delete|results
2994
3035
  optimize start|status|results
2995
3036
  `);
2996
3037
  prompts.command("list").description("List all prompts").option("-l, --limit <n>", "Limit results", "50").addHelpText("after", `
@@ -3068,20 +3109,24 @@ ${chalk7.dim("Tip: Combine --with-datasets and --with-evals to fetch all nested
3068
3109
  });
3069
3110
  prompts.command("create").description("Create a new prompt").option("-d, --data <json>", "Prompt as JSON string (recommended — curl-style inline)").option("-f, --file <path>", "Create from JSON file").option("--raw-file <path>", "Create from plain text file (used as rawPrompt)").option("-n, --name <name>", "Prompt name").option("-c, --content <content>", "Prompt content (rawPrompt) [DEPRECATED: use --raw]").option("-r, --raw <text>", "Raw prompt text (single prompt)").option("--system <text>", "System prompt (use with --human)").option("--human <text>", "Human prompt (use with --system)").option("--messages <json>", `Messages array as JSON (e.g., '[{"role":"system","content":"..."}]')`).option("--output-schema <json>", "Output schema as JSON string (required for optimization)").addHelpText("after", `
3070
3111
  Examples:
3071
- ${chalk7.dim("$")} mutagent prompts create -d '{"name":"summarizer","systemPrompt":"Summarize","humanPrompt":"{{text}}","outputSchema":{"type":"object","properties":{"summary":{"type":"string"}}}}'
3072
- ${chalk7.dim("$")} mutagent prompts create --name "my-prompt" --system "You are helpful" --human "Hello" --output-schema '{"type":"object","properties":{"result":{"type":"string"}}}'
3073
- ${chalk7.dim("$")} mutagent prompts create --file prompt.json
3074
- ${chalk7.dim("$")} mutagent prompts create --name "raw-prompt" --raw "Summarize: {{text}}"
3112
+ ${chalk7.dim("$")} mutagent prompts create --name "my-prompt" --system "You are helpful" --human "{{input}}" --output-schema '{"type":"object","properties":{"result":{"type":"string","description":"The result"}}}'
3113
+ ${chalk7.dim("$")} mutagent prompts create --name "raw-prompt" --raw "Summarize: {{text}}" --output-schema '{"type":"object","properties":{"summary":{"type":"string","description":"Summary"}}}'
3114
+ ${chalk7.dim("$")} mutagent prompts create -d '{"name":"summarizer","systemPrompt":"Summarize","humanPrompt":"{{text}}","outputSchema":{"type":"object","properties":{"summary":{"type":"string","description":"Summary"}}}}'
3115
+ ${chalk7.dim("$")} mutagent prompts create --file prompt.json ${chalk7.dim("# full prompt object as JSON file")}
3075
3116
 
3076
3117
  Prompt Input Methods (pick one, priority order):
3077
- -d, --data Inline JSON object (recommended for CI/scripts)
3078
- --file Load from JSON file (full prompt object)
3079
- --raw-file Load plain text file as raw prompt
3080
- --system/--human Structured system + user message pair
3118
+ --system/--human Structured system + user message pair ${chalk7.green("(recommended)")}
3081
3119
  --raw Single raw prompt text with {{variables}}
3120
+ -d, --data Inline JSON object (CI/scripts/agents)
3082
3121
  --messages Full messages array as JSON
3122
+ --raw-file Load plain text file as raw prompt
3123
+ --file Load from JSON file (full prompt object)
3083
3124
 
3084
- ${chalk7.dim("Note: --data and --file are mutually exclusive. outputSchema is required (include in --data/--file or use --output-schema).")}
3125
+ Expected JSON (--data):
3126
+ ${chalk7.dim(`'{"name":"my-prompt","systemPrompt":"You are...","humanPrompt":"{{input}}","outputSchema":{"type":"object","properties":{"result":{"type":"string","description":"The result"}}},"inputSchema":{"type":"object","properties":{"input":{"type":"string","description":"User input"}}}}'`)}
3127
+
3128
+ ${chalk7.yellow("Note: Prefer --system/--human or --data over --file to avoid stale files living in your repo.")}
3129
+ ${chalk7.red("outputSchema is required.")} ${chalk7.dim("--data and --file are mutually exclusive.")}
3085
3130
  `).action(async (options) => {
3086
3131
  const isJson = getJsonFlag(prompts);
3087
3132
  const output = new OutputFormatter(isJson ? "json" : "table");
@@ -3229,15 +3274,15 @@ Or use interactive mode (TTY) to define variables step by step.`);
3229
3274
  });
3230
3275
  prompts.command("update").description("Update a prompt").argument("<id>", "Prompt ID (from: mutagent prompts list)").option("-d, --data <json>", "Update fields as JSON string (recommended — curl-style inline)").option("-f, --file <path>", "Update from JSON file").option("--raw-file <path>", "Update from plain text file (used as rawPrompt)").option("-n, --name <name>", "New name").option("-c, --content <content>", "New content (rawPrompt) [DEPRECATED: use --raw]").option("-r, --raw <text>", "Raw prompt text (single prompt)").option("--system <text>", "System prompt (use with --human)").option("--human <text>", "Human prompt (use with --system)").option("--messages <json>", `Messages array as JSON (e.g., '[{"role":"system","content":"..."}]')`).option("--input-schema <json>", "Input schema as JSON string").option("--input-schema-file <path>", "Input schema from JSON file").option("--output-schema <json>", "Output schema as JSON string").option("--output-schema-file <path>", "Output schema from JSON file").addHelpText("after", `
3231
3276
  Examples:
3232
- ${chalk7.dim("$")} mutagent prompts update <id> -d '{"name":"new-name","systemPrompt":"Updated prompt"}'
3277
+ ${chalk7.dim("$")} mutagent prompts update <id> --system "Updated system prompt" --human "{{input}}"
3233
3278
  ${chalk7.dim("$")} mutagent prompts update <id> --name "new-name"
3234
- ${chalk7.dim("$")} mutagent prompts update <id> --system "Updated system prompt"
3235
- ${chalk7.dim("$")} mutagent prompts update <id> --raw-file updated-prompt.txt
3236
- ${chalk7.dim("$")} mutagent prompts update <id> --file updated-prompt.json
3279
+ ${chalk7.dim("$")} mutagent prompts update <id> --raw "Summarize: {{text}}"
3280
+ ${chalk7.dim("$")} mutagent prompts update <id> -d '{"name":"new-name","systemPrompt":"Updated prompt"}'
3237
3281
  ${chalk7.dim("$")} mutagent prompts update <id> --input-schema '{"type":"object","properties":{"text":{"type":"string","description":"Input text"}}}'
3238
- ${chalk7.dim("$")} mutagent prompts update <id> --input-schema-file schema.json
3282
+ ${chalk7.dim("$")} mutagent prompts update <id> --file updated-prompt.json ${chalk7.dim("# full prompt object")}
3239
3283
 
3240
- ${chalk7.dim("Note: --data and --file are mutually exclusive. CLI flags (--name) override --data fields.")}
3284
+ ${chalk7.yellow("Note: Prefer --system/--human or --data over --file to avoid stale files living in your repo.")}
3285
+ ${chalk7.dim("--data and --file are mutually exclusive. CLI flags (--name) override --data fields.")}
3241
3286
  `).action(async (id, options) => {
3242
3287
  const isJson = getJsonFlag(prompts);
3243
3288
  const output = new OutputFormatter(isJson ? "json" : "table");
@@ -3378,7 +3423,7 @@ ${chalk7.dim("Tip: Use --force to skip confirmation (required for non-interactiv
3378
3423
  const isJson = getJsonFlag(prompts);
3379
3424
  const output = new OutputFormatter(isJson ? "json" : "table");
3380
3425
  try {
3381
- if (!options.force && !isJson) {
3426
+ if (!options.force && !isJson && !process.env.MUTAGENT_NON_INTERACTIVE) {
3382
3427
  const inquirer3 = (await import("inquirer")).default;
3383
3428
  const answers = await inquirer3.prompt([{
3384
3429
  type: "confirm",
@@ -3392,18 +3437,37 @@ ${chalk7.dim("Tip: Use --force to skip confirmation (required for non-interactiv
3392
3437
  }
3393
3438
  }
3394
3439
  const client = getSDKClient();
3395
- await client.deletePrompt(id);
3396
- output.success(`Deleted prompt: ${id}`);
3440
+ try {
3441
+ await client.deletePrompt(id);
3442
+ } catch (deleteError) {
3443
+ if (deleteError instanceof ApiError && deleteError.statusCode === 404) {
3444
+ if (isJson) {
3445
+ output.output({ success: true, deletedId: id });
3446
+ } else {
3447
+ output.success(`Prompt ${id} already deleted (idempotent)`);
3448
+ }
3449
+ return;
3450
+ }
3451
+ throw deleteError;
3452
+ }
3453
+ if (isJson) {
3454
+ output.output({ success: true, deletedId: id });
3455
+ } else {
3456
+ output.success(`Deleted prompt: ${id}`);
3457
+ }
3397
3458
  } catch (error) {
3398
3459
  handleError(error, isJson);
3399
3460
  }
3400
3461
  });
3401
- const dataset = prompts.command("dataset").description("Manage datasets for prompts").addHelpText("after", `
3462
+ const dataset = new Command3("dataset").description("Manage datasets for prompts").addHelpText("after", `
3402
3463
  Examples:
3403
3464
  ${chalk7.dim("$")} mutagent prompts dataset list <prompt-id>
3404
3465
  ${chalk7.dim("$")} mutagent prompts dataset add <prompt-id> --file dataset.jsonl
3405
3466
  ${chalk7.dim("$")} mutagent prompts dataset remove <prompt-id> <dataset-id>
3406
- `);
3467
+ `).action(() => {
3468
+ dataset.help();
3469
+ });
3470
+ prompts.addCommand(dataset);
3407
3471
  dataset.command("list").description("List datasets for a prompt").argument("<prompt-id>", "Prompt ID (from: mutagent prompts list)").addHelpText("after", `
3408
3472
  Examples:
3409
3473
  ${chalk7.dim("$")} mutagent prompts dataset list <prompt-id>
@@ -3433,10 +3497,9 @@ Examples:
3433
3497
  });
3434
3498
  dataset.command("add").description("Add dataset to a prompt").argument("<prompt-id>", "Prompt ID (from: mutagent prompts list)").option("-f, --file <path>", "Dataset file (JSON array, JSONL, or CSV)").option("-d, --data <json>", "Inline JSON array of dataset items").option("-n, --name <name>", "Dataset name (default: timestamped)").addHelpText("after", `
3435
3499
  Examples:
3436
- ${chalk7.dim("$")} mutagent prompts dataset add <prompt-id> --file dataset.json
3437
- ${chalk7.dim("$")} mutagent prompts dataset add <prompt-id> --file dataset.jsonl --name "My Dataset"
3438
- ${chalk7.dim("$")} mutagent prompts dataset add <prompt-id> --file dataset.csv
3439
3500
  ${chalk7.dim("$")} mutagent prompts dataset add <prompt-id> -d '[{"input":{"text":"hello"},"expectedOutput":{"result":"world"}}]'
3501
+ ${chalk7.dim("$")} mutagent prompts dataset add <prompt-id> -d '[{"input":{"text":"hello"},"expectedOutput":{"result":"world"}}]' --name "My Dataset"
3502
+ ${chalk7.dim("$")} mutagent prompts dataset add <prompt-id> --file dataset.jsonl ${chalk7.dim("# also supports .json, .csv")}
3440
3503
 
3441
3504
  Supported file formats:
3442
3505
  ${chalk7.dim(".json")} JSON array of objects: [{"input": {...}, "expectedOutput": {...}}, ...]
@@ -3447,7 +3510,12 @@ Inline data format (-d):
3447
3510
  JSON array of objects, e.g.:
3448
3511
  ${chalk7.dim('[{"input": {"text": "hello"}, "expectedOutput": {"result": "world"}}]')}
3449
3512
 
3450
- ${chalk7.dim("Note: --file and -d are mutually exclusive. Provide one or the other.")}
3513
+ Expected item format:
3514
+ ${chalk7.dim('{"input": {"<field>": "<value>"}, "expectedOutput": {"<field>": "<value>"}}')}
3515
+
3516
+ ${chalk7.red("Required: --data or --file must be provided.")}
3517
+ ${chalk7.yellow("Note: Prefer -d/--data for inline JSON over --file to avoid stale files living in your repo.")}
3518
+ ${chalk7.dim("--file and -d are mutually exclusive.")}
3451
3519
  `).action(async (promptId, options) => {
3452
3520
  const isJson = getJsonFlag(prompts);
3453
3521
  const output = new OutputFormatter(isJson ? "json" : "table");
@@ -3528,27 +3596,61 @@ ${chalk7.dim("Note: --file and -d are mutually exclusive. Provide one or the oth
3528
3596
  handleError(error, isJson);
3529
3597
  }
3530
3598
  });
3531
- dataset.command("remove").description("Remove dataset from a prompt").argument("<prompt-id>", "Prompt ID (from: mutagent prompts list)").argument("<dataset-id>", "Dataset ID (from: mutagent prompts dataset list <prompt-id>)").addHelpText("after", `
3599
+ dataset.command("remove").description("Remove dataset from a prompt").argument("<prompt-id>", "Prompt ID (from: mutagent prompts list)").argument("<dataset-id>", "Dataset ID (from: mutagent prompts dataset list <prompt-id>)").option("--force", "Skip confirmation").addHelpText("after", `
3532
3600
  Examples:
3533
3601
  ${chalk7.dim("$")} mutagent prompts dataset remove <prompt-id> <dataset-id>
3534
- ${chalk7.dim("$")} mutagent prompts dataset remove <prompt-id> <dataset-id> --json
3535
- `).action(async (promptId, datasetId) => {
3602
+ ${chalk7.dim("$")} mutagent prompts dataset remove <prompt-id> <dataset-id> --force
3603
+ ${chalk7.dim("$")} mutagent prompts dataset remove <prompt-id> <dataset-id> --force --json
3604
+ `).action(async (promptId, datasetId, options) => {
3536
3605
  const isJson = getJsonFlag(prompts);
3537
3606
  const output = new OutputFormatter(isJson ? "json" : "table");
3538
3607
  try {
3608
+ if (!options.force && !isJson && !process.env.MUTAGENT_NON_INTERACTIVE) {
3609
+ const inquirer3 = (await import("inquirer")).default;
3610
+ const answers = await inquirer3.prompt([{
3611
+ type: "confirm",
3612
+ name: "confirm",
3613
+ message: `Remove dataset ${datasetId} from prompt ${promptId}?`,
3614
+ default: false
3615
+ }]);
3616
+ if (!answers.confirm) {
3617
+ output.info("Cancelled");
3618
+ return;
3619
+ }
3620
+ }
3539
3621
  const client = getSDKClient();
3540
- await client.removeDataset(promptId, datasetId);
3541
- output.success(`Removed dataset ${datasetId} from prompt ${promptId}`);
3622
+ try {
3623
+ await client.removeDataset(promptId, datasetId);
3624
+ } catch (deleteError) {
3625
+ if (deleteError instanceof ApiError && deleteError.statusCode === 404) {
3626
+ if (isJson) {
3627
+ output.output({ success: true, deletedId: datasetId });
3628
+ } else {
3629
+ output.success(`Dataset ${datasetId} already removed (idempotent)`);
3630
+ }
3631
+ return;
3632
+ }
3633
+ throw deleteError;
3634
+ }
3635
+ if (isJson) {
3636
+ output.output({ success: true, deletedId: datasetId });
3637
+ } else {
3638
+ output.success(`Removed dataset ${datasetId} from prompt ${promptId}`);
3639
+ }
3542
3640
  } catch (error) {
3543
3641
  handleError(error, isJson);
3544
3642
  }
3545
3643
  });
3546
- const evaluation = prompts.command("evaluation").description("Manage evaluations for prompts").addHelpText("after", `
3644
+ const evaluation = new Command3("evaluation").description("Manage evaluations for prompts").addHelpText("after", `
3547
3645
  Examples:
3548
3646
  ${chalk7.dim("$")} mutagent prompts evaluation list <prompt-id>
3549
3647
  ${chalk7.dim("$")} mutagent prompts evaluation create <prompt-id> --name "My Eval"
3648
+ ${chalk7.dim("$")} mutagent prompts evaluation delete <evaluation-id>
3550
3649
  ${chalk7.dim("$")} mutagent prompts evaluation results <run-id>
3551
- `);
3650
+ `).action(() => {
3651
+ evaluation.help();
3652
+ });
3653
+ prompts.addCommand(evaluation);
3552
3654
  evaluation.command("list").description("List evaluations for a prompt").argument("<prompt-id>", "Prompt ID (from: mutagent prompts list)").addHelpText("after", `
3553
3655
  Examples:
3554
3656
  ${chalk7.dim("$")} mutagent prompts evaluation list <prompt-id>
@@ -3578,17 +3680,19 @@ Examples:
3578
3680
  });
3579
3681
  evaluation.command("create").description("Create an evaluation configuration for a prompt").argument("<prompt-id>", "Prompt ID (from: mutagent prompts list)").option("-d, --data <json>", "Evaluation as JSON string (recommended — curl-style inline)").option("-n, --name <name>", "Evaluation name (required unless --guided)").option("-f, --file <path>", "Load evaluation criteria from JSON file").option("--description <text>", "Evaluation description").option("--dataset <id>", "Dataset ID to associate (from: mutagent prompts dataset list)").option("--guided", "Interactive guided mode — build criteria step by step").addHelpText("after", `
3580
3682
  Examples:
3581
- ${chalk7.dim("$")} mutagent prompts evaluation create <prompt-id> --guided
3582
- ${chalk7.dim("$")} mutagent prompts evaluation create <prompt-id> --data '{"name":"Accuracy","evalConfig":{"criteria":[...]}}' --name "Accuracy"
3583
- ${chalk7.dim("$")} mutagent prompts evaluation create <prompt-id> --name "Accuracy Check"
3683
+ ${chalk7.dim("$")} mutagent prompts evaluation create <prompt-id> --guided ${chalk7.dim("# recommended: interactive walkthrough")}
3684
+ ${chalk7.dim("$")} mutagent prompts evaluation create <prompt-id> --name "Accuracy" -d '{"evalConfig":{"criteria":[{"name":"Accuracy","description":"Score 1.0 if output matches expected","evaluationParameter":"result"}]}}'
3584
3685
  ${chalk7.dim("$")} mutagent prompts evaluation create <prompt-id> --name "Full Eval" --file criteria.json --dataset <dataset-id>
3585
- ${chalk7.dim("$")} mutagent prompts evaluation create <prompt-id> --name "QA" --description "Quality assurance eval" --json
3586
3686
 
3587
- Criteria format (each criterion needs: name, description, evaluationParameter):
3588
- ${chalk7.dim('{ "name": "...", "evalConfig": { "criteria": [{ "name": "...", "description": "...", "evaluationParameter": "outputField" }] } }')}
3687
+ Expected JSON (--data):
3688
+ ${chalk7.dim('{"evalConfig":{"criteria":[{"name":"<name>","description":"<scoring rubric>","evaluationParameter":"<output field to score>"}]}}')}
3589
3689
 
3590
- ${chalk7.dim("Note: --data and --file are mutually exclusive. CLI flags (--name, --description) override --data fields.")}
3591
- ${chalk7.dim("Tip: Use --guided for an interactive walkthrough of criteria creation.")}
3690
+ ${chalk7.red("Each criterion MUST have: name, description, evaluationParameter")}
3691
+ evaluationParameter maps to output schema fields (e.g., "result", "classification")
3692
+
3693
+ ${chalk7.yellow("Note: Prefer --guided or --data over --file to avoid stale files living in your repo.")}
3694
+ ${chalk7.red("Required: --name (unless --guided). Criteria must include evaluationParameter.")}
3695
+ ${chalk7.dim("--data and --file are mutually exclusive. CLI flags (--name, --description) override --data fields.")}
3592
3696
  ${chalk7.dim("Get prompt IDs: mutagent prompts list | Get dataset IDs: mutagent prompts dataset list <prompt-id>")}
3593
3697
  `).action(async (promptId, options) => {
3594
3698
  const isJson = getJsonFlag(prompts);
@@ -3675,8 +3779,24 @@ ${chalk7.dim("Get prompt IDs: mutagent prompts list | Get dataset IDs: mutagent
3675
3779
  evalData.description = options.description;
3676
3780
  const criteria = evalData.evalConfig?.criteria;
3677
3781
  if (!criteria || !Array.isArray(criteria) || criteria.length === 0) {
3678
- throw new MutagentError("VALIDATION_ERROR", "Evaluation criteria are required. Provide criteria via --data, --file, or use --guided mode.", `Each criterion needs: name, description, evaluationParameter.
3679
-
3782
+ let fieldsHint = "";
3783
+ try {
3784
+ const client2 = getSDKClient();
3785
+ const prompt = await client2.getPrompt(promptId);
3786
+ if (prompt.outputSchema && typeof prompt.outputSchema === "object") {
3787
+ const props = prompt.outputSchema.properties;
3788
+ if (props && typeof props === "object") {
3789
+ const fields = Object.keys(props);
3790
+ if (fields.length > 0) {
3791
+ fieldsHint = `
3792
+ Detected output fields from prompt schema: ${fields.join(", ")}
3793
+ `;
3794
+ }
3795
+ }
3796
+ }
3797
+ } catch {}
3798
+ throw new MutagentError("VALIDATION_ERROR", "Evaluation criteria are required. Provide criteria via --data, --file, or use --guided mode.", `Each criterion needs: name, description, evaluationParameter (the output field to score).
3799
+ ` + fieldsHint + `
3680
3800
  Example JSON (--data flag):
3681
3801
  --data '{"evalConfig":{"criteria":[{"name":"Accuracy","description":"Score 1.0 if output matches expected, 0.0 otherwise","evaluationParameter":"classification"}]}}'
3682
3802
 
@@ -3699,6 +3819,37 @@ Or use --guided for interactive creation: mutagent prompts evaluation create <id
3699
3819
  delete c.targetField;
3700
3820
  }
3701
3821
  }
3822
+ const seenParams = new Set;
3823
+ const duplicateParams = [];
3824
+ for (const c of criteria) {
3825
+ const param = c.evaluationParameter;
3826
+ if (param) {
3827
+ if (seenParams.has(param)) {
3828
+ duplicateParams.push(param);
3829
+ }
3830
+ seenParams.add(param);
3831
+ }
3832
+ }
3833
+ if (duplicateParams.length > 0) {
3834
+ let availableFields = [];
3835
+ try {
3836
+ const client2 = getSDKClient();
3837
+ const prompt = await client2.getPrompt(promptId);
3838
+ if (prompt.outputSchema && typeof prompt.outputSchema === "object") {
3839
+ const props = prompt.outputSchema.properties;
3840
+ if (props && typeof props === "object") {
3841
+ availableFields = Object.keys(props);
3842
+ }
3843
+ }
3844
+ } catch {}
3845
+ const uniqueDupes = [...new Set(duplicateParams)];
3846
+ const fieldsHint = availableFields.length > 0 ? `
3847
+ Available output fields: ${availableFields.join(", ")}` : "";
3848
+ throw new MutagentError("VALIDATION_ERROR", `Duplicate evaluationParameter: "${uniqueDupes.join('", "')}". Each criterion must target a unique output field.`, "Each criterion scores a different output field. Fix by changing the evaluationParameter to a unique field." + fieldsHint + `
3849
+
3850
+ Example:
3851
+ --data '{"evalConfig":{"criteria":[{"name":"Accuracy","description":"...","evaluationParameter":"classification"},{"name":"Confidence","description":"...","evaluationParameter":"confidence"}]}}'`);
3852
+ }
3702
3853
  if (options.dataset) {
3703
3854
  evalData.datasetId = parseInt(options.dataset, 10);
3704
3855
  if (isNaN(evalData.datasetId)) {
@@ -3746,15 +3897,63 @@ Examples:
3746
3897
  handleError(error, isJson);
3747
3898
  }
3748
3899
  });
3749
- const optimize = prompts.command("optimize").description("Manage prompt optimization jobs").addHelpText("after", `
3900
+ evaluation.command("delete").description("Delete an evaluation").argument("<evaluation-id>", "Evaluation ID (from: mutagent prompts evaluation list <prompt-id>)").option("--force", "Skip confirmation").addHelpText("after", `
3901
+ Examples:
3902
+ ${chalk7.dim("$")} mutagent prompts evaluation delete <evaluation-id>
3903
+ ${chalk7.dim("$")} mutagent prompts evaluation delete <evaluation-id> --force
3904
+ ${chalk7.dim("$")} mutagent prompts evaluation delete <evaluation-id> --force --json
3905
+ `).action(async (evaluationId, options) => {
3906
+ const isJson = getJsonFlag(prompts);
3907
+ const output = new OutputFormatter(isJson ? "json" : "table");
3908
+ try {
3909
+ if (!options.force && !isJson && !process.env.MUTAGENT_NON_INTERACTIVE) {
3910
+ const inquirer3 = (await import("inquirer")).default;
3911
+ const answers = await inquirer3.prompt([{
3912
+ type: "confirm",
3913
+ name: "confirm",
3914
+ message: `Delete evaluation ${evaluationId}?`,
3915
+ default: false
3916
+ }]);
3917
+ if (!answers.confirm) {
3918
+ output.info("Cancelled");
3919
+ return;
3920
+ }
3921
+ }
3922
+ const client = getSDKClient();
3923
+ try {
3924
+ await client.deleteEvaluation(evaluationId);
3925
+ } catch (deleteError) {
3926
+ if (deleteError instanceof ApiError && deleteError.statusCode === 404) {
3927
+ if (isJson) {
3928
+ output.output({ success: true, deletedId: evaluationId });
3929
+ } else {
3930
+ output.success(`Evaluation ${evaluationId} already deleted (idempotent)`);
3931
+ }
3932
+ return;
3933
+ }
3934
+ throw deleteError;
3935
+ }
3936
+ if (isJson) {
3937
+ output.output({ success: true, deletedId: evaluationId });
3938
+ } else {
3939
+ output.success(`Evaluation ${evaluationId} deleted successfully`);
3940
+ }
3941
+ } catch (error) {
3942
+ handleError(error, isJson);
3943
+ }
3944
+ });
3945
+ const optimize = new Command3("optimize").description("Manage prompt optimization jobs").addHelpText("after", `
3750
3946
  Examples:
3751
3947
  ${chalk7.dim("$")} mutagent prompts optimize start <prompt-id> --dataset <dataset-id>
3752
3948
  ${chalk7.dim("$")} mutagent prompts optimize status <job-id>
3753
3949
  ${chalk7.dim("$")} mutagent prompts optimize results <job-id>
3754
3950
 
3755
3951
  Workflow: start -> status (poll) -> results
3756
- `);
3757
- optimize.command("start").description("Start prompt optimization").argument("<prompt-id>", "Prompt ID (from: mutagent prompts list)").requiredOption("-d, --dataset <id>", "Dataset ID for optimization (from: mutagent prompts dataset list <prompt-id>)").option("--max-iterations <n>", "Max optimization iterations (default: 3)").option("--target-score <n>", "Target accuracy 0-1 (e.g., 0.9)").option("--patience <n>", "Iterations without improvement before stopping").option("--model <model-id>", 'Target LLM model (e.g., "claude-sonnet-4-5-20250929")').option("--eval-model <model-id>", "Evaluation model (defaults to target model)").addHelpText("after", `
3952
+ `).action(() => {
3953
+ optimize.help();
3954
+ });
3955
+ prompts.addCommand(optimize);
3956
+ optimize.command("start").description("Start prompt optimization").argument("<prompt-id>", "Prompt ID (from: mutagent prompts list)").requiredOption("-d, --dataset <id>", "Dataset ID for optimization (from: mutagent prompts dataset list <prompt-id>)").option("--max-iterations <n>", "Max optimization iterations (default: 1)").option("--target-score <n>", "Target accuracy 0-1 (default: 0.8)").option("--patience <n>", "Iterations without improvement before stopping").option("--model <model-id>", 'Target LLM model (e.g., "claude-sonnet-4-5-20250929")').option("--eval-model <model-id>", "Evaluation model (defaults to target model)").addHelpText("after", `
3758
3957
  Examples:
3759
3958
  ${chalk7.dim("$")} mutagent prompts optimize start <prompt-id> --dataset <dataset-id>
3760
3959
  ${chalk7.dim("$")} mutagent prompts optimize start <prompt-id> --dataset <dataset-id> --max-iterations 5
@@ -3763,6 +3962,8 @@ Examples:
3763
3962
  ${PREREQUISITES_TEXT}
3764
3963
 
3765
3964
  ${chalk7.dim("Monitor progress with: mutagent prompts optimize status <job-id>")}
3965
+
3966
+ ${chalk7.dim("AI Agent Note: After running commands, present CLI output to the user as a status report. Use --json for structured parsing.")}
3766
3967
  `).action(async (promptId, options) => {
3767
3968
  const isJson = getJsonFlag(prompts);
3768
3969
  const output = new OutputFormatter(isJson ? "json" : "table");
@@ -4766,7 +4967,13 @@ It returns INSTRUCTIONS that AI agents execute - it does NOT auto-install packag
4766
4967
  if (options.output) {
4767
4968
  writeFileSync3(options.output, explorationInstructions);
4768
4969
  output.success(`Exploration instructions saved to ${options.output}`);
4769
- } else if (options.raw === true || isJson) {
4970
+ } else if (isJson) {
4971
+ output.output({
4972
+ type: "exploration",
4973
+ frameworks: Array.from(frameworkRegistry.keys()),
4974
+ instructions: explorationInstructions
4975
+ });
4976
+ } else if (options.raw === true) {
4770
4977
  process.stdout.write(explorationInstructions);
4771
4978
  } else {
4772
4979
  output.info("MutagenT Integration - Framework Detection");
@@ -4814,7 +5021,14 @@ AI AGENT: Execute these steps using Bash for install, Write/Edit for code change
4814
5021
  if (options.output) {
4815
5022
  writeFileSync3(options.output, instructions);
4816
5023
  output.success(`Integration instructions saved to ${options.output}`);
4817
- } else if (options.raw === true || isJson) {
5024
+ } else if (isJson) {
5025
+ output.output({
5026
+ framework: frameworkArg,
5027
+ displayName: meta?.displayName ?? frameworkArg,
5028
+ mutagentPackage: mutagentPackage ?? null,
5029
+ instructions
5030
+ });
5031
+ } else if (options.raw === true) {
4818
5032
  process.stdout.write(instructions);
4819
5033
  } else {
4820
5034
  const displayName = meta?.displayName ?? frameworkArg;
@@ -4961,19 +5175,47 @@ System Prompt:`));
4961
5175
  handleError(error, isJson);
4962
5176
  }
4963
5177
  });
4964
- agents.command("create").description("Create a new agent").option("-f, --file <path>", "Create from JSON file").option("-n, --name <name>", "Agent name").option("-s, --slug <slug>", "Agent slug (URL-friendly identifier)").option("-p, --system-prompt <prompt>", "System prompt").option("-m, --model <model>", "Model (claude-sonnet-4-5, claude-opus-4-5, claude-haiku-4-5)").option("-d, --description <description>", "Agent description").addHelpText("after", `
5178
+ agents.command("create").description("Create a new agent").option("-d, --data <json>", "Agent as JSON string (recommended for CI/scripts/agents)").option("-f, --file <path>", "Create from JSON file").option("-n, --name <name>", "Agent name").option("-s, --slug <slug>", "Agent slug (URL-friendly identifier)").option("-p, --system-prompt <prompt>", "System prompt").option("-m, --model <model>", "Model (claude-sonnet-4-5, claude-opus-4-5, claude-haiku-4-5)").option("--description <desc>", "Agent description").addHelpText("after", `
4965
5179
  Examples:
4966
- ${chalk10.dim("$")} mutagent agents create --name "Code Reviewer" --slug code-reviewer --system-prompt "Review code for bugs"
4967
- ${chalk10.dim("$")} mutagent agents create --file agent.json
4968
- ${chalk10.dim("$")} mutagent agents create --name "Helper" --slug helper --system-prompt "..." --model claude-sonnet-4-5 --json
5180
+ ${chalk10.dim("$")} mutagent agents create --name "Code Reviewer" --slug code-reviewer --system-prompt "You are a code reviewer..."
5181
+ ${chalk10.dim("$")} mutagent agents create -d '{"name":"Code Reviewer","slug":"code-reviewer","systemPrompt":"You are a code reviewer..."}'
5182
+ ${chalk10.dim("$")} mutagent agents create --file agent.json ${chalk10.dim("# full agent object as JSON file")}
4969
5183
 
4970
- ${chalk10.dim("Required: --name, --slug, and --system-prompt (or --file).")}
5184
+ Expected JSON (--data):
5185
+ ${chalk10.dim('{"name":"<name>","slug":"<slug>","systemPrompt":"<system prompt>","model":"<model-id>","description":"<description>"}')}
5186
+
5187
+ Input Methods (pick one, priority order):
5188
+ --name/--slug/... Individual flags ${chalk10.green("(recommended)")}
5189
+ -d, --data Inline JSON object (CI/scripts/agents)
5190
+ --file Load from JSON file
5191
+
5192
+ ${chalk10.yellow("Note: Prefer individual flags or --data over --file to avoid stale files living in your repo.")}
5193
+ ${chalk10.red("Required: name, slug, systemPrompt.")} ${chalk10.dim("--data and --file are mutually exclusive. CLI flags override --data fields.")}
4971
5194
  `).action(async (options) => {
4972
5195
  const isJson = getJsonFlag(agents);
4973
5196
  const output = new OutputFormatter(isJson ? "json" : "table");
4974
5197
  try {
4975
5198
  let data;
4976
- if (options.file) {
5199
+ if (options.data && options.file) {
5200
+ throw new MutagentError("INVALID_ARGUMENTS", "Cannot use --data and --file together", "Use --data for inline JSON or --file for file-based input, not both");
5201
+ }
5202
+ if (options.data) {
5203
+ try {
5204
+ data = JSON.parse(options.data);
5205
+ } catch {
5206
+ throw new MutagentError("INVALID_JSON", "Invalid JSON in --data flag", `Provide a valid JSON object, e.g., '{"name":"my-agent","slug":"my-agent","systemPrompt":"You are..."}'`);
5207
+ }
5208
+ if (options.name)
5209
+ data.name = options.name;
5210
+ if (options.slug)
5211
+ data.slug = options.slug;
5212
+ if (options.systemPrompt)
5213
+ data.systemPrompt = options.systemPrompt;
5214
+ if (options.model)
5215
+ data.model = options.model;
5216
+ if (options.description)
5217
+ data.description = options.description;
5218
+ } else if (options.file) {
4977
5219
  const content = readFileSync9(options.file, "utf-8");
4978
5220
  data = JSON.parse(content);
4979
5221
  } else if (options.name && options.slug && options.systemPrompt) {
@@ -4987,7 +5229,7 @@ ${chalk10.dim("Required: --name, --slug, and --system-prompt (or --file).")}
4987
5229
  if (options.description)
4988
5230
  data.description = options.description;
4989
5231
  } else {
4990
- throw new MutagentError("MISSING_ARGUMENTS", "Either --file or (--name, --slug, and --system-prompt) are required", "Use --file to load from JSON or provide --name, --slug, and --system-prompt");
5232
+ throw new MutagentError("MISSING_ARGUMENTS", "Either --data, --file, or (--name, --slug, and --system-prompt) are required", "Use --data for inline JSON, --file to load from JSON, or provide --name, --slug, and --system-prompt");
4991
5233
  }
4992
5234
  const client = getSDKClient();
4993
5235
  const agent = await client.createAgent(data);
@@ -4997,17 +5239,45 @@ ${chalk10.dim("Required: --name, --slug, and --system-prompt (or --file).")}
4997
5239
  handleError(error, isJson);
4998
5240
  }
4999
5241
  });
5000
- agents.command("update").description("Update an agent").argument("<id>", "Agent ID").option("-f, --file <path>", "Update from JSON file").option("-n, --name <name>", "New name").option("-p, --system-prompt <prompt>", "New system prompt").option("-m, --model <model>", "New model").option("-d, --description <description>", "New description").option("-s, --status <status>", "New status (active, paused, archived)").addHelpText("after", `
5242
+ agents.command("update").description("Update an agent").argument("<id>", "Agent ID").option("-d, --data <json>", "Agent updates as JSON string (CI/scripts/agents)").option("-f, --file <path>", "Update from JSON file").option("-n, --name <name>", "New name").option("-p, --system-prompt <prompt>", "New system prompt").option("-m, --model <model>", "New model").option("--description <desc>", "New description").option("-s, --status <status>", "New status (active, paused, archived)").addHelpText("after", `
5001
5243
  Examples:
5002
5244
  ${chalk10.dim("$")} mutagent agents update <id> --name "New Name"
5003
- ${chalk10.dim("$")} mutagent agents update <id> --status paused
5004
- ${chalk10.dim("$")} mutagent agents update <id> --file updated-agent.json --json
5245
+ ${chalk10.dim("$")} mutagent agents update <id> --system-prompt "Updated prompt" --status active
5246
+ ${chalk10.dim("$")} mutagent agents update <id> -d '{"name":"New Name","systemPrompt":"Updated prompt"}'
5247
+ ${chalk10.dim("$")} mutagent agents update <id> --file updated-agent.json ${chalk10.dim("# full agent object")}
5248
+
5249
+ Input Methods (pick one, priority order):
5250
+ --name/--system-prompt/... Individual flags ${chalk10.green("(recommended)")}
5251
+ -d, --data Inline JSON object (CI/scripts/agents)
5252
+ --file Load from JSON file
5253
+
5254
+ ${chalk10.yellow("Note: Prefer individual flags or --data over --file to avoid stale files living in your repo.")}
5255
+ ${chalk10.dim("--data and --file are mutually exclusive. CLI flags override --data fields.")}
5005
5256
  `).action(async (id, options) => {
5006
5257
  const isJson = getJsonFlag(agents);
5007
5258
  const output = new OutputFormatter(isJson ? "json" : "table");
5008
5259
  try {
5009
5260
  let data = {};
5010
- if (options.file) {
5261
+ if (options.data && options.file) {
5262
+ throw new MutagentError("INVALID_ARGUMENTS", "Cannot use --data and --file together", "Use --data for inline JSON or --file for file-based input, not both");
5263
+ }
5264
+ if (options.data) {
5265
+ try {
5266
+ data = JSON.parse(options.data);
5267
+ } catch {
5268
+ throw new MutagentError("INVALID_JSON", "Invalid JSON in --data flag", `Provide a valid JSON object, e.g., '{"name":"new-name","systemPrompt":"Updated prompt"}'`);
5269
+ }
5270
+ if (options.name)
5271
+ data.name = options.name;
5272
+ if (options.systemPrompt)
5273
+ data.systemPrompt = options.systemPrompt;
5274
+ if (options.model)
5275
+ data.model = options.model;
5276
+ if (options.description)
5277
+ data.description = options.description;
5278
+ if (options.status)
5279
+ data.status = options.status;
5280
+ } else if (options.file) {
5011
5281
  const content = readFileSync9(options.file, "utf-8");
5012
5282
  data = JSON.parse(content);
5013
5283
  } else {
@@ -5023,7 +5293,7 @@ Examples:
5023
5293
  data.status = options.status;
5024
5294
  }
5025
5295
  if (Object.keys(data).length === 0) {
5026
- throw new MutagentError("MISSING_ARGUMENTS", "No update data provided", "Use --file, --name, --system-prompt, --model, --description, or --status");
5296
+ throw new MutagentError("MISSING_ARGUMENTS", "No update data provided", "Use --data, --file, --name, --system-prompt, --model, --description, or --status");
5027
5297
  }
5028
5298
  const client = getSDKClient();
5029
5299
  const agent = await client.updateAgent(id, data);
@@ -5064,12 +5334,15 @@ ${chalk10.dim("Tip: Use --force to skip confirmation (required for non-interacti
5064
5334
  handleError(error, isJson);
5065
5335
  }
5066
5336
  });
5067
- const conversations = agents.command("conversations").description("Manage conversations for agents").addHelpText("after", `
5337
+ const conversations = new Command6("conversations").description("Manage conversations for agents").addHelpText("after", `
5068
5338
  Examples:
5069
5339
  ${chalk10.dim("$")} mutagent agents conversations list <agent-id>
5070
5340
  ${chalk10.dim("$")} mutagent agents conversations create <agent-id>
5071
5341
  ${chalk10.dim("$")} mutagent agents conversations messages <agent-id> <conversation-id>
5072
- `);
5342
+ `).action(() => {
5343
+ conversations.help();
5344
+ });
5345
+ agents.addCommand(conversations);
5073
5346
  conversations.command("list").description("List conversations for an agent").argument("<agent-id>", "Agent ID").option("-l, --limit <n>", "Limit results", "50").option("-o, --offset <n>", "Offset for pagination").addHelpText("after", `
5074
5347
  Examples:
5075
5348
  ${chalk10.dim("$")} mutagent agents conversations list <agent-id>
@@ -5218,6 +5491,8 @@ Examples:
5218
5491
  init_config();
5219
5492
  import { Command as Command7 } from "commander";
5220
5493
  import chalk11 from "chalk";
5494
+ init_errors();
5495
+ var VALID_CONFIG_KEYS = ["apiKey", "endpoint", "format", "timeout", "defaultWorkspace", "defaultOrganization"];
5221
5496
  function createConfigCommand() {
5222
5497
  const config = new Command7("config").description("Manage CLI configuration").addHelpText("after", `
5223
5498
  Examples:
@@ -5250,21 +5525,27 @@ ${chalk11.dim("Keys: apiKey, endpoint, format, timeout, defaultWorkspace, defaul
5250
5525
  `).action((key) => {
5251
5526
  const isJson = getJsonFlag(config);
5252
5527
  const output = new OutputFormatter(isJson ? "json" : "table");
5253
- const cfg = loadConfig();
5254
- if (!(key in cfg)) {
5255
- output.error(`Unknown config key: ${key}`);
5256
- process.exit(1);
5257
- }
5258
- const value = cfg[key];
5259
- const stringValue = typeof value === "object" ? JSON.stringify(value) : String(value ?? "");
5260
- const displayValue = key === "apiKey" && value ? `${stringValue.slice(0, 8)}...` : stringValue;
5261
- if (isJson) {
5262
- output.output({ [key]: displayValue });
5263
- } else {
5264
- output.info(displayValue);
5528
+ try {
5529
+ const cfg = loadConfig();
5530
+ if (!(key in cfg)) {
5531
+ throw new MutagentError("INVALID_ARGUMENTS", `Unknown config key: "${key}"`, `Valid keys: ${VALID_CONFIG_KEYS.join(", ")}
5532
+ ` + "Example: mutagent config get endpoint");
5533
+ }
5534
+ const value = cfg[key];
5535
+ const stringValue = typeof value === "object" ? JSON.stringify(value) : String(value ?? "");
5536
+ const displayValue = key === "apiKey" && value ? `${stringValue.slice(0, 8)}...` : stringValue;
5537
+ if (isJson) {
5538
+ output.output({ [key]: displayValue });
5539
+ } else {
5540
+ output.info(displayValue);
5541
+ }
5542
+ } catch (error) {
5543
+ handleError(error, isJson);
5265
5544
  }
5266
5545
  });
5267
- const set = new Command7("set").description("Set configuration value");
5546
+ const set = new Command7("set").description("Set configuration value").action(() => {
5547
+ set.help();
5548
+ });
5268
5549
  set.command("workspace").description("Set default workspace ID").argument("<id>", "Workspace ID to set as default").addHelpText("after", `
5269
5550
  Examples:
5270
5551
  ${chalk11.dim("$")} mutagent config set workspace <workspace-id>
@@ -5356,12 +5637,13 @@ Examples:
5356
5637
  ${chalk12.dim("$")} mutagent playground run <prompt-id> --system "You are helpful" --human "Hello"
5357
5638
  ${chalk12.dim("$")} mutagent playground run <prompt-id> --input '{}' --model gpt-4-turbo --json
5358
5639
 
5359
- Input Methods (pick one):
5640
+ Input Methods (pick one, priority order):
5641
+ --system/--human Quick system + user message ${chalk12.green("(recommended)")}
5360
5642
  --input '{"key":"value"}' Inline JSON variables
5361
- --file input.json Load from JSON file
5362
- --system/--human Quick system + user message
5363
5643
  --messages '[...]' Full messages array
5644
+ --file input.json Load from JSON file
5364
5645
 
5646
+ ${chalk12.yellow("Note: Prefer --system/--human or --input over --file to avoid stale files living in your repo.")}
5365
5647
  ${chalk12.dim(`Hint: Test before evaluating: mutagent playground run <id> --input '{"key":"value"}'`)}
5366
5648
  `).action(async (promptId, options) => {
5367
5649
  const isJson = getJsonFlag(playground);
@@ -5693,6 +5975,10 @@ Subcommands:
5693
5975
  list, get, test
5694
5976
 
5695
5977
  Note: Provider management (create, update, delete) is available in the Admin Panel only.
5978
+
5979
+ ${chalk14.yellow("Note:")} The providers module is not yet active. This is a placeholder
5980
+ for future external provider configuration. The server currently uses
5981
+ built-in provider settings.
5696
5982
  `);
5697
5983
  providers.command("list").description("List all providers").option("-l, --limit <n>", "Limit results", "50").option("-o, --offset <n>", "Offset for pagination").option("-t, --type <type>", "Filter by provider type").addHelpText("after", `
5698
5984
  Examples:
@@ -5721,6 +6007,8 @@ Examples:
5721
6007
  }));
5722
6008
  output.output({ ...result, data: withLinks });
5723
6009
  } else {
6010
+ console.log(chalk14.yellow("Note: The providers module is not yet active. This is a placeholder for future external provider configuration."));
6011
+ console.log("");
5724
6012
  if (result.data.length === 0) {
5725
6013
  output.info("No providers configured.");
5726
6014
  output.info(`Configure at: ${providerSettingsLink()}`);
@@ -5754,6 +6042,8 @@ Examples:
5754
6042
  if (isJson) {
5755
6043
  output.output({ ...provider, _links: providerLinks(provider.id) });
5756
6044
  } else {
6045
+ console.log(chalk14.yellow("Note: The providers module is not yet active. This is a placeholder for future external provider configuration."));
6046
+ console.log("");
5757
6047
  const formatted = {
5758
6048
  id: provider.id,
5759
6049
  name: provider.name,
@@ -5783,6 +6073,8 @@ ${chalk14.dim("Tests connectivity and lists available models for the provider.")
5783
6073
  try {
5784
6074
  const client = getSDKClient();
5785
6075
  if (!isJson) {
6076
+ console.log(chalk14.yellow("Note: The providers module is not yet active. This is a placeholder for future external provider configuration."));
6077
+ console.log("");
5786
6078
  output.info(`Testing provider ${id}...`);
5787
6079
  }
5788
6080
  const result = await client.testProvider(id);
@@ -5818,7 +6110,7 @@ init_config();
5818
6110
  import { Command as Command11 } from "commander";
5819
6111
  import inquirer3 from "inquirer";
5820
6112
  import chalk15 from "chalk";
5821
- import { existsSync as existsSync10, readFileSync as readFileSync11, writeFileSync as writeFileSync4 } from "fs";
6113
+ import { existsSync as existsSync10, mkdirSync as mkdirSync3, readFileSync as readFileSync11, writeFileSync as writeFileSync4 } from "fs";
5822
6114
  import { execSync as execSync2 } from "child_process";
5823
6115
  import { join as join5 } from "path";
5824
6116
  init_errors();
@@ -6091,6 +6383,32 @@ Modes:
6091
6383
  output.info('Ready! Run "mutagent --help" to see available commands.');
6092
6384
  }
6093
6385
  }
6386
+ if (!isNonInteractive) {
6387
+ const skillPath = join5(cwd, ".claude/skills/mutagent-cli/SKILL.md");
6388
+ if (!existsSync10(skillPath)) {
6389
+ const { installSkill } = await inquirer3.prompt([{
6390
+ type: "confirm",
6391
+ name: "installSkill",
6392
+ message: "Install MutagenT skill for Claude Code? (Teaches AI agents how to use the CLI)",
6393
+ default: true
6394
+ }]);
6395
+ if (installSkill) {
6396
+ try {
6397
+ const skillDir = join5(cwd, ".claude/skills/mutagent-cli");
6398
+ if (!existsSync10(skillDir)) {
6399
+ mkdirSync3(skillDir, { recursive: true });
6400
+ }
6401
+ execSync2("node " + join5(cwd, "node_modules/.bin/mutagent") + " skills install", {
6402
+ cwd,
6403
+ stdio: "ignore"
6404
+ });
6405
+ output.success("Installed MutagenT CLI skill for Claude Code");
6406
+ } catch {
6407
+ output.info("Install skill manually: mutagent skills install");
6408
+ }
6409
+ }
6410
+ }
6411
+ }
6094
6412
  if (isJson) {
6095
6413
  output.output({
6096
6414
  success: true,
@@ -6219,7 +6537,7 @@ Scanning ${scanPath}...
6219
6537
  // src/commands/skills.ts
6220
6538
  import { Command as Command13 } from "commander";
6221
6539
  import chalk17 from "chalk";
6222
- import { existsSync as existsSync11, mkdirSync as mkdirSync3, writeFileSync as writeFileSync5 } from "fs";
6540
+ import { existsSync as existsSync11, mkdirSync as mkdirSync4, writeFileSync as writeFileSync5 } from "fs";
6223
6541
  import { join as join6 } from "path";
6224
6542
  var SKILL_FRONTMATTER = `---
6225
6543
  name: mutagent-cli
@@ -6234,6 +6552,22 @@ var SKILL_BODY = `# MutagenT CLI Skill
6234
6552
 
6235
6553
  ## Quick Reference
6236
6554
  Run \`mutagent --help\` for full command list and examples.
6555
+ Run \`mutagent <command> --help\` for on-the-fly instructions before using any command.
6556
+
6557
+ Key commands:
6558
+ - \`mutagent auth login\` — Authenticate
6559
+ - \`mutagent prompts list\` — List prompts
6560
+ - \`mutagent usage\` — Check plan limits and remaining optimization runs
6561
+ - \`mutagent explore\` — Discover prompts in codebase
6562
+ - \`mutagent integrate <framework>\` — Framework integration instructions
6563
+
6564
+ ## Agent Usage Guidelines
6565
+
6566
+ - **ALWAYS** run \`mutagent prompts list --json\` and \`mutagent usage --json\` BEFORE creating or modifying resources
6567
+ - **ALWAYS** use the \`--json\` flag for machine-readable output
6568
+ - **ALWAYS** confirm with the user before write operations (create, update, delete, optimize)
6569
+ - **CHECK** \`mutagent usage\` before running \`optimize start\` to verify remaining optimization runs
6570
+ - Format dashboard links as markdown: \`[View on Dashboard](url)\`
6237
6571
 
6238
6572
  ## Post-Onboarding Decision Tree
6239
6573
 
@@ -6252,8 +6586,9 @@ After \`mutagent auth login\`, the user lands in one of 3 paths:
6252
6586
  3. \`mutagent prompts create --data '{...}'\` — Upload with REQUIRED outputSchema
6253
6587
  4. Guided eval creator OR \`mutagent prompts evaluation create\`
6254
6588
  5. \`mutagent prompts dataset add\` — Upload/curate datasets (named)
6255
- 6. \`mutagent prompts optimize start\` — Run optimization
6256
- 7. Review scorecard Apply/Reject optimized prompt
6589
+ 6. \`mutagent usage --json\` — Verify remaining optimization runs
6590
+ 7. \`mutagent prompts optimize start\` Run optimization
6591
+ 8. Review scorecard → Apply/Reject optimized prompt
6257
6592
 
6258
6593
  ### Path C: Manual
6259
6594
  User uses CLI commands directly. Run \`mutagent --help\`.
@@ -6327,7 +6662,7 @@ that teaches coding agents how to use the MutagenT CLI effectively.
6327
6662
  const skillDir = join6(process.cwd(), SKILL_DIR);
6328
6663
  const skillPath = join6(skillDir, SKILL_FILE);
6329
6664
  if (!existsSync11(skillDir)) {
6330
- mkdirSync3(skillDir, { recursive: true });
6665
+ mkdirSync4(skillDir, { recursive: true });
6331
6666
  }
6332
6667
  const content = `${SKILL_FRONTMATTER}
6333
6668
 
@@ -6351,8 +6686,108 @@ ${SKILL_BODY}
6351
6686
  return skills;
6352
6687
  }
6353
6688
 
6689
+ // src/commands/usage.ts
6690
+ init_config();
6691
+ import { Command as Command14 } from "commander";
6692
+ import chalk18 from "chalk";
6693
+ init_errors();
6694
+ init_sdk_client();
6695
+ var TRIAL_OPTIMIZATION_LIMIT = 5;
6696
+ var BILLING_URL = "https://app.mutagent.io/settings/billing";
6697
+ function renderProgressBar(used, limit, width = 30) {
6698
+ const ratio = Math.min(used / limit, 1);
6699
+ const filled = Math.round(ratio * width);
6700
+ const empty = width - filled;
6701
+ const bar = "█".repeat(filled) + "░".repeat(empty);
6702
+ const percent = Math.round(ratio * 100);
6703
+ return `${bar} ${String(percent)}%`;
6704
+ }
6705
+ function createUsageCommand() {
6706
+ const usage = new Command14("usage").description("Show resource counts and optimization run limits").addHelpText("after", `
6707
+ Examples:
6708
+ ${chalk18.dim("$")} mutagent usage
6709
+ ${chalk18.dim("$")} mutagent usage --json
6710
+ `);
6711
+ usage.action(async () => {
6712
+ const isJson = getJsonFlag(usage);
6713
+ const output = new OutputFormatter(isJson ? "json" : "table");
6714
+ try {
6715
+ const apiKey = getApiKey();
6716
+ if (!apiKey) {
6717
+ throw new AuthenticationError;
6718
+ }
6719
+ const spinner = await createSpinner("Fetching usage data...", isJson).start();
6720
+ const client = getSDKClient();
6721
+ const prompts = await client.listPrompts();
6722
+ const promptCount = prompts.length;
6723
+ let datasetCount = 0;
6724
+ let evaluationCount = 0;
6725
+ if (promptCount > 0) {
6726
+ const results = await Promise.all(prompts.map(async (prompt) => {
6727
+ const id = String(prompt.id);
6728
+ const [datasets, evaluations] = await Promise.all([
6729
+ client.listDatasets(id).catch(() => []),
6730
+ client.listEvaluations(id).catch(() => [])
6731
+ ]);
6732
+ return { datasets: datasets.length, evaluations: evaluations.length };
6733
+ }));
6734
+ for (const r of results) {
6735
+ datasetCount += r.datasets;
6736
+ evaluationCount += r.evaluations;
6737
+ }
6738
+ }
6739
+ const optimizationLimit = TRIAL_OPTIMIZATION_LIMIT;
6740
+ const optimizationUsed = 0;
6741
+ const optimizationRemaining = optimizationLimit - optimizationUsed;
6742
+ if (spinner && typeof spinner.stop === "function") {
6743
+ spinner.stop();
6744
+ }
6745
+ if (isJson) {
6746
+ output.output({
6747
+ resources: {
6748
+ prompts: promptCount,
6749
+ datasets: datasetCount,
6750
+ evaluations: evaluationCount
6751
+ },
6752
+ optimizationRuns: {
6753
+ used: optimizationUsed,
6754
+ limit: optimizationLimit,
6755
+ remaining: optimizationRemaining,
6756
+ plan: "trial"
6757
+ },
6758
+ _links: {
6759
+ billing: BILLING_URL
6760
+ }
6761
+ });
6762
+ } else {
6763
+ console.log("");
6764
+ console.log(chalk18.bold("\uD83D\uDCCA MutagenT Usage"));
6765
+ console.log(chalk18.dim("─".repeat(45)));
6766
+ console.log("");
6767
+ console.log(chalk18.bold("Resources:"));
6768
+ console.log(` Prompts: ${chalk18.cyan(String(promptCount))}`);
6769
+ console.log(` Datasets: ${chalk18.cyan(String(datasetCount))}`);
6770
+ console.log(` Evaluations: ${chalk18.cyan(String(evaluationCount))}`);
6771
+ console.log("");
6772
+ console.log(chalk18.bold(`Optimization Runs (${chalk18.yellow("trial")} plan):`));
6773
+ console.log(` Remaining: ${chalk18.cyan(String(optimizationRemaining))} / ${String(optimizationLimit)}`);
6774
+ console.log(` ${renderProgressBar(optimizationUsed, optimizationLimit)}`);
6775
+ console.log("");
6776
+ console.log(chalk18.yellow(` ⚠ ${String(optimizationRemaining)} optimization runs remaining`));
6777
+ console.log(chalk18.dim(` ℹ Optimization run counts are approximate`));
6778
+ console.log(` Upgrade: ${chalk18.underline(BILLING_URL)}`);
6779
+ console.log("");
6780
+ }
6781
+ } catch (error) {
6782
+ handleError(error, isJson);
6783
+ }
6784
+ });
6785
+ return usage;
6786
+ }
6787
+
6354
6788
  // src/bin/cli.ts
6355
6789
  init_config();
6790
+ import { existsSync as existsSync12 } from "fs";
6356
6791
  var cliVersion = "0.1.1";
6357
6792
  if (process.env.CLI_VERSION) {
6358
6793
  cliVersion = process.env.CLI_VERSION;
@@ -6364,7 +6799,7 @@ if (process.env.CLI_VERSION) {
6364
6799
  cliVersion = pkg.version ?? cliVersion;
6365
6800
  } catch {}
6366
6801
  }
6367
- var program = new Command14;
6802
+ var program = new Command15;
6368
6803
  program.name("mutagent").description(`MutagenT CLI - AI-native prompt optimization platform
6369
6804
 
6370
6805
  Documentation: https://docs.mutagent.io/cli
@@ -6373,45 +6808,47 @@ program.name("mutagent").description(`MutagenT CLI - AI-native prompt optimizati
6373
6808
  showGlobalOptions: true
6374
6809
  });
6375
6810
  program.addHelpText("after", `
6376
- ${chalk18.yellow("Non-Interactive Mode (CI/CD & Coding Agents):")}
6377
- Export your API key: ${chalk18.green("export MUTAGENT_API_KEY=mt_your_key_here")}
6378
- Or pass inline: ${chalk18.green("mutagent prompts list --api-key mt_your_key")}
6379
- Machine-readable output: ${chalk18.green("mutagent prompts list --json")}
6380
- Disable prompts: ${chalk18.green("mutagent prompts list --non-interactive")}
6381
- Set default workspace: ${chalk18.green("mutagent config set workspace <workspace-id>")}
6382
- Set default org: ${chalk18.green("mutagent config set org <org-id>")}
6811
+ ${chalk19.yellow("Non-Interactive Mode (CI/CD & Coding Agents):")}
6812
+ Export your API key: ${chalk19.green("export MUTAGENT_API_KEY=mt_your_key_here")}
6813
+ Or pass inline: ${chalk19.green("mutagent prompts list --api-key mt_your_key")}
6814
+ Machine-readable output: ${chalk19.green("mutagent prompts list --json")}
6815
+ Disable prompts: ${chalk19.green("mutagent prompts list --non-interactive")}
6816
+ Set default workspace: ${chalk19.green("mutagent config set workspace <workspace-id>")}
6817
+ Set default org: ${chalk19.green("mutagent config set org <org-id>")}
6383
6818
  `);
6384
6819
  program.addHelpText("after", `
6385
- ${chalk18.yellow("Workflows:")}
6386
- ${chalk18.bold("Evaluate → Optimize Loop:")}
6820
+ ${chalk19.yellow("Workflows:")}
6821
+ ${chalk19.bold("Evaluate → Optimize Loop:")}
6387
6822
  1. mutagent prompts create --name "..." --raw-file prompt.txt
6388
6823
  2. mutagent prompts dataset add <prompt-id> --name "..." --file data.json
6389
6824
  3. mutagent prompts evaluation create <prompt-id> --name "..." --file criteria.json
6390
6825
  4. mutagent prompts optimize start <prompt-id> --dataset <id> --max-iterations 3
6391
6826
 
6392
- ${chalk18.bold("Quick Test:")}
6827
+ ${chalk19.bold("Quick Test:")}
6393
6828
  mutagent playground run <prompt-id> --input '{"key":"value"}'
6394
6829
 
6395
- ${chalk18.bold("Prerequisites for Optimization:")}
6396
- ${chalk18.green("✓")} Prompt with input/output parameters
6397
- ${chalk18.green("✓")} Dataset with items (input + expectedOutput pairs)
6398
- ${chalk18.green("✓")} Evaluation with criteria (field-level, input/output focused)
6399
- ${chalk18.dim("•")} LLM provider ${chalk18.dim("(only when server uses external providers)")}
6830
+ ${chalk19.bold("Prerequisites for Optimization:")}
6831
+ ${chalk19.green("✓")} Prompt with input/output parameters
6832
+ ${chalk19.green("✓")} Dataset with items (input + expectedOutput pairs)
6833
+ ${chalk19.green("✓")} Evaluation with criteria (field-level, input/output focused)
6834
+ ${chalk19.dim("•")} LLM provider ${chalk19.dim("(only when server uses external providers)")}
6400
6835
  `);
6401
6836
  program.addHelpText("after", `
6402
- ${chalk18.cyan("┌─ AI AGENT INTEGRATION HINT ────────────────────────────────────────────────┐")}
6403
- ${chalk18.cyan("│")} ${chalk18.cyan("│")}
6404
- ${chalk18.cyan("│")} Frameworks: langchain, langgraph, vercel-ai, openai ${chalk18.cyan("│")}
6405
- ${chalk18.cyan("│")} ${chalk18.cyan("│")}
6406
- ${chalk18.cyan("│")} Get integration guide: mutagent integrate <framework> ${chalk18.cyan("│")}
6407
- ${chalk18.cyan("│")} Verify setup: mutagent integrate <framework> --verify ${chalk18.cyan("│")}
6408
- ${chalk18.cyan("│")} Use --json for AI parsing: mutagent <command> --json ${chalk18.cyan("│")}
6409
- ${chalk18.cyan("│")} ${chalk18.cyan("│")}
6410
- ${chalk18.cyan("└────────────────────────────────────────────────────────────────────────────┘")}
6411
- ${!hasCredentials() ? chalk18.yellow(`
6837
+ ${chalk19.cyan("┌─ AI AGENT INTEGRATION HINT ────────────────────────────────────────────────┐")}
6838
+ ${chalk19.cyan("│")} ${chalk19.cyan("│")}
6839
+ ${chalk19.cyan("│")} Frameworks: langchain, langgraph, vercel-ai, openai ${chalk19.cyan("│")}
6840
+ ${chalk19.cyan("│")} ${chalk19.cyan("│")}
6841
+ ${chalk19.cyan("│")} Get integration guide: mutagent integrate <framework> ${chalk19.cyan("│")}
6842
+ ${chalk19.cyan("│")} Verify setup: mutagent integrate <framework> --verify ${chalk19.cyan("│")}
6843
+ ${chalk19.cyan("│")} Use --json for AI parsing: mutagent <command> --json ${chalk19.cyan("│")}
6844
+ ${chalk19.cyan("│")} ${chalk19.cyan("│")}
6845
+ ${chalk19.cyan("└────────────────────────────────────────────────────────────────────────────┘")}
6846
+ ${!hasCredentials() ? chalk19.yellow(`
6412
6847
  Warning: Not authenticated. Run: mutagent auth login --browser
6413
- `) : ""}${!hasRcConfig() ? chalk18.green(`
6848
+ `) : ""}${!hasRcConfig() ? chalk19.green(`
6414
6849
  Get started: mutagent init
6850
+ `) : ""}${!existsSync12(join7(process.cwd(), ".claude/skills/mutagent-cli/SKILL.md")) ? chalk19.magenta(`
6851
+ Using Claude Code? Install the MutagenT skill: mutagent skills install
6415
6852
  `) : ""}`);
6416
6853
  program.hook("preAction", (thisCommand) => {
6417
6854
  const globalOpts = thisCommand.optsWithGlobals();
@@ -6438,7 +6875,8 @@ program.addCommand(createWorkspacesCommand());
6438
6875
  program.addCommand(createProvidersCommand());
6439
6876
  program.addCommand(createExploreCommand());
6440
6877
  program.addCommand(createSkillsCommand());
6878
+ program.addCommand(createUsageCommand());
6441
6879
  program.parse();
6442
6880
 
6443
- //# debugId=8F5D422F7273E03E64756E2164756E21
6881
+ //# debugId=F0BA27BEF939892164756E2164756E21
6444
6882
  //# sourceMappingURL=cli.js.map