@mono-agent/agent-app 0.5.1 → 0.6.1

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 (111) hide show
  1. package/README.md +7 -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 +28 -10
  11. package/dist/cli.d.ts.map +1 -1
  12. package/dist/cli.js +287 -164
  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 +131 -7
  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 +17 -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/provider-setup.d.ts +44 -0
  55. package/dist/provider-setup.d.ts.map +1 -0
  56. package/dist/provider-setup.js +174 -0
  57. package/dist/provider-setup.js.map +1 -0
  58. package/dist/wizard/answers.d.ts +87 -0
  59. package/dist/wizard/answers.d.ts.map +1 -0
  60. package/dist/wizard/answers.js +244 -0
  61. package/dist/wizard/answers.js.map +1 -0
  62. package/dist/wizard/from-flags.d.ts +27 -0
  63. package/dist/wizard/from-flags.d.ts.map +1 -0
  64. package/dist/wizard/from-flags.js +45 -0
  65. package/dist/wizard/from-flags.js.map +1 -0
  66. package/dist/wizard/index.d.ts +11 -0
  67. package/dist/wizard/index.d.ts.map +1 -0
  68. package/dist/wizard/index.js +6 -0
  69. package/dist/wizard/index.js.map +1 -0
  70. package/dist/wizard/model-discovery.d.ts +35 -0
  71. package/dist/wizard/model-discovery.d.ts.map +1 -0
  72. package/dist/wizard/model-discovery.js +239 -0
  73. package/dist/wizard/model-discovery.js.map +1 -0
  74. package/dist/wizard/presets.d.ts +40 -0
  75. package/dist/wizard/presets.d.ts.map +1 -0
  76. package/dist/wizard/presets.js +85 -0
  77. package/dist/wizard/presets.js.map +1 -0
  78. package/dist/wizard/prompts.d.ts +55 -0
  79. package/dist/wizard/prompts.d.ts.map +1 -0
  80. package/dist/wizard/prompts.js +152 -0
  81. package/dist/wizard/prompts.js.map +1 -0
  82. package/dist/wizard/run.d.ts +20 -0
  83. package/dist/wizard/run.d.ts.map +1 -0
  84. package/dist/wizard/run.js +374 -0
  85. package/dist/wizard/run.js.map +1 -0
  86. package/package.json +17 -16
  87. package/schema/mono-agent.config.schema.json +5 -3
  88. package/skills/mono-agent-composer/SKILL.md +8 -8
  89. package/skills/mono-agent-composer/references/config-blueprint.md +7 -7
  90. package/skills/mono-agent-composer/references/discovery-questions.md +13 -10
  91. package/skills/mono-agent-composer/references/feature-coverage.md +12 -10
  92. package/skills/mono-agent-composer/references/package-map.md +7 -6
  93. package/skills/mono-agent-composer/references/playbooks.md +22 -22
  94. package/skills/mono-agent-composer/references/validation.md +3 -3
  95. package/dist/recipes/base.d.ts +0 -15
  96. package/dist/recipes/base.d.ts.map +0 -1
  97. package/dist/recipes/base.js.map +0 -1
  98. package/dist/recipes/catalog.d.ts +0 -4
  99. package/dist/recipes/catalog.d.ts.map +0 -1
  100. package/dist/recipes/catalog.js +0 -525
  101. package/dist/recipes/catalog.js.map +0 -1
  102. package/dist/recipes/index.d.ts +0 -11
  103. package/dist/recipes/index.d.ts.map +0 -1
  104. package/dist/recipes/index.js +0 -14
  105. package/dist/recipes/index.js.map +0 -1
  106. package/dist/recipes/types.d.ts +0 -70
  107. package/dist/recipes/types.d.ts.map +0 -1
  108. package/dist/setup.d.ts +0 -29
  109. package/dist/setup.d.ts.map +0 -1
  110. package/dist/setup.js +0 -97
  111. package/dist/setup.js.map +0 -1
package/dist/cli.js CHANGED
@@ -1,11 +1,10 @@
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";
6
5
  import process from "node:process";
7
6
  import { fileURLToPath } from "node:url";
8
- import { buildMonoAgentConfigView, findJsonSecretConfigWarnings, findRemovedConfigWarnings, readMonoAgentConfigJson, redactMonoAgentConfig, } from "@mono-agent/config";
7
+ import { buildMonoAgentConfigView, EFFORT_LEVELS, findJsonSecretConfigWarnings, findRemovedConfigWarnings, readMonoAgentConfigJson, redactMonoAgentConfig, } from "@mono-agent/config";
9
8
  import { listRecordedRuns } from "@mono-agent/observability";
10
9
  import { describeSandboxEffectiveState, sandboxEffectiveStateWarning, } from "@mono-agent/runtime-adapter";
11
10
  import { startMonoAgentApp } from "./app.js";
@@ -18,13 +17,16 @@ 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";
29
+ import { executeProviderSetupPlan, planProviderSetup, providerSetupActionCommandLine } from "./provider-setup.js";
28
30
  import * as ui from "./ui.js";
29
31
  const DEFAULT_LOG_LINES = 200;
30
32
  // Node's maximum setInterval/setTimeout delay (2^31 - 1 ms, ~24.8 days). A
@@ -32,7 +34,7 @@ const DEFAULT_LOG_LINES = 200;
32
34
  // busy-waiting; larger values silently overflow to a 1ms delay.
33
35
  const KEEP_ALIVE_INTERVAL_MS = 2_147_483_647;
34
36
  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"];
37
+ 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
38
  export function parseCliArgs(argv) {
37
39
  const [command, ...rest] = argv;
38
40
  if (command === undefined || command === "help" || command === "--help" || command === "-h") {
@@ -44,16 +46,27 @@ export function parseCliArgs(argv) {
44
46
  if (!KNOWN_COMMANDS.includes(command)) {
45
47
  throw new Error(`Unknown command \`${command}\`. Expected ${KNOWN_COMMANDS.join(", ")}.`);
46
48
  }
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);
49
+ // `doctor`/`setup`/`recipes` are aliases; normalize here so every downstream
50
+ // path (routing, env-file resolution, --consumer) applies unchanged. `doctor`
51
+ // `validate`, `setup` `init`, `recipes` `presets`.
52
+ const cmd = (command === "doctor"
53
+ ? "validate"
54
+ : command === "setup"
55
+ ? "init"
56
+ : command === "recipes"
57
+ ? "presets"
58
+ : command);
50
59
  const isLogs = cmd === "logs";
51
60
  let configPath;
52
61
  let model;
53
62
  let fallbackModels;
63
+ let effort;
54
64
  let memory;
65
+ let preset;
55
66
  let recipe;
56
67
  let withChannels;
68
+ let yes = false;
69
+ let auth = false;
57
70
  const positionals = [];
58
71
  let envFile;
59
72
  let target;
@@ -184,6 +197,14 @@ export function parseCliArgs(argv) {
184
197
  .map((entry) => entry.trim())
185
198
  .filter((entry) => entry.length > 0);
186
199
  break;
200
+ case "--effort": {
201
+ const raw = requireValue(rest, ++i, flag);
202
+ if (!EFFORT_LEVELS.includes(raw)) {
203
+ throw new Error(`--effort must be ${EFFORT_LEVELS.join(", ")}.`);
204
+ }
205
+ effort = raw;
206
+ break;
207
+ }
187
208
  case "--memory": {
188
209
  const raw = requireValue(rest, ++i, flag);
189
210
  if (raw !== "lite" && raw !== "journal" && raw !== "bujo") {
@@ -192,9 +213,18 @@ export function parseCliArgs(argv) {
192
213
  memory = raw;
193
214
  break;
194
215
  }
216
+ case "--preset":
217
+ preset = requireValue(rest, ++i, flag);
218
+ break;
195
219
  case "--recipe":
196
220
  recipe = requireValue(rest, ++i, flag);
197
221
  break;
222
+ case "--yes":
223
+ yes = true;
224
+ break;
225
+ case "--auth":
226
+ auth = true;
227
+ break;
198
228
  case "--with":
199
229
  withChannels = requireValue(rest, ++i, flag)
200
230
  .split(",")
@@ -264,14 +294,21 @@ export function parseCliArgs(argv) {
264
294
  if (limit !== undefined && cmd !== "memory") {
265
295
  throw new Error("--limit is only supported for `mono-agent memory`.");
266
296
  }
297
+ if (auth && cmd !== "init") {
298
+ throw new Error("--auth is only supported for `mono-agent init`.");
299
+ }
267
300
  return {
268
301
  command: cmd,
269
302
  ...(configPath === undefined ? {} : { configPath }),
270
303
  ...(model === undefined ? {} : { model }),
271
304
  ...(fallbackModels === undefined ? {} : { fallbackModels }),
305
+ ...(effort === undefined ? {} : { effort }),
272
306
  ...(memory === undefined ? {} : { memory }),
307
+ ...(preset === undefined ? {} : { preset }),
273
308
  ...(recipe === undefined ? {} : { recipe }),
274
309
  ...(withChannels === undefined ? {} : { withChannels }),
310
+ ...(yes ? { yes } : {}),
311
+ ...(auth ? { auth } : {}),
275
312
  positionals,
276
313
  ...(envFile === undefined ? {} : { envFile }),
277
314
  ...(target === undefined ? {} : { target }),
@@ -332,36 +369,34 @@ function requireValue(args, index, flag) {
332
369
  }
333
370
  const HELP_COMMANDS = [
334
371
  {
335
- signature: "mono-agent init [--recipe <id>] [--with <csv>] [--dry-run]\n" +
336
- " [--model <ref>] [--fallback-models <csv>] [--memory lite|journal|bujo]",
372
+ signature: "mono-agent init [--preset <id>] [--with <csv>] [--yes] [--auth] [--dry-run]\n" +
373
+ " [--model <ref>] [--fallback-models <csv>] [--effort <level>]\n" +
374
+ " [--memory lite|journal|bujo]",
337
375
  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.",
376
+ "Scaffold a mono-agent in the current folder. On a TTY with no flags, launches",
377
+ "the step-by-step wizard; with --yes or any flag, writes the default/preset",
378
+ "scaffold non-interactively. --preset seeds a blueprint, --with adds channels,",
379
+ "--effort writes runtime.effort, --auth runs supported provider auth/preflight",
380
+ "commands before writing, and --dry-run previews only. Existing files are never overwritten.",
341
381
  ],
342
382
  },
343
383
  {
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
- ],
384
+ signature: "mono-agent setup",
385
+ lines: ["Alias of `init`."],
351
386
  },
352
387
  {
353
- signature: "mono-agent recipes list | show <id>",
388
+ signature: "mono-agent presets list | show <id>",
354
389
  lines: [
355
- "List the executable config blueprints, or show one's generated config,",
390
+ "List the built-in setup presets, or show one's generated config,",
356
391
  ".env.example, and follow-up checklist.",
357
392
  ],
358
393
  },
359
394
  {
360
- signature: "mono-agent validate [--recipe <id>] [--consumer <path>] [--config <path>] [--env-file <path>]",
395
+ signature: "mono-agent validate [--preset <id>] [--consumer <path>] [--config <path>] [--env-file <path>]",
361
396
  lines: [
362
397
  "Load every config section and report what would run, wait, or fail.",
363
398
  "--consumer validates another agent folder read-only, including its .env.",
364
- "With --recipe, also report whether the recipe's capabilities are live.",
399
+ "With --preset, also report whether the preset's capabilities are live.",
365
400
  "`mono-agent doctor` is an alias for this command.",
366
401
  ],
367
402
  },
@@ -473,8 +508,12 @@ const HELP_NOTES = `Background mode runs the agent under launchd, keeping it ali
473
508
  .env file in the working directory, the same as foreground mode. The background
474
509
  commands require macOS; elsewhere use start --foreground.
475
510
 
476
- Model references look like claude:claude-sonnet-4-6, codex:gpt-5.5, or
477
- pi:<provider>:<model> (e.g. pi:ollama:gemma4:31b).
511
+ Init model references look like claude:claude-sonnet-4-6, codex:gpt-5.5,
512
+ or pi:<provider>:<model> (e.g. pi:openai-codex:gpt-5.5,
513
+ pi:opencode-go:kimi-k2.6, or pi:ollama:gemma4:31b). The init wizard prefers
514
+ Pi OpenAI-Codex when that auth is configured, while direct codex:gpt-5.5
515
+ remains selectable as a Codex CLI fallback path. Direct opencode:<provider>:<model>
516
+ refs are for hand-authored runtime backend config.
478
517
 
479
518
  A .env file in the current folder is loaded automatically when present;
480
519
  already-exported shell variables take precedence.
@@ -536,14 +575,12 @@ export async function runCli(argv) {
536
575
  return 0;
537
576
  case "init":
538
577
  return await runInit(args);
539
- case "setup":
540
- return await runSetup(args);
541
578
  case "validate":
542
579
  return await runValidate(args);
543
580
  case "config":
544
581
  return await runConfig(args);
545
- case "recipes":
546
- return runRecipes(args);
582
+ case "presets":
583
+ return runPresets(args);
547
584
  case "start":
548
585
  return await runStart(args);
549
586
  case "restart":
@@ -623,93 +660,146 @@ export async function runCli(argv) {
623
660
  }
624
661
  }
625
662
  async function runInit(args) {
626
- const recipe = resolveRecipeArg(args);
627
- if (recipe === "unknown") {
663
+ // On an interactive TTY with no overriding flags, walk the step-by-step wizard;
664
+ // any flag (or a piped/non-TTY invocation) takes the silent default/preset path.
665
+ const wantsWizard = process.stdin.isTTY === true && process.stdout.isTTY === true
666
+ && args.yes !== true && args.preset === undefined && args.recipe === undefined
667
+ && args.model === undefined && args.fallbackModels === undefined
668
+ && args.effort === undefined && args.memory === undefined && args.withChannels === undefined && args.dryRun !== true;
669
+ if (wantsWizard) {
670
+ // Existing-config pre-check — don't walk the wizard into a guaranteed no-op.
671
+ if (await pathExists(resolve(process.cwd(), "mono-agent.config.json"))) {
672
+ 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"));
673
+ return 0;
674
+ }
675
+ const outcome = await runInitWizard({ cwd: process.cwd() });
676
+ if (outcome.status === "cancelled") {
677
+ return 1;
678
+ }
679
+ if (outcome.runProviderSetup) {
680
+ const previewPlan = composeWizardPlan(outcome.answers, {
681
+ dirBasename: basename(process.cwd()),
682
+ skillsRootExists: await pathExists(resolve(process.cwd(), "skills")),
683
+ });
684
+ const setup = await runProviderSetupBeforeInit({
685
+ modelRefs: [outcome.answers.model, ...outcome.answers.fallbackModels],
686
+ cwd: process.cwd(),
687
+ auth: true,
688
+ dryRun: false,
689
+ ...(typeof previewPlan.configJson.providers?.piAuthPath === "string" ? { piAuthPath: previewPlan.configJson.providers.piAuthPath } : {}),
690
+ });
691
+ if (setup === "failed") {
692
+ return 1;
693
+ }
694
+ }
695
+ const result = await initMonoAgentFolder({ dir: process.cwd(), answers: outcome.answers });
696
+ printInitResult(result);
697
+ const report = await validateMonoAgentFolder({ env: process.env, cwd: process.cwd(), configPath: result.configPath });
698
+ process.stdout.write("\n" + ui.heading("Validation"));
699
+ for (const section of report.sections) {
700
+ process.stdout.write(formatSection(section));
701
+ }
702
+ process.stdout.write(renderPlanCompleteness(result.plan.validateExpectations, "Selected capabilities", report));
703
+ printSecretsChecklist(result.plan.secrets);
704
+ printNextSteps(result.configPath);
705
+ return report.ok ? 0 : 1;
706
+ }
707
+ const presetId = resolveInitPresetId(args);
708
+ if (presetId === "unknown") {
628
709
  return 1;
629
710
  }
630
711
  const withChannels = resolveWithChannels(args);
631
712
  if (withChannels === "invalid") {
632
713
  return 1;
633
714
  }
634
- const result = await initMonoAgentFolder({
635
- dir: process.cwd(),
715
+ const answers = answersFromCli({
636
716
  ...(args.model === undefined ? {} : { model: args.model }),
637
717
  ...(args.fallbackModels === undefined ? {} : { fallbackModels: args.fallbackModels }),
718
+ ...(args.effort === undefined ? {} : { effort: args.effort }),
638
719
  ...(args.memory === undefined ? {} : { memory: args.memory }),
639
- ...(recipe === undefined ? {} : { recipe }),
640
720
  ...(withChannels === undefined ? {} : { withChannels }),
641
- dryRun: args.dryRun,
721
+ ...(presetId === undefined ? {} : { presetId }),
642
722
  });
643
- printInitResult(result, recipe);
644
- printNextSteps(result.configPath, recipe);
645
- return 0;
646
- }
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,
723
+ const previewPlan = composeWizardPlan(answers, {
724
+ dirBasename: basename(process.cwd()),
725
+ skillsRootExists: await pathExists(resolve(process.cwd(), "skills")),
726
+ });
727
+ const setup = await runProviderSetupBeforeInit({
728
+ modelRefs: [answers.model, ...answers.fallbackModels],
729
+ cwd: process.cwd(),
730
+ auth: args.auth === true,
680
731
  dryRun: args.dryRun,
732
+ ...(typeof previewPlan.configJson.providers?.piAuthPath === "string" ? { piAuthPath: previewPlan.configJson.providers.piAuthPath } : {}),
681
733
  });
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));
692
- }
693
- process.stdout.write(renderRecipeCompleteness(collected.recipe, report));
694
- printSecretsChecklist(collected.secrets);
695
- printNextSteps(result.configPath, collected.recipe);
696
- return report.ok ? 0 : 1;
734
+ if (setup === "failed") {
735
+ return 1;
697
736
  }
698
- printSecretsChecklist(collected.secrets);
699
- printNextSteps(result.configPath, collected.recipe);
737
+ const result = await initMonoAgentFolder({ dir: process.cwd(), answers, dryRun: args.dryRun });
738
+ printInitResult(result);
739
+ printSecretsChecklist(result.plan.secrets);
740
+ printNextSteps(result.configPath);
700
741
  return 0;
701
742
  }
702
- function resolveRecipeArg(args) {
703
- if (args.recipe === undefined) {
704
- return undefined;
743
+ export async function runProviderSetupBeforeInit(options) {
744
+ const plan = planProviderSetup(options);
745
+ if (plan.actions.length === 0) {
746
+ return "skipped";
747
+ }
748
+ if (options.dryRun) {
749
+ process.stdout.write("\n" + ui.heading("Provider setup"));
750
+ process.stdout.write(ui.style.dim("Dry run - provider auth/preflight commands were not launched.\n"));
751
+ printProviderSetupPlan(plan);
752
+ return "skipped";
753
+ }
754
+ if (!options.auth) {
755
+ return "skipped";
756
+ }
757
+ process.stdout.write("\n" + ui.heading("Provider setup"));
758
+ printProviderSetupPlan(plan);
759
+ const results = await (options.execute ?? executeProviderSetupPlan)(plan);
760
+ for (const result of results) {
761
+ const badge = result.status === "ok" ? ui.badge("ok") : ui.badge("error");
762
+ process.stdout.write(`${badge}${result.action.label}: ${result.detail}\n`);
763
+ }
764
+ if (results.some((result) => result.status === "failed")) {
765
+ process.stderr.write(ui.errorLine("Provider setup failed; init stopped before writing files."));
766
+ return "failed";
767
+ }
768
+ return "ok";
769
+ }
770
+ function printProviderSetupPlan(plan) {
771
+ for (const action of plan.actions) {
772
+ process.stdout.write(` ${action.label}: ${providerSetupActionCommandLine(action)} ${ui.style.dim(`(cwd: ${action.cwd})`)}\n`);
705
773
  }
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";
774
+ }
775
+ /**
776
+ * Resolve the preset id for `init`: `--preset` wins, `--recipe` is a deprecated
777
+ * alias mapped to the preset that replaced it. Returns the preset id to compose
778
+ * from, `undefined` for the default scaffold, or `"unknown"` after emitting the
779
+ * error/hint (an unknown preset, or a retired recipe with no replacement).
780
+ */
781
+ function resolveInitPresetId(args) {
782
+ if (args.preset !== undefined) {
783
+ const preset = findPreset(args.preset);
784
+ if (preset === undefined) {
785
+ process.stderr.write(ui.errorLine(`Unknown preset \`${args.preset}\`.`));
786
+ process.stderr.write(ui.hint(`Available presets: ${presetIds().join(", ")}. Run \`mono-agent presets list\`.`));
787
+ return "unknown";
788
+ }
789
+ return preset.id;
711
790
  }
712
- return recipe;
791
+ if (args.recipe !== undefined) {
792
+ const preset = RECIPE_TO_PRESET.get(args.recipe);
793
+ if (preset === undefined) {
794
+ process.stderr.write(ui.errorLine(`Recipe \`${args.recipe}\` was retired; the wizard composes capabilities directly.`));
795
+ process.stderr.write(ui.hint(`Use \`mono-agent init --preset <id>\` (${presetIds().join(", ")}), or \`mono-agent init\` for the step-by-step wizard.`));
796
+ process.stderr.write(ui.hint("See the mono-agent-composer skill and docs/playbooks for capability recipes."));
797
+ return "unknown";
798
+ }
799
+ process.stderr.write(ui.hint(`--recipe is deprecated; using preset ${preset.id}. See \`mono-agent presets list\`.`));
800
+ return preset.id;
801
+ }
802
+ return undefined;
713
803
  }
714
804
  function resolveWithChannels(args) {
715
805
  if (args.withChannels === undefined) {
@@ -723,7 +813,7 @@ function resolveWithChannels(args) {
723
813
  }
724
814
  return args.withChannels.filter(isWithChannel);
725
815
  }
726
- function printInitResult(result, recipe) {
816
+ function printInitResult(result) {
727
817
  if (result.dryRun) {
728
818
  process.stdout.write(ui.style.dim("Dry run — nothing was written.\n"));
729
819
  }
@@ -737,31 +827,35 @@ function printInitResult(result, recipe) {
737
827
  if (result.knowledgeFiles.length > 0) {
738
828
  process.stdout.write(`\nIdentity references existing knowledge: ${ui.style.cyan(result.knowledgeFiles.join(", "))}\n`);
739
829
  }
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"));
830
+ // Internal `provider:*` modules are auto-added for local models; they are an
831
+ // implementation detail, not a user-facing capability, so exclude them here.
832
+ const capabilities = result.plan.selectedModules.filter((module) => module.kind !== "provider");
833
+ if (capabilities.length > 0) {
834
+ process.stdout.write("\n" + ui.heading("Capabilities"));
835
+ for (const module of capabilities) {
836
+ process.stdout.write(` ${ui.style.cyan(module.title)} ${ui.style.dim(`(risk: ${riskColor(module.riskLevel)})`)}\n`);
744
837
  }
745
838
  }
839
+ if (result.plan.envExample !== undefined) {
840
+ process.stdout.write("\n" + ui.style.dim("Fill the secret placeholders in .env.example, then copy it to .env.\n"));
841
+ }
746
842
  }
747
843
  function printSecretsChecklist(secrets) {
748
844
  process.stdout.write("\n" + ui.heading("Secrets checklist"));
749
845
  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"));
846
+ process.stdout.write(ui.style.dim("No secrets required by the selected capabilities.\n"));
751
847
  return;
752
848
  }
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"));
849
+ process.stdout.write(ui.style.dim("Copy .env.example to .env, then fill these variables. Secret values are never written to JSON.\n"));
754
850
  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`);
851
+ process.stdout.write(` ${ui.style.bold(secret.envVar)} ${ui.style.dim(`- ${secret.label}: ${secret.description}`)}\n`);
757
852
  }
758
853
  }
759
- function printNextSteps(configPath, recipe) {
760
- const validateCmd = recipe === undefined ? "mono-agent validate" : `mono-agent validate --recipe ${recipe.id}`;
854
+ function printNextSteps(configPath) {
761
855
  process.stdout.write("\n" +
762
856
  ui.heading("Next steps") +
763
857
  ` ${ui.style.bold("1.")} Edit ${configPath} ${ui.style.dim("(model, channels, skills, memory, sandbox)")}\n` +
764
- ` ${ui.style.bold("2.")} ${validateCmd}\n` +
858
+ ` ${ui.style.bold("2.")} mono-agent validate\n` +
765
859
  ` ${ui.style.bold("3.")} mono-agent start\n`);
766
860
  }
767
861
  async function runInstallSkill(args) {
@@ -792,14 +886,16 @@ async function runValidate(args) {
792
886
  for (const section of report.sections) {
793
887
  process.stdout.write(formatSection(section));
794
888
  }
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));
889
+ const preset = resolveValidatePreset(args);
890
+ if (preset === "unknown") {
891
+ return 1;
892
+ }
893
+ if (preset !== undefined) {
894
+ const plan = composeWizardPlan(presetAnswers(preset), {
895
+ dirBasename: basename(context.cwd),
896
+ skillsRootExists: false,
897
+ });
898
+ process.stdout.write(renderPlanCompleteness(plan.validateExpectations, `Preset: ${preset.id}`, report));
803
899
  }
804
900
  process.stdout.write(report.ok
805
901
  ? `\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 +904,42 @@ async function runValidate(args) {
808
904
  return report.ok ? 0 : 1;
809
905
  }
810
906
  /**
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.
907
+ * Resolve the preset to check `validate` against: `--preset` wins, `--recipe` is a
908
+ * deprecated alias. Returns the preset, `undefined` (no capability check), or
909
+ * `"unknown"` after emitting the error/hint.
910
+ */
911
+ function resolveValidatePreset(args) {
912
+ if (args.preset !== undefined) {
913
+ const preset = findPreset(args.preset);
914
+ if (preset === undefined) {
915
+ process.stderr.write(ui.errorLine(`Unknown preset \`${args.preset}\`.`));
916
+ process.stderr.write(ui.hint(`Available presets: ${presetIds().join(", ")}. Run \`mono-agent presets list\`.`));
917
+ return "unknown";
918
+ }
919
+ return preset;
920
+ }
921
+ if (args.recipe !== undefined) {
922
+ const preset = RECIPE_TO_PRESET.get(args.recipe);
923
+ if (preset === undefined) {
924
+ process.stderr.write(ui.errorLine(`Recipe \`${args.recipe}\` was retired; validate against a preset instead.`));
925
+ process.stderr.write(ui.hint(`Available presets: ${presetIds().join(", ")}. Run \`mono-agent presets list\`.`));
926
+ return "unknown";
927
+ }
928
+ process.stderr.write(ui.hint(`--recipe is deprecated; using preset ${preset.id}. See \`mono-agent presets list\`.`));
929
+ return preset;
930
+ }
931
+ return undefined;
932
+ }
933
+ /**
934
+ * Capability-aware completeness check: for each capability a preset (or module
935
+ * set) promises, report whether the matching doctor section reached the expected
936
+ * status. `waiting` stays non-fatal (it never changes the validate exit code) but
937
+ * is surfaced as "incomplete" so the operator knows what is left to wire up.
815
938
  */
816
- function renderRecipeCompleteness(recipe, report) {
817
- let out = "\n" + ui.heading(`Recipe: ${recipe.id}`);
939
+ function renderPlanCompleteness(expectations, label, report) {
940
+ let out = "\n" + ui.heading(label);
818
941
  let incomplete = 0;
819
- for (const expectation of recipe.validateExpectations) {
942
+ for (const expectation of expectations) {
820
943
  const section = report.sections.find((entry) => entry.id === expectation.sectionId);
821
944
  const status = section?.status ?? "missing";
822
945
  const met = status === expectation.mustBe;
@@ -830,8 +953,8 @@ function renderRecipeCompleteness(recipe, report) {
830
953
  }
831
954
  }
832
955
  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`);
956
+ ? `${ui.style.green(`✓ ${label} is fully configured.`)}\n`
957
+ : ui.style.yellow(`⚠ ${label} incomplete: ${incomplete} capability(ies) not yet live.\n`);
835
958
  return out;
836
959
  }
837
960
  function riskColor(risk) {
@@ -843,72 +966,72 @@ function riskColor(risk) {
843
966
  }
844
967
  return ui.style.green(risk);
845
968
  }
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");
969
+ /** `mono-agent presets list` — one block per preset. */
970
+ export function renderPresetList() {
971
+ let out = ui.banner("mono-agent", "presets") + "\n";
972
+ for (const preset of PRESET_CATALOG) {
973
+ out += `${ui.style.bold(ui.style.cyan(preset.id))} ${ui.style.dim(`[${riskColor(preset.riskLevel)}]`)}\n`;
974
+ out += ` ${preset.title}\n`;
975
+ out += ` ${ui.style.dim(preset.description)}\n`;
976
+ }
977
+ out += "\n" + ui.style.dim("Scaffold one with: mono-agent init --preset <id>\n");
978
+ out += ui.style.dim("Build interactively with: mono-agent init\n");
855
979
  return out;
856
980
  }
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`);
981
+ /** `mono-agent presets show <id>` — description, composed JSON, env example, checklist. */
982
+ export function renderPresetShow(preset) {
983
+ const plan = composeWizardPlan(presetAnswers(preset), { dirBasename: "your-agent", skillsRootExists: false });
984
+ let out = ui.banner("mono-agent", `preset: ${preset.id}`) + "\n";
985
+ out += `${ui.style.bold(preset.title)} ${ui.style.dim(`(risk: ${riskColor(preset.riskLevel)})`)}\n`;
986
+ out += `${preset.description}\n`;
987
+ if (preset.playbook !== undefined) {
988
+ out += ui.style.dim(`Playbook: docs/playbooks/${preset.playbook}\n`);
865
989
  }
866
990
  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);
991
+ out += JSON.stringify(plan.configJson, null, 2) + "\n";
992
+ const envExample = plan.envExample;
869
993
  if (envExample !== undefined && envExample.trim().length > 0) {
870
994
  out += "\n" + ui.heading(".env.example");
871
995
  out += envExample.endsWith("\n") ? envExample : envExample + "\n";
872
996
  }
873
- const files = recipe.files?.(inputs) ?? [];
874
- if (files.length > 0) {
997
+ if (plan.files.length > 0) {
875
998
  out += "\n" + ui.heading("Scaffolded files");
876
- for (const file of files) {
999
+ for (const file of plan.files) {
877
1000
  out += ` ${ui.style.cyan(file.path)}\n`;
878
1001
  }
879
1002
  }
880
- if (recipe.validateExpectations.length > 0) {
1003
+ if (plan.validateExpectations.length > 0) {
881
1004
  out += "\n" + ui.heading("Follow-up checklist");
882
- for (const expectation of recipe.validateExpectations) {
1005
+ for (const expectation of plan.validateExpectations) {
883
1006
  const note = expectation.note === undefined ? "" : ` — ${expectation.note}`;
884
1007
  out += ` ${ui.style.gray("•")} ${expectation.sectionId} ${ui.style.dim(`must be ${expectation.mustBe}`)}${ui.style.dim(note)}\n`;
885
1008
  }
886
1009
  }
887
1010
  return out;
888
1011
  }
889
- /** Dispatch `mono-agent recipes list|show <id>`. */
890
- function runRecipes(args) {
1012
+ /** Dispatch `mono-agent presets list|show <id>`. */
1013
+ function runPresets(args) {
891
1014
  const [sub, id] = args.positionals;
892
1015
  if (sub === undefined || sub === "list") {
893
- process.stdout.write(renderRecipeList());
1016
+ process.stdout.write(renderPresetList());
894
1017
  return 0;
895
1018
  }
896
1019
  if (sub === "show") {
897
1020
  if (id === undefined) {
898
- process.stderr.write(ui.errorLine("Usage: mono-agent recipes show <id>."));
899
- process.stderr.write(ui.hint(`Available: ${recipeIds().join(", ")}.`));
1021
+ process.stderr.write(ui.errorLine("Usage: mono-agent presets show <id>."));
1022
+ process.stderr.write(ui.hint(`Available: ${presetIds().join(", ")}.`));
900
1023
  return 2;
901
1024
  }
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."));
1025
+ const preset = findPreset(id);
1026
+ if (preset === undefined) {
1027
+ process.stderr.write(ui.errorLine(`Unknown preset \`${id}\`.`));
1028
+ process.stderr.write(ui.hint(`Available: ${presetIds().join(", ")}. Run \`mono-agent presets list\`.`));
906
1029
  return 1;
907
1030
  }
908
- process.stdout.write(renderRecipeShow(recipe));
1031
+ process.stdout.write(renderPresetShow(preset));
909
1032
  return 0;
910
1033
  }
911
- process.stderr.write(ui.errorLine(`Unknown recipes subcommand \`${sub}\`. Expected list or show.`));
1034
+ process.stderr.write(ui.errorLine(`Unknown presets subcommand \`${sub}\`. Expected list or show.`));
912
1035
  return 2;
913
1036
  }
914
1037
  const SOURCE_TAG = {