argsbarg 4.1.1 → 5.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +27 -1
- package/README.md +5 -5
- package/docs/README.md +3 -3
- package/docs/ai-skills.md +9 -9
- package/docs/bundled-docs.md +4 -4
- package/docs/cli-program.md +8 -8
- package/docs/config-schema.md +2 -2
- package/docs/configure.md +177 -0
- package/docs/developing.md +1 -1
- package/docs/distribution-homebrew.md +10 -9
- package/docs/mcp.md +9 -9
- package/examples/full-example/README.md +3 -3
- package/examples/full-example/justfile +6 -6
- package/examples/full-example/scripts/formula-shared.ts +2 -2
- package/examples/full-example/src/program.ts +3 -3
- package/examples/nested.ts +1 -1
- package/index.d.ts +23 -16
- package/package.json +1 -1
- package/src/builtins/builtins.test.ts +82 -61
- package/src/builtins/completion-group.ts +4 -6
- package/src/builtins/configure-copy.ts +86 -0
- package/src/builtins/configure.ts +70 -0
- package/src/builtins/dispatch.ts +13 -33
- package/src/builtins/index.ts +1 -1
- package/src/builtins/mcp.ts +2 -2
- package/src/builtins/registry.ts +6 -6
- package/src/capabilities.ts +22 -13
- package/src/cli-tool/cli-smoke.test.ts +13 -3
- package/src/cli-tool/create.test.ts +23 -1
- package/src/cli-tool/create.ts +26 -4
- package/src/cli-tool/full-example-capabilities.test.ts +2 -2
- package/src/cli-tool/program.ts +20 -5
- package/src/cli-tool/run-create.ts +8 -19
- package/src/config/bootstrap.ts +11 -9
- package/src/config/file.test.ts +1 -1
- package/src/config/resolve.ts +2 -2
- package/src/configure/configure.test.ts +148 -0
- package/src/configure/index.ts +284 -0
- package/src/configure/prompt.ts +40 -0
- package/src/docs/builtin.ts +3 -5
- package/src/docs/docs.test.ts +5 -5
- package/src/docs/mcp-guide.ts +4 -4
- package/src/index.ts +2 -2
- package/src/install/install-validate.test.ts +5 -5
- package/src/install/opts.ts +17 -0
- package/src/install/target-effective.ts +8 -8
- package/src/install/target-scope.ts +11 -8
- package/src/install/targets/configure.ts +1 -1
- package/src/install/targets.test.ts +4 -4
- package/src/invoke.test.ts +1 -1
- package/src/mcp/tools.ts +1 -1
- package/src/mcp.integration.test.ts +4 -4
- package/src/parse.test.ts +11 -13
- package/src/schema.ts +1 -9
- package/src/skill/hint.ts +2 -2
- package/src/types.ts +22 -14
- package/src/validate.ts +17 -17
- package/docs/install.md +0 -206
- package/src/builtins/install.ts +0 -106
- package/src/builtins/uninstall.ts +0 -80
- package/src/install/index.ts +0 -409
- package/src/install/install.test.ts +0 -317
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
import { resolveCapabilities } from "../capabilities.ts";
|
|
2
|
-
import { type CliLeaf, type CliOption, CliOptionKind, type CliProgram } from "../types.ts";
|
|
3
|
-
|
|
4
|
-
/** Uninstall command options (dynamic: `--mcp` only when MCP is enabled). */
|
|
5
|
-
export function uninstallBuiltinOptions(root: CliProgram): CliOption[] {
|
|
6
|
-
const opts: CliOption[] = [
|
|
7
|
-
{
|
|
8
|
-
name: "all",
|
|
9
|
-
description: "Remove all detected agent artifacts (default when no scope flags are given).",
|
|
10
|
-
kind: CliOptionKind.Presence,
|
|
11
|
-
},
|
|
12
|
-
{
|
|
13
|
-
name: "skill",
|
|
14
|
-
description: "Remove agent skills only.",
|
|
15
|
-
kind: CliOptionKind.Presence,
|
|
16
|
-
},
|
|
17
|
-
];
|
|
18
|
-
|
|
19
|
-
if (resolveCapabilities(root).mcp) {
|
|
20
|
-
opts.push({
|
|
21
|
-
name: "mcp",
|
|
22
|
-
description: "Remove MCP server configuration only.",
|
|
23
|
-
kind: CliOptionKind.Presence,
|
|
24
|
-
});
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
if (root.appConfig) {
|
|
28
|
-
opts.push({
|
|
29
|
-
name: "configure",
|
|
30
|
-
description: "Remove the app config file only.",
|
|
31
|
-
kind: CliOptionKind.Presence,
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
opts.push(
|
|
36
|
-
{
|
|
37
|
-
name: "yes",
|
|
38
|
-
description: "Skip the confirmation prompt.",
|
|
39
|
-
kind: CliOptionKind.Presence,
|
|
40
|
-
shortName: "y",
|
|
41
|
-
},
|
|
42
|
-
{
|
|
43
|
-
name: "dry",
|
|
44
|
-
description: "Show what would change without writing files.",
|
|
45
|
-
kind: CliOptionKind.Presence,
|
|
46
|
-
},
|
|
47
|
-
{
|
|
48
|
-
name: "json",
|
|
49
|
-
description: "Print changed paths on stdout.",
|
|
50
|
-
kind: CliOptionKind.Presence,
|
|
51
|
-
},
|
|
52
|
-
);
|
|
53
|
-
|
|
54
|
-
return opts;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/** Builds the `uninstall` built-in command. */
|
|
58
|
-
export function cliBuiltinUninstallCommand(root: CliProgram): CliLeaf {
|
|
59
|
-
const app = root.key;
|
|
60
|
-
const notesLines = [
|
|
61
|
-
"Remove agent artifacts installed by this app (skills, MCP, config).",
|
|
62
|
-
"Run before `brew uninstall` while the CLI is still on PATH:",
|
|
63
|
-
"",
|
|
64
|
-
` ${app} uninstall --yes`,
|
|
65
|
-
` brew uninstall <tap>/${app}`,
|
|
66
|
-
"",
|
|
67
|
-
"Remove app config only:",
|
|
68
|
-
` ${app} uninstall --configure --yes`,
|
|
69
|
-
"",
|
|
70
|
-
"Use --dry to preview changes without writing files.",
|
|
71
|
-
"Use --json for machine-readable output.",
|
|
72
|
-
];
|
|
73
|
-
return {
|
|
74
|
-
key: "uninstall",
|
|
75
|
-
description: "Remove agent skills, MCP config, and app config for this app.",
|
|
76
|
-
options: uninstallBuiltinOptions(root),
|
|
77
|
-
notes: notesLines.join("\n"),
|
|
78
|
-
handler: () => {},
|
|
79
|
-
};
|
|
80
|
-
}
|
package/src/install/index.ts
DELETED
|
@@ -1,409 +0,0 @@
|
|
|
1
|
-
import { readSync } from "node:fs";
|
|
2
|
-
import { resolveCapabilities } from "../capabilities.ts";
|
|
3
|
-
import { displayAppConfigPath, runInstallConfigure } from "../config/bootstrap.ts";
|
|
4
|
-
import { cliSkillInstall, skillTargetFromActionKind } from "../skill/install.ts";
|
|
5
|
-
import type { CliProgram } from "../types.ts";
|
|
6
|
-
import { normalizeInstallRawOpts } from "./normalize.ts";
|
|
7
|
-
import { normalizeUninstallRawOpts } from "./normalize-uninstall.ts";
|
|
8
|
-
import { resolveInstallPaths } from "./paths.ts";
|
|
9
|
-
import { buildInstallPlan, buildUpdatePlan, type InstallAction, type InstallOpts } from "./plan.ts";
|
|
10
|
-
import {
|
|
11
|
-
installErr,
|
|
12
|
-
installInfo,
|
|
13
|
-
installOut,
|
|
14
|
-
printInstallStatus,
|
|
15
|
-
writeInteractiveInstallIntro,
|
|
16
|
-
} from "./status.ts";
|
|
17
|
-
import { runTargetPreflight } from "./target-plan-build.ts";
|
|
18
|
-
import type { InstallActionKind } from "./target-types.ts";
|
|
19
|
-
import {
|
|
20
|
-
buildUninstallPlan,
|
|
21
|
-
skillDirFromUninstallSummary,
|
|
22
|
-
type UninstallAction,
|
|
23
|
-
uninstallSkillDir,
|
|
24
|
-
} from "./uninstall.ts";
|
|
25
|
-
|
|
26
|
-
export function parseInstallOpts(raw: Record<string, string>): InstallOpts {
|
|
27
|
-
const flag = (name: string) => raw[name] === "1";
|
|
28
|
-
return {
|
|
29
|
-
all: flag("all"),
|
|
30
|
-
skill: flag("skill"),
|
|
31
|
-
mcp: flag("mcp"),
|
|
32
|
-
reinstall: flag("reinstall"),
|
|
33
|
-
status: flag("status"),
|
|
34
|
-
uninstall: flag("uninstall"),
|
|
35
|
-
configure: flag("configure"),
|
|
36
|
-
yes: flag("yes"),
|
|
37
|
-
dry: flag("dry"),
|
|
38
|
-
json: flag("json"),
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export function validateInstallOpts(opts: InstallOpts): string | null {
|
|
43
|
-
const configureOnlyInstall =
|
|
44
|
-
opts.configure && !opts.all && !opts.skill && !opts.mcp && !opts.reinstall && !opts.status;
|
|
45
|
-
|
|
46
|
-
if (configureOnlyInstall) {
|
|
47
|
-
return null;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
if (opts.uninstall) {
|
|
51
|
-
return "Use `uninstall` instead of `install --uninstall`.";
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
if (opts.json) {
|
|
55
|
-
opts.yes = true;
|
|
56
|
-
}
|
|
57
|
-
if (opts.reinstall) {
|
|
58
|
-
opts.yes = true;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const mutationFlags = opts.all || opts.skill || opts.mcp || opts.reinstall || opts.configure;
|
|
62
|
-
if (opts.status && mutationFlags) {
|
|
63
|
-
return "--status is mutually exclusive with install/reinstall targets.";
|
|
64
|
-
}
|
|
65
|
-
if (opts.reinstall && (opts.all || opts.skill || opts.mcp || opts.status || opts.configure)) {
|
|
66
|
-
return "--reinstall cannot be combined with other target flags.";
|
|
67
|
-
}
|
|
68
|
-
if (!opts.status && !opts.reinstall) {
|
|
69
|
-
const hasTarget = opts.all || opts.skill || opts.mcp || opts.configure;
|
|
70
|
-
if (!hasTarget) {
|
|
71
|
-
return "Specify at least one target: --all, --skill, --mcp, or --configure.";
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
return null;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
export function validateUninstallOpts(opts: InstallOpts): string | null {
|
|
78
|
-
if (opts.json) {
|
|
79
|
-
opts.yes = true;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
if (opts.status || opts.reinstall) {
|
|
83
|
-
return "uninstall does not support --status or --reinstall.";
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
const hasTarget = opts.all || opts.skill || opts.mcp || opts.configure;
|
|
87
|
-
if (!hasTarget) {
|
|
88
|
-
return "Specify at least one target: --all, --skill, --mcp, or --configure.";
|
|
89
|
-
}
|
|
90
|
-
return null;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
function parseSelectionIndices(input: string, max: number): number[] | null {
|
|
94
|
-
const trimmed = input.trim();
|
|
95
|
-
if (!trimmed) return null;
|
|
96
|
-
const parts = trimmed.split(/[\s,]+/).filter(Boolean);
|
|
97
|
-
const indices: number[] = [];
|
|
98
|
-
for (const part of parts) {
|
|
99
|
-
const n = Number(part);
|
|
100
|
-
if (!Number.isInteger(n) || n < 1 || n > max) {
|
|
101
|
-
return null;
|
|
102
|
-
}
|
|
103
|
-
if (!indices.includes(n)) {
|
|
104
|
-
indices.push(n);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
return indices.sort((a, b) => a - b);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/** Interactive install: legacy helper (app no longer installed). */
|
|
111
|
-
export function interactiveSelectionAssumesApp(
|
|
112
|
-
_actions: Array<InstallAction | UninstallAction>,
|
|
113
|
-
_uninstall: boolean,
|
|
114
|
-
): boolean {
|
|
115
|
-
return false;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
export function mergeInteractiveSelection(
|
|
119
|
-
indices: number[],
|
|
120
|
-
count: number,
|
|
121
|
-
alwaysIncludeFirst: boolean,
|
|
122
|
-
): number[] {
|
|
123
|
-
if (!alwaysIncludeFirst || count === 0 || indices.includes(1)) {
|
|
124
|
-
return indices;
|
|
125
|
-
}
|
|
126
|
-
return [1, ...indices].sort((a, b) => a - b);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
function promptSelectiveConfirm(count: number, alwaysIncludeFirst: boolean): number[] | null {
|
|
130
|
-
const hint = alwaysIncludeFirst
|
|
131
|
-
? "Proceed [y/N], or numbers to include (e.g. 2,3): "
|
|
132
|
-
: "Proceed [y/N], or numbers to include (e.g. 1,3): ";
|
|
133
|
-
process.stderr.write(hint);
|
|
134
|
-
const buf = Buffer.alloc(256);
|
|
135
|
-
const n = readSync(0, buf, { length: 256 });
|
|
136
|
-
const ans = buf.toString("utf8", 0, n).trim();
|
|
137
|
-
if (ans === "y" || ans === "Y") {
|
|
138
|
-
return Array.from({ length: count }, (_, i) => i + 1);
|
|
139
|
-
}
|
|
140
|
-
if (ans === "n" || ans === "N" || ans === "") {
|
|
141
|
-
return null;
|
|
142
|
-
}
|
|
143
|
-
const parsed = parseSelectionIndices(ans, count);
|
|
144
|
-
if (!parsed) return null;
|
|
145
|
-
return mergeInteractiveSelection(parsed, count, alwaysIncludeFirst);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
function runSkillAction(root: CliProgram, kind: InstallActionKind, opts: InstallOpts): string[] {
|
|
149
|
-
const target = skillTargetFromActionKind(kind);
|
|
150
|
-
if (!target) return [];
|
|
151
|
-
return cliSkillInstall(root, target, {
|
|
152
|
-
global: true,
|
|
153
|
-
rimraf: true,
|
|
154
|
-
dry: opts.dry,
|
|
155
|
-
});
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
function executePlan(
|
|
159
|
-
root: CliProgram,
|
|
160
|
-
actions: Array<InstallAction | UninstallAction>,
|
|
161
|
-
opts: InstallOpts,
|
|
162
|
-
showProgress: boolean,
|
|
163
|
-
): string[] {
|
|
164
|
-
const changed: string[] = [];
|
|
165
|
-
const paths = resolveInstallPaths(root);
|
|
166
|
-
for (const action of actions) {
|
|
167
|
-
if (showProgress) {
|
|
168
|
-
installInfo(action.message, opts);
|
|
169
|
-
}
|
|
170
|
-
if ("kind" in action && action.kind) {
|
|
171
|
-
const skillTarget = skillTargetFromActionKind(action.kind);
|
|
172
|
-
if (skillTarget) {
|
|
173
|
-
changed.push(...runSkillAction(root, action.kind as InstallActionKind, opts));
|
|
174
|
-
continue;
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
if (!("kind" in action) || !action.kind) {
|
|
178
|
-
const skillDir = skillDirFromUninstallSummary(action.summary, paths);
|
|
179
|
-
if (skillDir) {
|
|
180
|
-
changed.push(...uninstallSkillDir(skillDir, !!opts.dry));
|
|
181
|
-
continue;
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
changed.push(...action.run());
|
|
185
|
-
}
|
|
186
|
-
return changed;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
/** Runs install/reinstall mutations without exiting the process. */
|
|
190
|
-
export async function runInstallMutation(
|
|
191
|
-
root: CliProgram,
|
|
192
|
-
rawOpts: Record<string, string>,
|
|
193
|
-
): Promise<{
|
|
194
|
-
changed: string[];
|
|
195
|
-
opts: InstallOpts;
|
|
196
|
-
paths: ReturnType<typeof resolveInstallPaths>;
|
|
197
|
-
}> {
|
|
198
|
-
const normalized = normalizeInstallRawOpts(rawOpts);
|
|
199
|
-
const opts = parseInstallOpts(normalized);
|
|
200
|
-
const err = validateInstallOpts(opts);
|
|
201
|
-
if (err) {
|
|
202
|
-
throw new Error(err);
|
|
203
|
-
}
|
|
204
|
-
return runInstallMutationInternal(root, opts);
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
/** Runs uninstall mutations without exiting the process. */
|
|
208
|
-
export async function runUninstallMutation(
|
|
209
|
-
root: CliProgram,
|
|
210
|
-
rawOpts: Record<string, string>,
|
|
211
|
-
): Promise<{
|
|
212
|
-
changed: string[];
|
|
213
|
-
opts: InstallOpts;
|
|
214
|
-
paths: ReturnType<typeof resolveInstallPaths>;
|
|
215
|
-
}> {
|
|
216
|
-
const normalized = normalizeUninstallRawOpts(rawOpts);
|
|
217
|
-
const opts = parseInstallOpts(normalized);
|
|
218
|
-
const err = validateUninstallOpts(opts);
|
|
219
|
-
if (err) {
|
|
220
|
-
throw new Error(err);
|
|
221
|
-
}
|
|
222
|
-
return runInstallMutationInternal(root, opts);
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
async function runInstallMutationInternal(
|
|
226
|
-
root: CliProgram,
|
|
227
|
-
opts: InstallOpts,
|
|
228
|
-
): Promise<{
|
|
229
|
-
changed: string[];
|
|
230
|
-
opts: InstallOpts;
|
|
231
|
-
paths: ReturnType<typeof resolveInstallPaths>;
|
|
232
|
-
}> {
|
|
233
|
-
const paths = resolveInstallPaths(root);
|
|
234
|
-
|
|
235
|
-
if (opts.status) {
|
|
236
|
-
printInstallStatus(root, opts);
|
|
237
|
-
return { changed: [], opts, paths };
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
let actions: Array<InstallAction | UninstallAction>;
|
|
241
|
-
if (opts.uninstall) {
|
|
242
|
-
actions = buildUninstallPlan(root, paths, opts);
|
|
243
|
-
} else if (opts.reinstall) {
|
|
244
|
-
actions = buildUpdatePlan(root, paths, opts);
|
|
245
|
-
} else {
|
|
246
|
-
actions = buildInstallPlan(root, paths, opts);
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
const installActions = actions.filter(
|
|
250
|
-
(a): a is InstallAction => "kind" in a && typeof a.kind === "string" && a.kind.includes("-mcp"),
|
|
251
|
-
);
|
|
252
|
-
if (!opts.uninstall && resolveCapabilities(root).mcp && installActions.length > 0) {
|
|
253
|
-
runTargetPreflight(root, paths, opts, installActions);
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
let selectedActions = actions;
|
|
257
|
-
const autoYes = !!(opts.yes || opts.json || opts.reinstall || opts.dry);
|
|
258
|
-
|
|
259
|
-
if (!autoYes && !opts.json && process.stdin.isTTY) {
|
|
260
|
-
writeInteractiveInstallIntro(root);
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
if (actions.length === 0) {
|
|
264
|
-
if (!autoYes && !opts.json && process.stdin.isTTY && !opts.configure) {
|
|
265
|
-
installOut("Nothing to install for the selected targets.", opts);
|
|
266
|
-
}
|
|
267
|
-
return { changed: [], opts, paths };
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
if (!autoYes) {
|
|
271
|
-
installOut(
|
|
272
|
-
`About to ${opts.uninstall ? "remove" : opts.reinstall ? "reinstall" : "install"}:`,
|
|
273
|
-
opts,
|
|
274
|
-
);
|
|
275
|
-
for (let i = 0; i < actions.length; i++) {
|
|
276
|
-
installOut(` ${i + 1}. ${actions[i]?.summary}`, opts);
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
if (!autoYes) {
|
|
281
|
-
if (!process.stdin.isTTY) {
|
|
282
|
-
throw new Error("Refusing to proceed without --yes (stdin is not a TTY).");
|
|
283
|
-
}
|
|
284
|
-
const assumeApp = interactiveSelectionAssumesApp(actions, !!opts.uninstall);
|
|
285
|
-
const selection = promptSelectiveConfirm(actions.length, assumeApp);
|
|
286
|
-
if (!selection) {
|
|
287
|
-
throw new Error("Aborted.");
|
|
288
|
-
}
|
|
289
|
-
process.stderr.write("Done.\n");
|
|
290
|
-
selectedActions = selection.map((i) => actions[i - 1]).filter(Boolean) as typeof actions;
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
const changed = executePlan(root, selectedActions, opts, autoYes);
|
|
294
|
-
return { changed, opts, paths };
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
/** Main install command orchestrator. */
|
|
298
|
-
export async function cliInstall(
|
|
299
|
-
root: CliProgram,
|
|
300
|
-
rawOpts: Record<string, string>,
|
|
301
|
-
): Promise<never> {
|
|
302
|
-
if (rawOpts.uninstall === "1") {
|
|
303
|
-
installErr(`Use \`${root.key} uninstall\` instead of \`${root.key} install --uninstall\`.`);
|
|
304
|
-
process.exit(1);
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
const normalized = normalizeInstallRawOpts(rawOpts);
|
|
308
|
-
const opts = parseInstallOpts(normalized);
|
|
309
|
-
|
|
310
|
-
const configureOnly = opts.configure && !opts.all && !opts.skill && !opts.mcp;
|
|
311
|
-
|
|
312
|
-
if (configureOnly) {
|
|
313
|
-
if (!root.appConfig) {
|
|
314
|
-
installErr("This app does not support --configure.");
|
|
315
|
-
process.exit(1);
|
|
316
|
-
}
|
|
317
|
-
if (process.stdin.isTTY) {
|
|
318
|
-
writeInteractiveInstallIntro(root);
|
|
319
|
-
}
|
|
320
|
-
const result = runInstallConfigure(root, { context: "standalone" });
|
|
321
|
-
if (result.changed) {
|
|
322
|
-
installOut(`Wrote config: ${displayAppConfigPath(root)}`, opts);
|
|
323
|
-
} else {
|
|
324
|
-
installOut(`Config unchanged: ${displayAppConfigPath(root)}`, opts);
|
|
325
|
-
}
|
|
326
|
-
process.exit(0);
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
const err = validateInstallOpts(opts);
|
|
330
|
-
if (err) {
|
|
331
|
-
installErr(err);
|
|
332
|
-
process.exit(1);
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
let result: Awaited<ReturnType<typeof runInstallMutation>>;
|
|
336
|
-
try {
|
|
337
|
-
result = await runInstallMutation(root, normalized);
|
|
338
|
-
} catch (err) {
|
|
339
|
-
installErr(err instanceof Error ? err.message : String(err));
|
|
340
|
-
process.exit(1);
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
const { changed, opts: mutationOpts } = result;
|
|
344
|
-
|
|
345
|
-
if (mutationOpts.status) {
|
|
346
|
-
process.exit(0);
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
if (mutationOpts.json) {
|
|
350
|
-
process.stdout.write(`${JSON.stringify(changed, null, 2)}\n`);
|
|
351
|
-
process.exit(0);
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
if (changed.length > 0) {
|
|
355
|
-
const verb = mutationOpts.uninstall
|
|
356
|
-
? "Removed"
|
|
357
|
-
: mutationOpts.reinstall
|
|
358
|
-
? "Reinstalled"
|
|
359
|
-
: "Installed";
|
|
360
|
-
installOut(`${verb} ${changed.length} file(s).`, mutationOpts);
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
if (mutationOpts.configure && root.appConfig) {
|
|
364
|
-
const configResult = runInstallConfigure(root, {
|
|
365
|
-
context: changed.length > 0 ? "after-install" : "standalone",
|
|
366
|
-
});
|
|
367
|
-
if (configResult.changed) {
|
|
368
|
-
installOut(`Wrote config: ${displayAppConfigPath(root)}`, mutationOpts);
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
process.exit(0);
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
/** Main uninstall command orchestrator. */
|
|
376
|
-
export async function cliUninstall(
|
|
377
|
-
root: CliProgram,
|
|
378
|
-
rawOpts: Record<string, string>,
|
|
379
|
-
): Promise<never> {
|
|
380
|
-
const normalized = normalizeUninstallRawOpts(rawOpts);
|
|
381
|
-
const opts = parseInstallOpts(normalized);
|
|
382
|
-
|
|
383
|
-
const err = validateUninstallOpts(opts);
|
|
384
|
-
if (err) {
|
|
385
|
-
installErr(err);
|
|
386
|
-
process.exit(1);
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
let result: Awaited<ReturnType<typeof runUninstallMutation>>;
|
|
390
|
-
try {
|
|
391
|
-
result = await runUninstallMutation(root, rawOpts);
|
|
392
|
-
} catch (mutationErr) {
|
|
393
|
-
installErr(mutationErr instanceof Error ? mutationErr.message : String(mutationErr));
|
|
394
|
-
process.exit(1);
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
const { changed, opts: mutationOpts } = result;
|
|
398
|
-
|
|
399
|
-
if (mutationOpts.json) {
|
|
400
|
-
process.stdout.write(`${JSON.stringify(changed, null, 2)}\n`);
|
|
401
|
-
process.exit(0);
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
if (changed.length > 0) {
|
|
405
|
-
installOut(`Removed ${changed.length} file(s).`, mutationOpts);
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
process.exit(0);
|
|
409
|
-
}
|