argsbarg 5.1.13 → 5.1.15
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 +11 -1
- package/package.json +1 -1
- package/src/configure/configure.test.ts +70 -1
- package/src/configure/index.ts +114 -40
- package/src/install/target-skill.ts +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,11 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [5.1.15] - 2026-07-06
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
## [5.1.14] - 2026-07-06
|
|
14
|
+
|
|
15
|
+
|
|
10
16
|
## [5.1.13] - 2026-07-06
|
|
11
17
|
|
|
12
18
|
### Fixed
|
|
13
19
|
|
|
14
20
|
- **`configure --remove-all`** — skill uninstall actions no longer reinstall skills after printing "Removing …".
|
|
21
|
+
- **Interactive `configure`** — choosing `n` on an installed skill now removes the skill directory (same bug as `--remove-all`, different code path).
|
|
22
|
+
- **Configure closing summary** — counts artifacts (not per-file paths) and uses Removed/Installed/Synced instead of always "Updated N file(s)."
|
|
15
23
|
|
|
16
24
|
### Changed
|
|
17
25
|
|
|
@@ -673,7 +681,9 @@ const cli = { ... } satisfies CliProgram; // or : CliProgram
|
|
|
673
681
|
- 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`).
|
|
674
682
|
- Imports: use `CliPositional` where needed; replace `CliOptionDef` with `CliOption` or `CliPositional` as appropriate.
|
|
675
683
|
|
|
676
|
-
[Unreleased]: https://github.com/bdombro/bun-argsbarg/compare/v5.1.
|
|
684
|
+
[Unreleased]: https://github.com/bdombro/bun-argsbarg/compare/v5.1.15...HEAD
|
|
685
|
+
[5.1.15]: https://github.com/bdombro/bun-argsbarg/releases/tag/v5.1.15
|
|
686
|
+
[5.1.14]: https://github.com/bdombro/bun-argsbarg/releases/tag/v5.1.14
|
|
677
687
|
[5.1.13]: https://github.com/bdombro/bun-argsbarg/releases/tag/v5.1.13
|
|
678
688
|
[5.1.11]: https://github.com/bdombro/bun-argsbarg/releases/tag/v5.1.11
|
|
679
689
|
[5.1.10]: https://github.com/bdombro/bun-argsbarg/releases/tag/v5.1.10
|
package/package.json
CHANGED
|
@@ -7,12 +7,19 @@ import { existsSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:
|
|
|
7
7
|
import { tmpdir } from "node:os";
|
|
8
8
|
import { join } from "node:path";
|
|
9
9
|
import { appConfigFileExists } from "../config/file.ts";
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
appConfigHasEntries,
|
|
12
|
+
formatConfigureMutationSummary,
|
|
13
|
+
parseConfigureOpts,
|
|
14
|
+
validateConfigureOpts,
|
|
15
|
+
} from "../configure/index.ts";
|
|
11
16
|
import { Cli } from "../index.ts";
|
|
12
17
|
import { detectInstalledArtifacts } from "../install/detect-installed.ts";
|
|
13
18
|
import { parseInstallOpts } from "../install/opts.ts";
|
|
14
19
|
import { resolveClaudeDesktopMcpPath, resolveInstallPaths } from "../install/paths.ts";
|
|
15
20
|
import { buildInstallPlan, buildUpdatePlan } from "../install/plan.ts";
|
|
21
|
+
import { installTargetForKey } from "../install/target-registry.ts";
|
|
22
|
+
import { buildDetectedSnapshot, buildTargetPlanContext } from "../install/target-scope.ts";
|
|
16
23
|
import { buildUninstallPlan } from "../install/uninstall.ts";
|
|
17
24
|
import type { CliProgram } from "../types.ts";
|
|
18
25
|
|
|
@@ -62,6 +69,45 @@ describe("configure opts", () => {
|
|
|
62
69
|
});
|
|
63
70
|
});
|
|
64
71
|
|
|
72
|
+
describe("configure mutation summary", () => {
|
|
73
|
+
test("interactive uninstall reports removed artifacts not file count", () => {
|
|
74
|
+
const msg = formatConfigureMutationSummary(
|
|
75
|
+
{ paths: ["~/.cursor/skills/testapp/"], installed: 0, removed: 1, configured: 0 },
|
|
76
|
+
{},
|
|
77
|
+
);
|
|
78
|
+
expect(msg).toBe("Removed 1 artifact.");
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test("interactive install reports installed artifacts not file count", () => {
|
|
82
|
+
const msg = formatConfigureMutationSummary(
|
|
83
|
+
{
|
|
84
|
+
paths: ["~/.cursor/skills/testapp/SKILL.md", "~/.cursor/skills/testapp/reference.md"],
|
|
85
|
+
installed: 1,
|
|
86
|
+
removed: 0,
|
|
87
|
+
configured: 0,
|
|
88
|
+
},
|
|
89
|
+
{},
|
|
90
|
+
);
|
|
91
|
+
expect(msg).toBe("Installed 1 artifact.");
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test("sync mode uses synced verb and artifact count", () => {
|
|
95
|
+
const msg = formatConfigureMutationSummary(
|
|
96
|
+
{ paths: ["a", "b", "c"], installed: 3, removed: 0, configured: 0 },
|
|
97
|
+
{ sync: true },
|
|
98
|
+
);
|
|
99
|
+
expect(msg).toBe("Synced 3 artifacts.");
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test("remove-all uses removed verb", () => {
|
|
103
|
+
const msg = formatConfigureMutationSummary(
|
|
104
|
+
{ paths: ["a"], installed: 0, removed: 2, configured: 0 },
|
|
105
|
+
{ removeAll: true },
|
|
106
|
+
);
|
|
107
|
+
expect(msg).toBe("Removed 2 artifacts.");
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
|
|
65
111
|
/** Tests for install paths. */
|
|
66
112
|
describe("install paths", () => {
|
|
67
113
|
test("resolveInstallPaths includes skill and mcp paths", () => {
|
|
@@ -153,6 +199,29 @@ describe("remove plan", () => {
|
|
|
153
199
|
expect(result.exitCode).toBe(0);
|
|
154
200
|
expect(existsSync(paths.cursorSkillDir)).toBe(false);
|
|
155
201
|
});
|
|
202
|
+
|
|
203
|
+
test("scoped skill uninstall action removes directory without global uninstall flag", () => {
|
|
204
|
+
const skillApp: CliProgram = {
|
|
205
|
+
...fixture,
|
|
206
|
+
configure: { agentIntegration: "skill" },
|
|
207
|
+
};
|
|
208
|
+
const paths = resolveInstallPaths(skillApp);
|
|
209
|
+
mkdirSync(paths.cursorSkillDir, { recursive: true });
|
|
210
|
+
writeFileSync(join(paths.cursorSkillDir, "SKILL.md"), "# test\n", "utf8");
|
|
211
|
+
|
|
212
|
+
const detected = buildDetectedSnapshot(skillApp, paths);
|
|
213
|
+
const ctx = buildTargetPlanContext(skillApp, paths, {}, detected);
|
|
214
|
+
const target = installTargetForKey("cursorSkill");
|
|
215
|
+
expect(target).toBeDefined();
|
|
216
|
+
const actions = target!.planUninstall({
|
|
217
|
+
...ctx,
|
|
218
|
+
mode: "uninstall-scoped",
|
|
219
|
+
include: (key) => key === "cursorSkill",
|
|
220
|
+
});
|
|
221
|
+
expect(actions).toHaveLength(1);
|
|
222
|
+
expect(actions[0]!.run().length).toBeGreaterThan(0);
|
|
223
|
+
expect(existsSync(paths.cursorSkillDir)).toBe(false);
|
|
224
|
+
});
|
|
156
225
|
});
|
|
157
226
|
|
|
158
227
|
/** Tests for app config wizard. */
|
package/src/configure/index.ts
CHANGED
|
@@ -105,43 +105,113 @@ function runSkillAction(root: CliProgram, kind: InstallActionKind, opts: Install
|
|
|
105
105
|
});
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
+
/** Tallies configure mutations for the closing summary and `--json` output. */
|
|
109
|
+
export interface ConfigureMutationSummary {
|
|
110
|
+
paths: string[];
|
|
111
|
+
installed: number;
|
|
112
|
+
removed: number;
|
|
113
|
+
configured: number;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function emptyMutationSummary(): ConfigureMutationSummary {
|
|
117
|
+
return { paths: [], installed: 0, removed: 0, configured: 0 };
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function mergeMutationSummary(into: ConfigureMutationSummary, from: ConfigureMutationSummary): void {
|
|
121
|
+
into.paths.push(...from.paths);
|
|
122
|
+
into.installed += from.installed;
|
|
123
|
+
into.removed += from.removed;
|
|
124
|
+
into.configured += from.configured;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function recordArtifactMutation(
|
|
128
|
+
summary: ConfigureMutationSummary,
|
|
129
|
+
paths: string[],
|
|
130
|
+
kind: "installed" | "removed" | "configured",
|
|
131
|
+
): void {
|
|
132
|
+
if (paths.length === 0) return;
|
|
133
|
+
summary.paths.push(...paths);
|
|
134
|
+
summary[kind]++;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** Human-readable closing line for configure mutations. */
|
|
138
|
+
export function formatConfigureMutationSummary(summary: ConfigureMutationSummary, opts: ConfigureOpts): string | null {
|
|
139
|
+
if (summary.paths.length === 0) return null;
|
|
140
|
+
|
|
141
|
+
if (opts.removeAll || opts.removeConfig) {
|
|
142
|
+
const n = summary.removed;
|
|
143
|
+
if (n === 0) return null;
|
|
144
|
+
return n === 1 ? "Removed 1 artifact." : `Removed ${n} artifacts.`;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (opts.sync) {
|
|
148
|
+
const n = summary.installed;
|
|
149
|
+
if (n === 0) return null;
|
|
150
|
+
return n === 1 ? "Synced 1 artifact." : `Synced ${n} artifacts.`;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const parts: string[] = [];
|
|
154
|
+
if (summary.removed > 0) {
|
|
155
|
+
parts.push(summary.removed === 1 ? "Removed 1 artifact" : `Removed ${summary.removed} artifacts`);
|
|
156
|
+
}
|
|
157
|
+
if (summary.installed > 0) {
|
|
158
|
+
parts.push(summary.installed === 1 ? "Installed 1 artifact" : `Installed ${summary.installed} artifacts`);
|
|
159
|
+
}
|
|
160
|
+
if (summary.configured > 0) {
|
|
161
|
+
parts.push("Updated app config");
|
|
162
|
+
}
|
|
163
|
+
if (parts.length === 0) return null;
|
|
164
|
+
return `${parts.join("; ")}.`;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/** Runs one install/uninstall action and returns changed paths. */
|
|
168
|
+
function runPlanAction(
|
|
169
|
+
root: CliProgram,
|
|
170
|
+
action: InstallAction | UninstallAction,
|
|
171
|
+
opts: InstallOpts,
|
|
172
|
+
paths: ReturnType<typeof resolveInstallPaths>,
|
|
173
|
+
): string[] {
|
|
174
|
+
if ("kind" in action && action.kind) {
|
|
175
|
+
const skillTarget = skillTargetFromActionKind(action.kind);
|
|
176
|
+
if (skillTarget) {
|
|
177
|
+
const runResult = action.run();
|
|
178
|
+
if (runResult.length > 0) return runResult;
|
|
179
|
+
if (opts.uninstall) {
|
|
180
|
+
const skillDir = skillDirFromUninstallSummary(action.summary, paths);
|
|
181
|
+
return skillDir ? uninstallSkillDir(skillDir, !!opts.dry) : [];
|
|
182
|
+
}
|
|
183
|
+
return runSkillAction(root, action.kind as InstallActionKind, opts);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
if (!("kind" in action) || !action.kind) {
|
|
187
|
+
const skillDir = skillDirFromUninstallSummary(action.summary, paths);
|
|
188
|
+
if (skillDir) return uninstallSkillDir(skillDir, !!opts.dry);
|
|
189
|
+
}
|
|
190
|
+
return action.run();
|
|
191
|
+
}
|
|
192
|
+
|
|
108
193
|
/** Runs install or uninstall actions and collects changed paths. */
|
|
109
194
|
function executePlan(
|
|
110
195
|
root: CliProgram,
|
|
111
196
|
actions: Array<InstallAction | UninstallAction>,
|
|
112
197
|
opts: InstallOpts,
|
|
113
198
|
showProgress: boolean,
|
|
114
|
-
):
|
|
115
|
-
const
|
|
199
|
+
): ConfigureMutationSummary {
|
|
200
|
+
const summary = emptyMutationSummary();
|
|
116
201
|
const paths = resolveInstallPaths(root);
|
|
117
202
|
for (const action of actions) {
|
|
118
203
|
if (showProgress) {
|
|
119
204
|
installInfo(action.message, opts);
|
|
120
205
|
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
changed.push(...uninstallSkillDir(skillDir, !!opts.dry));
|
|
128
|
-
}
|
|
129
|
-
} else {
|
|
130
|
-
changed.push(...runSkillAction(root, action.kind as InstallActionKind, opts));
|
|
131
|
-
}
|
|
132
|
-
continue;
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
if (!("kind" in action) || !action.kind) {
|
|
136
|
-
const skillDir = skillDirFromUninstallSummary(action.summary, paths);
|
|
137
|
-
if (skillDir) {
|
|
138
|
-
changed.push(...uninstallSkillDir(skillDir, !!opts.dry));
|
|
139
|
-
continue;
|
|
140
|
-
}
|
|
206
|
+
const changed = runPlanAction(root, action, opts, paths);
|
|
207
|
+
if (changed.length === 0) continue;
|
|
208
|
+
if (action.kind === "configure") {
|
|
209
|
+
recordArtifactMutation(summary, changed, opts.uninstall ? "removed" : "configured");
|
|
210
|
+
} else {
|
|
211
|
+
recordArtifactMutation(summary, changed, opts.uninstall ? "removed" : "installed");
|
|
141
212
|
}
|
|
142
|
-
changed.push(...action.run());
|
|
143
213
|
}
|
|
144
|
-
return
|
|
214
|
+
return summary;
|
|
145
215
|
}
|
|
146
216
|
|
|
147
217
|
/** Builds plan context limited to a single artifact key. */
|
|
@@ -181,7 +251,7 @@ function actionsForTarget(
|
|
|
181
251
|
}
|
|
182
252
|
|
|
183
253
|
/** Walks enabled targets with per-target prompts (TTY required). */
|
|
184
|
-
async function runInteractiveConfigure(root: CliProgram, opts: ConfigureOpts): Promise<
|
|
254
|
+
async function runInteractiveConfigure(root: CliProgram, opts: ConfigureOpts): Promise<ConfigureMutationSummary> {
|
|
185
255
|
if (!process.stdin.isTTY) {
|
|
186
256
|
throw new Error("Interactive configure requires a TTY. Use flags such as --sync --yes.");
|
|
187
257
|
}
|
|
@@ -191,7 +261,7 @@ async function runInteractiveConfigure(root: CliProgram, opts: ConfigureOpts): P
|
|
|
191
261
|
const detected = buildDetectedSnapshot(root, paths);
|
|
192
262
|
const effective = resolveEffectiveInstallTargets(root.configure, root);
|
|
193
263
|
const mutationOpts: InstallOpts = { dry: opts.dry, json: opts.json };
|
|
194
|
-
const
|
|
264
|
+
const summary = emptyMutationSummary();
|
|
195
265
|
|
|
196
266
|
for (const target of INSTALL_TARGETS) {
|
|
197
267
|
if (target.key === "app") continue;
|
|
@@ -205,7 +275,7 @@ async function runInteractiveConfigure(root: CliProgram, opts: ConfigureOpts): P
|
|
|
205
275
|
const result = runConfigure(root, { context: "standalone", showHeading: false });
|
|
206
276
|
if (result.changed) {
|
|
207
277
|
installOut(`Wrote config: ${displayAppConfigPath(root)}`, mutationOpts);
|
|
208
|
-
|
|
278
|
+
recordArtifactMutation(summary, [displayAppConfigPath(root)], "configured");
|
|
209
279
|
}
|
|
210
280
|
continue;
|
|
211
281
|
}
|
|
@@ -228,29 +298,32 @@ async function runInteractiveConfigure(root: CliProgram, opts: ConfigureOpts): P
|
|
|
228
298
|
runTargetPreflight(root, paths, mutationOpts, installActions);
|
|
229
299
|
}
|
|
230
300
|
|
|
231
|
-
|
|
301
|
+
mergeMutationSummary(
|
|
302
|
+
summary,
|
|
303
|
+
executePlan(root, actions, { ...mutationOpts, uninstall: choice === "uninstall" }, true),
|
|
304
|
+
);
|
|
232
305
|
}
|
|
233
306
|
|
|
234
|
-
return
|
|
307
|
+
return summary;
|
|
235
308
|
}
|
|
236
309
|
|
|
237
310
|
/** Runs sync, remove, or status modes without per-target prompts. */
|
|
238
|
-
async function runAutomatedConfigure(root: CliProgram, opts: ConfigureOpts): Promise<
|
|
311
|
+
async function runAutomatedConfigure(root: CliProgram, opts: ConfigureOpts): Promise<ConfigureMutationSummary> {
|
|
239
312
|
const installOpts = configureToInstallOpts(opts);
|
|
240
313
|
const paths = resolveInstallPaths(root);
|
|
241
314
|
|
|
242
315
|
if (installOpts.status) {
|
|
243
316
|
printInstallStatus(root, installOpts);
|
|
244
|
-
return
|
|
317
|
+
return emptyMutationSummary();
|
|
245
318
|
}
|
|
246
319
|
|
|
247
|
-
const
|
|
320
|
+
const summary = emptyMutationSummary();
|
|
248
321
|
|
|
249
322
|
if (installOpts.reinstall && !installOpts.uninstall) {
|
|
250
323
|
const bootstrapped = ensureAppConfigFile(root, !!installOpts.dry);
|
|
251
324
|
if (bootstrapped) {
|
|
252
325
|
const display = displayAppConfigPath(root);
|
|
253
|
-
|
|
326
|
+
recordArtifactMutation(summary, [display], "configured");
|
|
254
327
|
installOut(`Initialized config: ${display}`, installOpts);
|
|
255
328
|
}
|
|
256
329
|
}
|
|
@@ -271,7 +344,8 @@ async function runAutomatedConfigure(root: CliProgram, opts: ConfigureOpts): Pro
|
|
|
271
344
|
runTargetPreflight(root, paths, installOpts, installActions);
|
|
272
345
|
}
|
|
273
346
|
|
|
274
|
-
|
|
347
|
+
mergeMutationSummary(summary, executePlan(root, actions, installOpts, true));
|
|
348
|
+
return summary;
|
|
275
349
|
}
|
|
276
350
|
|
|
277
351
|
/** Main configure command orchestrator. */
|
|
@@ -285,22 +359,22 @@ export async function cliConfigure(root: CliProgram, rawOpts: Record<string, str
|
|
|
285
359
|
|
|
286
360
|
const isInteractive = !opts.sync && !opts.removeAll && !opts.removeConfig && !opts.status;
|
|
287
361
|
|
|
288
|
-
let
|
|
362
|
+
let summary = emptyMutationSummary();
|
|
289
363
|
try {
|
|
290
|
-
|
|
364
|
+
summary = isInteractive ? await runInteractiveConfigure(root, opts) : await runAutomatedConfigure(root, opts);
|
|
291
365
|
} catch (mutationErr) {
|
|
292
366
|
installErr(mutationErr instanceof Error ? mutationErr.message : String(mutationErr));
|
|
293
367
|
process.exit(1);
|
|
294
368
|
}
|
|
295
369
|
|
|
296
370
|
if (opts.json && !opts.status) {
|
|
297
|
-
process.stdout.write(`${JSON.stringify(
|
|
371
|
+
process.stdout.write(`${JSON.stringify(summary.paths, null, 2)}\n`);
|
|
298
372
|
process.exit(0);
|
|
299
373
|
}
|
|
300
374
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
installOut(
|
|
375
|
+
const closing = formatConfigureMutationSummary(summary, opts);
|
|
376
|
+
if (!opts.status && closing) {
|
|
377
|
+
installOut(closing, configureToInstallOpts(opts));
|
|
304
378
|
}
|
|
305
379
|
|
|
306
380
|
process.exit(0);
|
|
@@ -12,6 +12,7 @@ import type {
|
|
|
12
12
|
TargetPlanContext,
|
|
13
13
|
UninstallAction,
|
|
14
14
|
} from "./target-types.ts";
|
|
15
|
+
import { uninstallSkillDir } from "./uninstall.ts";
|
|
15
16
|
|
|
16
17
|
export interface SkillHostSpec {
|
|
17
18
|
key: CliInstallArtifactKey;
|
|
@@ -97,7 +98,7 @@ export class SkillInstallTarget extends InstallTarget {
|
|
|
97
98
|
kind: this.actionKind,
|
|
98
99
|
summary: `${this.uninstallPrefix}: ${displayInstallPath(dir)}/`,
|
|
99
100
|
message: `Removing ${this.spec.label} skill ${displayInstallPath(dir)}/`,
|
|
100
|
-
run: () =>
|
|
101
|
+
run: () => uninstallSkillDir(dir, ctx.dry),
|
|
101
102
|
},
|
|
102
103
|
];
|
|
103
104
|
}
|