@mono-agent/agent-app 0.5.1 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/README.md +5 -0
  2. package/dist/adapter-send-tools.d.ts +7 -7
  3. package/dist/adapter-send-tools.d.ts.map +1 -1
  4. package/dist/adapter-send-tools.js +59 -50
  5. package/dist/adapter-send-tools.js.map +1 -1
  6. package/dist/app.js +8 -8
  7. package/dist/app.js.map +1 -1
  8. package/dist/channels.js +3 -3
  9. package/dist/channels.js.map +1 -1
  10. package/dist/cli.d.ts +14 -10
  11. package/dist/cli.d.ts.map +1 -1
  12. package/dist/cli.js +197 -162
  13. package/dist/cli.js.map +1 -1
  14. package/dist/config-reference.js +4 -4
  15. package/dist/config-reference.js.map +1 -1
  16. package/dist/consumer-contract.d.ts.map +1 -1
  17. package/dist/consumer-contract.js +5 -3
  18. package/dist/consumer-contract.js.map +1 -1
  19. package/dist/doctor.d.ts.map +1 -1
  20. package/dist/doctor.js +126 -5
  21. package/dist/doctor.js.map +1 -1
  22. package/dist/init.d.ts +11 -22
  23. package/dist/init.d.ts.map +1 -1
  24. package/dist/init.js +25 -116
  25. package/dist/init.js.map +1 -1
  26. package/dist/interaction-bridge.d.ts +3 -3
  27. package/dist/interaction-bridge.d.ts.map +1 -1
  28. package/dist/memory-recall.d.ts +2 -2
  29. package/dist/memory-recall.d.ts.map +1 -1
  30. package/dist/memory-recall.js +3 -3
  31. package/dist/memory-recall.js.map +1 -1
  32. package/dist/modules/base.d.ts +25 -0
  33. package/dist/modules/base.d.ts.map +1 -0
  34. package/dist/{recipes → modules}/base.js +16 -11
  35. package/dist/modules/base.js.map +1 -0
  36. package/dist/modules/catalog.d.ts +12 -0
  37. package/dist/modules/catalog.d.ts.map +1 -0
  38. package/dist/modules/catalog.js +412 -0
  39. package/dist/modules/catalog.js.map +1 -0
  40. package/dist/modules/index.d.ts +8 -0
  41. package/dist/modules/index.d.ts.map +1 -0
  42. package/dist/modules/index.js +5 -0
  43. package/dist/modules/index.js.map +1 -0
  44. package/dist/modules/known-tools.d.ts +44 -0
  45. package/dist/modules/known-tools.d.ts.map +1 -0
  46. package/dist/modules/known-tools.js +88 -0
  47. package/dist/modules/known-tools.js.map +1 -0
  48. package/dist/modules/types.d.ts +73 -0
  49. package/dist/modules/types.d.ts.map +1 -0
  50. package/dist/{recipes → modules}/types.js +3 -3
  51. package/dist/{recipes → modules}/types.js.map +1 -1
  52. package/dist/posted-message-index.d.ts +2 -2
  53. package/dist/posted-message-index.js +2 -2
  54. package/dist/wizard/answers.d.ts +85 -0
  55. package/dist/wizard/answers.d.ts.map +1 -0
  56. package/dist/wizard/answers.js +231 -0
  57. package/dist/wizard/answers.js.map +1 -0
  58. package/dist/wizard/from-flags.d.ts +26 -0
  59. package/dist/wizard/from-flags.d.ts.map +1 -0
  60. package/dist/wizard/from-flags.js +44 -0
  61. package/dist/wizard/from-flags.js.map +1 -0
  62. package/dist/wizard/index.d.ts +11 -0
  63. package/dist/wizard/index.d.ts.map +1 -0
  64. package/dist/wizard/index.js +6 -0
  65. package/dist/wizard/index.js.map +1 -0
  66. package/dist/wizard/presets.d.ts +40 -0
  67. package/dist/wizard/presets.d.ts.map +1 -0
  68. package/dist/wizard/presets.js +85 -0
  69. package/dist/wizard/presets.js.map +1 -0
  70. package/dist/wizard/prompts.d.ts +52 -0
  71. package/dist/wizard/prompts.d.ts.map +1 -0
  72. package/dist/wizard/prompts.js +141 -0
  73. package/dist/wizard/prompts.js.map +1 -0
  74. package/dist/wizard/run.d.ts +19 -0
  75. package/dist/wizard/run.d.ts.map +1 -0
  76. package/dist/wizard/run.js +345 -0
  77. package/dist/wizard/run.js.map +1 -0
  78. package/package.json +17 -16
  79. package/schema/mono-agent.config.schema.json +5 -3
  80. package/skills/mono-agent-composer/SKILL.md +7 -7
  81. package/skills/mono-agent-composer/references/config-blueprint.md +7 -7
  82. package/skills/mono-agent-composer/references/discovery-questions.md +8 -6
  83. package/skills/mono-agent-composer/references/feature-coverage.md +10 -8
  84. package/skills/mono-agent-composer/references/package-map.md +5 -5
  85. package/skills/mono-agent-composer/references/playbooks.md +22 -22
  86. package/skills/mono-agent-composer/references/validation.md +3 -3
  87. package/dist/recipes/base.d.ts +0 -15
  88. package/dist/recipes/base.d.ts.map +0 -1
  89. package/dist/recipes/base.js.map +0 -1
  90. package/dist/recipes/catalog.d.ts +0 -4
  91. package/dist/recipes/catalog.d.ts.map +0 -1
  92. package/dist/recipes/catalog.js +0 -525
  93. package/dist/recipes/catalog.js.map +0 -1
  94. package/dist/recipes/index.d.ts +0 -11
  95. package/dist/recipes/index.d.ts.map +0 -1
  96. package/dist/recipes/index.js +0 -14
  97. package/dist/recipes/index.js.map +0 -1
  98. package/dist/recipes/types.d.ts +0 -70
  99. package/dist/recipes/types.d.ts.map +0 -1
  100. package/dist/setup.d.ts +0 -29
  101. package/dist/setup.d.ts.map +0 -1
  102. package/dist/setup.js +0 -97
  103. package/dist/setup.js.map +0 -1
package/dist/cli.js CHANGED
@@ -1,5 +1,4 @@
1
1
  #!/usr/bin/env node
2
- import { createInterface } from "node:readline/promises";
3
2
  import { stat } from "node:fs/promises";
4
3
  import { createRequire } from "node:module";
5
4
  import { basename, resolve } from "node:path";
@@ -18,13 +17,15 @@ import { formatChannelFactValue } from "./channel-fact-format.js";
18
17
  import { resolveChannelDrivers } from "./channels.js";
19
18
  import { findUnknownAppConfigWarnings } from "./config-reference.js";
20
19
  import { validateMonoAgentFolder } from "./doctor.js";
21
- import { initMonoAgentFolder, isWithChannel } from "./init.js";
20
+ import { initMonoAgentFolder } from "./init.js";
22
21
  import { installComposerSkill } from "./install-skill.js";
23
22
  import { runMetrics } from "./metrics.js";
24
- import { findRecipe, RECIPE_CATALOG, recipeIds, resolveRecipeInputs } from "./recipes/index.js";
25
23
  import { buildRunsHealthDisplay, RUNS_HEALTH_MAX_RUNS } from "./runs-health.js";
26
24
  import { purgeSessions } from "./sessions.js";
27
- import { collectSetupOptions } from "./setup.js";
25
+ import { composeWizardPlan } from "./wizard/answers.js";
26
+ import { answersFromCli, isWithChannel } from "./wizard/from-flags.js";
27
+ import { findPreset, PRESET_CATALOG, presetAnswers, presetIds, RECIPE_TO_PRESET } from "./wizard/presets.js";
28
+ import { runInitWizard } from "./wizard/run.js";
28
29
  import * as ui from "./ui.js";
29
30
  const DEFAULT_LOG_LINES = 200;
30
31
  // Node's maximum setInterval/setTimeout delay (2^31 - 1 ms, ~24.8 days). A
@@ -32,7 +33,7 @@ const DEFAULT_LOG_LINES = 200;
32
33
  // busy-waiting; larger values silently overflow to a 1ms delay.
33
34
  const KEEP_ALIVE_INTERVAL_MS = 2_147_483_647;
34
35
  const BACKGROUND_COMMANDS = ["start", "restart", "stop", "status", "logs"];
35
- const KNOWN_COMMANDS = ["init", "setup", "validate", "doctor", "config", "recipes", "start", "restart", "stop", "status", "logs", "tui", "web", "install-skill", "backfill", "audit-runs", "metrics", "memory"];
36
+ const KNOWN_COMMANDS = ["init", "setup", "validate", "doctor", "config", "recipes", "presets", "start", "restart", "stop", "status", "logs", "tui", "web", "install-skill", "backfill", "audit-runs", "metrics", "memory"];
36
37
  export function parseCliArgs(argv) {
37
38
  const [command, ...rest] = argv;
38
39
  if (command === undefined || command === "help" || command === "--help" || command === "-h") {
@@ -44,16 +45,25 @@ export function parseCliArgs(argv) {
44
45
  if (!KNOWN_COMMANDS.includes(command)) {
45
46
  throw new Error(`Unknown command \`${command}\`. Expected ${KNOWN_COMMANDS.join(", ")}.`);
46
47
  }
47
- // `doctor` is an alias of `validate`; normalize here so every downstream
48
- // validate path (env-file resolution, --consumer, routing) applies unchanged.
49
- const cmd = (command === "doctor" ? "validate" : command);
48
+ // `doctor`/`setup`/`recipes` are aliases; normalize here so every downstream
49
+ // path (routing, env-file resolution, --consumer) applies unchanged. `doctor`
50
+ // `validate`, `setup` `init`, `recipes` `presets`.
51
+ const cmd = (command === "doctor"
52
+ ? "validate"
53
+ : command === "setup"
54
+ ? "init"
55
+ : command === "recipes"
56
+ ? "presets"
57
+ : command);
50
58
  const isLogs = cmd === "logs";
51
59
  let configPath;
52
60
  let model;
53
61
  let fallbackModels;
54
62
  let memory;
63
+ let preset;
55
64
  let recipe;
56
65
  let withChannels;
66
+ let yes = false;
57
67
  const positionals = [];
58
68
  let envFile;
59
69
  let target;
@@ -192,9 +202,15 @@ export function parseCliArgs(argv) {
192
202
  memory = raw;
193
203
  break;
194
204
  }
205
+ case "--preset":
206
+ preset = requireValue(rest, ++i, flag);
207
+ break;
195
208
  case "--recipe":
196
209
  recipe = requireValue(rest, ++i, flag);
197
210
  break;
211
+ case "--yes":
212
+ yes = true;
213
+ break;
198
214
  case "--with":
199
215
  withChannels = requireValue(rest, ++i, flag)
200
216
  .split(",")
@@ -270,8 +286,10 @@ export function parseCliArgs(argv) {
270
286
  ...(model === undefined ? {} : { model }),
271
287
  ...(fallbackModels === undefined ? {} : { fallbackModels }),
272
288
  ...(memory === undefined ? {} : { memory }),
289
+ ...(preset === undefined ? {} : { preset }),
273
290
  ...(recipe === undefined ? {} : { recipe }),
274
291
  ...(withChannels === undefined ? {} : { withChannels }),
292
+ ...(yes ? { yes } : {}),
275
293
  positionals,
276
294
  ...(envFile === undefined ? {} : { envFile }),
277
295
  ...(target === undefined ? {} : { target }),
@@ -332,36 +350,32 @@ function requireValue(args, index, flag) {
332
350
  }
333
351
  const HELP_COMMANDS = [
334
352
  {
335
- signature: "mono-agent init [--recipe <id>] [--with <csv>] [--dry-run]\n" +
353
+ signature: "mono-agent init [--preset <id>] [--with <csv>] [--yes] [--dry-run]\n" +
336
354
  " [--model <ref>] [--fallback-models <csv>] [--memory lite|journal|bujo]",
337
355
  lines: [
338
- "Scaffold mono-agent.config.json, IDENTITY.md, and .mono-agent/ in the",
339
- "current folder. With --recipe, build from a blueprint (+ .env.example);",
340
- "--with adds channels, --dry-run previews. Existing files are never overwritten.",
356
+ "Scaffold a mono-agent in the current folder. On a TTY with no flags, launches",
357
+ "the step-by-step wizard; with --yes or any flag, writes the default/preset",
358
+ "scaffold non-interactively. --preset seeds a blueprint, --with adds channels,",
359
+ "--dry-run previews. Existing files are never overwritten.",
341
360
  ],
342
361
  },
343
362
  {
344
- signature: "mono-agent setup [--recipe <id>] [--with <csv>] [--dry-run]\n" +
345
- " [--model <ref>] [--fallback-models <csv>] [--memory lite|journal|bujo]",
346
- lines: [
347
- "Guided setup when attached to a TTY: choose a recipe, answer non-secret",
348
- "inputs, select channel add-ons, scaffold, validate, and print the secrets",
349
- "checklist. In non-TTY contexts it falls back to init-style flags.",
350
- ],
363
+ signature: "mono-agent setup",
364
+ lines: ["Alias of `init`."],
351
365
  },
352
366
  {
353
- signature: "mono-agent recipes list | show <id>",
367
+ signature: "mono-agent presets list | show <id>",
354
368
  lines: [
355
- "List the executable config blueprints, or show one's generated config,",
369
+ "List the built-in setup presets, or show one's generated config,",
356
370
  ".env.example, and follow-up checklist.",
357
371
  ],
358
372
  },
359
373
  {
360
- signature: "mono-agent validate [--recipe <id>] [--consumer <path>] [--config <path>] [--env-file <path>]",
374
+ signature: "mono-agent validate [--preset <id>] [--consumer <path>] [--config <path>] [--env-file <path>]",
361
375
  lines: [
362
376
  "Load every config section and report what would run, wait, or fail.",
363
377
  "--consumer validates another agent folder read-only, including its .env.",
364
- "With --recipe, also report whether the recipe's capabilities are live.",
378
+ "With --preset, also report whether the preset's capabilities are live.",
365
379
  "`mono-agent doctor` is an alias for this command.",
366
380
  ],
367
381
  },
@@ -536,14 +550,12 @@ export async function runCli(argv) {
536
550
  return 0;
537
551
  case "init":
538
552
  return await runInit(args);
539
- case "setup":
540
- return await runSetup(args);
541
553
  case "validate":
542
554
  return await runValidate(args);
543
555
  case "config":
544
556
  return await runConfig(args);
545
- case "recipes":
546
- return runRecipes(args);
557
+ case "presets":
558
+ return runPresets(args);
547
559
  case "start":
548
560
  return await runStart(args);
549
561
  case "restart":
@@ -623,93 +635,83 @@ export async function runCli(argv) {
623
635
  }
624
636
  }
625
637
  async function runInit(args) {
626
- const recipe = resolveRecipeArg(args);
627
- if (recipe === "unknown") {
638
+ // On an interactive TTY with no overriding flags, walk the step-by-step wizard;
639
+ // any flag (or a piped/non-TTY invocation) takes the silent default/preset path.
640
+ const wantsWizard = process.stdin.isTTY === true && process.stdout.isTTY === true
641
+ && args.yes !== true && args.preset === undefined && args.recipe === undefined
642
+ && args.model === undefined && args.fallbackModels === undefined
643
+ && args.memory === undefined && args.withChannels === undefined && args.dryRun !== true;
644
+ if (wantsWizard) {
645
+ // Existing-config pre-check — don't walk the wizard into a guaranteed no-op.
646
+ if (await pathExists(resolve(process.cwd(), "mono-agent.config.json"))) {
647
+ process.stdout.write(ui.hint("Found an existing mono-agent.config.json — `mono-agent init` never overwrites. Run `mono-agent validate`, or start in an empty folder.\n"));
648
+ return 0;
649
+ }
650
+ const outcome = await runInitWizard({ cwd: process.cwd() });
651
+ if (outcome.status === "cancelled") {
652
+ return 1;
653
+ }
654
+ const result = await initMonoAgentFolder({ dir: process.cwd(), answers: outcome.answers });
655
+ printInitResult(result);
656
+ const report = await validateMonoAgentFolder({ env: process.env, cwd: process.cwd(), configPath: result.configPath });
657
+ process.stdout.write("\n" + ui.heading("Validation"));
658
+ for (const section of report.sections) {
659
+ process.stdout.write(formatSection(section));
660
+ }
661
+ process.stdout.write(renderPlanCompleteness(result.plan.validateExpectations, "Selected capabilities", report));
662
+ printSecretsChecklist(result.plan.secrets);
663
+ printNextSteps(result.configPath);
664
+ return report.ok ? 0 : 1;
665
+ }
666
+ const presetId = resolveInitPresetId(args);
667
+ if (presetId === "unknown") {
628
668
  return 1;
629
669
  }
630
670
  const withChannels = resolveWithChannels(args);
631
671
  if (withChannels === "invalid") {
632
672
  return 1;
633
673
  }
634
- const result = await initMonoAgentFolder({
635
- dir: process.cwd(),
674
+ const answers = answersFromCli({
636
675
  ...(args.model === undefined ? {} : { model: args.model }),
637
676
  ...(args.fallbackModels === undefined ? {} : { fallbackModels: args.fallbackModels }),
638
677
  ...(args.memory === undefined ? {} : { memory: args.memory }),
639
- ...(recipe === undefined ? {} : { recipe }),
640
678
  ...(withChannels === undefined ? {} : { withChannels }),
641
- dryRun: args.dryRun,
679
+ ...(presetId === undefined ? {} : { presetId }),
642
680
  });
643
- printInitResult(result, recipe);
644
- printNextSteps(result.configPath, recipe);
681
+ const result = await initMonoAgentFolder({ dir: process.cwd(), answers, dryRun: args.dryRun });
682
+ printInitResult(result);
683
+ printSecretsChecklist(result.plan.secrets);
684
+ printNextSteps(result.configPath);
645
685
  return 0;
646
686
  }
647
- async function runSetup(args) {
648
- if (process.stdin.isTTY !== true) {
649
- return await runInit(args);
650
- }
651
- const recipeArg = resolveRecipeArg(args);
652
- if (recipeArg === "unknown") {
653
- return 1;
654
- }
655
- const withChannelsArg = resolveWithChannels(args);
656
- if (withChannelsArg === "invalid") {
657
- return 1;
658
- }
659
- const rl = createInterface({ input: process.stdin, output: process.stdout });
660
- let collected;
661
- try {
662
- collected = await collectSetupOptions({
663
- prompt: { question: (prompt) => rl.question(prompt) },
664
- ...(recipeArg === undefined ? {} : { recipe: recipeArg }),
665
- ...(args.model === undefined ? {} : { model: args.model }),
666
- ...(args.fallbackModels === undefined ? {} : { fallbackModels: args.fallbackModels }),
667
- ...(withChannelsArg === undefined ? {} : { withChannels: withChannelsArg }),
668
- });
669
- }
670
- finally {
671
- rl.close();
672
- }
673
- const result = await initMonoAgentFolder({
674
- dir: process.cwd(),
675
- ...(collected.fallbackModels.length === 0 ? {} : { fallbackModels: collected.fallbackModels }),
676
- ...(args.memory === undefined ? {} : { memory: args.memory }),
677
- recipe: collected.recipe,
678
- recipeInputs: collected.recipeInputs,
679
- withChannels: collected.withChannels,
680
- dryRun: args.dryRun,
681
- });
682
- printInitResult(result, collected.recipe);
683
- if (!result.dryRun) {
684
- const report = await validateMonoAgentFolder({
685
- env: process.env,
686
- cwd: process.cwd(),
687
- configPath: result.configPath,
688
- });
689
- process.stdout.write("\n" + ui.heading("Validation"));
690
- for (const section of report.sections) {
691
- process.stdout.write(formatSection(section));
687
+ /**
688
+ * Resolve the preset id for `init`: `--preset` wins, `--recipe` is a deprecated
689
+ * alias mapped to the preset that replaced it. Returns the preset id to compose
690
+ * from, `undefined` for the default scaffold, or `"unknown"` after emitting the
691
+ * error/hint (an unknown preset, or a retired recipe with no replacement).
692
+ */
693
+ function resolveInitPresetId(args) {
694
+ if (args.preset !== undefined) {
695
+ const preset = findPreset(args.preset);
696
+ if (preset === undefined) {
697
+ process.stderr.write(ui.errorLine(`Unknown preset \`${args.preset}\`.`));
698
+ process.stderr.write(ui.hint(`Available presets: ${presetIds().join(", ")}. Run \`mono-agent presets list\`.`));
699
+ return "unknown";
692
700
  }
693
- process.stdout.write(renderRecipeCompleteness(collected.recipe, report));
694
- printSecretsChecklist(collected.secrets);
695
- printNextSteps(result.configPath, collected.recipe);
696
- return report.ok ? 0 : 1;
697
- }
698
- printSecretsChecklist(collected.secrets);
699
- printNextSteps(result.configPath, collected.recipe);
700
- return 0;
701
- }
702
- function resolveRecipeArg(args) {
703
- if (args.recipe === undefined) {
704
- return undefined;
701
+ return preset.id;
705
702
  }
706
- const recipe = findRecipe(args.recipe);
707
- if (recipe === undefined) {
708
- process.stderr.write(ui.errorLine(`Unknown recipe \`${args.recipe}\`.`));
709
- process.stderr.write(ui.hint("Run `mono-agent recipes list` to see available recipes."));
710
- return "unknown";
703
+ if (args.recipe !== undefined) {
704
+ const preset = RECIPE_TO_PRESET.get(args.recipe);
705
+ if (preset === undefined) {
706
+ process.stderr.write(ui.errorLine(`Recipe \`${args.recipe}\` was retired; the wizard composes capabilities directly.`));
707
+ process.stderr.write(ui.hint(`Use \`mono-agent init --preset <id>\` (${presetIds().join(", ")}), or \`mono-agent init\` for the step-by-step wizard.`));
708
+ process.stderr.write(ui.hint("See the mono-agent-composer skill and docs/playbooks for capability recipes."));
709
+ return "unknown";
710
+ }
711
+ process.stderr.write(ui.hint(`--recipe is deprecated; using preset ${preset.id}. See \`mono-agent presets list\`.`));
712
+ return preset.id;
711
713
  }
712
- return recipe;
714
+ return undefined;
713
715
  }
714
716
  function resolveWithChannels(args) {
715
717
  if (args.withChannels === undefined) {
@@ -723,7 +725,7 @@ function resolveWithChannels(args) {
723
725
  }
724
726
  return args.withChannels.filter(isWithChannel);
725
727
  }
726
- function printInitResult(result, recipe) {
728
+ function printInitResult(result) {
727
729
  if (result.dryRun) {
728
730
  process.stdout.write(ui.style.dim("Dry run — nothing was written.\n"));
729
731
  }
@@ -737,31 +739,35 @@ function printInitResult(result, recipe) {
737
739
  if (result.knowledgeFiles.length > 0) {
738
740
  process.stdout.write(`\nIdentity references existing knowledge: ${ui.style.cyan(result.knowledgeFiles.join(", "))}\n`);
739
741
  }
740
- if (recipe !== undefined) {
741
- process.stdout.write(`\n${ui.style.bold("Recipe:")} ${ui.style.cyan(recipe.id)} ${ui.style.dim(`(risk: ${recipe.riskLevel})`)}\n`);
742
- if (recipe.envExample !== undefined) {
743
- process.stdout.write(ui.style.dim("Fill the secret placeholders in .env.example, then copy it to .env.\n"));
742
+ // Internal `provider:*` modules are auto-added for local models; they are an
743
+ // implementation detail, not a user-facing capability, so exclude them here.
744
+ const capabilities = result.plan.selectedModules.filter((module) => module.kind !== "provider");
745
+ if (capabilities.length > 0) {
746
+ process.stdout.write("\n" + ui.heading("Capabilities"));
747
+ for (const module of capabilities) {
748
+ process.stdout.write(` ${ui.style.cyan(module.title)} ${ui.style.dim(`(risk: ${riskColor(module.riskLevel)})`)}\n`);
744
749
  }
745
750
  }
751
+ if (result.plan.envExample !== undefined) {
752
+ process.stdout.write("\n" + ui.style.dim("Fill the secret placeholders in .env.example, then copy it to .env.\n"));
753
+ }
746
754
  }
747
755
  function printSecretsChecklist(secrets) {
748
756
  process.stdout.write("\n" + ui.heading("Secrets checklist"));
749
757
  if (secrets.length === 0) {
750
- process.stdout.write(ui.style.dim("No secret recipe inputs were prompted. Review .env.example if the recipe created one.\n"));
758
+ process.stdout.write(ui.style.dim("No secrets required by the selected capabilities.\n"));
751
759
  return;
752
760
  }
753
- process.stdout.write(ui.style.dim("Copy .env.example to .env, then fill these variables. Secret values were not prompted or written to JSON.\n"));
761
+ process.stdout.write(ui.style.dim("Copy .env.example to .env, then fill these variables. Secret values are never written to JSON.\n"));
754
762
  for (const secret of secrets) {
755
- const key = secret.envVar ?? secret.id;
756
- process.stdout.write(` ${ui.style.bold(key)} ${ui.style.dim(`- ${secret.label}: ${secret.description}`)}\n`);
763
+ process.stdout.write(` ${ui.style.bold(secret.envVar)} ${ui.style.dim(`- ${secret.label}: ${secret.description}`)}\n`);
757
764
  }
758
765
  }
759
- function printNextSteps(configPath, recipe) {
760
- const validateCmd = recipe === undefined ? "mono-agent validate" : `mono-agent validate --recipe ${recipe.id}`;
766
+ function printNextSteps(configPath) {
761
767
  process.stdout.write("\n" +
762
768
  ui.heading("Next steps") +
763
769
  ` ${ui.style.bold("1.")} Edit ${configPath} ${ui.style.dim("(model, channels, skills, memory, sandbox)")}\n` +
764
- ` ${ui.style.bold("2.")} ${validateCmd}\n` +
770
+ ` ${ui.style.bold("2.")} mono-agent validate\n` +
765
771
  ` ${ui.style.bold("3.")} mono-agent start\n`);
766
772
  }
767
773
  async function runInstallSkill(args) {
@@ -792,14 +798,16 @@ async function runValidate(args) {
792
798
  for (const section of report.sections) {
793
799
  process.stdout.write(formatSection(section));
794
800
  }
795
- if (args.recipe !== undefined) {
796
- const recipe = findRecipe(args.recipe);
797
- if (recipe === undefined) {
798
- process.stderr.write(ui.errorLine(`Unknown recipe \`${args.recipe}\`.`));
799
- process.stderr.write(ui.hint("Run `mono-agent recipes list` to see available recipes."));
800
- return 1;
801
- }
802
- process.stdout.write(renderRecipeCompleteness(recipe, report));
801
+ const preset = resolveValidatePreset(args);
802
+ if (preset === "unknown") {
803
+ return 1;
804
+ }
805
+ if (preset !== undefined) {
806
+ const plan = composeWizardPlan(presetAnswers(preset), {
807
+ dirBasename: basename(context.cwd),
808
+ skillsRootExists: false,
809
+ });
810
+ process.stdout.write(renderPlanCompleteness(plan.validateExpectations, `Preset: ${preset.id}`, report));
803
811
  }
804
812
  process.stdout.write(report.ok
805
813
  ? `\n${ui.style.green("✓ Config is ready to start.")}\n${ui.style.dim("Run `mono-agent config` for the full field-by-field view.")}\n`
@@ -808,15 +816,42 @@ async function runValidate(args) {
808
816
  return report.ok ? 0 : 1;
809
817
  }
810
818
  /**
811
- * Capability-aware recipe check: for each capability the recipe promises, report
812
- * whether the matching doctor section has reached the expected status. `waiting`
813
- * stays non-fatal (it never changes the validate exit code) but is surfaced as
814
- * "selected recipe incomplete" so the operator knows what is left to wire up.
819
+ * Resolve the preset to check `validate` against: `--preset` wins, `--recipe` is a
820
+ * deprecated alias. Returns the preset, `undefined` (no capability check), or
821
+ * `"unknown"` after emitting the error/hint.
822
+ */
823
+ function resolveValidatePreset(args) {
824
+ if (args.preset !== undefined) {
825
+ const preset = findPreset(args.preset);
826
+ if (preset === undefined) {
827
+ process.stderr.write(ui.errorLine(`Unknown preset \`${args.preset}\`.`));
828
+ process.stderr.write(ui.hint(`Available presets: ${presetIds().join(", ")}. Run \`mono-agent presets list\`.`));
829
+ return "unknown";
830
+ }
831
+ return preset;
832
+ }
833
+ if (args.recipe !== undefined) {
834
+ const preset = RECIPE_TO_PRESET.get(args.recipe);
835
+ if (preset === undefined) {
836
+ process.stderr.write(ui.errorLine(`Recipe \`${args.recipe}\` was retired; validate against a preset instead.`));
837
+ process.stderr.write(ui.hint(`Available presets: ${presetIds().join(", ")}. Run \`mono-agent presets list\`.`));
838
+ return "unknown";
839
+ }
840
+ process.stderr.write(ui.hint(`--recipe is deprecated; using preset ${preset.id}. See \`mono-agent presets list\`.`));
841
+ return preset;
842
+ }
843
+ return undefined;
844
+ }
845
+ /**
846
+ * Capability-aware completeness check: for each capability a preset (or module
847
+ * set) promises, report whether the matching doctor section reached the expected
848
+ * status. `waiting` stays non-fatal (it never changes the validate exit code) but
849
+ * is surfaced as "incomplete" so the operator knows what is left to wire up.
815
850
  */
816
- function renderRecipeCompleteness(recipe, report) {
817
- let out = "\n" + ui.heading(`Recipe: ${recipe.id}`);
851
+ function renderPlanCompleteness(expectations, label, report) {
852
+ let out = "\n" + ui.heading(label);
818
853
  let incomplete = 0;
819
- for (const expectation of recipe.validateExpectations) {
854
+ for (const expectation of expectations) {
820
855
  const section = report.sections.find((entry) => entry.id === expectation.sectionId);
821
856
  const status = section?.status ?? "missing";
822
857
  const met = status === expectation.mustBe;
@@ -830,8 +865,8 @@ function renderRecipeCompleteness(recipe, report) {
830
865
  }
831
866
  }
832
867
  out += incomplete === 0
833
- ? `${ui.style.green(`✓ Recipe ${recipe.id} is fully configured.`)}\n`
834
- : ui.style.yellow(`⚠ Selected recipe incomplete: ${incomplete} capability(ies) not yet live.\n`);
868
+ ? `${ui.style.green(`✓ ${label} is fully configured.`)}\n`
869
+ : ui.style.yellow(`⚠ ${label} incomplete: ${incomplete} capability(ies) not yet live.\n`);
835
870
  return out;
836
871
  }
837
872
  function riskColor(risk) {
@@ -843,72 +878,72 @@ function riskColor(risk) {
843
878
  }
844
879
  return ui.style.green(risk);
845
880
  }
846
- /** `mono-agent recipes list` — one line per recipe. */
847
- export function renderRecipeList() {
848
- let out = ui.banner("mono-agent", "recipes") + "\n";
849
- for (const recipe of RECIPE_CATALOG) {
850
- out += `${ui.style.bold(ui.style.cyan(recipe.id))} ${ui.style.dim(`[${riskColor(recipe.riskLevel)}]`)}\n`;
851
- out += ` ${recipe.title}\n`;
852
- out += ` ${ui.style.dim(recipe.tags.join(", "))}\n`;
853
- }
854
- out += "\n" + ui.style.dim("Scaffold one with: mono-agent init --recipe <id> [--dry-run] [--with slack,cron]\n");
881
+ /** `mono-agent presets list` — one block per preset. */
882
+ export function renderPresetList() {
883
+ let out = ui.banner("mono-agent", "presets") + "\n";
884
+ for (const preset of PRESET_CATALOG) {
885
+ out += `${ui.style.bold(ui.style.cyan(preset.id))} ${ui.style.dim(`[${riskColor(preset.riskLevel)}]`)}\n`;
886
+ out += ` ${preset.title}\n`;
887
+ out += ` ${ui.style.dim(preset.description)}\n`;
888
+ }
889
+ out += "\n" + ui.style.dim("Scaffold one with: mono-agent init --preset <id>\n");
890
+ out += ui.style.dim("Build interactively with: mono-agent init\n");
855
891
  return out;
856
892
  }
857
- /** `mono-agent recipes show <id>` — description, generated JSON, env example, checklist. */
858
- export function renderRecipeShow(recipe) {
859
- const inputs = resolveRecipeInputs(recipe);
860
- let out = ui.banner("mono-agent", `recipe: ${recipe.id}`) + "\n";
861
- out += `${ui.style.bold(recipe.title)} ${ui.style.dim(`(risk: ${riskColor(recipe.riskLevel)})`)}\n`;
862
- out += `${recipe.description}\n`;
863
- if (recipe.playbook !== undefined) {
864
- out += ui.style.dim(`Playbook: docs/playbooks/${recipe.playbook}\n`);
893
+ /** `mono-agent presets show <id>` — description, composed JSON, env example, checklist. */
894
+ export function renderPresetShow(preset) {
895
+ const plan = composeWizardPlan(presetAnswers(preset), { dirBasename: "your-agent", skillsRootExists: false });
896
+ let out = ui.banner("mono-agent", `preset: ${preset.id}`) + "\n";
897
+ out += `${ui.style.bold(preset.title)} ${ui.style.dim(`(risk: ${riskColor(preset.riskLevel)})`)}\n`;
898
+ out += `${preset.description}\n`;
899
+ if (preset.playbook !== undefined) {
900
+ out += ui.style.dim(`Playbook: docs/playbooks/${preset.playbook}\n`);
865
901
  }
866
902
  out += "\n" + ui.heading("Generated mono-agent.config.json");
867
- out += JSON.stringify(recipe.config(inputs), null, 2) + "\n";
868
- const envExample = recipe.envExample?.(inputs);
903
+ out += JSON.stringify(plan.configJson, null, 2) + "\n";
904
+ const envExample = plan.envExample;
869
905
  if (envExample !== undefined && envExample.trim().length > 0) {
870
906
  out += "\n" + ui.heading(".env.example");
871
907
  out += envExample.endsWith("\n") ? envExample : envExample + "\n";
872
908
  }
873
- const files = recipe.files?.(inputs) ?? [];
874
- if (files.length > 0) {
909
+ if (plan.files.length > 0) {
875
910
  out += "\n" + ui.heading("Scaffolded files");
876
- for (const file of files) {
911
+ for (const file of plan.files) {
877
912
  out += ` ${ui.style.cyan(file.path)}\n`;
878
913
  }
879
914
  }
880
- if (recipe.validateExpectations.length > 0) {
915
+ if (plan.validateExpectations.length > 0) {
881
916
  out += "\n" + ui.heading("Follow-up checklist");
882
- for (const expectation of recipe.validateExpectations) {
917
+ for (const expectation of plan.validateExpectations) {
883
918
  const note = expectation.note === undefined ? "" : ` — ${expectation.note}`;
884
919
  out += ` ${ui.style.gray("•")} ${expectation.sectionId} ${ui.style.dim(`must be ${expectation.mustBe}`)}${ui.style.dim(note)}\n`;
885
920
  }
886
921
  }
887
922
  return out;
888
923
  }
889
- /** Dispatch `mono-agent recipes list|show <id>`. */
890
- function runRecipes(args) {
924
+ /** Dispatch `mono-agent presets list|show <id>`. */
925
+ function runPresets(args) {
891
926
  const [sub, id] = args.positionals;
892
927
  if (sub === undefined || sub === "list") {
893
- process.stdout.write(renderRecipeList());
928
+ process.stdout.write(renderPresetList());
894
929
  return 0;
895
930
  }
896
931
  if (sub === "show") {
897
932
  if (id === undefined) {
898
- process.stderr.write(ui.errorLine("Usage: mono-agent recipes show <id>."));
899
- process.stderr.write(ui.hint(`Available: ${recipeIds().join(", ")}.`));
933
+ process.stderr.write(ui.errorLine("Usage: mono-agent presets show <id>."));
934
+ process.stderr.write(ui.hint(`Available: ${presetIds().join(", ")}.`));
900
935
  return 2;
901
936
  }
902
- const recipe = findRecipe(id);
903
- if (recipe === undefined) {
904
- process.stderr.write(ui.errorLine(`Unknown recipe \`${id}\`.`));
905
- process.stderr.write(ui.hint("Run `mono-agent recipes list` to see available recipes."));
937
+ const preset = findPreset(id);
938
+ if (preset === undefined) {
939
+ process.stderr.write(ui.errorLine(`Unknown preset \`${id}\`.`));
940
+ process.stderr.write(ui.hint(`Available: ${presetIds().join(", ")}. Run \`mono-agent presets list\`.`));
906
941
  return 1;
907
942
  }
908
- process.stdout.write(renderRecipeShow(recipe));
943
+ process.stdout.write(renderPresetShow(preset));
909
944
  return 0;
910
945
  }
911
- process.stderr.write(ui.errorLine(`Unknown recipes subcommand \`${sub}\`. Expected list or show.`));
946
+ process.stderr.write(ui.errorLine(`Unknown presets subcommand \`${sub}\`. Expected list or show.`));
912
947
  return 2;
913
948
  }
914
949
  const SOURCE_TAG = {