argsbarg 4.1.0 → 4.1.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 +48 -1
- package/README.md +90 -84
- package/docs/README.md +6 -6
- package/docs/bundled-docs.md +1 -1
- package/docs/cli-program.md +5 -3
- package/docs/config-schema.md +36 -9
- package/docs/developing.md +7 -7
- package/docs/distribution-homebrew.md +103 -0
- package/docs/install.md +105 -189
- package/docs/mcp.md +2 -3
- package/docs/output-schema.md +1 -1
- package/examples/full-example/Formula/.gitkeep +0 -0
- package/examples/full-example/README.md +98 -0
- package/examples/full-example/biome.json +22 -0
- package/examples/{consumer-app → full-example}/bun.lock +2 -0
- package/examples/full-example/justfile +134 -0
- package/examples/{consumer-app → full-example}/package.json +10 -3
- package/examples/{consumer-app → full-example}/schemas/generated/app-config.json +1 -1
- package/examples/{consumer-app → full-example}/schemas/generated/status.json +1 -1
- package/examples/full-example/scripts/create-identity.ts +11 -0
- package/examples/full-example/scripts/formula-shared.ts +73 -0
- package/examples/full-example/scripts/gen-dev-formula.ts +26 -0
- package/examples/full-example/scripts/print-identity.ts +27 -0
- package/examples/full-example/src/commands/echo/command.ts +21 -0
- package/examples/full-example/src/commands/status/command.test.ts +10 -0
- package/examples/full-example/src/commands/status/command.ts +36 -0
- package/examples/{consumer-app → full-example}/src/commands/status/types.ts +1 -1
- package/examples/full-example/src/index.ts +10 -0
- package/examples/full-example/src/program.ts +57 -0
- package/examples/{consumer-app → full-example}/src/types.ts +1 -1
- package/examples/nested.ts +1 -3
- package/index.d.ts +27 -66
- package/package.json +2 -2
- package/src/builtins/builtins.test.ts +22 -23
- package/src/builtins/completion-group.ts +17 -15
- package/src/builtins/dispatch.ts +25 -1
- package/src/builtins/install.ts +22 -52
- package/src/builtins/registry.ts +2 -0
- package/src/builtins/uninstall.ts +80 -0
- package/src/capabilities.ts +1 -3
- package/src/cli-tool/cli-smoke.test.ts +19 -0
- package/src/cli-tool/create.test.ts +119 -0
- package/src/cli-tool/create.ts +380 -0
- package/{examples/consumer-app/capabilities.test.ts → src/cli-tool/full-example-capabilities.test.ts} +15 -14
- package/src/cli-tool/main.ts +8 -0
- package/src/cli-tool/post-create.ts +111 -0
- package/src/cli-tool/program.ts +82 -0
- package/src/cli-tool/prompt.ts +28 -0
- package/src/cli-tool/run-create.ts +149 -0
- package/src/cli.ts +0 -2
- package/src/config/bootstrap.ts +16 -9
- package/src/config/resolve.test.ts +167 -0
- package/src/config/resolve.ts +50 -6
- package/src/docs/api-guide.test.ts +4 -5
- package/src/docs/docs.test.ts +2 -1
- package/src/docs/mcp-guide.ts +7 -8
- package/src/index.ts +3 -10
- package/src/install/binary-placement.test.ts +101 -0
- package/src/install/binary-placement.ts +47 -0
- package/src/install/index.ts +117 -123
- package/src/install/install.test.ts +89 -105
- package/src/install/normalize-uninstall.ts +11 -0
- package/src/install/normalize.ts +4 -19
- package/src/install/paths.ts +0 -22
- package/src/install/plan.ts +14 -6
- package/src/install/shell.ts +0 -14
- package/src/install/status.test.ts +6 -6
- package/src/install/status.ts +0 -6
- package/src/install/target-effective.ts +0 -2
- package/src/install/target-scope.ts +15 -28
- package/src/install/target-types.ts +0 -16
- package/src/install/targets/app.ts +19 -28
- package/src/install/targets/configure.ts +5 -1
- package/src/install/targets/index.ts +0 -3
- package/src/install/targets.test.ts +24 -42
- package/src/mcp/env.test.ts +92 -0
- package/src/mcp/env.ts +15 -14
- package/src/parse.test.ts +3 -2
- package/src/prompt.ts +10 -0
- package/src/schema.ts +9 -1
- package/src/types.ts +27 -9
- package/src/validate.ts +5 -11
- package/docs/templates/cursor/rules/cli-program.mdc +0 -31
- package/examples/config-app/main.ts +0 -20
- package/examples/config-app/program.ts +0 -78
- package/examples/config-app/schema.ts +0 -37
- package/examples/config-app/types.ts +0 -19
- package/examples/consumer-app/README.md +0 -56
- package/examples/consumer-app/src/main.ts +0 -15
- package/examples/consumer-app/src/program.ts +0 -108
- package/src/install/app.ts +0 -94
- package/src/install/bootstrap.ts +0 -22
- package/src/install/completions.ts +0 -56
- package/src/install/targets/completions.ts +0 -133
- package/src/install/update.test.ts +0 -123
- package/src/install/update.ts +0 -54
- /package/examples/{consumer-app → full-example}/schemas/configSchemas.ts +0 -0
- /package/examples/{consumer-app → full-example}/schemas/outputSchemas.ts +0 -0
- /package/examples/{consumer-app → full-example}/scripts/schemagen/discover-schema-roots.test.ts +0 -0
- /package/examples/{consumer-app → full-example}/scripts/schemagen/discover-schema-roots.ts +0 -0
- /package/examples/{consumer-app → full-example}/scripts/schemagen/naming.ts +0 -0
- /package/examples/{consumer-app → full-example}/scripts/schemagen.ts +0 -0
- /package/examples/{consumer-app → full-example}/tsconfig.json +0 -0
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { accessSync, constants, realpathSync } from "node:fs";
|
|
2
|
+
import { delimiter, join } from "node:path";
|
|
3
|
+
import type { CliProgram } from "../types.ts";
|
|
4
|
+
|
|
5
|
+
/** Resolves a command name on PATH to its real path, if present. */
|
|
6
|
+
export function resolvePathCommand(key: string): string | undefined {
|
|
7
|
+
const pathEnv = process.env.PATH ?? "";
|
|
8
|
+
for (const dir of pathEnv.split(delimiter)) {
|
|
9
|
+
if (!dir) continue;
|
|
10
|
+
const candidate = join(dir, key);
|
|
11
|
+
try {
|
|
12
|
+
accessSync(candidate, constants.F_OK);
|
|
13
|
+
return realpathSync(candidate);
|
|
14
|
+
} catch {}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const found = Bun.which(key);
|
|
18
|
+
if (found === null) return undefined;
|
|
19
|
+
try {
|
|
20
|
+
return realpathSync(found);
|
|
21
|
+
} catch {
|
|
22
|
+
return found;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function realpathOrSelf(path: string): string {
|
|
27
|
+
try {
|
|
28
|
+
return realpathSync(path);
|
|
29
|
+
} catch {
|
|
30
|
+
return path;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** True when PATH resolves to the running executable (e.g. Homebrew Cellar). */
|
|
35
|
+
export function isExternallyManagedBinary(
|
|
36
|
+
key: string,
|
|
37
|
+
execPath: string = process.execPath,
|
|
38
|
+
): boolean {
|
|
39
|
+
const resolved = resolvePathCommand(key);
|
|
40
|
+
if (!resolved) return false;
|
|
41
|
+
return resolved === realpathOrSelf(execPath);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** True when the app binary is available on PATH (e.g. Homebrew). */
|
|
45
|
+
export function isAppInstalled(program: CliProgram): boolean {
|
|
46
|
+
return isExternallyManagedBinary(program.key);
|
|
47
|
+
}
|
package/src/install/index.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { displayAppConfigPath, runInstallConfigure } from "../config/bootstrap.t
|
|
|
4
4
|
import { cliSkillInstall, skillTargetFromActionKind } from "../skill/install.ts";
|
|
5
5
|
import type { CliProgram } from "../types.ts";
|
|
6
6
|
import { normalizeInstallRawOpts } from "./normalize.ts";
|
|
7
|
+
import { normalizeUninstallRawOpts } from "./normalize-uninstall.ts";
|
|
7
8
|
import { resolveInstallPaths } from "./paths.ts";
|
|
8
9
|
import { buildInstallPlan, buildUpdatePlan, type InstallAction, type InstallOpts } from "./plan.ts";
|
|
9
10
|
import {
|
|
@@ -13,7 +14,6 @@ import {
|
|
|
13
14
|
printInstallStatus,
|
|
14
15
|
writeInteractiveInstallIntro,
|
|
15
16
|
} from "./status.ts";
|
|
16
|
-
import { resolveEffectiveInstallTargets } from "./target-effective.ts";
|
|
17
17
|
import { runTargetPreflight } from "./target-plan-build.ts";
|
|
18
18
|
import type { InstallActionKind } from "./target-types.ts";
|
|
19
19
|
import {
|
|
@@ -22,19 +22,14 @@ import {
|
|
|
22
22
|
type UninstallAction,
|
|
23
23
|
uninstallSkillDir,
|
|
24
24
|
} from "./uninstall.ts";
|
|
25
|
-
import { cliUpdate } from "./update.ts";
|
|
26
25
|
|
|
27
26
|
export function parseInstallOpts(raw: Record<string, string>): InstallOpts {
|
|
28
27
|
const flag = (name: string) => raw[name] === "1";
|
|
29
28
|
return {
|
|
30
29
|
all: flag("all"),
|
|
31
|
-
app: flag("app"),
|
|
32
|
-
completions: flag("completions"),
|
|
33
30
|
skill: flag("skill"),
|
|
34
31
|
mcp: flag("mcp"),
|
|
35
32
|
reinstall: flag("reinstall"),
|
|
36
|
-
update: flag("update"),
|
|
37
|
-
from: raw.from,
|
|
38
33
|
status: flag("status"),
|
|
39
34
|
uninstall: flag("uninstall"),
|
|
40
35
|
configure: flag("configure"),
|
|
@@ -46,91 +41,55 @@ export function parseInstallOpts(raw: Record<string, string>): InstallOpts {
|
|
|
46
41
|
|
|
47
42
|
export function validateInstallOpts(opts: InstallOpts): string | null {
|
|
48
43
|
const configureOnlyInstall =
|
|
49
|
-
opts.configure &&
|
|
50
|
-
!opts.uninstall &&
|
|
51
|
-
!opts.all &&
|
|
52
|
-
!opts.app &&
|
|
53
|
-
!opts.completions &&
|
|
54
|
-
!opts.skill &&
|
|
55
|
-
!opts.mcp &&
|
|
56
|
-
!opts.reinstall &&
|
|
57
|
-
!opts.update &&
|
|
58
|
-
!opts.status;
|
|
44
|
+
opts.configure && !opts.all && !opts.skill && !opts.mcp && !opts.reinstall && !opts.status;
|
|
59
45
|
|
|
60
46
|
if (configureOnlyInstall) {
|
|
61
47
|
return null;
|
|
62
48
|
}
|
|
63
49
|
|
|
50
|
+
if (opts.uninstall) {
|
|
51
|
+
return "Use `uninstall` instead of `install --uninstall`.";
|
|
52
|
+
}
|
|
53
|
+
|
|
64
54
|
if (opts.json) {
|
|
65
55
|
opts.yes = true;
|
|
66
56
|
}
|
|
67
57
|
if (opts.reinstall) {
|
|
68
58
|
opts.yes = true;
|
|
69
59
|
}
|
|
70
|
-
if (opts.update) {
|
|
71
|
-
opts.yes = true;
|
|
72
|
-
}
|
|
73
60
|
|
|
74
|
-
const mutationFlags =
|
|
75
|
-
opts.all ||
|
|
76
|
-
opts.app ||
|
|
77
|
-
opts.completions ||
|
|
78
|
-
opts.skill ||
|
|
79
|
-
opts.mcp ||
|
|
80
|
-
opts.reinstall ||
|
|
81
|
-
opts.update ||
|
|
82
|
-
opts.uninstall ||
|
|
83
|
-
opts.configure;
|
|
61
|
+
const mutationFlags = opts.all || opts.skill || opts.mcp || opts.reinstall || opts.configure;
|
|
84
62
|
if (opts.status && mutationFlags) {
|
|
85
|
-
return "--status is mutually exclusive with install/reinstall
|
|
86
|
-
}
|
|
87
|
-
if (
|
|
88
|
-
opts.reinstall &&
|
|
89
|
-
(opts.all ||
|
|
90
|
-
opts.completions ||
|
|
91
|
-
opts.skill ||
|
|
92
|
-
opts.mcp ||
|
|
93
|
-
opts.uninstall ||
|
|
94
|
-
opts.status ||
|
|
95
|
-
opts.update ||
|
|
96
|
-
opts.configure)
|
|
97
|
-
) {
|
|
98
|
-
return "--reinstall cannot be combined with other target flags.";
|
|
63
|
+
return "--status is mutually exclusive with install/reinstall targets.";
|
|
99
64
|
}
|
|
100
|
-
if (
|
|
101
|
-
|
|
102
|
-
(opts.all ||
|
|
103
|
-
opts.app ||
|
|
104
|
-
opts.completions ||
|
|
105
|
-
opts.skill ||
|
|
106
|
-
opts.mcp ||
|
|
107
|
-
opts.uninstall ||
|
|
108
|
-
opts.status ||
|
|
109
|
-
opts.reinstall ||
|
|
110
|
-
opts.configure)
|
|
111
|
-
) {
|
|
112
|
-
return "--update cannot be combined with other target flags.";
|
|
113
|
-
}
|
|
114
|
-
if (opts.uninstall && (opts.reinstall || opts.update || opts.status)) {
|
|
115
|
-
return "--uninstall cannot be combined with --reinstall, --update, or --status.";
|
|
116
|
-
}
|
|
117
|
-
if (opts.uninstall) {
|
|
118
|
-
const hasUninstallTarget =
|
|
119
|
-
opts.all || opts.app || opts.completions || opts.skill || opts.mcp || opts.configure;
|
|
120
|
-
if (!hasUninstallTarget) {
|
|
121
|
-
return "Specify at least one target: --all, --app, --completions, --skill, --mcp, or --configure.";
|
|
122
|
-
}
|
|
65
|
+
if (opts.reinstall && (opts.all || opts.skill || opts.mcp || opts.status || opts.configure)) {
|
|
66
|
+
return "--reinstall cannot be combined with other target flags.";
|
|
123
67
|
}
|
|
124
|
-
if (!opts.status && !opts.reinstall
|
|
125
|
-
const hasTarget =
|
|
126
|
-
opts.all || opts.app || opts.completions || opts.skill || opts.mcp || opts.configure;
|
|
68
|
+
if (!opts.status && !opts.reinstall) {
|
|
69
|
+
const hasTarget = opts.all || opts.skill || opts.mcp || opts.configure;
|
|
127
70
|
if (!hasTarget) {
|
|
128
|
-
return "Specify at least one target: --all, --
|
|
71
|
+
return "Specify at least one target: --all, --skill, --mcp, or --configure.";
|
|
129
72
|
}
|
|
130
73
|
}
|
|
131
74
|
return null;
|
|
132
75
|
}
|
|
133
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
|
+
|
|
134
93
|
function parseSelectionIndices(input: string, max: number): number[] | null {
|
|
135
94
|
const trimmed = input.trim();
|
|
136
95
|
if (!trimmed) return null;
|
|
@@ -148,14 +107,12 @@ function parseSelectionIndices(input: string, max: number): number[] | null {
|
|
|
148
107
|
return indices.sort((a, b) => a - b);
|
|
149
108
|
}
|
|
150
109
|
|
|
151
|
-
/** Interactive install:
|
|
110
|
+
/** Interactive install: legacy helper (app no longer installed). */
|
|
152
111
|
export function interactiveSelectionAssumesApp(
|
|
153
|
-
|
|
154
|
-
|
|
112
|
+
_actions: Array<InstallAction | UninstallAction>,
|
|
113
|
+
_uninstall: boolean,
|
|
155
114
|
): boolean {
|
|
156
|
-
|
|
157
|
-
const first = actions[0];
|
|
158
|
-
return first !== undefined && "kind" in first && first.kind === "app";
|
|
115
|
+
return false;
|
|
159
116
|
}
|
|
160
117
|
|
|
161
118
|
export function mergeInteractiveSelection(
|
|
@@ -229,18 +186,7 @@ function executePlan(
|
|
|
229
186
|
return changed;
|
|
230
187
|
}
|
|
231
188
|
|
|
232
|
-
|
|
233
|
-
if (opts.uninstall || opts.reinstall || opts.update || opts.status || !root.appConfig) {
|
|
234
|
-
return false;
|
|
235
|
-
}
|
|
236
|
-
if (!opts.all && !opts.app && !opts.completions && !opts.skill && !opts.mcp) {
|
|
237
|
-
return false;
|
|
238
|
-
}
|
|
239
|
-
const effective = resolveEffectiveInstallTargets(root.install, root);
|
|
240
|
-
return effective.configure.includedInAll && effective.configure.enabled;
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
/** Runs install/reinstall/uninstall mutations without exiting the process. */
|
|
189
|
+
/** Runs install/reinstall mutations without exiting the process. */
|
|
244
190
|
export async function runInstallMutation(
|
|
245
191
|
root: CliProgram,
|
|
246
192
|
rawOpts: Record<string, string>,
|
|
@@ -255,7 +201,35 @@ export async function runInstallMutation(
|
|
|
255
201
|
if (err) {
|
|
256
202
|
throw new Error(err);
|
|
257
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
|
+
}
|
|
258
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
|
+
}> {
|
|
259
233
|
const paths = resolveInstallPaths(root);
|
|
260
234
|
|
|
261
235
|
if (opts.status) {
|
|
@@ -280,13 +254,16 @@ export async function runInstallMutation(
|
|
|
280
254
|
}
|
|
281
255
|
|
|
282
256
|
let selectedActions = actions;
|
|
283
|
-
const autoYes = !!(opts.yes || opts.json || opts.reinstall || opts.
|
|
257
|
+
const autoYes = !!(opts.yes || opts.json || opts.reinstall || opts.dry);
|
|
284
258
|
|
|
285
259
|
if (!autoYes && !opts.json && process.stdin.isTTY) {
|
|
286
260
|
writeInteractiveInstallIntro(root);
|
|
287
261
|
}
|
|
288
262
|
|
|
289
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
|
+
}
|
|
290
267
|
return { changed: [], opts, paths };
|
|
291
268
|
}
|
|
292
269
|
|
|
@@ -322,17 +299,15 @@ export async function cliInstall(
|
|
|
322
299
|
root: CliProgram,
|
|
323
300
|
rawOpts: Record<string, string>,
|
|
324
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
|
+
|
|
325
307
|
const normalized = normalizeInstallRawOpts(rawOpts);
|
|
326
308
|
const opts = parseInstallOpts(normalized);
|
|
327
309
|
|
|
328
|
-
const configureOnly =
|
|
329
|
-
opts.configure &&
|
|
330
|
-
!opts.uninstall &&
|
|
331
|
-
!opts.all &&
|
|
332
|
-
!opts.app &&
|
|
333
|
-
!opts.completions &&
|
|
334
|
-
!opts.skill &&
|
|
335
|
-
!opts.mcp;
|
|
310
|
+
const configureOnly = opts.configure && !opts.all && !opts.skill && !opts.mcp;
|
|
336
311
|
|
|
337
312
|
if (configureOnly) {
|
|
338
313
|
if (!root.appConfig) {
|
|
@@ -357,14 +332,6 @@ export async function cliInstall(
|
|
|
357
332
|
process.exit(1);
|
|
358
333
|
}
|
|
359
334
|
|
|
360
|
-
if (opts.update) {
|
|
361
|
-
if (!resolveCapabilities(root).update) {
|
|
362
|
-
installErr("Remote updates are not supported by this app.");
|
|
363
|
-
process.exit(1);
|
|
364
|
-
}
|
|
365
|
-
await cliUpdate(root);
|
|
366
|
-
}
|
|
367
|
-
|
|
368
335
|
let result: Awaited<ReturnType<typeof runInstallMutation>>;
|
|
369
336
|
try {
|
|
370
337
|
result = await runInstallMutation(root, normalized);
|
|
@@ -373,23 +340,12 @@ export async function cliInstall(
|
|
|
373
340
|
process.exit(1);
|
|
374
341
|
}
|
|
375
342
|
|
|
376
|
-
const { changed, opts: mutationOpts
|
|
343
|
+
const { changed, opts: mutationOpts } = result;
|
|
377
344
|
|
|
378
345
|
if (mutationOpts.status) {
|
|
379
346
|
process.exit(0);
|
|
380
347
|
}
|
|
381
348
|
|
|
382
|
-
if (
|
|
383
|
-
shouldRunConfigureWizardAfterInstall(root, mutationOpts) &&
|
|
384
|
-
!mutationOpts.uninstall &&
|
|
385
|
-
!mutationOpts.dry
|
|
386
|
-
) {
|
|
387
|
-
const configResult = runInstallConfigure(root, { context: "after-install" });
|
|
388
|
-
if (configResult.changed) {
|
|
389
|
-
installOut(`Wrote config: ${displayAppConfigPath(root)}`, mutationOpts);
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
|
|
393
349
|
if (mutationOpts.json) {
|
|
394
350
|
process.stdout.write(`${JSON.stringify(changed, null, 2)}\n`);
|
|
395
351
|
process.exit(0);
|
|
@@ -402,14 +358,52 @@ export async function cliInstall(
|
|
|
402
358
|
? "Reinstalled"
|
|
403
359
|
: "Installed";
|
|
404
360
|
installOut(`${verb} ${changed.length} file(s).`, mutationOpts);
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
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);
|
|
411
369
|
}
|
|
412
370
|
}
|
|
413
371
|
|
|
414
372
|
process.exit(0);
|
|
415
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
|
+
}
|