argsbarg 3.3.1 → 3.3.2
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 +12 -1
- package/docs/cli-program.md +17 -2
- package/docs/install.md +6 -4
- package/docs/templates/cursor/rules/cli-program.mdc +1 -1
- package/package.json +1 -1
- package/src/builtins/install.ts +3 -3
- package/src/install/detect-installed.ts +0 -1
- package/src/install/index.ts +7 -9
- package/src/install/install.test.ts +64 -1
- package/src/install/mcp-config.ts +0 -1
- package/src/install/plan.ts +8 -8
- package/src/install/uninstall.ts +17 -15
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [3.3.2] - 2026-06-21
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
## [3.3.2] - 2026-06-21
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
|
|
17
|
+
- **`install --uninstall`** — symmetric with install: requires `--all` or scoped flags; `--uninstall --all` removes everything argsbarg installed; empty scope succeeds without error.
|
|
18
|
+
|
|
10
19
|
## [3.3.1] - 2026-06-21
|
|
11
20
|
|
|
12
21
|
### Added
|
|
@@ -243,7 +252,9 @@ const cli = { ... } satisfies CliProgram; // or : CliProgram
|
|
|
243
252
|
- Migrate schemas: rename every `children` property to **`commands`**; move positional definitions to **`CliPositional`** objects on `positionals` and strip `positional` / `argMin` / `argMax` from flag definitions under `options` (flags only carry `name`, `description`, `kind`, and optional `shortName`).
|
|
244
253
|
- Imports: use `CliPositional` where needed; replace `CliOptionDef` with `CliOption` or `CliPositional` as appropriate.
|
|
245
254
|
|
|
246
|
-
[Unreleased]: https://github.com/bdombro/bun-argsbarg/compare/v3.3.
|
|
255
|
+
[Unreleased]: https://github.com/bdombro/bun-argsbarg/compare/v3.3.2...HEAD
|
|
256
|
+
[3.3.2]: https://github.com/bdombro/bun-argsbarg/releases/tag/v3.3.2
|
|
257
|
+
[3.3.2]: https://github.com/bdombro/bun-argsbarg/releases/tag/v3.3.2
|
|
247
258
|
[3.3.1]: https://github.com/bdombro/bun-argsbarg/releases/tag/v3.3.1
|
|
248
259
|
[3.3.0]: https://github.com/bdombro/bun-argsbarg/releases/tag/v3.3.0
|
|
249
260
|
[3.2.0]: https://github.com/bdombro/bun-argsbarg/releases/tag/v3.2.0
|
package/docs/cli-program.md
CHANGED
|
@@ -52,12 +52,27 @@ ArgsBarg is **schema-first** — the program tree is the product. **Keep `CliPro
|
|
|
52
52
|
| --- | --- |
|
|
53
53
|
| Shared option objects (`DRY_RUN_OPTION`, `JSON_OPTION`) | Identical flag reused on many leaves |
|
|
54
54
|
| Shared spreads (`...MCP_TOOL_MUTATOR`) | Same `mcpTool` metadata on a family of commands |
|
|
55
|
-
| `
|
|
55
|
+
| `commands/<name>/command.tsx` module | Entry file is large; handler/body is substantial (Ink page, headless dispatch) |
|
|
56
56
|
| `docs.topics` text imports | Compile-time markdown bundling — not schema shape |
|
|
57
57
|
|
|
58
58
|
**Avoid extracting** thin indirection: a file that only re-exports `{ key, description, options }` with no logic, or splitting every leaf into its own module when the handler is a few lines. If extraction does not reduce duplication or file size materially, keep it inline.
|
|
59
59
|
|
|
60
|
-
|
|
60
|
+
When you extract a leaf or router, prefer a **plain exported object** — not a zero-arg wrapper function:
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
// commands/reserve/command.tsx
|
|
64
|
+
export const reserveCommand = {
|
|
65
|
+
key: "reserve",
|
|
66
|
+
description: "Reserve a QA environment.",
|
|
67
|
+
options: [YES_OPTION, DRY_RUN_OPTION],
|
|
68
|
+
positionals: [/* … */],
|
|
69
|
+
handler: async (ctx) => { /* … */ },
|
|
70
|
+
} satisfies CliLeaf;
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Use a **parameterized factory** only when the schema truly depends on inputs (e.g. `createUpsertCommand(deps)` for tests or injected config). A `reserveCommand()` that returns a static literal adds indirection without benefit.
|
|
74
|
+
|
|
75
|
+
**`satisfies CliProgram`** on the root (or **`satisfies CliLeaf`** / router type on extracted modules) preserves type-checking whether inline or not.
|
|
61
76
|
|
|
62
77
|
## Descriptions
|
|
63
78
|
|
package/docs/install.md
CHANGED
|
@@ -17,8 +17,8 @@ myapp update
|
|
|
17
17
|
# See what is installed
|
|
18
18
|
myapp install --status
|
|
19
19
|
|
|
20
|
-
# Remove everything
|
|
21
|
-
myapp install --uninstall --yes
|
|
20
|
+
# Remove everything installed with --all
|
|
21
|
+
myapp install --uninstall --all --yes
|
|
22
22
|
```
|
|
23
23
|
|
|
24
24
|
## What gets installed
|
|
@@ -33,7 +33,9 @@ myapp install --uninstall --yes
|
|
|
33
33
|
| Claude skill | `--skill` | `~/.claude/skills/<dir>/` when `~/.claude` exists |
|
|
34
34
|
| MCP config | `--mcp` | `~/.cursor/mcp.json` and `~/.claude.json` when MCP is enabled |
|
|
35
35
|
|
|
36
|
-
`--all` expands to `--bin`, `--completions`, `--skill`, and `--mcp` (when `mcpServer.enabled` is `true`).
|
|
36
|
+
`--all` expands to `--bin`, `--completions`, `--skill`, and `--mcp` (when `mcpServer.enabled` is `true`) for both install and uninstall. Missing targets are skipped silently (no error if nothing is on disk or a shell/agent directory does not exist).
|
|
37
|
+
|
|
38
|
+
`install --uninstall` requires the same target flags as install (`--all`, `--bin`, etc.) — bare `--uninstall` alone is an error.
|
|
37
39
|
|
|
38
40
|
Shells not on PATH are skipped silently (no warnings).
|
|
39
41
|
|
|
@@ -105,7 +107,7 @@ Environment:
|
|
|
105
107
|
| `--reinstall` | Reinstall artifacts already on disk (implies `--bin` + `--yes`) |
|
|
106
108
|
| `--from <path>` | Binary to copy with `--reinstall` (default: running executable) |
|
|
107
109
|
| `--status` | Read-only inventory |
|
|
108
|
-
| `--uninstall` | Remove
|
|
110
|
+
| `--uninstall` | Remove artifacts in scope (`--all`, `--bin`, `--completions`, `--skill`, `--mcp`); skips targets not installed |
|
|
109
111
|
|
|
110
112
|
`--update` is accepted as a deprecated alias for `--reinstall`.
|
|
111
113
|
|
|
@@ -13,7 +13,7 @@ alwaysApply: false
|
|
|
13
13
|
- Reserved root commands (do not declare): `completion`, `install`, `mcp`, `version`, `docs`, `update`
|
|
14
14
|
|
|
15
15
|
## Schema
|
|
16
|
-
- **Inline** options, positionals, and handler schema; extract
|
|
16
|
+
- **Inline** options, positionals, and handler schema; extract to `export const …Command = { … } satisfies CliLeaf` (or router type) when shared flags, `mcpTool` spreads, or large handlers justify a module — not zero-arg wrapper functions
|
|
17
17
|
- Leaf descriptions: action-oriented, not UI jargon
|
|
18
18
|
- Prefer option names `yes`, `dry-run`, `json` when semantics match; describe non-interactive use on the option
|
|
19
19
|
|
package/package.json
CHANGED
package/src/builtins/install.ts
CHANGED
|
@@ -42,7 +42,7 @@ export function installBuiltinOptions(root: CliProgram): CliOption[] {
|
|
|
42
42
|
},
|
|
43
43
|
{
|
|
44
44
|
name: "uninstall",
|
|
45
|
-
description: "Remove installed artifacts (all
|
|
45
|
+
description: "Remove installed artifacts (use --all or scoped flags; skips targets not on disk).",
|
|
46
46
|
kind: CliOptionKind.Presence,
|
|
47
47
|
},
|
|
48
48
|
{
|
|
@@ -96,8 +96,8 @@ export function cliBuiltinInstallCommand(root: CliProgram): CliLeaf {
|
|
|
96
96
|
` {app} update\n\n` +
|
|
97
97
|
"See what is installed:\n" +
|
|
98
98
|
` {app} install --status\n\n` +
|
|
99
|
-
"Remove everything:\n" +
|
|
100
|
-
` {app} install --uninstall --yes\n\n` +
|
|
99
|
+
"Remove everything installed with --all:\n" +
|
|
100
|
+
` {app} install --uninstall --all --yes\n\n` +
|
|
101
101
|
"Use --dry to preview changes, --json for machine-readable output.",
|
|
102
102
|
options: installBuiltinOptions(root),
|
|
103
103
|
handler: () => {},
|
package/src/install/index.ts
CHANGED
|
@@ -13,7 +13,7 @@ import { resolveInstallPaths } from "./paths.ts";
|
|
|
13
13
|
import { installErr, installInfo, installOut, printInstallStatus } from "./status.ts";
|
|
14
14
|
import { buildUninstallPlan, uninstallSkillDir, type UninstallAction } from "./uninstall.ts";
|
|
15
15
|
|
|
16
|
-
function parseInstallOpts(raw: Record<string, string>): InstallOpts {
|
|
16
|
+
export function parseInstallOpts(raw: Record<string, string>): InstallOpts {
|
|
17
17
|
const flag = (name: string) => raw[name] === "1";
|
|
18
18
|
const reinstall = flag("reinstall") || flag("update");
|
|
19
19
|
return {
|
|
@@ -34,7 +34,7 @@ function parseInstallOpts(raw: Record<string, string>): InstallOpts {
|
|
|
34
34
|
};
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
function
|
|
37
|
+
export function validateInstallOpts(opts: InstallOpts): string | null {
|
|
38
38
|
if (opts.quiet && opts.dry) {
|
|
39
39
|
return "--quiet cannot be combined with --dry.";
|
|
40
40
|
}
|
|
@@ -60,10 +60,10 @@ function validateOpts(opts: InstallOpts): string | null {
|
|
|
60
60
|
) {
|
|
61
61
|
return "--reinstall cannot be combined with other target flags.";
|
|
62
62
|
}
|
|
63
|
-
if (opts.uninstall && (opts.
|
|
64
|
-
return "--uninstall cannot be combined with --
|
|
63
|
+
if (opts.uninstall && (opts.reinstall || opts.status)) {
|
|
64
|
+
return "--uninstall cannot be combined with --reinstall or --status.";
|
|
65
65
|
}
|
|
66
|
-
if (!opts.status && !opts.reinstall
|
|
66
|
+
if (!opts.status && !opts.reinstall) {
|
|
67
67
|
const hasTarget = opts.all || opts.bin || opts.completions || opts.skill || opts.mcp;
|
|
68
68
|
if (!hasTarget) {
|
|
69
69
|
return "Specify at least one target: --all, --bin, --completions, --skill, or --mcp.";
|
|
@@ -126,7 +126,7 @@ export async function runInstallMutation(
|
|
|
126
126
|
rawOpts: Record<string, string>,
|
|
127
127
|
): Promise<{ changed: string[]; opts: InstallOpts; paths: ReturnType<typeof resolveInstallPaths> }> {
|
|
128
128
|
const opts = parseInstallOpts(rawOpts);
|
|
129
|
-
const err =
|
|
129
|
+
const err = validateInstallOpts(opts);
|
|
130
130
|
if (err) {
|
|
131
131
|
throw new Error(err);
|
|
132
132
|
}
|
|
@@ -159,7 +159,7 @@ export async function runInstallMutation(
|
|
|
159
159
|
}
|
|
160
160
|
|
|
161
161
|
if (actions.length === 0) {
|
|
162
|
-
|
|
162
|
+
return { changed: [], opts, paths };
|
|
163
163
|
}
|
|
164
164
|
|
|
165
165
|
if (!opts.quiet && !opts.json) {
|
|
@@ -218,5 +218,3 @@ export async function cliInstall(root: CliProgram, rawOpts: Record<string, strin
|
|
|
218
218
|
|
|
219
219
|
process.exit(0);
|
|
220
220
|
}
|
|
221
|
-
|
|
222
|
-
export { parseInstallOpts };
|
|
@@ -6,8 +6,9 @@ import { CliProgram } from "../types.ts";
|
|
|
6
6
|
import { detectInstalledArtifacts } from "./detect-installed.ts";
|
|
7
7
|
import { resolveInstallPaths } from "./paths.ts";
|
|
8
8
|
import { buildInstallPlan } from "./plan.ts";
|
|
9
|
+
import { buildUninstallPlan } from "./uninstall.ts";
|
|
9
10
|
import { printInstallStatus } from "./status.ts";
|
|
10
|
-
import { parseInstallOpts } from "./index.ts";
|
|
11
|
+
import { parseInstallOpts, runInstallMutation, validateInstallOpts } from "./index.ts";
|
|
11
12
|
|
|
12
13
|
const fixture: CliProgram = {
|
|
13
14
|
key: "testapp",
|
|
@@ -123,3 +124,65 @@ describe("parseInstallOpts", () => {
|
|
|
123
124
|
expect(opts.all).toBe(true);
|
|
124
125
|
});
|
|
125
126
|
});
|
|
127
|
+
|
|
128
|
+
describe("validateInstallOpts", () => {
|
|
129
|
+
test("uninstall requires a target flag", () => {
|
|
130
|
+
const opts = parseInstallOpts({ uninstall: "1", yes: "1" });
|
|
131
|
+
expect(validateInstallOpts(opts)).toContain("Specify at least one target");
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
test("uninstall allows --all", () => {
|
|
135
|
+
const opts = parseInstallOpts({ uninstall: "1", all: "1", yes: "1" });
|
|
136
|
+
expect(validateInstallOpts(opts)).toBeNull();
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
test("uninstall rejects --reinstall", () => {
|
|
140
|
+
const opts = parseInstallOpts({ uninstall: "1", reinstall: "1", all: "1" });
|
|
141
|
+
expect(validateInstallOpts(opts)).toContain("--reinstall");
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
describe("install mutation", () => {
|
|
146
|
+
test("uninstall --all with nothing installed succeeds", async () => {
|
|
147
|
+
const result = await runInstallMutation(fixture, { uninstall: "1", all: "1", yes: "1" });
|
|
148
|
+
expect(result.changed).toEqual([]);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
test("install --skill with no agent dirs succeeds", async () => {
|
|
152
|
+
const result = await runInstallMutation(fixture, { skill: "1", yes: "1", dry: "1" });
|
|
153
|
+
expect(result.changed).toEqual([]);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
test("uninstall --all removes detected binary", async () => {
|
|
157
|
+
const paths = resolveInstallPaths(fixture, {});
|
|
158
|
+
mkdirSync(paths.bindir, { recursive: true });
|
|
159
|
+
writeFileSync(paths.binaryPath, "fake", "utf8");
|
|
160
|
+
|
|
161
|
+
const result = await runInstallMutation(fixture, { uninstall: "1", all: "1", yes: "1" });
|
|
162
|
+
expect(result.changed).toContain(paths.binaryPath);
|
|
163
|
+
expect(existsSync(paths.binaryPath)).toBe(false);
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
describe("uninstall plan", () => {
|
|
168
|
+
test("buildUninstallPlan --all scopes like install --all", () => {
|
|
169
|
+
const paths = resolveInstallPaths(fixture, {});
|
|
170
|
+
mkdirSync(paths.bindir, { recursive: true });
|
|
171
|
+
writeFileSync(paths.binaryPath, "fake", "utf8");
|
|
172
|
+
|
|
173
|
+
const plan = buildUninstallPlan(fixture, paths, parseInstallOpts({ uninstall: "1", all: "1" }));
|
|
174
|
+
expect(plan.some((a) => a.summary.startsWith("binary:"))).toBe(true);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
test("buildUninstallPlan --bin ignores completions", () => {
|
|
178
|
+
const paths = resolveInstallPaths(fixture, {});
|
|
179
|
+
mkdirSync(paths.bindir, { recursive: true });
|
|
180
|
+
writeFileSync(paths.binaryPath, "fake", "utf8");
|
|
181
|
+
mkdirSync(join(home, ".bash_completion.d"), { recursive: true });
|
|
182
|
+
writeFileSync(paths.bashCompletion, "# bash", "utf8");
|
|
183
|
+
|
|
184
|
+
const plan = buildUninstallPlan(fixture, paths, parseInstallOpts({ uninstall: "1", bin: "1" }));
|
|
185
|
+
expect(plan).toHaveLength(1);
|
|
186
|
+
expect(plan[0]!.summary.startsWith("binary:")).toBe(true);
|
|
187
|
+
});
|
|
188
|
+
});
|
package/src/install/plan.ts
CHANGED
|
@@ -35,19 +35,19 @@ export interface InstallAction {
|
|
|
35
35
|
run: () => string[];
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
function
|
|
38
|
+
export function wantsInstallBin(opts: InstallOpts): boolean {
|
|
39
39
|
return !!(opts.all || opts.bin || opts.reinstall);
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
function
|
|
42
|
+
export function wantsInstallCompletions(opts: InstallOpts): boolean {
|
|
43
43
|
return !!(opts.all || opts.completions);
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
function
|
|
46
|
+
export function wantsInstallSkill(opts: InstallOpts): boolean {
|
|
47
47
|
return !!(opts.all || opts.skill);
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
function
|
|
50
|
+
export function wantsInstallMcp(opts: InstallOpts, root: CliProgram): boolean {
|
|
51
51
|
return !!(opts.mcp || opts.all) && resolveCapabilities(root).mcp;
|
|
52
52
|
}
|
|
53
53
|
|
|
@@ -56,7 +56,7 @@ export function buildInstallPlan(root: CliProgram, paths: InstallPaths, opts: In
|
|
|
56
56
|
const actions: InstallAction[] = [];
|
|
57
57
|
const dry = !!opts.dry;
|
|
58
58
|
|
|
59
|
-
if (
|
|
59
|
+
if (wantsInstallBin(opts)) {
|
|
60
60
|
const sourcePath = opts.from ?? process.execPath;
|
|
61
61
|
actions.push({
|
|
62
62
|
kind: "binary",
|
|
@@ -66,7 +66,7 @@ export function buildInstallPlan(root: CliProgram, paths: InstallPaths, opts: In
|
|
|
66
66
|
});
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
if (
|
|
69
|
+
if (wantsInstallCompletions(opts)) {
|
|
70
70
|
const shells = detectShells();
|
|
71
71
|
if (shells.bash) {
|
|
72
72
|
actions.push({
|
|
@@ -103,7 +103,7 @@ export function buildInstallPlan(root: CliProgram, paths: InstallPaths, opts: In
|
|
|
103
103
|
}
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
-
if (
|
|
106
|
+
if (wantsInstallSkill(opts)) {
|
|
107
107
|
const home = userHome();
|
|
108
108
|
if (existsSync(join(home, ".cursor"))) {
|
|
109
109
|
actions.push({
|
|
@@ -123,7 +123,7 @@ export function buildInstallPlan(root: CliProgram, paths: InstallPaths, opts: In
|
|
|
123
123
|
}
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
-
if (
|
|
126
|
+
if (wantsInstallMcp(opts, root)) {
|
|
127
127
|
const entry = expectedMcpEntry(root);
|
|
128
128
|
if (existsSync(join(userHome(), ".cursor"))) {
|
|
129
129
|
actions.push({
|
package/src/install/uninstall.ts
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
import { existsSync, rmSync } from "node:fs";
|
|
2
|
-
import { join } from "node:path";
|
|
3
|
-
import { resolveCapabilities } from "../capabilities.ts";
|
|
4
2
|
import { CliProgram } from "../types.ts";
|
|
5
3
|
import { uninstallBinary } from "./binary.ts";
|
|
6
4
|
import { uninstallCompletions } from "./completions.ts";
|
|
7
5
|
import { detectInstalledArtifacts } from "./detect-installed.ts";
|
|
8
6
|
import { removeMcpConfig } from "./mcp-config.ts";
|
|
9
|
-
import { InstallPaths
|
|
10
|
-
import
|
|
7
|
+
import { InstallPaths } from "./paths.ts";
|
|
8
|
+
import {
|
|
9
|
+
wantsInstallBin,
|
|
10
|
+
wantsInstallCompletions,
|
|
11
|
+
wantsInstallMcp,
|
|
12
|
+
wantsInstallSkill,
|
|
13
|
+
type InstallOpts,
|
|
14
|
+
} from "./plan.ts";
|
|
11
15
|
|
|
12
16
|
export interface UninstallAction {
|
|
13
17
|
summary: string;
|
|
@@ -15,22 +19,17 @@ export interface UninstallAction {
|
|
|
15
19
|
run: () => string[];
|
|
16
20
|
}
|
|
17
21
|
|
|
18
|
-
|
|
19
|
-
return !opts.bin && !opts.completions && !opts.skill && !opts.mcp;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/** Builds uninstall actions from detected artifacts. */
|
|
22
|
+
/** Builds uninstall actions for scoped targets (--all mirrors install --all). */
|
|
23
23
|
export function buildUninstallPlan(
|
|
24
24
|
root: CliProgram,
|
|
25
25
|
paths: InstallPaths,
|
|
26
26
|
opts: InstallOpts,
|
|
27
27
|
): UninstallAction[] {
|
|
28
28
|
const detected = detectInstalledArtifacts(paths);
|
|
29
|
-
const all = scopeAll(opts);
|
|
30
29
|
const dry = !!opts.dry;
|
|
31
30
|
const actions: UninstallAction[] = [];
|
|
32
31
|
|
|
33
|
-
if ((
|
|
32
|
+
if (wantsInstallBin(opts) && detected.binary) {
|
|
34
33
|
actions.push({
|
|
35
34
|
summary: `binary: ${paths.binaryPath}`,
|
|
36
35
|
message: `Removing binary ${paths.binaryPath}`,
|
|
@@ -38,7 +37,10 @@ export function buildUninstallPlan(
|
|
|
38
37
|
});
|
|
39
38
|
}
|
|
40
39
|
|
|
41
|
-
if (
|
|
40
|
+
if (
|
|
41
|
+
wantsInstallCompletions(opts) &&
|
|
42
|
+
(detected.bashCompletion || detected.zshCompletion || detected.fishCompletion)
|
|
43
|
+
) {
|
|
42
44
|
if (detected.bashCompletion) {
|
|
43
45
|
actions.push({
|
|
44
46
|
summary: `bash completion: ${paths.bashCompletion}`,
|
|
@@ -62,7 +64,7 @@ export function buildUninstallPlan(
|
|
|
62
64
|
}
|
|
63
65
|
}
|
|
64
66
|
|
|
65
|
-
if ((
|
|
67
|
+
if (wantsInstallSkill(opts) && detected.cursorSkill) {
|
|
66
68
|
actions.push({
|
|
67
69
|
summary: `cursor skill: ${paths.cursorSkillDir}/`,
|
|
68
70
|
message: `Removing Cursor skill ${paths.cursorSkillDir}/`,
|
|
@@ -70,7 +72,7 @@ export function buildUninstallPlan(
|
|
|
70
72
|
});
|
|
71
73
|
}
|
|
72
74
|
|
|
73
|
-
if ((
|
|
75
|
+
if (wantsInstallSkill(opts) && detected.claudeSkill) {
|
|
74
76
|
actions.push({
|
|
75
77
|
summary: `claude skill: ${paths.claudeSkillDir}/`,
|
|
76
78
|
message: `Removing Claude Code skill ${paths.claudeSkillDir}/`,
|
|
@@ -78,7 +80,7 @@ export function buildUninstallPlan(
|
|
|
78
80
|
});
|
|
79
81
|
}
|
|
80
82
|
|
|
81
|
-
if ((
|
|
83
|
+
if (wantsInstallMcp(opts, root)) {
|
|
82
84
|
if (detected.cursorMcp) {
|
|
83
85
|
actions.push({
|
|
84
86
|
summary: `cursor mcp: ${paths.cursorMcpPath}`,
|