@kisev/agentomatic 11.0.2 → 12.0.0-dev.113.g1bd7385234c6
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 +32 -16
- package/README.ru.md +33 -16
- package/dist/agent-profiles.d.ts +8 -2
- package/dist/agent-profiles.js +131 -82
- package/dist/assets/agents/architect.md +4 -4
- package/dist/assets/agents/critic.md +10 -11
- package/dist/assets/agents/manager.md +11 -12
- package/dist/assets/agents/mapper.md +4 -4
- package/dist/assets/agents/review.md +11 -12
- package/dist/assets/agents/worker.md +29 -32
- package/dist/assets/commands/asd-ste100.md +10 -0
- package/dist/assets/commands/code-simplify.md +12 -0
- package/dist/assets/commands/eli5.md +10 -0
- package/dist/assets/commands/handoff.md +10 -0
- package/dist/assets/commands/rtk-stats.md +2 -2
- package/dist/assets/commands/skill-doctor.md +10 -0
- package/dist/assets/migration-inventory.json +17 -6
- package/dist/assets/plugins/code-simplify.js +3 -0
- package/dist/assets/plugins/rtk.js +1 -1
- package/dist/assets/plugins/rules-injector.js +1 -1
- package/dist/assets/plugins/zed-bell.js +1 -1
- package/dist/catalog.d.ts +2 -2
- package/dist/catalog.js +9 -3
- package/dist/cli-output.js +6 -6
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +1 -1257
- package/dist/command-cli.d.ts +1 -0
- package/dist/command-cli.js +1004 -0
- package/dist/config-setup.d.ts +14 -8
- package/dist/config-setup.js +197 -64
- package/dist/contracts.js +26 -0
- package/dist/doctor.d.ts +0 -1
- package/dist/doctor.js +42 -55
- package/dist/index.d.ts +13 -118
- package/dist/index.js +118 -125
- package/dist/installer.d.ts +25 -2
- package/dist/installer.js +68 -23
- package/dist/jsonc.d.ts +18 -2
- package/dist/jsonc.js +109 -5
- package/dist/opencode-config.d.ts +11 -0
- package/dist/opencode-config.js +237 -0
- package/dist/plugins/code-simplify.d.ts +25 -0
- package/dist/plugins/code-simplify.js +49 -0
- package/dist/plugins/events.d.ts +20 -0
- package/dist/plugins/events.js +48 -0
- package/dist/plugins/rtk.d.ts +9 -12
- package/dist/plugins/rtk.js +20 -5
- package/dist/plugins/rules-injector.d.ts +21 -11
- package/dist/plugins/rules-injector.js +43 -31
- package/dist/plugins/zed-bell.d.ts +8 -5
- package/dist/plugins/zed-bell.js +10 -2
- package/dist/reconcile.d.ts +4 -1
- package/dist/reconcile.js +12 -16
- package/dist/registry.d.ts +1 -0
- package/dist/registry.js +21 -5
- package/dist/routing.d.ts +5 -3
- package/dist/routing.js +57 -22
- package/dist/self-install.d.ts +7 -0
- package/dist/self-install.js +52 -1
- package/dist/terminal-wizard.d.ts +1 -1
- package/dist/terminal-wizard.js +2 -2
- package/package.json +12 -7
- package/dist/assets/commands/skill-improve.md +0 -10
- package/dist/assets/commands/stopit.md +0 -10
package/dist/cli.js
CHANGED
|
@@ -1,1258 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import
|
|
3
|
-
import { renderConfigSetup, renderDoctor, renderInventory, renderPlan, renderReconcile, scopeArguments, shellCommand, terminalSafe, } from "./cli-output.js";
|
|
4
|
-
import { collectDoctorFacts, doctorExitCode } from "./doctor.js";
|
|
5
|
-
import { CATALOG } from "./catalog.js";
|
|
6
|
-
import { applyConfigSetup, CONFIG_FRAGMENTS, CONFIG_TARGETS, defaultConfigSelection, normalizeConfigSelection, previewConfigSetup, recoverConfigSetup, } from "./config-setup.js";
|
|
7
|
-
import { apply, defaultSelection, InstallerError, normalizeSelection, preview, SELECTABLE_PLUGINS, SKILL_COMMANDS, } from "./installer.js";
|
|
8
|
-
import { LifecycleError } from "./lifecycle.js";
|
|
9
|
-
import { skillsInstallerSpec } from "./package-metadata.js";
|
|
10
|
-
import { applyReconcile, previewReconcile } from "./reconcile.js";
|
|
11
|
-
import { confirmQuestion, promptText, selectOption, selectOptions } from "./terminal-wizard.js";
|
|
12
|
-
import { Command, Option, common, applyConfig, configFileSync, reporter } from "./generated/cli.js";
|
|
13
|
-
let diagnostics;
|
|
14
|
-
let machineOutput = false;
|
|
15
|
-
let diagnosticJSON = false;
|
|
16
|
-
function parseOptions(values) {
|
|
17
|
-
const parser = common(new Command()
|
|
18
|
-
.exitOverride()
|
|
19
|
-
.helpOption(false)
|
|
20
|
-
.configureOutput({ writeErr: () => { } }), "AGENTOMATIC").argument("[name]");
|
|
21
|
-
for (const [flags, description] of [
|
|
22
|
-
["--global", "global user scope"],
|
|
23
|
-
["--dry-run", "preview changes"],
|
|
24
|
-
["--yes", "explicitly confirm the displayed operation"],
|
|
25
|
-
["--core", "activate core integration"],
|
|
26
|
-
["--no-core", "disable core integration"],
|
|
27
|
-
["--provider <value>", "provider"],
|
|
28
|
-
["--model <value>", "model"],
|
|
29
|
-
["--variant <value>", "reasoning variant"],
|
|
30
|
-
["--clear-variant", "clear the variant"],
|
|
31
|
-
["--no-dependency", "do not provision dependencies"],
|
|
32
|
-
["--commands <value>", "command selection"],
|
|
33
|
-
["--skill-commands <value>", "command selection alias"],
|
|
34
|
-
["--agents <value>", "agent selection"],
|
|
35
|
-
["--plugins <value>", "plugin selection"],
|
|
36
|
-
["--targets <value>", "config targets"],
|
|
37
|
-
["--fragments <value>", "config fragments"],
|
|
38
|
-
]) {
|
|
39
|
-
const option = new Option(flags, description);
|
|
40
|
-
if (!["--yes", "--core", "--no-core", "--clear-variant", "--no-dependency"].includes(flags))
|
|
41
|
-
option.env(`AGENTOMATIC_${option
|
|
42
|
-
.attributeName()
|
|
43
|
-
.replace(/[A-Z]/g, (char) => `_${char}`)
|
|
44
|
-
.toUpperCase()}`);
|
|
45
|
-
parser.addOption(option);
|
|
46
|
-
}
|
|
47
|
-
try {
|
|
48
|
-
for (const flag of ["--global", "--dry-run", "--yes", "--json", "--no-dependency"])
|
|
49
|
-
if (values.filter((value) => value === flag).length > 1)
|
|
50
|
-
throw new Error(`${flag} may be supplied once`);
|
|
51
|
-
if (values.includes("--core") && values.includes("--no-core"))
|
|
52
|
-
throw new Error("Use only one core integration option");
|
|
53
|
-
if (values.includes("--variant") && values.includes("--clear-variant"))
|
|
54
|
-
throw new Error("Use only one variant option");
|
|
55
|
-
parser.parse(values, { from: "user" });
|
|
56
|
-
applyConfig(parser, configFileSync(parser.opts().config));
|
|
57
|
-
const parsed = parser.opts();
|
|
58
|
-
machineOutput = parsed.json === true;
|
|
59
|
-
diagnosticJSON = parsed.logFormat === "json";
|
|
60
|
-
diagnostics ??= reporter({ ...parsed, progress: parsed.progress ?? "never" });
|
|
61
|
-
diagnostics.emit({
|
|
62
|
-
phase: "agentomatic",
|
|
63
|
-
message: "Validated command options",
|
|
64
|
-
level: "debug",
|
|
65
|
-
});
|
|
66
|
-
const list = (value) => value === undefined
|
|
67
|
-
? undefined
|
|
68
|
-
: Array.isArray(value)
|
|
69
|
-
? value.map(String)
|
|
70
|
-
: String(value) === "none"
|
|
71
|
-
? []
|
|
72
|
-
: String(value)
|
|
73
|
-
.split(",")
|
|
74
|
-
.map((item) => item.trim())
|
|
75
|
-
.filter(Boolean);
|
|
76
|
-
return {
|
|
77
|
-
scope: parsed.global ? "global" : "project",
|
|
78
|
-
global: parsed.global === true,
|
|
79
|
-
dryRun: parsed.dryRun === true,
|
|
80
|
-
json: parsed.json === true,
|
|
81
|
-
yes: parsed.yes === true,
|
|
82
|
-
name: parser.args[0],
|
|
83
|
-
core: ["cli", "env", "config"].includes(parser.getOptionValueSource("core") ?? "")
|
|
84
|
-
? parsed.core
|
|
85
|
-
: undefined,
|
|
86
|
-
provider: parsed.provider,
|
|
87
|
-
model: parsed.model,
|
|
88
|
-
variant: parsed.clearVariant ? null : parsed.variant,
|
|
89
|
-
noDependency: parsed.dependency === false,
|
|
90
|
-
commands: list(parsed.commands ?? parsed.skillCommands),
|
|
91
|
-
agents: list(parsed.agents),
|
|
92
|
-
plugins: list(parsed.plugins),
|
|
93
|
-
configTargets: list(parsed.targets),
|
|
94
|
-
configFragments: list(parsed.fragments),
|
|
95
|
-
selectionFlag: [
|
|
96
|
-
"commands",
|
|
97
|
-
"skillCommands",
|
|
98
|
-
"agents",
|
|
99
|
-
"plugins",
|
|
100
|
-
"targets",
|
|
101
|
-
"fragments",
|
|
102
|
-
].some((key) => parsed[key] !== undefined),
|
|
103
|
-
};
|
|
104
|
-
}
|
|
105
|
-
catch (error) {
|
|
106
|
-
throw new InstallerError("invalid_input", error instanceof Error ? error.message : String(error));
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
function helpRows(rows) {
|
|
110
|
-
const width = Math.max(...rows.map(([label]) => label.length));
|
|
111
|
-
return rows.map(([label, description]) => ` ${label.padEnd(width)} ${description}`);
|
|
112
|
-
}
|
|
113
|
-
function commonRows() {
|
|
114
|
-
const parser = common(new Command("agentomatic"), "AGENTOMATIC");
|
|
115
|
-
const help = parser.createHelp();
|
|
116
|
-
return parser.options
|
|
117
|
-
.filter((option) => option.attributeName() !== "json")
|
|
118
|
-
.map((option) => [help.optionTerm(option), help.optionDescription(option)]);
|
|
119
|
-
}
|
|
120
|
-
function rootHelp() {
|
|
121
|
-
return [
|
|
122
|
-
`agentomatic ${CATALOG.version}`,
|
|
123
|
-
"",
|
|
124
|
-
"Manage OpenCode integration assets, agents, diagnostics, and migration state.",
|
|
125
|
-
`Portable Agent Skills are installed separately with npx --yes ${skillsInstallerSpec()}.`,
|
|
126
|
-
"",
|
|
127
|
-
"Usage:",
|
|
128
|
-
" agentomatic <command> [options]",
|
|
129
|
-
" Add --help after any command or command group for focused guidance.",
|
|
130
|
-
"",
|
|
131
|
-
"Commands:",
|
|
132
|
-
...helpRows([
|
|
133
|
-
["install", "Select and deploy skill commands, agents, and plugin wrappers."],
|
|
134
|
-
[
|
|
135
|
-
"uninstall",
|
|
136
|
-
"Archive and remove exact-owned assets while preserving conflicts and user files.",
|
|
137
|
-
],
|
|
138
|
-
["config", "Connect the package and recommended fragments into user configuration files."],
|
|
139
|
-
["doctor", "Inspect versions, ownership, drift, config, archives, tools, and LSP."],
|
|
140
|
-
["capabilities", "Print the versioned commands, agents, plugins, and tools catalog as JSON."],
|
|
141
|
-
["reconcile", "Classify current and retired assets; archive exact-owned retired entries."],
|
|
142
|
-
["agent list", "List profiles, models, ownership, collisions, and drift."],
|
|
143
|
-
["agent configure", "Choose an agent model interactively or with explicit options."],
|
|
144
|
-
["agent model-set", "Set one agent model directly without the interactive wizard."],
|
|
145
|
-
["agent reconcile", "Re-render package-managed agents from saved profile configuration."],
|
|
146
|
-
["critic add", "Add a named critic with a selected model."],
|
|
147
|
-
["critic remove", "Remove a package-managed additional critic."],
|
|
148
|
-
]),
|
|
149
|
-
"",
|
|
150
|
-
"Common options:",
|
|
151
|
-
...helpRows([
|
|
152
|
-
["--global", "Use global scope; project scope is the default."],
|
|
153
|
-
["--dry-run", "Preview a mutation without changing anything."],
|
|
154
|
-
["--yes", "Apply without the interactive final confirmation (required outside a terminal)."],
|
|
155
|
-
["--json", "Emit stable machine-readable output when supported."],
|
|
156
|
-
["--help", "Show this help and exit."],
|
|
157
|
-
["--version", "Show the package version and exit."],
|
|
158
|
-
...commonRows(),
|
|
159
|
-
]),
|
|
160
|
-
"",
|
|
161
|
-
"Install selection:",
|
|
162
|
-
...helpRows([
|
|
163
|
-
["--commands <list|none>", "Select adapters for installed portable skills."],
|
|
164
|
-
["--skill-commands <list|none>", "Select adapters for installed portable skills."],
|
|
165
|
-
["--agents <list|none>", "Select fixed agents."],
|
|
166
|
-
["--plugins <list|none>", "Select optional plugin wrappers; rtk is the default."],
|
|
167
|
-
["--core / --no-core", "Toggle plugin entry wiring and npm dependency provisioning."],
|
|
168
|
-
["--targets <list|none>", "Select config targets for the config command."],
|
|
169
|
-
["--fragments <list|none>", "Select config fragments for the config command."],
|
|
170
|
-
]),
|
|
171
|
-
" Lists are comma-separated. Outside a TTY, provide command selection, --agents, and --plugins;",
|
|
172
|
-
" the config command requires --targets and --fragments.",
|
|
173
|
-
"",
|
|
174
|
-
"Agent model options:",
|
|
175
|
-
...helpRows([
|
|
176
|
-
["--provider <id>", "Provider for a model name that is not provider/model."],
|
|
177
|
-
["--model <id>", "Exact provider/model or a model paired with --provider."],
|
|
178
|
-
["--variant <id>", "Set an optional model variant."],
|
|
179
|
-
["--clear-variant", "Remove the configured variant during agent model-set."],
|
|
180
|
-
]),
|
|
181
|
-
" Used by agent configure, agent model-set, and critic add.",
|
|
182
|
-
"",
|
|
183
|
-
"Safe mutation workflow:",
|
|
184
|
-
" 1. Run the command with --dry-run.",
|
|
185
|
-
" 2. Review the target, operations, conflicts, and restart requirement.",
|
|
186
|
-
" 3. Run the command without --dry-run; confirm the summary when asked",
|
|
187
|
-
" (add --yes to skip the question or to apply outside a terminal).",
|
|
188
|
-
" 4. Restart OpenCode when the applied plan requires it.",
|
|
189
|
-
" install and uninstall never edit opencode.json; the config command is the",
|
|
190
|
-
" confirmed path for configuration fragments. No command installs portable skills.",
|
|
191
|
-
"",
|
|
192
|
-
"Scope behavior:",
|
|
193
|
-
...helpRows([
|
|
194
|
-
["default", "Targets .opencode under the current directory; run from the project root."],
|
|
195
|
-
["--global", "Targets ~/.config/opencode and can run from any directory."],
|
|
196
|
-
]),
|
|
197
|
-
"",
|
|
198
|
-
"Examples:",
|
|
199
|
-
` ${shellCommand(["capabilities", "--json"])}`,
|
|
200
|
-
` ${shellCommand(["install", "--global", "--dry-run"])}`,
|
|
201
|
-
` ${shellCommand(["doctor", "--global"])}`,
|
|
202
|
-
` ${shellCommand([
|
|
203
|
-
"agent",
|
|
204
|
-
"model-set",
|
|
205
|
-
"worker",
|
|
206
|
-
"--global",
|
|
207
|
-
"--model",
|
|
208
|
-
"openai/gpt-5",
|
|
209
|
-
"--variant",
|
|
210
|
-
"high",
|
|
211
|
-
"--dry-run",
|
|
212
|
-
])}`,
|
|
213
|
-
"",
|
|
214
|
-
"Documentation:",
|
|
215
|
-
" https://github.com/kisev/skills/blob/main/docs/how-to/opencode-integration.md",
|
|
216
|
-
"",
|
|
217
|
-
].join("\n");
|
|
218
|
-
}
|
|
219
|
-
function commandHelp(topic, summary, usage, options, behavior, examples) {
|
|
220
|
-
return [
|
|
221
|
-
`agentomatic ${CATALOG.version} - ${topic}`,
|
|
222
|
-
"",
|
|
223
|
-
summary,
|
|
224
|
-
"",
|
|
225
|
-
"Usage:",
|
|
226
|
-
...usage.map((line) => ` ${line}`),
|
|
227
|
-
"",
|
|
228
|
-
"Options:",
|
|
229
|
-
...helpRows(options.map(([flag, description]) => {
|
|
230
|
-
const match = /^--([a-z][a-z-]*)/.exec(flag);
|
|
231
|
-
const environment = match &&
|
|
232
|
-
!["yes", "core", "no-core", "no-dependency", "clear-variant", "help"].includes(match[1]);
|
|
233
|
-
return [
|
|
234
|
-
flag,
|
|
235
|
-
environment
|
|
236
|
-
? `${description} (env: AGENTOMATIC_${match[1].replaceAll("-", "_").toUpperCase()})`
|
|
237
|
-
: description,
|
|
238
|
-
];
|
|
239
|
-
})),
|
|
240
|
-
"",
|
|
241
|
-
...helpRows(commonRows()),
|
|
242
|
-
"",
|
|
243
|
-
"Behavior:",
|
|
244
|
-
...behavior.map((line) => ` ${line}`),
|
|
245
|
-
"",
|
|
246
|
-
"Examples:",
|
|
247
|
-
...examples.map((line) => ` ${line}`),
|
|
248
|
-
"",
|
|
249
|
-
].join("\n");
|
|
250
|
-
}
|
|
251
|
-
function groupHelp(topic, summary, usage, commands, behavior, examples) {
|
|
252
|
-
return [
|
|
253
|
-
`agentomatic ${CATALOG.version} - ${topic}`,
|
|
254
|
-
"",
|
|
255
|
-
summary,
|
|
256
|
-
"",
|
|
257
|
-
"Usage:",
|
|
258
|
-
` ${usage}`,
|
|
259
|
-
"",
|
|
260
|
-
"Commands:",
|
|
261
|
-
...helpRows(commands),
|
|
262
|
-
"",
|
|
263
|
-
"Behavior:",
|
|
264
|
-
...behavior.map((line) => ` ${line}`),
|
|
265
|
-
"",
|
|
266
|
-
"Examples:",
|
|
267
|
-
...examples.map((line) => ` ${line}`),
|
|
268
|
-
"",
|
|
269
|
-
].join("\n");
|
|
270
|
-
}
|
|
271
|
-
function contextualHelp(arguments_) {
|
|
272
|
-
const [domain, operation] = arguments_.filter((value) => value !== "--help");
|
|
273
|
-
if (domain === "install")
|
|
274
|
-
return commandHelp("install", "Select and deploy package-owned OpenCode integration assets.", [
|
|
275
|
-
"agentomatic install [--global] --dry-run [selection options]",
|
|
276
|
-
"agentomatic install [--global] [--dry-run | --yes] [selection options]",
|
|
277
|
-
], [
|
|
278
|
-
["--global", "Use global scope; project scope is the default."],
|
|
279
|
-
["--dry-run", "Preview operations without changing anything."],
|
|
280
|
-
["--yes", "Apply without the interactive final confirmation."],
|
|
281
|
-
["--no-dependency", "Skip provisioning the persistent npm dependency for the core plugin."],
|
|
282
|
-
["--commands <list|none>", "Select installed-skill adapters."],
|
|
283
|
-
["--skill-commands <list|none>", "Select installed-skill adapters."],
|
|
284
|
-
["--agents <list|none>", "Select fixed agents."],
|
|
285
|
-
["--plugins <list|none>", "Select optional plugin wrappers; rtk is preselected."],
|
|
286
|
-
["--core", "Wire the plugin entry and pin the npm dependency in one run."],
|
|
287
|
-
["--no-core", "Skip plugin entry wiring and npm dependency provisioning."],
|
|
288
|
-
["--json", "Emit stable machine-readable output."],
|
|
289
|
-
["--help", "Show this command help and exit."],
|
|
290
|
-
], [
|
|
291
|
-
"Without selection flags, a TTY opens command, agent, plugin, and core selectors.",
|
|
292
|
-
"Use Up/Down to move, Space to toggle, A/N for all/none, and Enter to confirm.",
|
|
293
|
-
"Outside a TTY, provide command selection, --agents, and --plugins.",
|
|
294
|
-
"Confirmed core integration wires the core-plugin fragment in the same run;",
|
|
295
|
-
"asset writes never touch opencode.json directly.",
|
|
296
|
-
"Portable skills are installed separately; restart OpenCode after asset changes.",
|
|
297
|
-
], [
|
|
298
|
-
shellCommand(["install", "--global", "--dry-run"]),
|
|
299
|
-
shellCommand([
|
|
300
|
-
"install",
|
|
301
|
-
"--commands",
|
|
302
|
-
"doctor,reconcile,agent-profiles",
|
|
303
|
-
"--agents",
|
|
304
|
-
"manager,architect,mapper,worker,review,critic",
|
|
305
|
-
"--plugins",
|
|
306
|
-
"none",
|
|
307
|
-
"--dry-run",
|
|
308
|
-
]),
|
|
309
|
-
]);
|
|
310
|
-
if (domain === "config")
|
|
311
|
-
return commandHelp("config", "Connect the package and recommended fragments into user configuration files.", [
|
|
312
|
-
"agentomatic config [--global] --dry-run [selection options]",
|
|
313
|
-
"agentomatic config [--global] [--dry-run | --yes] [selection options]",
|
|
314
|
-
"agentomatic config recover [--global] [--dry-run | --yes]",
|
|
315
|
-
], [
|
|
316
|
-
["--global", "Use global scope; project scope is the default."],
|
|
317
|
-
["--dry-run", "Preview config operations without changing anything."],
|
|
318
|
-
["--yes", "Apply without the interactive final confirmation."],
|
|
319
|
-
["--targets <list|none>", `Select targets: ${CONFIG_TARGETS.join(", ")}.`],
|
|
320
|
-
[
|
|
321
|
-
"--fragments <list|none>",
|
|
322
|
-
`Select fragments: ${CONFIG_FRAGMENTS.map((item) => item.name).join(", ")}.`,
|
|
323
|
-
],
|
|
324
|
-
["--json", "Emit stable machine-readable output."],
|
|
325
|
-
["--help", "Show this command help and exit."],
|
|
326
|
-
], [
|
|
327
|
-
"In a TTY, target and fragment selectors open when flags are omitted.",
|
|
328
|
-
"Global scope edits opencode.json(c), tui.json, kilo.json(c), and mimocode.json(c).",
|
|
329
|
-
"Project scope edits the project opencode.json(c) file only.",
|
|
330
|
-
"Existing entries, comments, and unrelated keys are preserved; only absent keys are added.",
|
|
331
|
-
"Writes occur only after the exact confirmation; conflicting fragments are skipped as findings.",
|
|
332
|
-
"Interrupted config transactions require an explicit config recover preview and confirmation.",
|
|
333
|
-
], [
|
|
334
|
-
shellCommand(["config", "--global", "--dry-run"]),
|
|
335
|
-
shellCommand([
|
|
336
|
-
"config",
|
|
337
|
-
"--global",
|
|
338
|
-
"--targets",
|
|
339
|
-
"opencode",
|
|
340
|
-
"--fragments",
|
|
341
|
-
"core-plugin,skills-state-permissions",
|
|
342
|
-
"--dry-run",
|
|
343
|
-
]),
|
|
344
|
-
]);
|
|
345
|
-
if (domain === "uninstall")
|
|
346
|
-
return commandHelp("uninstall", "Remove package-owned integration assets safely.", [
|
|
347
|
-
"agentomatic uninstall [--global] --dry-run",
|
|
348
|
-
"agentomatic uninstall [--global] [--dry-run | --yes]",
|
|
349
|
-
], [
|
|
350
|
-
["--global", "Use global scope; project scope is the default."],
|
|
351
|
-
["--dry-run", "Preview removals, archives, and conflicts."],
|
|
352
|
-
["--yes", "Apply without the interactive final confirmation."],
|
|
353
|
-
["--json", "Emit stable machine-readable output."],
|
|
354
|
-
["--help", "Show this command help and exit."],
|
|
355
|
-
], [
|
|
356
|
-
"Archives and removes only exact manifest-owned assets after confirmation.",
|
|
357
|
-
"Preserves modified and user-owned files, worktrees, runtime state, and portable skills.",
|
|
358
|
-
"Remove the plugin config entry and npm dependency only after managed assets.",
|
|
359
|
-
], [
|
|
360
|
-
shellCommand(["uninstall", "--global", "--dry-run"]),
|
|
361
|
-
shellCommand(["uninstall", "--dry-run"]),
|
|
362
|
-
]);
|
|
363
|
-
if (domain === "doctor")
|
|
364
|
-
return commandHelp("doctor", "Inspect integration health without changing state.", ["agentomatic doctor [--global] [--json]"], [
|
|
365
|
-
["--global", "Inspect global scope; project scope is the default."],
|
|
366
|
-
["--json", "Emit the complete stable machine-readable report."],
|
|
367
|
-
["--help", "Show this command help and exit."],
|
|
368
|
-
], [
|
|
369
|
-
"Creates no receipts and starts no recovery, plugin factories, or LSP servers.",
|
|
370
|
-
"Reports versions, ownership, drift, collisions, config, archives, tools, and LSP facts.",
|
|
371
|
-
"Exit status: 0 clean, 1 findings, 2 invalid input or incomplete probing.",
|
|
372
|
-
], [shellCommand(["doctor"]), shellCommand(["doctor", "--global", "--json"])]);
|
|
373
|
-
if (domain === "capabilities")
|
|
374
|
-
return commandHelp("capabilities", "Print the versioned package capability catalog.", ["agentomatic capabilities [--json]"], [
|
|
375
|
-
["--json", "Explicitly request stable JSON output; JSON is the default."],
|
|
376
|
-
["--help", "Show this command help and exit."],
|
|
377
|
-
], [
|
|
378
|
-
"Requires no scope and performs no mutation.",
|
|
379
|
-
"Lists exact skills, commands, agents, plugins, and package tools.",
|
|
380
|
-
], [shellCommand(["capabilities", "--json"])]);
|
|
381
|
-
if (domain === "reconcile")
|
|
382
|
-
return commandHelp("reconcile", "Classify current and historical assets and retire exact-owned entries.", [
|
|
383
|
-
"agentomatic reconcile [--global] --dry-run",
|
|
384
|
-
"agentomatic reconcile [--global] [--dry-run | --yes]",
|
|
385
|
-
], [
|
|
386
|
-
["--global", "Reconcile global scope; project scope is the default."],
|
|
387
|
-
["--dry-run", "Preview classifications, archives, and blockers."],
|
|
388
|
-
["--yes", "Apply without the interactive final confirmation."],
|
|
389
|
-
["--json", "Emit stable machine-readable output."],
|
|
390
|
-
["--help", "Show this command help and exit."],
|
|
391
|
-
], [
|
|
392
|
-
"Preview is read-only and blocks Apply on modified managed files or ownership conflicts.",
|
|
393
|
-
"Confirmed reconcile archives exact-owned retired package assets.",
|
|
394
|
-
"Portable skills are managed separately by the skills CLI.",
|
|
395
|
-
], [
|
|
396
|
-
shellCommand(["reconcile", "--global", "--dry-run"]),
|
|
397
|
-
shellCommand(["reconcile", "--dry-run", "--json"]),
|
|
398
|
-
]);
|
|
399
|
-
if (domain === "agent" && operation === "list")
|
|
400
|
-
return commandHelp("agent list", "List agent profiles, models, ownership, collisions, and drift.", ["agentomatic agent list [--global] [--json]"], [
|
|
401
|
-
["--global", "Inspect global scope; project scope is the default."],
|
|
402
|
-
["--json", "Emit stable machine-readable inventory."],
|
|
403
|
-
["--help", "Show this command help and exit."],
|
|
404
|
-
], ["Read-only inventory; creates no receipts and changes no files or configuration."], [shellCommand(["agent", "list", "--global"]), shellCommand(["agent", "list", "--json"])]);
|
|
405
|
-
if (domain === "agent" && operation === "configure")
|
|
406
|
-
return commandHelp("agent configure", "Choose an agent model interactively or with explicit options.", [
|
|
407
|
-
"agentomatic agent configure [name] [--global] --dry-run",
|
|
408
|
-
"agentomatic agent configure <name> [--global] --model <provider/model> [--variant <id>] --dry-run",
|
|
409
|
-
"agentomatic agent configure <name> [--global] --model <provider/model> [--variant <id>] [--dry-run | --yes]",
|
|
410
|
-
], [
|
|
411
|
-
["--global", "Use global scope; project scope is the default."],
|
|
412
|
-
["--provider <id>", "Provider when --model is not provider/model."],
|
|
413
|
-
["--model <id>", "Exact provider/model or model paired with --provider."],
|
|
414
|
-
["--variant <id>", "Set an optional model variant."],
|
|
415
|
-
["--clear-variant", "Remove the configured variant."],
|
|
416
|
-
["--dry-run", "Preview the profile change."],
|
|
417
|
-
["--yes", "Apply without the interactive final confirmation."],
|
|
418
|
-
["--json", "Emit stable machine-readable output."],
|
|
419
|
-
["--help", "Show this command help and exit."],
|
|
420
|
-
], [
|
|
421
|
-
"Opens a TTY wizard unless the agent and model are explicit.",
|
|
422
|
-
"Uses the cached OpenCode model catalog and makes no LLM call.",
|
|
423
|
-
"The preview normally prints an agent model-set Apply command.",
|
|
424
|
-
], [
|
|
425
|
-
shellCommand(["agent", "configure", "manager", "--global", "--dry-run"]),
|
|
426
|
-
shellCommand([
|
|
427
|
-
"agent",
|
|
428
|
-
"configure",
|
|
429
|
-
"worker",
|
|
430
|
-
"--global",
|
|
431
|
-
"--model",
|
|
432
|
-
"openai/gpt-5",
|
|
433
|
-
"--variant",
|
|
434
|
-
"high",
|
|
435
|
-
"--dry-run",
|
|
436
|
-
]),
|
|
437
|
-
]);
|
|
438
|
-
if (domain === "agent" && operation === "model-set")
|
|
439
|
-
return commandHelp("agent model-set", "Set one agent model and optional variant directly.", [
|
|
440
|
-
"agentomatic agent model-set <name> [--global] --model <provider/model> [--variant <id>] --dry-run",
|
|
441
|
-
"agentomatic agent model-set <name> [--global] --model <provider/model> [--variant <id>] [--dry-run | --yes]",
|
|
442
|
-
], [
|
|
443
|
-
["--global", "Use global scope; project scope is the default."],
|
|
444
|
-
["--provider <id>", "Provider when --model is not provider/model."],
|
|
445
|
-
["--model <id>", "Exact provider/model or model paired with --provider."],
|
|
446
|
-
["--variant <id>", "Set an optional model variant."],
|
|
447
|
-
["--clear-variant", "Remove the configured variant."],
|
|
448
|
-
["--dry-run", "Preview the profile change."],
|
|
449
|
-
["--yes", "Apply without the interactive final confirmation."],
|
|
450
|
-
["--json", "Emit stable machine-readable output."],
|
|
451
|
-
["--help", "Show this command help and exit."],
|
|
452
|
-
], [
|
|
453
|
-
"Accepts exact provider/model or a provider and model pair.",
|
|
454
|
-
"Keeps fixed prompts and permissions unchanged and makes no LLM call.",
|
|
455
|
-
"Writes only after exact confirmation.",
|
|
456
|
-
], [
|
|
457
|
-
shellCommand([
|
|
458
|
-
"agent",
|
|
459
|
-
"model-set",
|
|
460
|
-
"worker",
|
|
461
|
-
"--global",
|
|
462
|
-
"--model",
|
|
463
|
-
"openai/gpt-5",
|
|
464
|
-
"--variant",
|
|
465
|
-
"high",
|
|
466
|
-
"--dry-run",
|
|
467
|
-
]),
|
|
468
|
-
shellCommand([
|
|
469
|
-
"agent",
|
|
470
|
-
"model-set",
|
|
471
|
-
"manager",
|
|
472
|
-
"--global",
|
|
473
|
-
"--model",
|
|
474
|
-
"openai/gpt-5",
|
|
475
|
-
"--clear-variant",
|
|
476
|
-
"--dry-run",
|
|
477
|
-
]),
|
|
478
|
-
]);
|
|
479
|
-
if (domain === "agent" && operation === "reconcile")
|
|
480
|
-
return commandHelp("agent reconcile", "Re-render managed agent files from saved profile configuration.", [
|
|
481
|
-
"agentomatic agent reconcile [--global] --dry-run",
|
|
482
|
-
"agentomatic agent reconcile [--global] [--dry-run | --yes]",
|
|
483
|
-
], [
|
|
484
|
-
["--global", "Use global scope; project scope is the default."],
|
|
485
|
-
["--dry-run", "Preview rendered agent changes and conflicts."],
|
|
486
|
-
["--yes", "Apply without the interactive final confirmation."],
|
|
487
|
-
["--json", "Emit stable machine-readable output."],
|
|
488
|
-
["--help", "Show this command help and exit."],
|
|
489
|
-
], [
|
|
490
|
-
"Uses saved models and variants while preserving package prompts and permissions.",
|
|
491
|
-
"Preserves user-owned files and collisions; writes only after confirmation.",
|
|
492
|
-
], [
|
|
493
|
-
shellCommand(["agent", "reconcile", "--global", "--dry-run"]),
|
|
494
|
-
shellCommand(["agent", "reconcile", "--dry-run", "--json"]),
|
|
495
|
-
]);
|
|
496
|
-
if (domain === "agent")
|
|
497
|
-
return groupHelp("agent", "Inspect and manage fixed agents and additional critics.", "agentomatic agent <command> [options]", [
|
|
498
|
-
["list", "List profiles, models, ownership, collisions, and drift."],
|
|
499
|
-
["configure", "Choose an agent model interactively or with explicit options."],
|
|
500
|
-
["model-set", "Set one agent model directly without the interactive wizard."],
|
|
501
|
-
["reconcile", "Re-render package-managed agents from saved profile configuration."],
|
|
502
|
-
], [
|
|
503
|
-
"Prompts and permissions remain package-owned.",
|
|
504
|
-
"Profile configuration changes models, variants, and additional critics.",
|
|
505
|
-
"Mutations use preview and exact confirmation.",
|
|
506
|
-
], [shellCommand(["agent", "list", "--global"]), shellCommand(["agent", "model-set", "--help"])]);
|
|
507
|
-
if (domain === "critic" && operation === "add")
|
|
508
|
-
return commandHelp("critic add", "Add an additional named critic with a selected model.", [
|
|
509
|
-
"agentomatic critic add [name] [--global] --dry-run",
|
|
510
|
-
"agentomatic critic add <name> [--global] --model <provider/model> [--variant <id>] --dry-run",
|
|
511
|
-
"agentomatic critic add <name> [--global] --model <provider/model> [--variant <id>] [--dry-run | --yes]",
|
|
512
|
-
], [
|
|
513
|
-
["--global", "Use global scope; project scope is the default."],
|
|
514
|
-
["--provider <id>", "Provider when --model is not provider/model."],
|
|
515
|
-
["--model <id>", "Exact provider/model or model paired with --provider."],
|
|
516
|
-
["--variant <id>", "Set an optional model variant."],
|
|
517
|
-
["--dry-run", "Preview critic creation and pool changes."],
|
|
518
|
-
["--yes", "Apply without the interactive final confirmation."],
|
|
519
|
-
["--json", "Emit stable machine-readable output."],
|
|
520
|
-
["--help", "Show this command help and exit."],
|
|
521
|
-
], [
|
|
522
|
-
"Prefixes names with critic- when needed.",
|
|
523
|
-
"Uses TTY model selection when the model is omitted and makes no LLM call.",
|
|
524
|
-
"Updates manager and review critic pools after confirmation.",
|
|
525
|
-
], [
|
|
526
|
-
shellCommand([
|
|
527
|
-
"critic",
|
|
528
|
-
"add",
|
|
529
|
-
"security",
|
|
530
|
-
"--global",
|
|
531
|
-
"--model",
|
|
532
|
-
"anthropic/claude-sonnet-4-6",
|
|
533
|
-
"--dry-run",
|
|
534
|
-
]),
|
|
535
|
-
shellCommand(["critic", "add", "performance", "--global", "--dry-run"]),
|
|
536
|
-
]);
|
|
537
|
-
if (domain === "critic" && operation === "remove")
|
|
538
|
-
return commandHelp("critic remove", "Remove a package-managed additional critic.", [
|
|
539
|
-
"agentomatic critic remove <name> [--global] --dry-run",
|
|
540
|
-
"agentomatic critic remove <name> [--global] [--dry-run | --yes]",
|
|
541
|
-
], [
|
|
542
|
-
["--global", "Use global scope; project scope is the default."],
|
|
543
|
-
["--dry-run", "Preview critic removal and pool changes."],
|
|
544
|
-
["--yes", "Apply without the interactive final confirmation."],
|
|
545
|
-
["--json", "Emit stable machine-readable output."],
|
|
546
|
-
["--help", "Show this command help and exit."],
|
|
547
|
-
], [
|
|
548
|
-
"Accepts the name with or without the critic- prefix.",
|
|
549
|
-
"Preserves the fixed critic and user-owned files.",
|
|
550
|
-
"Updates manager and review critic pools after confirmation.",
|
|
551
|
-
], [
|
|
552
|
-
shellCommand(["critic", "remove", "security", "--global", "--dry-run"]),
|
|
553
|
-
shellCommand(["critic", "remove", "security", "--dry-run"]),
|
|
554
|
-
]);
|
|
555
|
-
if (domain === "critic")
|
|
556
|
-
return groupHelp("critic", "Manage additional package-owned critic profiles.", "agentomatic critic <command> [options]", [
|
|
557
|
-
["add", "Add a named critic with a selected model."],
|
|
558
|
-
["remove", "Remove a package-managed additional critic."],
|
|
559
|
-
], [
|
|
560
|
-
"Names use critic-<safe-suffix>.",
|
|
561
|
-
"Manager and review critic pools update with profile configuration.",
|
|
562
|
-
"Mutations use preview and exact confirmation.",
|
|
563
|
-
], [
|
|
564
|
-
shellCommand(["critic", "add", "security", "--help"]),
|
|
565
|
-
shellCommand(["critic", "remove", "security", "--help"]),
|
|
566
|
-
]);
|
|
567
|
-
return undefined;
|
|
568
|
-
}
|
|
569
|
-
async function interactiveInstallerSelection() {
|
|
570
|
-
if (!process.stdin.isTTY || !process.stderr.isTTY)
|
|
571
|
-
throw new InstallerError("terminal_required", "install requires explicit selection flags outside a terminal");
|
|
572
|
-
process.stderr.write([
|
|
573
|
-
"Portable skills are installed separately through npx skills.",
|
|
574
|
-
"This installer does not install, update, or remove portable skills.",
|
|
575
|
-
"Skill command adapters are OpenCode slash commands that load an already-installed skill with the same name.",
|
|
576
|
-
"Selecting a command adapter does not select or install its skill.",
|
|
577
|
-
"\n",
|
|
578
|
-
].join("\n"));
|
|
579
|
-
const group = async (label, names, initialSelected) => {
|
|
580
|
-
const selected = await selectOptions(label, names, initialSelected, process.stdin, process.stderr);
|
|
581
|
-
if (selected === null)
|
|
582
|
-
throw new InstallerError("cancelled", "Wizard cancelled");
|
|
583
|
-
return selected;
|
|
584
|
-
};
|
|
585
|
-
const defaults = defaultSelection();
|
|
586
|
-
const commands = await group("Skill command adapters", SKILL_COMMANDS, SKILL_COMMANDS);
|
|
587
|
-
const agents = await group("Fixed agents", defaults.agents, defaults.agents);
|
|
588
|
-
const plugins = await group("Selectable plugins", SELECTABLE_PLUGINS, defaults.plugins);
|
|
589
|
-
const core = await selectOption("Core integration: wire the plugin entry and pin the npm dependency?", ["Yes", "No"], process.stdin, process.stderr);
|
|
590
|
-
if (core === null)
|
|
591
|
-
throw new InstallerError("cancelled", "Wizard cancelled");
|
|
592
|
-
return normalizeSelection({
|
|
593
|
-
commands,
|
|
594
|
-
agents: agents,
|
|
595
|
-
plugins: plugins,
|
|
596
|
-
core_activation: core === 0,
|
|
597
|
-
});
|
|
598
|
-
}
|
|
599
|
-
function installerSelection(options) {
|
|
600
|
-
return normalizeSelection({
|
|
601
|
-
commands: options.commands,
|
|
602
|
-
agents: options.agents,
|
|
603
|
-
plugins: options.plugins,
|
|
604
|
-
...(options.core === undefined ? {} : { core_activation: options.core }),
|
|
605
|
-
});
|
|
606
|
-
}
|
|
607
|
-
function configSelectionFromOptions(options) {
|
|
608
|
-
return {
|
|
609
|
-
targets: options.configTargets,
|
|
610
|
-
fragments: options.configFragments,
|
|
611
|
-
};
|
|
612
|
-
}
|
|
613
|
-
function configSelectionArguments(selection) {
|
|
614
|
-
return [
|
|
615
|
-
"--targets",
|
|
616
|
-
selection.targets.join(",") || "none",
|
|
617
|
-
"--fragments",
|
|
618
|
-
selection.fragments.join(",") || "none",
|
|
619
|
-
];
|
|
620
|
-
}
|
|
621
|
-
async function interactiveConfigSelection(options) {
|
|
622
|
-
if (!process.stdin.isTTY || !process.stderr.isTTY)
|
|
623
|
-
throw new InstallerError("terminal_required", "config requires --targets and --fragments outside a terminal");
|
|
624
|
-
process.stderr.write([
|
|
625
|
-
"This command edits user-owned configuration files after an exact confirmation.",
|
|
626
|
-
"Global scope targets opencode.json(c), tui.json, kilo.json(c), and mimocode.json(c).",
|
|
627
|
-
"Existing entries, comments, and unrelated keys are preserved.",
|
|
628
|
-
"\n",
|
|
629
|
-
].join("\n"));
|
|
630
|
-
const defaults = await defaultConfigSelection(options.scope);
|
|
631
|
-
const availableTargets = options.scope === "project" ? ["opencode"] : CONFIG_TARGETS;
|
|
632
|
-
const targets = await selectOptions("Config targets", [...availableTargets], defaults.targets.filter((target) => availableTargets.includes(target)), process.stdin, process.stderr);
|
|
633
|
-
if (targets === null)
|
|
634
|
-
throw new InstallerError("cancelled", "Wizard cancelled");
|
|
635
|
-
const availableFragments = CONFIG_FRAGMENTS.filter((fragment) => fragment.targets.some((target) => targets.includes(target))).map((fragment) => fragment.name);
|
|
636
|
-
if (!availableFragments.length)
|
|
637
|
-
return normalizeConfigSelection(options.scope, {
|
|
638
|
-
targets: targets,
|
|
639
|
-
fragments: [],
|
|
640
|
-
});
|
|
641
|
-
const fragments = await selectOptions("Config fragments", availableFragments, defaults.fragments.filter((fragment) => availableFragments.includes(fragment)), process.stdin, process.stderr);
|
|
642
|
-
if (fragments === null)
|
|
643
|
-
throw new InstallerError("cancelled", "Wizard cancelled");
|
|
644
|
-
return normalizeConfigSelection(options.scope, {
|
|
645
|
-
targets: targets,
|
|
646
|
-
fragments: fragments,
|
|
647
|
-
});
|
|
648
|
-
}
|
|
649
|
-
function selectionArguments(selection) {
|
|
650
|
-
return [
|
|
651
|
-
"--commands",
|
|
652
|
-
selection.commands.join(",") || "none",
|
|
653
|
-
"--agents",
|
|
654
|
-
selection.agents.join(",") || "none",
|
|
655
|
-
"--plugins",
|
|
656
|
-
selection.plugins.join(",") || "none",
|
|
657
|
-
];
|
|
658
|
-
}
|
|
659
|
-
function interactiveTerminal() {
|
|
660
|
-
return Boolean(process.stdin.isTTY && process.stderr.isTTY);
|
|
661
|
-
}
|
|
662
|
-
function requireApplyMode(options) {
|
|
663
|
-
if (options.dryRun && options.yes)
|
|
664
|
-
throw new InstallerError("invalid_input", "Use either --dry-run or --yes, not both");
|
|
665
|
-
if (!options.dryRun && !options.yes && !interactiveTerminal())
|
|
666
|
-
throw new InstallerError("invalid_input", "Applying outside a terminal requires --yes; use --dry-run to preview");
|
|
667
|
-
}
|
|
668
|
-
async function confirmSummary() {
|
|
669
|
-
const choice = await confirmQuestion("Apply these changes?");
|
|
670
|
-
if (choice !== true)
|
|
671
|
-
throw new InstallerError("cancelled", "Cancelled before apply");
|
|
672
|
-
}
|
|
673
|
-
function applyHint(command) {
|
|
674
|
-
return `${shellCommand(command)} (confirm interactively; add --yes outside a terminal)`;
|
|
675
|
-
}
|
|
676
|
-
function exactModel(options) {
|
|
677
|
-
if (!options.model)
|
|
678
|
-
return undefined;
|
|
679
|
-
if (options.model.includes("/")) {
|
|
680
|
-
const model = validateModel(options.model);
|
|
681
|
-
if (options.provider && model.split("/", 1)[0] !== options.provider)
|
|
682
|
-
throw new InstallerError("invalid_input", "--provider does not match the exact --model value");
|
|
683
|
-
return model;
|
|
684
|
-
}
|
|
685
|
-
if (!options.provider)
|
|
686
|
-
throw new InstallerError("invalid_input", "--provider is required when --model is not provider/model");
|
|
687
|
-
return validateModel(`${options.provider}/${options.model}`);
|
|
688
|
-
}
|
|
689
|
-
async function interactiveSelection(options, action = "model-set") {
|
|
690
|
-
if (!process.stdin.isTTY || !process.stderr.isTTY) {
|
|
691
|
-
throw new InstallerError("terminal_required", "agent configure requires a terminal or explicit agent <name> and either exact --model provider/model or --provider + --model");
|
|
692
|
-
}
|
|
693
|
-
const inventory = await listAgentProfiles(options.scope);
|
|
694
|
-
const configurable = inventory.profiles.filter((item) => item.ownership !== "user-owned");
|
|
695
|
-
let name;
|
|
696
|
-
if (options.name) {
|
|
697
|
-
name =
|
|
698
|
-
action === "critic-add" && !options.name.startsWith("critic-")
|
|
699
|
-
? validateAgentName(`critic-${options.name}`)
|
|
700
|
-
: validateAgentName(options.name);
|
|
701
|
-
}
|
|
702
|
-
else if (action === "critic-add") {
|
|
703
|
-
const raw = await promptText("Critic name (critic-<suffix>):");
|
|
704
|
-
if (raw === null)
|
|
705
|
-
throw new InstallerError("cancelled", "Wizard cancelled");
|
|
706
|
-
name = raw.startsWith("critic-") ? raw : `critic-${raw}`;
|
|
707
|
-
validateAgentName(name);
|
|
708
|
-
}
|
|
709
|
-
else {
|
|
710
|
-
const agentNames = configurable.map((item) => item.name);
|
|
711
|
-
if (agentNames.length === 0)
|
|
712
|
-
agentNames.push(...FIXED_AGENT_ROLES);
|
|
713
|
-
const index = await selectOption("Select agent:", agentNames);
|
|
714
|
-
if (index === null)
|
|
715
|
-
throw new InstallerError("cancelled", "Wizard cancelled");
|
|
716
|
-
name = agentNames[index];
|
|
717
|
-
}
|
|
718
|
-
const currentProfile = configurable.find((item) => item.name === name);
|
|
719
|
-
const currentModel = currentProfile?.model;
|
|
720
|
-
const currentVariant = currentProfile?.variant;
|
|
721
|
-
const showTarget = (model, variant) => {
|
|
722
|
-
process.stderr.write(`Target: ${model}${variant ? ` / ${variant}` : ""}\n`);
|
|
723
|
-
};
|
|
724
|
-
if (options.provider && options.model) {
|
|
725
|
-
const model = exactModel(options);
|
|
726
|
-
const variant = validateVariant(options.variant);
|
|
727
|
-
return { name, model, ...(variant ? { variant } : {}) };
|
|
728
|
-
}
|
|
729
|
-
while (true) {
|
|
730
|
-
const actions = [];
|
|
731
|
-
if (currentModel) {
|
|
732
|
-
actions.push(`Keep current (${currentModel}${currentVariant ? ` / ${currentVariant}` : ""})`);
|
|
733
|
-
}
|
|
734
|
-
actions.push("Change model");
|
|
735
|
-
if (currentVariant)
|
|
736
|
-
actions.push("Clear variant");
|
|
737
|
-
if (!options.name && action === "model-set")
|
|
738
|
-
actions.push("Back");
|
|
739
|
-
actions.push("Cancel");
|
|
740
|
-
const currentLabel = `Agent: ${name}${currentModel ? ` (current: ${currentModel}${currentVariant ? ` / ${currentVariant}` : ""})` : ""}`;
|
|
741
|
-
const actionIndex = await selectOption(currentLabel, actions);
|
|
742
|
-
if (actionIndex === null)
|
|
743
|
-
throw new InstallerError("cancelled", "Wizard cancelled");
|
|
744
|
-
const chosen = actions[actionIndex];
|
|
745
|
-
if (chosen.startsWith("Keep current")) {
|
|
746
|
-
if (!currentModel)
|
|
747
|
-
throw new InstallerError("invalid_state", "No current model to keep");
|
|
748
|
-
showTarget(currentModel, currentVariant);
|
|
749
|
-
return { name, model: currentModel, ...(currentVariant ? { variant: currentVariant } : {}) };
|
|
750
|
-
}
|
|
751
|
-
if (chosen === "Change model") {
|
|
752
|
-
let models;
|
|
753
|
-
try {
|
|
754
|
-
models = await availableModels();
|
|
755
|
-
}
|
|
756
|
-
catch (error) {
|
|
757
|
-
if (error instanceof AgentProfileError && error.code === "catalog_unavailable") {
|
|
758
|
-
process.stderr.write("\nModel catalog is unavailable.\nUse direct CLI with exact --model provider/model and optional --variant.\n\n");
|
|
759
|
-
throw new InstallerError("catalog_unavailable", "Use direct CLI with exact --model provider/model");
|
|
760
|
-
}
|
|
761
|
-
throw error;
|
|
762
|
-
}
|
|
763
|
-
const providers = [...new Set(models.map((m) => m.split("/", 1)[0]))].sort();
|
|
764
|
-
const providerIndex = await selectOption("Select provider:", providers);
|
|
765
|
-
if (providerIndex === null)
|
|
766
|
-
continue;
|
|
767
|
-
const provider = providers[providerIndex];
|
|
768
|
-
const filteredModels = models.filter((m) => m.startsWith(`${provider}/`));
|
|
769
|
-
const modelIndex = await selectOption("Select model:", filteredModels);
|
|
770
|
-
if (modelIndex === null)
|
|
771
|
-
continue;
|
|
772
|
-
const selectedModel = filteredModels[modelIndex];
|
|
773
|
-
let selectedVariant;
|
|
774
|
-
try {
|
|
775
|
-
const variants = await availableModelVariants(selectedModel);
|
|
776
|
-
if (variants.length) {
|
|
777
|
-
const variantOptions = ["(none)", ...variants];
|
|
778
|
-
const variantIndex = await selectOption("Select variant:", variantOptions);
|
|
779
|
-
if (variantIndex === null)
|
|
780
|
-
continue;
|
|
781
|
-
if (variantIndex > 0)
|
|
782
|
-
selectedVariant = variantOptions[variantIndex];
|
|
783
|
-
}
|
|
784
|
-
}
|
|
785
|
-
catch (error) {
|
|
786
|
-
if (error instanceof AgentProfileError && error.code === "catalog_unavailable") {
|
|
787
|
-
process.stderr.write("\nModel variant metadata is unavailable.\nUse direct CLI with exact --model provider/model and optional --variant.\n\n");
|
|
788
|
-
throw new InstallerError("catalog_unavailable", "Use direct CLI with exact --model provider/model");
|
|
789
|
-
}
|
|
790
|
-
throw error;
|
|
791
|
-
}
|
|
792
|
-
showTarget(selectedModel, selectedVariant);
|
|
793
|
-
return {
|
|
794
|
-
name,
|
|
795
|
-
model: selectedModel,
|
|
796
|
-
...(selectedVariant ? { variant: selectedVariant } : {}),
|
|
797
|
-
};
|
|
798
|
-
}
|
|
799
|
-
if (chosen === "Clear variant") {
|
|
800
|
-
if (!currentModel)
|
|
801
|
-
throw new InstallerError("invalid_state", "No model to clear variant for");
|
|
802
|
-
showTarget(currentModel);
|
|
803
|
-
return { name, model: currentModel };
|
|
804
|
-
}
|
|
805
|
-
if (chosen === "Back") {
|
|
806
|
-
return interactiveSelection({ ...options, name: undefined }, action);
|
|
807
|
-
}
|
|
808
|
-
if (chosen === "Cancel") {
|
|
809
|
-
throw new InstallerError("cancelled", "Wizard cancelled");
|
|
810
|
-
}
|
|
811
|
-
}
|
|
812
|
-
}
|
|
813
|
-
async function runProfile(request, options) {
|
|
814
|
-
requireApplyMode(options);
|
|
815
|
-
if (options.dryRun) {
|
|
816
|
-
const plan = await previewAgentProfileChange(request, options.scope);
|
|
817
|
-
if (options.json)
|
|
818
|
-
process.stdout.write(`${JSON.stringify({ status: "ok", applied: false, requires_restart: false, plan }, null, 2)}\n`);
|
|
819
|
-
else
|
|
820
|
-
process.stdout.write(renderPlan(plan, {
|
|
821
|
-
applied: false,
|
|
822
|
-
applyHint: applyHint(profileApplyArguments(request, options.scope)),
|
|
823
|
-
}));
|
|
824
|
-
}
|
|
825
|
-
else {
|
|
826
|
-
if (!options.yes) {
|
|
827
|
-
const plan = await previewAgentProfileChange(request, options.scope);
|
|
828
|
-
process.stdout.write(renderPlan(plan, { applied: false }));
|
|
829
|
-
await confirmSummary();
|
|
830
|
-
}
|
|
831
|
-
const applied = await applyAgentProfileChange(request, options.scope);
|
|
832
|
-
if (options.json)
|
|
833
|
-
process.stdout.write(`${JSON.stringify(applied, null, 2)}\n`);
|
|
834
|
-
else
|
|
835
|
-
process.stdout.write(renderPlan(applied.plan, { applied: true }));
|
|
836
|
-
}
|
|
837
|
-
}
|
|
838
|
-
function profileApplyArguments(request, scope) {
|
|
839
|
-
if (request.action === "reconcile")
|
|
840
|
-
return ["agent", "reconcile", ...scopeArguments(scope)];
|
|
841
|
-
if (request.action === "critic-remove")
|
|
842
|
-
return ["critic", "remove", request.name, ...scopeArguments(scope)];
|
|
843
|
-
const command = request.action === "critic-add"
|
|
844
|
-
? ["critic", "add", request.name]
|
|
845
|
-
: ["agent", "model-set", request.name];
|
|
846
|
-
command.push(...scopeArguments(scope), "--model", request.model);
|
|
847
|
-
if (request.variant)
|
|
848
|
-
command.push("--variant", request.variant);
|
|
849
|
-
else if (request.action === "model-set")
|
|
850
|
-
command.push("--clear-variant");
|
|
851
|
-
return command;
|
|
852
|
-
}
|
|
853
|
-
async function run(arguments_) {
|
|
854
|
-
const removedScope = arguments_.find((value) => value === "--scope" || value.startsWith("--scope="));
|
|
855
|
-
if (removedScope)
|
|
856
|
-
throw new InstallerError("invalid_input", "--scope has been removed; omit it for project scope or use --global");
|
|
857
|
-
if (arguments_.filter((value) => value === "--global").length > 1)
|
|
858
|
-
throw new InstallerError("invalid_input", "--global may be supplied once");
|
|
859
|
-
if (arguments_[0] === "capabilities" && arguments_.includes("--global"))
|
|
860
|
-
throw new InstallerError("invalid_input", "capabilities accepts only --json");
|
|
861
|
-
if (arguments_.includes("--help")) {
|
|
862
|
-
const [domain, operation] = arguments_.filter((value) => !value.startsWith("--"));
|
|
863
|
-
if ((domain === "agent" &&
|
|
864
|
-
operation &&
|
|
865
|
-
!["list", "configure", "model-set", "reconcile"].includes(operation)) ||
|
|
866
|
-
(domain === "critic" && operation && !["add", "remove"].includes(operation)))
|
|
867
|
-
throw new InstallerError("invalid_input", `Unknown command: ${domain} ${operation}`);
|
|
868
|
-
const page = contextualHelp(arguments_);
|
|
869
|
-
if (!page && arguments_[0] !== "--help")
|
|
870
|
-
throw new InstallerError("invalid_input", `Unknown command: ${arguments_[0]}`);
|
|
871
|
-
for (const value of arguments_.filter((item) => item.startsWith("--") && item !== "--help"))
|
|
872
|
-
if (!(page ?? rootHelp()).includes(value))
|
|
873
|
-
throw new InstallerError("invalid_input", `Unknown argument: ${value}`);
|
|
874
|
-
process.stdout.write(page ?? rootHelp());
|
|
875
|
-
return;
|
|
876
|
-
}
|
|
877
|
-
if (arguments_.length === 0) {
|
|
878
|
-
process.stdout.write(rootHelp());
|
|
879
|
-
return;
|
|
880
|
-
}
|
|
881
|
-
if (arguments_.includes("--version")) {
|
|
882
|
-
if (arguments_.some((value) => value !== "--version" && value !== "--json"))
|
|
883
|
-
throw new InstallerError("invalid_input", "--version accepts only --json");
|
|
884
|
-
process.stdout.write(arguments_.includes("--json")
|
|
885
|
-
? `${JSON.stringify({ status: "ok", version: CATALOG.version })}\n`
|
|
886
|
-
: `${CATALOG.version}\n`);
|
|
887
|
-
return;
|
|
888
|
-
}
|
|
889
|
-
const [domain, operation, ...rest] = arguments_;
|
|
890
|
-
if (domain === "doctor") {
|
|
891
|
-
if (operation)
|
|
892
|
-
rest.unshift(operation);
|
|
893
|
-
const options = parseOptions(rest);
|
|
894
|
-
if (options.dryRun ||
|
|
895
|
-
options.yes ||
|
|
896
|
-
options.name ||
|
|
897
|
-
options.provider ||
|
|
898
|
-
options.model ||
|
|
899
|
-
options.variant !== undefined ||
|
|
900
|
-
options.selectionFlag)
|
|
901
|
-
throw new InstallerError("invalid_input", "doctor accepts only --global and --json");
|
|
902
|
-
const report = await collectDoctorFacts(options.scope);
|
|
903
|
-
if (options.json)
|
|
904
|
-
process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
|
|
905
|
-
else
|
|
906
|
-
process.stdout.write(renderDoctor(report));
|
|
907
|
-
process.exitCode = doctorExitCode(report);
|
|
908
|
-
return;
|
|
909
|
-
}
|
|
910
|
-
if (domain === "capabilities") {
|
|
911
|
-
if (operation)
|
|
912
|
-
rest.unshift(operation);
|
|
913
|
-
const options = parseOptions(rest);
|
|
914
|
-
if (options.dryRun ||
|
|
915
|
-
options.yes ||
|
|
916
|
-
options.name ||
|
|
917
|
-
options.provider ||
|
|
918
|
-
options.model ||
|
|
919
|
-
options.variant !== undefined ||
|
|
920
|
-
options.global ||
|
|
921
|
-
options.selectionFlag)
|
|
922
|
-
throw new InstallerError("invalid_input", "capabilities accepts only --json");
|
|
923
|
-
process.stdout.write(`${JSON.stringify({ schema_version: 1, status: "ok", ...CATALOG }, null, 2)}\n`);
|
|
924
|
-
return;
|
|
925
|
-
}
|
|
926
|
-
if (domain === "reconcile") {
|
|
927
|
-
if (operation)
|
|
928
|
-
rest.unshift(operation);
|
|
929
|
-
const options = parseOptions(rest);
|
|
930
|
-
if (options.name ||
|
|
931
|
-
options.provider ||
|
|
932
|
-
options.model ||
|
|
933
|
-
options.variant !== undefined ||
|
|
934
|
-
options.selectionFlag)
|
|
935
|
-
throw new InstallerError("invalid_input", "reconcile accepts only --global, --dry-run, --yes, and --json");
|
|
936
|
-
requireApplyMode(options);
|
|
937
|
-
if (options.dryRun) {
|
|
938
|
-
const plan = await previewReconcile(options.scope);
|
|
939
|
-
if (options.json)
|
|
940
|
-
process.stdout.write(`${JSON.stringify({ status: "ok", applied: false, plan }, null, 2)}\n`);
|
|
941
|
-
else
|
|
942
|
-
process.stdout.write(renderReconcile(plan, {
|
|
943
|
-
applied: false,
|
|
944
|
-
applyHint: plan.confirmable
|
|
945
|
-
? applyHint(["reconcile", ...scopeArguments(options.scope)])
|
|
946
|
-
: undefined,
|
|
947
|
-
}));
|
|
948
|
-
}
|
|
949
|
-
else {
|
|
950
|
-
if (!options.yes) {
|
|
951
|
-
const plan = await previewReconcile(options.scope);
|
|
952
|
-
process.stdout.write(renderReconcile(plan, { applied: false }));
|
|
953
|
-
await confirmSummary();
|
|
954
|
-
}
|
|
955
|
-
const applied = await applyReconcile(options.scope);
|
|
956
|
-
if (options.json)
|
|
957
|
-
process.stdout.write(`${JSON.stringify(applied, null, 2)}\n`);
|
|
958
|
-
else
|
|
959
|
-
process.stdout.write(renderReconcile(applied.plan, { applied: true }));
|
|
960
|
-
}
|
|
961
|
-
return;
|
|
962
|
-
}
|
|
963
|
-
if (domain === "install" || domain === "uninstall") {
|
|
964
|
-
if (operation?.startsWith("--") || operation === undefined)
|
|
965
|
-
rest.unshift(...(operation ? [operation] : []));
|
|
966
|
-
else
|
|
967
|
-
throw new InstallerError("invalid_input", `Unexpected argument: ${operation}`);
|
|
968
|
-
const options = parseOptions(rest);
|
|
969
|
-
const action = domain;
|
|
970
|
-
if (options.name ||
|
|
971
|
-
options.provider ||
|
|
972
|
-
options.model ||
|
|
973
|
-
options.variant !== undefined ||
|
|
974
|
-
(action === "uninstall" && options.selectionFlag))
|
|
975
|
-
throw new InstallerError("invalid_input", action === "install"
|
|
976
|
-
? "install does not accept model options or positional arguments"
|
|
977
|
-
: "uninstall accepts only --global, --dry-run, --yes, and --json");
|
|
978
|
-
if (action === "install" &&
|
|
979
|
-
options.selectionFlag &&
|
|
980
|
-
(options.commands === undefined ||
|
|
981
|
-
options.agents === undefined ||
|
|
982
|
-
options.plugins === undefined))
|
|
983
|
-
throw new InstallerError("invalid_input", "Non-TTY install requires --commands, --agents, and --plugins");
|
|
984
|
-
const selection = action === "install"
|
|
985
|
-
? options.selectionFlag
|
|
986
|
-
? installerSelection(options)
|
|
987
|
-
: await interactiveInstallerSelection()
|
|
988
|
-
: undefined;
|
|
989
|
-
const coreSelection = action === "install" && selection?.core_activation
|
|
990
|
-
? normalizeConfigSelection(options.scope, {
|
|
991
|
-
targets: ["opencode"],
|
|
992
|
-
fragments: ["core-plugin"],
|
|
993
|
-
})
|
|
994
|
-
: undefined;
|
|
995
|
-
const applyCoreIntegration = async () => {
|
|
996
|
-
if (!coreSelection)
|
|
997
|
-
return null;
|
|
998
|
-
try {
|
|
999
|
-
return await applyConfigSetup(coreSelection, options.scope, process.cwd(), undefined, {
|
|
1000
|
-
provisionDependency: false,
|
|
1001
|
-
receipt: corePreview?.receipt,
|
|
1002
|
-
});
|
|
1003
|
-
}
|
|
1004
|
-
catch (error) {
|
|
1005
|
-
const known = error instanceof LifecycleError
|
|
1006
|
-
? error
|
|
1007
|
-
: new InstallerError("internal_error", error instanceof Error ? error.message : String(error));
|
|
1008
|
-
process.stderr.write(`Assets applied, but core integration failed [${known.code}]: ${terminalSafe(known.message)}\nRetry with: ${shellCommand(["config", ...scopeArguments(options.scope), "--targets", "opencode", "--fragments", "core-plugin"])}\n`);
|
|
1009
|
-
process.exitCode = 2;
|
|
1010
|
-
return null;
|
|
1011
|
-
}
|
|
1012
|
-
};
|
|
1013
|
-
requireApplyMode(options);
|
|
1014
|
-
const corePreview = coreSelection
|
|
1015
|
-
? await previewConfigSetup(coreSelection, options.scope, process.cwd(), undefined, false)
|
|
1016
|
-
: undefined;
|
|
1017
|
-
if (options.dryRun) {
|
|
1018
|
-
const plan = await preview(action, options.scope, process.cwd(), undefined, selection, !options.noDependency);
|
|
1019
|
-
const corePlan = corePreview;
|
|
1020
|
-
if (options.json)
|
|
1021
|
-
process.stdout.write(`${JSON.stringify({ status: "ok", applied: false, requires_restart: plan.requires_restart, plan, ...(corePlan ? { core_integration: corePlan } : {}) }, null, 2)}\n`);
|
|
1022
|
-
else {
|
|
1023
|
-
process.stdout.write(renderPlan(plan, {
|
|
1024
|
-
applied: false,
|
|
1025
|
-
applyHint: applyHint([
|
|
1026
|
-
action,
|
|
1027
|
-
...scopeArguments(options.scope),
|
|
1028
|
-
...(action === "install" ? selectionArguments(plan.selection) : []),
|
|
1029
|
-
...(options.noDependency ? ["--no-dependency"] : []),
|
|
1030
|
-
]),
|
|
1031
|
-
}));
|
|
1032
|
-
if (corePlan)
|
|
1033
|
-
process.stdout.write(renderConfigSetup(corePlan, { applied: false }));
|
|
1034
|
-
}
|
|
1035
|
-
}
|
|
1036
|
-
else {
|
|
1037
|
-
if (!options.yes) {
|
|
1038
|
-
const plan = await preview(action, options.scope, process.cwd(), undefined, selection, !options.noDependency);
|
|
1039
|
-
process.stdout.write(renderPlan(plan, { applied: false }));
|
|
1040
|
-
if (corePreview)
|
|
1041
|
-
process.stdout.write(renderConfigSetup(corePreview, { applied: false }));
|
|
1042
|
-
await confirmSummary();
|
|
1043
|
-
}
|
|
1044
|
-
const plan = await apply(action, options.scope, process.cwd(), undefined, {}, selection, !options.noDependency);
|
|
1045
|
-
const corePlan = await applyCoreIntegration();
|
|
1046
|
-
if (options.json)
|
|
1047
|
-
process.stdout.write(`${JSON.stringify({ status: "ok", applied: true, requires_restart: plan.requires_restart, plan, ...(corePlan ? { core_integration: corePlan } : {}) }, null, 2)}\n`);
|
|
1048
|
-
else
|
|
1049
|
-
process.stdout.write(renderPlan(plan, {
|
|
1050
|
-
applied: true,
|
|
1051
|
-
...(action === "install" && !corePlan
|
|
1052
|
-
? {
|
|
1053
|
-
hint: `Connect the package into user configs: ${shellCommand(["config", ...scopeArguments(options.scope), "--dry-run"])}`,
|
|
1054
|
-
}
|
|
1055
|
-
: {}),
|
|
1056
|
-
}));
|
|
1057
|
-
if (corePlan && !options.json)
|
|
1058
|
-
process.stdout.write(renderConfigSetup(corePlan, { applied: true }));
|
|
1059
|
-
}
|
|
1060
|
-
return;
|
|
1061
|
-
}
|
|
1062
|
-
if (domain === "agent" && operation === "list") {
|
|
1063
|
-
const options = parseOptions(rest);
|
|
1064
|
-
if (options.dryRun ||
|
|
1065
|
-
options.yes ||
|
|
1066
|
-
options.name ||
|
|
1067
|
-
options.provider ||
|
|
1068
|
-
options.model ||
|
|
1069
|
-
options.variant !== undefined ||
|
|
1070
|
-
options.selectionFlag)
|
|
1071
|
-
throw new InstallerError("invalid_input", "agent list accepts only --global and --json");
|
|
1072
|
-
const inventory = await listAgentProfiles(options.scope);
|
|
1073
|
-
if (options.json)
|
|
1074
|
-
process.stdout.write(`${JSON.stringify({ status: "ok", inventory }, null, 2)}\n`);
|
|
1075
|
-
else
|
|
1076
|
-
process.stdout.write(renderInventory(inventory));
|
|
1077
|
-
return;
|
|
1078
|
-
}
|
|
1079
|
-
if (domain === "agent" && operation === "configure") {
|
|
1080
|
-
const options = parseOptions(rest);
|
|
1081
|
-
if (options.selectionFlag)
|
|
1082
|
-
throw new InstallerError("invalid_input", "agent configure does not accept selection options");
|
|
1083
|
-
requireApplyMode(options);
|
|
1084
|
-
const model = exactModel(options);
|
|
1085
|
-
const selected = options.name && model
|
|
1086
|
-
? {
|
|
1087
|
-
name: validateAgentName(options.name),
|
|
1088
|
-
model,
|
|
1089
|
-
...(validateVariant(options.variant)
|
|
1090
|
-
? { variant: validateVariant(options.variant) }
|
|
1091
|
-
: {}),
|
|
1092
|
-
}
|
|
1093
|
-
: await interactiveSelection(options, "model-set");
|
|
1094
|
-
await runProfile({ action: "model-set", ...selected, variant: selected.variant ?? null }, options);
|
|
1095
|
-
return;
|
|
1096
|
-
}
|
|
1097
|
-
if (domain === "agent" && operation === "model-set") {
|
|
1098
|
-
const options = parseOptions(rest);
|
|
1099
|
-
if (options.selectionFlag)
|
|
1100
|
-
throw new InstallerError("invalid_input", "agent model-set does not accept selection options");
|
|
1101
|
-
const name = validateAgentName(options.name ?? "");
|
|
1102
|
-
const model = exactModel(options);
|
|
1103
|
-
if (!model)
|
|
1104
|
-
throw new InstallerError("invalid_input", "agent model-set requires --provider and --model, or exact --model provider/model");
|
|
1105
|
-
await runProfile({ action: "model-set", name, model, variant: options.variant ?? null }, options);
|
|
1106
|
-
return;
|
|
1107
|
-
}
|
|
1108
|
-
if (domain === "agent" && operation === "reconcile") {
|
|
1109
|
-
const options = parseOptions(rest);
|
|
1110
|
-
if (options.name ||
|
|
1111
|
-
options.provider ||
|
|
1112
|
-
options.model ||
|
|
1113
|
-
options.variant !== undefined ||
|
|
1114
|
-
options.selectionFlag)
|
|
1115
|
-
throw new InstallerError("invalid_input", "agent reconcile accepts only --global, --dry-run, --yes, and --json");
|
|
1116
|
-
await runProfile({ action: "reconcile" }, options);
|
|
1117
|
-
return;
|
|
1118
|
-
}
|
|
1119
|
-
if (domain === "critic" && (operation === "add" || operation === "remove")) {
|
|
1120
|
-
const options = parseOptions(rest);
|
|
1121
|
-
if (options.selectionFlag)
|
|
1122
|
-
throw new InstallerError("invalid_input", `critic ${operation} does not accept selection options`);
|
|
1123
|
-
const name = options.name === "critic" || options.name?.startsWith("critic-")
|
|
1124
|
-
? options.name
|
|
1125
|
-
: `critic-${options.name ?? ""}`;
|
|
1126
|
-
if (operation === "add") {
|
|
1127
|
-
if (options.variant === null)
|
|
1128
|
-
throw new InstallerError("invalid_input", "critic add does not accept --clear-variant");
|
|
1129
|
-
const model = exactModel(options);
|
|
1130
|
-
if (!model) {
|
|
1131
|
-
const selected = await interactiveSelection(options, "critic-add");
|
|
1132
|
-
await runProfile({
|
|
1133
|
-
action: "critic-add",
|
|
1134
|
-
name: selected.name,
|
|
1135
|
-
model: selected.model,
|
|
1136
|
-
variant: selected.variant ?? null,
|
|
1137
|
-
}, options);
|
|
1138
|
-
}
|
|
1139
|
-
else {
|
|
1140
|
-
await runProfile({ action: "critic-add", name, model, variant: options.variant ?? null }, options);
|
|
1141
|
-
}
|
|
1142
|
-
}
|
|
1143
|
-
else {
|
|
1144
|
-
if (options.provider ||
|
|
1145
|
-
options.model ||
|
|
1146
|
-
options.variant !== undefined ||
|
|
1147
|
-
options.selectionFlag)
|
|
1148
|
-
throw new InstallerError("invalid_input", "critic remove does not accept model options");
|
|
1149
|
-
await runProfile({ action: "critic-remove", name }, options);
|
|
1150
|
-
}
|
|
1151
|
-
return;
|
|
1152
|
-
}
|
|
1153
|
-
if (domain === "config" && operation === "recover") {
|
|
1154
|
-
if (rest.some((value) => !["--global", "--dry-run", "--yes", "--json"].includes(value)))
|
|
1155
|
-
throw new InstallerError("invalid_input", "config recover accepts only --global, --dry-run, --yes, and --json");
|
|
1156
|
-
const options = parseOptions(rest);
|
|
1157
|
-
requireApplyMode(options);
|
|
1158
|
-
const preview = await recoverConfigSetup(options.scope, true);
|
|
1159
|
-
if (!options.json)
|
|
1160
|
-
process.stdout.write(`Config recovery restores these interrupted transaction paths:\n${preview.paths.map((path) => ` ${terminalSafe(path)}`).join("\n")}\n`);
|
|
1161
|
-
if (!options.dryRun && !options.yes && preview.paths.length)
|
|
1162
|
-
await confirmSummary();
|
|
1163
|
-
const result = options.dryRun
|
|
1164
|
-
? preview
|
|
1165
|
-
: await recoverConfigSetup(options.scope, false, process.cwd(), undefined, preview.digest);
|
|
1166
|
-
if (options.json)
|
|
1167
|
-
process.stdout.write(`${JSON.stringify(result)}\n`);
|
|
1168
|
-
else
|
|
1169
|
-
process.stdout.write(options.dryRun
|
|
1170
|
-
? "Apply with config recover and confirmation.\n"
|
|
1171
|
-
: "Recovery complete; request a fresh config preview.\n");
|
|
1172
|
-
return;
|
|
1173
|
-
}
|
|
1174
|
-
if (domain === "config") {
|
|
1175
|
-
if (operation?.startsWith("--") || operation === undefined)
|
|
1176
|
-
rest.unshift(...(operation ? [operation] : []));
|
|
1177
|
-
else
|
|
1178
|
-
throw new InstallerError("invalid_input", `Unexpected argument: ${operation}`);
|
|
1179
|
-
const options = parseOptions(rest);
|
|
1180
|
-
if (options.name ||
|
|
1181
|
-
options.provider ||
|
|
1182
|
-
options.model ||
|
|
1183
|
-
options.variant !== undefined ||
|
|
1184
|
-
options.commands !== undefined ||
|
|
1185
|
-
options.agents !== undefined ||
|
|
1186
|
-
options.plugins !== undefined)
|
|
1187
|
-
throw new InstallerError("invalid_input", "config accepts only --global, --targets, --fragments, --dry-run, --yes, and --json");
|
|
1188
|
-
if (options.selectionFlag &&
|
|
1189
|
-
(options.configTargets === undefined || options.configFragments === undefined))
|
|
1190
|
-
throw new InstallerError("invalid_input", "Non-TTY config requires both --targets and --fragments");
|
|
1191
|
-
const selection = options.selectionFlag
|
|
1192
|
-
? normalizeConfigSelection(options.scope, configSelectionFromOptions(options))
|
|
1193
|
-
: await interactiveConfigSelection(options);
|
|
1194
|
-
requireApplyMode(options);
|
|
1195
|
-
if (options.dryRun) {
|
|
1196
|
-
const plan = await previewConfigSetup(selection, options.scope);
|
|
1197
|
-
if (options.json)
|
|
1198
|
-
process.stdout.write(`${JSON.stringify({ status: "ok", applied: false, plan }, null, 2)}\n`);
|
|
1199
|
-
else
|
|
1200
|
-
process.stdout.write(renderConfigSetup(plan, {
|
|
1201
|
-
applied: false,
|
|
1202
|
-
applyHint: plan.confirmable
|
|
1203
|
-
? applyHint([
|
|
1204
|
-
"config",
|
|
1205
|
-
...scopeArguments(options.scope),
|
|
1206
|
-
...configSelectionArguments(plan.selection),
|
|
1207
|
-
])
|
|
1208
|
-
: undefined,
|
|
1209
|
-
}));
|
|
1210
|
-
}
|
|
1211
|
-
else {
|
|
1212
|
-
const preview = await previewConfigSetup(selection, options.scope);
|
|
1213
|
-
if (!options.yes) {
|
|
1214
|
-
process.stdout.write(renderConfigSetup(preview, { applied: false }));
|
|
1215
|
-
await confirmSummary();
|
|
1216
|
-
}
|
|
1217
|
-
const plan = await applyConfigSetup(selection, options.scope, process.cwd(), undefined, {
|
|
1218
|
-
receipt: preview.receipt,
|
|
1219
|
-
});
|
|
1220
|
-
if (options.json)
|
|
1221
|
-
process.stdout.write(`${JSON.stringify({ status: "ok", applied: true, requires_restart: plan.requires_restart, plan }, null, 2)}\n`);
|
|
1222
|
-
else
|
|
1223
|
-
process.stdout.write(renderConfigSetup(plan, {
|
|
1224
|
-
applied: true,
|
|
1225
|
-
hint: `Restart OpenCode, Kilo Code, and MiMo Code when their configs changed.`,
|
|
1226
|
-
}));
|
|
1227
|
-
}
|
|
1228
|
-
return;
|
|
1229
|
-
}
|
|
1230
|
-
throw new InstallerError("invalid_input", "Use install, uninstall, doctor, capabilities, reconcile, config, agent list|configure|model-set|reconcile, or critic add|remove");
|
|
1231
|
-
}
|
|
1232
|
-
async function main() {
|
|
1233
|
-
try {
|
|
1234
|
-
let args = process.argv
|
|
1235
|
-
.slice(2)
|
|
1236
|
-
.map((value) => (value === "-h" ? "--help" : value === "-V" ? "--version" : value));
|
|
1237
|
-
if (args[0] === "help")
|
|
1238
|
-
args = [...args.slice(1), "--help"];
|
|
1239
|
-
await run(args);
|
|
1240
|
-
}
|
|
1241
|
-
catch (error) {
|
|
1242
|
-
const known = error instanceof LifecycleError
|
|
1243
|
-
? error
|
|
1244
|
-
: new InstallerError("internal_error", error instanceof Error ? error.message : String(error));
|
|
1245
|
-
if (process.argv.includes("--json") || machineOutput)
|
|
1246
|
-
process.stdout.write(`${JSON.stringify({ status: "error", error: { code: known.code, message: known.message } })}\n`);
|
|
1247
|
-
else if (diagnosticJSON)
|
|
1248
|
-
process.stderr.write(`${JSON.stringify({ level: "error", phase: "agentomatic.error", code: known.code, message: known.message })}\n`);
|
|
1249
|
-
else
|
|
1250
|
-
process.stderr.write(`Error [${known.code}]: ${terminalSafe(known.message)}\n`);
|
|
1251
|
-
process.exitCode = 2;
|
|
1252
|
-
}
|
|
1253
|
-
finally {
|
|
1254
|
-
diagnostics?.emit({ phase: "agentomatic.done", message: "Command finished", level: "debug" });
|
|
1255
|
-
diagnostics?.close();
|
|
1256
|
-
}
|
|
1257
|
-
}
|
|
1258
|
-
void main();
|
|
2
|
+
import "./command-cli.js";
|