argsbarg 5.1.14 → 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 +6 -1
- package/package.json +1 -1
- package/src/configure/configure.test.ts +45 -1
- package/src/configure/index.ts +114 -43
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,9 @@ 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
|
+
|
|
10
13
|
## [5.1.14] - 2026-07-06
|
|
11
14
|
|
|
12
15
|
|
|
@@ -16,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
16
19
|
|
|
17
20
|
- **`configure --remove-all`** — skill uninstall actions no longer reinstall skills after printing "Removing …".
|
|
18
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)."
|
|
19
23
|
|
|
20
24
|
### Changed
|
|
21
25
|
|
|
@@ -677,7 +681,8 @@ const cli = { ... } satisfies CliProgram; // or : CliProgram
|
|
|
677
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`).
|
|
678
682
|
- Imports: use `CliPositional` where needed; replace `CliOptionDef` with `CliOption` or `CliPositional` as appropriate.
|
|
679
683
|
|
|
680
|
-
[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
|
|
681
686
|
[5.1.14]: https://github.com/bdombro/bun-argsbarg/releases/tag/v5.1.14
|
|
682
687
|
[5.1.13]: https://github.com/bdombro/bun-argsbarg/releases/tag/v5.1.13
|
|
683
688
|
[5.1.11]: https://github.com/bdombro/bun-argsbarg/releases/tag/v5.1.11
|
package/package.json
CHANGED
|
@@ -7,7 +7,12 @@ 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";
|
|
@@ -64,6 +69,45 @@ describe("configure opts", () => {
|
|
|
64
69
|
});
|
|
65
70
|
});
|
|
66
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
|
+
|
|
67
111
|
/** Tests for install paths. */
|
|
68
112
|
describe("install paths", () => {
|
|
69
113
|
test("resolveInstallPaths includes skill and mcp paths", () => {
|
package/src/configure/index.ts
CHANGED
|
@@ -105,46 +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
|
-
} else if (opts.uninstall) {
|
|
128
|
-
const skillDir = skillDirFromUninstallSummary(action.summary, paths);
|
|
129
|
-
if (skillDir) {
|
|
130
|
-
changed.push(...uninstallSkillDir(skillDir, !!opts.dry));
|
|
131
|
-
}
|
|
132
|
-
} else {
|
|
133
|
-
changed.push(...runSkillAction(root, action.kind as InstallActionKind, opts));
|
|
134
|
-
}
|
|
135
|
-
continue;
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
if (!("kind" in action) || !action.kind) {
|
|
139
|
-
const skillDir = skillDirFromUninstallSummary(action.summary, paths);
|
|
140
|
-
if (skillDir) {
|
|
141
|
-
changed.push(...uninstallSkillDir(skillDir, !!opts.dry));
|
|
142
|
-
continue;
|
|
143
|
-
}
|
|
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");
|
|
144
212
|
}
|
|
145
|
-
changed.push(...action.run());
|
|
146
213
|
}
|
|
147
|
-
return
|
|
214
|
+
return summary;
|
|
148
215
|
}
|
|
149
216
|
|
|
150
217
|
/** Builds plan context limited to a single artifact key. */
|
|
@@ -184,7 +251,7 @@ function actionsForTarget(
|
|
|
184
251
|
}
|
|
185
252
|
|
|
186
253
|
/** Walks enabled targets with per-target prompts (TTY required). */
|
|
187
|
-
async function runInteractiveConfigure(root: CliProgram, opts: ConfigureOpts): Promise<
|
|
254
|
+
async function runInteractiveConfigure(root: CliProgram, opts: ConfigureOpts): Promise<ConfigureMutationSummary> {
|
|
188
255
|
if (!process.stdin.isTTY) {
|
|
189
256
|
throw new Error("Interactive configure requires a TTY. Use flags such as --sync --yes.");
|
|
190
257
|
}
|
|
@@ -194,7 +261,7 @@ async function runInteractiveConfigure(root: CliProgram, opts: ConfigureOpts): P
|
|
|
194
261
|
const detected = buildDetectedSnapshot(root, paths);
|
|
195
262
|
const effective = resolveEffectiveInstallTargets(root.configure, root);
|
|
196
263
|
const mutationOpts: InstallOpts = { dry: opts.dry, json: opts.json };
|
|
197
|
-
const
|
|
264
|
+
const summary = emptyMutationSummary();
|
|
198
265
|
|
|
199
266
|
for (const target of INSTALL_TARGETS) {
|
|
200
267
|
if (target.key === "app") continue;
|
|
@@ -208,7 +275,7 @@ async function runInteractiveConfigure(root: CliProgram, opts: ConfigureOpts): P
|
|
|
208
275
|
const result = runConfigure(root, { context: "standalone", showHeading: false });
|
|
209
276
|
if (result.changed) {
|
|
210
277
|
installOut(`Wrote config: ${displayAppConfigPath(root)}`, mutationOpts);
|
|
211
|
-
|
|
278
|
+
recordArtifactMutation(summary, [displayAppConfigPath(root)], "configured");
|
|
212
279
|
}
|
|
213
280
|
continue;
|
|
214
281
|
}
|
|
@@ -231,29 +298,32 @@ async function runInteractiveConfigure(root: CliProgram, opts: ConfigureOpts): P
|
|
|
231
298
|
runTargetPreflight(root, paths, mutationOpts, installActions);
|
|
232
299
|
}
|
|
233
300
|
|
|
234
|
-
|
|
301
|
+
mergeMutationSummary(
|
|
302
|
+
summary,
|
|
303
|
+
executePlan(root, actions, { ...mutationOpts, uninstall: choice === "uninstall" }, true),
|
|
304
|
+
);
|
|
235
305
|
}
|
|
236
306
|
|
|
237
|
-
return
|
|
307
|
+
return summary;
|
|
238
308
|
}
|
|
239
309
|
|
|
240
310
|
/** Runs sync, remove, or status modes without per-target prompts. */
|
|
241
|
-
async function runAutomatedConfigure(root: CliProgram, opts: ConfigureOpts): Promise<
|
|
311
|
+
async function runAutomatedConfigure(root: CliProgram, opts: ConfigureOpts): Promise<ConfigureMutationSummary> {
|
|
242
312
|
const installOpts = configureToInstallOpts(opts);
|
|
243
313
|
const paths = resolveInstallPaths(root);
|
|
244
314
|
|
|
245
315
|
if (installOpts.status) {
|
|
246
316
|
printInstallStatus(root, installOpts);
|
|
247
|
-
return
|
|
317
|
+
return emptyMutationSummary();
|
|
248
318
|
}
|
|
249
319
|
|
|
250
|
-
const
|
|
320
|
+
const summary = emptyMutationSummary();
|
|
251
321
|
|
|
252
322
|
if (installOpts.reinstall && !installOpts.uninstall) {
|
|
253
323
|
const bootstrapped = ensureAppConfigFile(root, !!installOpts.dry);
|
|
254
324
|
if (bootstrapped) {
|
|
255
325
|
const display = displayAppConfigPath(root);
|
|
256
|
-
|
|
326
|
+
recordArtifactMutation(summary, [display], "configured");
|
|
257
327
|
installOut(`Initialized config: ${display}`, installOpts);
|
|
258
328
|
}
|
|
259
329
|
}
|
|
@@ -274,7 +344,8 @@ async function runAutomatedConfigure(root: CliProgram, opts: ConfigureOpts): Pro
|
|
|
274
344
|
runTargetPreflight(root, paths, installOpts, installActions);
|
|
275
345
|
}
|
|
276
346
|
|
|
277
|
-
|
|
347
|
+
mergeMutationSummary(summary, executePlan(root, actions, installOpts, true));
|
|
348
|
+
return summary;
|
|
278
349
|
}
|
|
279
350
|
|
|
280
351
|
/** Main configure command orchestrator. */
|
|
@@ -288,22 +359,22 @@ export async function cliConfigure(root: CliProgram, rawOpts: Record<string, str
|
|
|
288
359
|
|
|
289
360
|
const isInteractive = !opts.sync && !opts.removeAll && !opts.removeConfig && !opts.status;
|
|
290
361
|
|
|
291
|
-
let
|
|
362
|
+
let summary = emptyMutationSummary();
|
|
292
363
|
try {
|
|
293
|
-
|
|
364
|
+
summary = isInteractive ? await runInteractiveConfigure(root, opts) : await runAutomatedConfigure(root, opts);
|
|
294
365
|
} catch (mutationErr) {
|
|
295
366
|
installErr(mutationErr instanceof Error ? mutationErr.message : String(mutationErr));
|
|
296
367
|
process.exit(1);
|
|
297
368
|
}
|
|
298
369
|
|
|
299
370
|
if (opts.json && !opts.status) {
|
|
300
|
-
process.stdout.write(`${JSON.stringify(
|
|
371
|
+
process.stdout.write(`${JSON.stringify(summary.paths, null, 2)}\n`);
|
|
301
372
|
process.exit(0);
|
|
302
373
|
}
|
|
303
374
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
installOut(
|
|
375
|
+
const closing = formatConfigureMutationSummary(summary, opts);
|
|
376
|
+
if (!opts.status && closing) {
|
|
377
|
+
installOut(closing, configureToInstallOpts(opts));
|
|
307
378
|
}
|
|
308
379
|
|
|
309
380
|
process.exit(0);
|