@kisev/agentomatic 12.0.1-dev.129.g99196bf396c1 → 12.0.1-dev.130.g78ea8685c8d4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -110,8 +110,9 @@ npx agentomatic configure integration --global --dry-run
110
110
  }
111
111
  ```
112
112
 
113
- Restart OpenCode after activation or asset changes. A confirmed `install` can
114
- apply its core config step and stage optional models and additional critics.
113
+ Restart OpenCode after activation or asset changes. A confirmed `install`
114
+ deploys the six fixed agents only as one complete package; agent models and
115
+ critics live in the confirmed `configure agent` command.
115
116
  `configure` provides one settings menu; `status` shows the installed set and models.
116
117
  `uninstall` proposes plugin disconnection, retains models, and removes the npm
117
118
  dependency only with an explicit selection (`--remove-dependency`). Other
package/README.ru.md CHANGED
@@ -114,7 +114,9 @@ npx agentomatic configure integration --global --dry-run
114
114
  ```
115
115
 
116
116
  Перезапустите OpenCode после активации или изменения файлов. Подтверждённый
117
- `install` настраивает core и предлагает модели и дополнительных критиков.
117
+ `install` настраивает core и разворачивает шесть фиксированных агентов только
118
+ целиком пакетом; модели агентов и критиков меняет подтверждённая команда
119
+ `configure agent`.
118
120
  `configure` открывает единое меню настроек, `status` показывает состав и модели.
119
121
  `uninstall` предлагает отключение плагина, сохраняет модели и удаляет npm-зависимость
120
122
  только при явном выборе (`--remove-dependency`). Другие фрагменты применяет
@@ -1,4 +1,4 @@
1
- import type { AgentInventory, AgentProfilePlan } from "./agent-profiles.js";
1
+ import { type AgentInventory, type AgentProfilePlan } from "./agent-profiles.js";
2
2
  import type { ConfigSetupPlan } from "./config-setup.js";
3
3
  import type { Plan as InstallerPlan } from "./installer.js";
4
4
  import type { ReconcilePlan } from "./reconcile.js";
@@ -17,6 +17,7 @@ export declare function renderConfigSetup(plan: ConfigSetupPlan, options: {
17
17
  hint?: string;
18
18
  }): string;
19
19
  export declare function renderCriticsTable(inventory: AgentInventory): string;
20
+ export declare function renderFixedRoles(inventory: AgentInventory): string;
20
21
  export declare function renderInventory(inventory: AgentInventory): string;
21
22
  export declare function shellCommand(arguments_: readonly string[]): string;
22
23
  export declare function scopeArguments(scope: Scope): [] | ["--global"];
@@ -1,3 +1,4 @@
1
+ import { FIXED_AGENT_ROLES } from "./agent-profiles.js";
1
2
  import { CATALOG } from "./catalog.js";
2
3
  const GROUPS = ["Agents", "Commands", "Plugins", "State"];
3
4
  const OPERATIONS = [
@@ -116,11 +117,12 @@ export function renderPlan(plan, options) {
116
117
  lines.push("", "Details:", ...details);
117
118
  const conflicts = plan.operations.filter((item) => item.operation === "conflict").length;
118
119
  lines.push("", `Conflicts: ${conflicts || "none"}`);
119
- lines.push(options.applied
120
- ? `Restart required: ${plan.requires_restart ? "yes" : "no"}`
121
- : `Restart after apply: ${plan.requires_restart ? "yes" : "no"}`);
120
+ const restartNote = "restart the host session so the deployed roles and settings load";
121
+ lines.push(plan.requires_restart
122
+ ? `${options.applied ? "Restart required" : "Restart after apply"}: yes — ${restartNote}`
123
+ : `${options.applied ? "Restart required" : "Restart after apply"}: no`);
122
124
  if (!options.applied && options.applyHint)
123
- lines.push("", "Apply:", ` ${options.applyHint}`);
125
+ lines.push("", "Apply (repeat non-interactively):", ` ${options.applyHint}`);
124
126
  if (options.applied && options.hint)
125
127
  lines.push("", "Next:", ` ${options.hint}`);
126
128
  return `${lines.join("\n")}\n`;
@@ -145,14 +147,14 @@ export function renderConfigSetup(plan, options) {
145
147
  lines.push("", "Skipped fragments:", ...plan.skipped_fragments.map((item) => ` ${item.fragment}: ${terminalSafe(item.reason)}`));
146
148
  const conflicts = plan.operations.filter((item) => item.operation === "conflict");
147
149
  lines.push("", `Conflicts: ${conflicts.length || "none"}`);
148
- lines.push(options.applied
149
- ? `Restart required: ${plan.requires_restart ? "yes" : "no"}`
150
- : `Restart after apply: ${plan.requires_restart ? "yes" : "no"}`);
150
+ lines.push(plan.requires_restart
151
+ ? `${options.applied ? "Restart required" : "Restart after apply"}: yes — restart the host session so the merged presets load`
152
+ : `${options.applied ? "Restart required" : "Restart after apply"}: no`);
151
153
  if (!options.applied) {
152
154
  if (!plan.confirmable)
153
155
  lines.push("", "No configuration changes are required.");
154
156
  else if (options.applyHint)
155
- lines.push("", "Apply:", ` ${options.applyHint}`);
157
+ lines.push("", "Apply (repeat non-interactively):", ` ${options.applyHint}`);
156
158
  }
157
159
  if (options.applied && options.hint)
158
160
  lines.push("", "Next:", ` ${options.hint}`);
@@ -166,19 +168,43 @@ function table(rows) {
166
168
  .trimEnd());
167
169
  }
168
170
  export function renderCriticsTable(inventory) {
169
- const critics = inventory.profiles.filter((profile) => profile.name.startsWith("critic-") && profile.ownership !== "user-owned");
171
+ // The panel keeps the package-owned fixed critic (`critic`) apart from the
172
+ // additional `critic-*` pool entries and points at the canonical overview.
173
+ const critics = inventory.profiles.filter((profile) => profile.ownership !== "user-owned" &&
174
+ (profile.name === "critic" || profile.name.startsWith("critic-")));
170
175
  if (!critics.length)
171
176
  return "Critics:\n No critics\n";
177
+ const pool = critics.filter((profile) => profile.name !== "critic");
172
178
  const rows = [
173
179
  ["NAME", "MODEL", "VARIANT", "PROVIDER"],
174
180
  ...critics.map((profile) => [
175
181
  terminalSafe(profile.name),
176
- terminalSafe(profile.model ?? "default"),
182
+ terminalSafe(profile.model ?? "default (host)"),
177
183
  terminalSafe(profile.variant ?? "-"),
178
184
  terminalSafe(profile.model?.split("/", 1)[0] ?? "-"),
179
185
  ]),
180
186
  ];
181
- return `Critics:\n${table(rows).join("\n")}\n`;
187
+ return (`Critics:\n${table(rows).join("\n")}\n` +
188
+ `Pool: additional critics are optional specialist profiles (` +
189
+ `${pool.length ? pool.map((profile) => terminalSafe(profile.name)).join(", ") : "none"}); ` +
190
+ "the fixed critic always deploys with the package.\n");
191
+ }
192
+ export function renderFixedRoles(inventory) {
193
+ const roles = inventory.profiles.filter((profile) => profile.ownership !== "user-owned" &&
194
+ FIXED_AGENT_ROLES.includes(profile.name));
195
+ const rows = [
196
+ ["NAME", "MODEL", "VARIANT"],
197
+ ...roles.map((profile) => [
198
+ terminalSafe(profile.name),
199
+ terminalSafe(profile.model ?? "default (host)"),
200
+ terminalSafe(profile.variant ?? "-"),
201
+ ]),
202
+ ];
203
+ return [
204
+ "Fixed roles (all six belong to the package; models and critic composition: agentomatic configure agent):",
205
+ ...table(rows),
206
+ "",
207
+ ].join("\n");
182
208
  }
183
209
  export function renderInventory(inventory) {
184
210
  const rows = [
@@ -254,7 +280,7 @@ export function renderReconcile(plan, options) {
254
280
  lines.push("", "No cleanup changes are required.");
255
281
  }
256
282
  else if (options.applyHint) {
257
- lines.push("", "Apply:", ` ${options.applyHint}`);
283
+ lines.push("", "Apply (repeat non-interactively):", ` ${options.applyHint}`);
258
284
  }
259
285
  }
260
286
  return `${lines.join("\n")}\n`;
@@ -1,7 +1,8 @@
1
1
  import { Command, Option, common, applyConfig, configFileSync, reporter } from "./generated/cli.js";
2
2
  import { applyAgentProfileChange, previewAgentProfileChange, listAgentProfiles, availableModels, availableModelVariants, validateAgentName, validateModel, validateVariant, suggestCriticName, FIXED_AGENT_ROLES, } from "./agent-profiles.js";
3
- import { apply, preview, installedSelection, defaultSelection, normalizeSelection, InstallerError, SKILL_COMMANDS, SELECTABLE_PLUGINS, } from "./installer.js";
4
- import { applyConfigSetup, previewConfigSetup, recoverConfigSetup, normalizeConfigSelection, defaultConfigSelection, CONFIG_TARGETS, CONFIG_FRAGMENTS, inspectIntegration, } from "./config-setup.js";
3
+ import { apply, preview, installedSelection, defaultSelection, normalizeSelection, InstallerError, SKILL_COMMANDS, SELECTABLE_PLUGINS, PLUGIN_DESCRIPTIONS, } from "./installer.js";
4
+ import { applyConfigSetup, previewConfigSetup, recoverConfigSetup, normalizeConfigSelection, defaultConfigSelection, CONFIG_TARGETS, CONFIG_FRAGMENTS, TARGET_DESCRIPTIONS, inspectIntegration, } from "./config-setup.js";
5
+ import { COMMAND_REGISTRY } from "./registry.js";
5
6
  import { collectDoctorFacts, doctorExitCode } from "./doctor.js";
6
7
  import { applyReconcile, previewReconcile } from "./reconcile.js";
7
8
  import { CATALOG } from "./catalog.js";
@@ -9,14 +10,14 @@ import { skillsInstallerSpec } from "./package-metadata.js";
9
10
  import { LifecycleError } from "./lifecycle.js";
10
11
  import { previewDependencyRemoval, removeDependency, planDependency } from "./self-install.js";
11
12
  import { selectOption, selectOptions, promptText, confirmQuestion } from "./terminal-wizard.js";
12
- import { renderPlan, renderInventory, renderCriticsTable, renderDoctor, renderConfigSetup, renderReconcile, terminalSafe, shellCommand, scopeArguments, } from "./cli-output.js";
13
+ import { renderPlan, renderInventory, renderCriticsTable, renderFixedRoles, renderDoctor, renderConfigSetup, renderReconcile, terminalSafe, shellCommand, scopeArguments, } from "./cli-output.js";
13
14
  let diagnostics;
14
15
  let machineOutput = false;
15
16
  let diagnosticJSON = false;
16
17
  const commands = {
17
18
  install: {
18
- description: "Install components, connect integration, and optionally configure models and critics.",
19
- options: ["commands", "agents", "plugins", "core", "dependency", "targets", "fragments"],
19
+ description: "Deploy the fixed agent package with chosen command adapters, plugins, and harness integration; agent models stay with configure agent.",
20
+ options: ["commands", "plugins", "core", "dependency", "targets", "fragments"],
20
21
  },
21
22
  configure: {
22
23
  description: "Choose components, agent models, critics, or application integration.",
@@ -24,7 +25,7 @@ const commands = {
24
25
  },
25
26
  "configure components": {
26
27
  description: "Change the installed component set without resetting models.",
27
- options: ["commands", "agents", "plugins", "core", "dependency", "targets", "fragments"],
28
+ options: ["commands", "plugins", "core", "dependency", "targets", "fragments"],
28
29
  },
29
30
  "configure agent": {
30
31
  description: "Open a staged editor for every agent, or set one model with a name and --model provider/model.",
@@ -165,7 +166,8 @@ function help(topic = "") {
165
166
  : []),
166
167
  ...(!topic || ["install", "configure components"].includes(topic)
167
168
  ? [
168
- "Non-TTY component selection: --commands <list|none> --agents <list|none> --plugins <list|none>",
169
+ "Fixed agents deploy only as the complete six-role package; no install flag or prompt changes agent composition.",
170
+ "Non-TTY component selection: --commands <list|none> --plugins <list|none>",
169
171
  "Saved selection is reused on repeat runs. --no-core disconnects the plugin; npm removal is an explicit uninstall choice.",
170
172
  ]
171
173
  : []),
@@ -312,7 +314,7 @@ async function integrationSelection(options, presetsOnly = false) {
312
314
  });
313
315
  }
314
316
  const defaults = await defaultConfigSelection(options.scope);
315
- const targets = value(await selectOptions("Applications to configure", options.scope === "project" ? ["opencode"] : CONFIG_TARGETS, defaults.targets));
317
+ const targets = value(await selectOptions("Agent harnesses to configure", options.scope === "project" ? ["opencode"] : CONFIG_TARGETS, defaults.targets, CONFIG_TARGETS.map((target) => TARGET_DESCRIPTIONS[target])));
316
318
  if (!targets.length)
317
319
  return { targets, fragments: [] };
318
320
  const connection = [];
@@ -329,7 +331,7 @@ async function integrationSelection(options, presetsOnly = false) {
329
331
  }
330
332
  const available = CONFIG_FRAGMENTS.filter((fragment) => !["core-plugin", "core-disable"].includes(fragment.name) &&
331
333
  fragment.targets.some((target) => targets.includes(target))).map((fragment) => fragment.name);
332
- const fragments = value(await selectOptions("Application presets", available, defaults.fragments.filter((fragment) => available.includes(fragment))));
334
+ const fragments = value(await selectOptions("Application presets to merge", available, defaults.fragments.filter((fragment) => available.includes(fragment)), available.map((name) => CONFIG_FRAGMENTS.find((fragment) => fragment.name === name)?.description)));
333
335
  return normalizeConfigSelection(options.scope, {
334
336
  targets,
335
337
  fragments: [...connection, ...fragments],
@@ -449,17 +451,24 @@ async function profileDraft(options, names) {
449
451
  }
450
452
  }
451
453
  }
454
+ // Descriptions for the adapter multiselect come from the command registry
455
+ // (one-to-one catalog data), with an explicit note for commandless skills.
456
+ function commandHints() {
457
+ const descriptions = new Map(COMMAND_REGISTRY.filter((command) => "skill" in command).map((command) => [command.name, command.description.split(" Russian trigger:")[0]]));
458
+ return SKILL_COMMANDS.map((name) => descriptions.get(name) ?? `No adapter: ${name} loads through the native Skill tool`);
459
+ }
452
460
  async function componentSelection(options) {
453
- const supplied = [options.commands, options.agents, options.plugins].some((selection) => selection !== undefined);
454
- if (supplied && (!options.commands || !options.agents || !options.plugins))
455
- fail("invalid_input", "Supply --commands, --agents, and --plugins together (none selects an empty set)");
461
+ const agents = [...FIXED_AGENT_ROLES];
462
+ const supplied = [options.commands, options.plugins].some((selection) => selection !== undefined);
463
+ if (supplied && (!options.commands || !options.plugins))
464
+ fail("invalid_input", "Supply --commands and --plugins together (none selects an empty set)");
456
465
  const overrides = supplied
457
466
  ? {
458
467
  commands: options.commands,
459
- agents: options.agents,
468
+ agents,
460
469
  plugins: options.plugins,
461
470
  }
462
- : {};
471
+ : { agents };
463
472
  const saved = await installedSelection(options.scope, undefined, undefined, overrides);
464
473
  const defaults = saved ?? defaultSelection();
465
474
  if (supplied) {
@@ -470,16 +479,15 @@ async function componentSelection(options) {
470
479
  }
471
480
  if (!tty()) {
472
481
  if (!saved)
473
- fail("terminal_required", "First install outside a terminal requires --commands, --agents, and --plugins");
474
- return { ...saved, core_activation: options.core ?? saved.core_activation };
482
+ fail("terminal_required", "First install outside a terminal requires --commands and --plugins");
483
+ return { ...saved, agents, core_activation: options.core ?? saved.core_activation };
475
484
  }
476
- process.stderr.write(`Portable skills are installed separately through the skills CLI (npx --yes ${skillsInstallerSpec()} add https://kisev.github.io/skills --agent opencode --copy). Selecting an adapter does not install its skill.\n`);
477
- const commands = value(await selectOptions("Skill command adapters", SKILL_COMMANDS, defaults.commands));
478
- const agents = value(await selectOptions("Fixed agents", FIXED_AGENT_ROLES, defaults.agents));
479
- const plugins = value(await selectOptions("Optional plugins", SELECTABLE_PLUGINS, defaults.plugins));
485
+ process.stderr.write(`Portable skills are installed separately through the skills CLI (npx --yes ${skillsInstallerSpec()} add https://kisev.github.io/skills --agent opencode --copy). Selecting an adapter does not install its skill.\nAll six fixed agent roles deploy as one package; set their models or add critics with \`agentomatic configure agent\` afterwards.\n`);
486
+ const commands = value(await selectOptions("Skill command adapters", SKILL_COMMANDS, defaults.commands, commandHints()));
487
+ const plugins = value(await selectOptions("Optional plugins", SELECTABLE_PLUGINS, defaults.plugins, SELECTABLE_PLUGINS.map((plugin) => PLUGIN_DESCRIPTIONS[plugin])));
480
488
  const core_activation = options.core ??
481
489
  value(await confirmQuestion("Connect the OpenCode plugin and pin its npm dependency?", process.stdin, process.stderr, defaults.core_activation));
482
- return normalizeSelection({ commands, agents, plugins, core_activation });
490
+ return normalizeSelection({ commands: commands ?? [], agents, plugins, core_activation });
483
491
  }
484
492
  async function deploy(options, repair = false) {
485
493
  const selection = repair
@@ -495,7 +503,8 @@ async function deploy(options, repair = false) {
495
503
  if (!repair &&
496
504
  (options.targets !== undefined ||
497
505
  options.fragments !== undefined ||
498
- (tty() && value(await confirmQuestion("Configure application presets as well?"))))) {
506
+ (tty() &&
507
+ value(await confirmQuestion("Configure presets for the selected agent harnesses as well? Presets merge permission rules, the secrets guard, and terminal settings into each harness config."))))) {
499
508
  integration = await integrationSelection(options, true);
500
509
  const chosenCore = integration.fragments.includes("core-plugin")
501
510
  ? true
@@ -543,8 +552,6 @@ async function deploy(options, repair = false) {
543
552
  ? [
544
553
  "--commands",
545
554
  selection.commands.join(",") || "none",
546
- "--agents",
547
- selection.agents.join(",") || "none",
548
555
  "--plugins",
549
556
  selection.plugins.join(",") || "none",
550
557
  selection.core_activation ? "--core" : "--no-core",
@@ -569,7 +576,7 @@ async function deploy(options, repair = false) {
569
576
  if (changes.length)
570
577
  process.stdout.write(`Staged profiles: ${terminalSafe(JSON.stringify(changes))}\n`);
571
578
  process.stdout.write("Components, npm, and application configuration are separate stages; a later failure does not roll back completed stages.\n");
572
- await criticsEpilogue(options);
579
+ await epilogue(options, true);
573
580
  }
574
581
  if (options.dryRun) {
575
582
  if (options.json)
@@ -610,7 +617,7 @@ async function deploy(options, repair = false) {
610
617
  json(result);
611
618
  else {
612
619
  process.stdout.write(renderPlan(applied, { applied: true }) + renderConfigSetup(connected, { applied: true }));
613
- await criticsEpilogue(options);
620
+ await epilogue(options, true);
614
621
  }
615
622
  }
616
623
  catch (error) {
@@ -621,13 +628,15 @@ async function deploy(options, repair = false) {
621
628
  partial(options, error, completed, `Run status, then ${repair ? "maintenance repair" : "install"} with the same selected components; use configure integration for configuration-only continuation.`, completed.includes("owned-components-and-profiles") && plan.requires_restart);
622
629
  }
623
630
  }
624
- // The critic panel epilogue: the table plus the canonical view and mutation
625
- // pointers, printed wherever the panel matters (install, staged applies,
626
- // configure integration).
627
- async function criticsEpilogue(options) {
631
+ // The epilogue: install adds the fixed-role package overview first, then the
632
+ // critic panel, and every variant points at the canonical `agent list` view
633
+ // plus the configure agent mutation point.
634
+ async function epilogue(options, overview) {
628
635
  if (options.json)
629
636
  return;
630
637
  const inventory = await listAgentProfiles(options.scope);
638
+ if (overview)
639
+ process.stdout.write(renderFixedRoles(inventory));
631
640
  process.stdout.write(renderCriticsTable(inventory));
632
641
  process.stdout.write("Inspect any time: agentomatic agent list | Manage critics: agentomatic configure agent\n");
633
642
  }
@@ -689,7 +698,7 @@ async function configureIntegration(options) {
689
698
  });
690
699
  else {
691
700
  process.stdout.write(renderConfigSetup(result, { applied: true }));
692
- await criticsEpilogue(options);
701
+ await epilogue(options, false);
693
702
  }
694
703
  }
695
704
  catch (error) {
@@ -738,7 +747,7 @@ async function profileChange(options, request) {
738
747
  else {
739
748
  process.stdout.write(renderPlan(result.plan, { applied: true }));
740
749
  // After a panel mutation the fresh table closes the loop.
741
- await criticsEpilogue(options);
750
+ await epilogue(options, false);
742
751
  }
743
752
  }
744
753
  async function uninstall(options) {
@@ -5,6 +5,7 @@ export declare class ConfigSetupError extends LifecycleError {
5
5
  export declare const CONFIG_TARGETS: readonly ["opencode", "kilo", "mimo"];
6
6
  export type ConfigTargetName = (typeof CONFIG_TARGETS)[number];
7
7
  export type TargetFileKind = "main" | "tui";
8
+ export declare const TARGET_DESCRIPTIONS: Record<ConfigTargetName, string>;
8
9
  export declare const CONFIG_FRAGMENTS: readonly [{
9
10
  readonly name: "core-disable";
10
11
  readonly description: "Disconnect only the agentomatic plugin, preserving unrelated configuration";
@@ -50,6 +50,13 @@ const SECRET_PATHS = [
50
50
  export class ConfigSetupError extends LifecycleError {
51
51
  }
52
52
  export const CONFIG_TARGETS = ["opencode", "kilo", "mimo"];
53
+ // One-line descriptions for the interactive harness prompt, kept next to the
54
+ // target list so the wizard never invents them ad hoc.
55
+ export const TARGET_DESCRIPTIONS = {
56
+ opencode: "OpenCode: config opencode.json(c) and terminal cli.json",
57
+ kilo: "Kilo Code: config kilo.json(c) and TUI settings",
58
+ mimo: "MiMo: config mimocode.json(c) and TUI settings",
59
+ };
53
60
  export const CONFIG_FRAGMENTS = [
54
61
  {
55
62
  name: "core-disable",
@@ -62,6 +62,7 @@ export type ArchiveCandidate = {
62
62
  };
63
63
  export declare const SELECTABLE_PLUGINS: SelectablePlugin[];
64
64
  export declare const SKILL_COMMANDS: string[];
65
+ export declare const PLUGIN_DESCRIPTIONS: Record<SelectablePlugin, string>;
65
66
  export declare function defaultSelection(): InstallerSelection;
66
67
  export declare function savedSelection(manifest: Pick<Manifest, "commands" | "agents" | "plugins">): Partial<InstallerSelection>;
67
68
  export declare function installedSelection(scope: Scope, cwd?: string, home?: string, overrides?: Partial<InstallerSelection>): Promise<InstallerSelection | null>;
package/dist/installer.js CHANGED
@@ -28,6 +28,14 @@ function retiredAssetPaths() {
28
28
  export const SELECTABLE_PLUGINS = [...CATALOG.plugins];
29
29
  export const SKILL_COMMANDS = [...CATALOG.skills];
30
30
  const PACKAGE_COMMANDS = [...CATALOG.package_commands];
31
+ // One-line descriptions for the interactive wrappers prompt, kept next to the
32
+ // selectable set so the wizard never invents them ad hoc.
33
+ export const PLUGIN_DESCRIPTIONS = {
34
+ "code-simplify": "Injects the code-simplify prevention ladder into agent sessions",
35
+ "rules-injector": "Injects AGENTS.md rules into the session on demand",
36
+ rtk: "Compresses verbose shell tool output through the RTK CLI",
37
+ "zed-bell": "Plays a terminal bell on session events (Zed integrated)",
38
+ };
31
39
  export function defaultSelection() {
32
40
  return {
33
41
  commands: [...SKILL_COMMANDS].sort(),
@@ -1,5 +1,5 @@
1
1
  import type { Readable, Writable } from "node:stream";
2
2
  export declare function selectOption(label: string, options: readonly string[], stdin?: Readable, stderr?: Writable, initial?: number): Promise<number | null>;
3
- export declare function selectOptions(label: string, options: readonly string[], initialSelected?: readonly string[], stdin?: Readable, stderr?: Writable): Promise<string[] | null>;
3
+ export declare function selectOptions(label: string, options: readonly string[], initialSelected?: readonly string[], hints?: readonly (string | undefined)[], stdin?: Readable, stderr?: Writable): Promise<string[] | null>;
4
4
  export declare function promptText(label: string, stdin?: Readable, stderr?: Writable): Promise<string | null>;
5
5
  export declare function confirmQuestion(label: string, stdin?: Readable, stderr?: Writable, initialValue?: boolean): Promise<boolean | null>;
@@ -27,7 +27,10 @@ export async function selectOption(label, options, stdin = process.stdin, stderr
27
27
  return null;
28
28
  return value;
29
29
  }
30
- export async function selectOptions(label, options, initialSelected = [], stdin = process.stdin, stderr = process.stderr) {
30
+ // The multiselect contract: each option carries a data-driven description and
31
+ // the message states the all/none controls, so every group is self-explanatory.
32
+ const MULTISELECT_CONTROLS = " (move: up/down; toggle: space; all/none: a; enter confirms)";
33
+ export async function selectOptions(label, options, initialSelected = [], hints, stdin = process.stdin, stderr = process.stderr) {
31
34
  interactiveStreams(stdin, stderr);
32
35
  validateOptions(options);
33
36
  const allowed = new Set(options);
@@ -36,8 +39,12 @@ export async function selectOptions(label, options, initialSelected = [], stdin
36
39
  throw new InstallerError("invalid_input", `Unknown initial selector option: ${value}`);
37
40
  }
38
41
  const value = await clack.multiselect({
39
- message: label,
40
- options: options.map((option) => ({ value: option, label: option })),
42
+ message: `${label}${MULTISELECT_CONTROLS}`,
43
+ options: options.map((option, index) => ({
44
+ value: option,
45
+ label: option,
46
+ ...(hints?.[index] ? { hint: hints[index] } : {}),
47
+ })),
41
48
  initialValues: [...initialSelected],
42
49
  required: false,
43
50
  input: stdin,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kisev/agentomatic",
3
- "version": "12.0.1-dev.129.g99196bf396c1",
3
+ "version": "12.0.1-dev.130.g78ea8685c8d4",
4
4
  "description": "OpenCode integration and opt-in installer for portable Agent Skills.",
5
5
  "homepage": "https://github.com/kisev/skills#readme",
6
6
  "bugs": {
@@ -50,7 +50,7 @@
50
50
  },
51
51
  "dependencies": {
52
52
  "@clack/prompts": "^1.8.1",
53
- "@kisev/safe-fs": "12.0.1-dev.129.g99196bf396c1",
53
+ "@kisev/safe-fs": "12.0.1-dev.130.g78ea8685c8d4",
54
54
  "commander": "15.0.0",
55
55
  "zod": "4.1.8"
56
56
  },