argsbarg 5.1.11 → 5.1.13

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 CHANGED
@@ -7,6 +7,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [5.1.13] - 2026-07-06
11
+
12
+ ### Fixed
13
+
14
+ - **`configure --remove-all`** — skill uninstall actions no longer reinstall skills after printing "Removing …".
15
+
16
+ ### Changed
17
+
18
+ - **Homebrew formula `uninstall`** — generated formulae run `{key} configure --remove-all --yes` (skills, MCP, and app config).
19
+
20
+ ### Changed
21
+
22
+ - **`scripts/dev-formula.ts`** — `install` stages a `file://` dev formula; `reset` restores the release formula. `just install-local` orchestrates stage → `brew install` → reset.
23
+
10
24
  ## [5.1.11] - 2026-07-05
11
25
 
12
26
 
@@ -659,7 +673,8 @@ const cli = { ... } satisfies CliProgram; // or : CliProgram
659
673
  - 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`).
660
674
  - Imports: use `CliPositional` where needed; replace `CliOptionDef` with `CliOption` or `CliPositional` as appropriate.
661
675
 
662
- [Unreleased]: https://github.com/bdombro/bun-argsbarg/compare/v5.1.11...HEAD
676
+ [Unreleased]: https://github.com/bdombro/bun-argsbarg/compare/v5.1.13...HEAD
677
+ [5.1.13]: https://github.com/bdombro/bun-argsbarg/releases/tag/v5.1.13
663
678
  [5.1.11]: https://github.com/bdombro/bun-argsbarg/releases/tag/v5.1.11
664
679
  [5.1.10]: https://github.com/bdombro/bun-argsbarg/releases/tag/v5.1.10
665
680
  [5.1.9]: https://github.com/bdombro/bun-argsbarg/releases/tag/v5.1.9
package/README.md CHANGED
@@ -266,7 +266,7 @@ To refresh the Cursor rule in an existing consumer: `bun scripts/merge-cli-progr
266
266
  | Command layout | `src/commands/<name>/command.ts`; registration in `src/program.ts` |
267
267
  | MCP doc topics | `docs.topics` auto-exposed as `<key>://docs/<topic>` resources when docs + MCP enabled |
268
268
  | Package import | `from "argsbarg"` (not relative to argsbarg `src/`) |
269
- | Homebrew distribution | `scripts/formula-shared.ts`, `scripts/with-dev-formula.ts`, `Formula/`, `justfile` |
269
+ | Homebrew distribution | `scripts/formula-shared.ts`, `scripts/dev-formula.ts`, `Formula/`, `justfile` |
270
270
  | Dev tooling | Biome (`just format` / `just lint`), TypeScript, colocated tests |
271
271
  | Cursor rules | `.cursor/rules/cli-program.mdc`, `.cursor/rules/code.mdc` |
272
272
 
package/docs/configure.md CHANGED
@@ -16,14 +16,15 @@ brew install <tap>/<key>
16
16
 
17
17
  Upgrade with `brew upgrade <key>`. Shell completions are installed by Homebrew during `brew install`. Users must configure their shell per [Homebrew Shell Completion](https://docs.brew.sh/Shell-Completion).
18
18
 
19
- **Uninstall the binary:** remove agent artifacts first (while the CLI is still on PATH), then `brew uninstall`:
19
+ **Uninstall the binary:**
20
20
 
21
21
  ```bash
22
- <key> configure --remove-all --yes
23
22
  brew uninstall <tap>/<key>
24
23
  ```
25
24
 
26
- `brew uninstall` runs the formula `uninstall` hook, which removes app config (`configure --remove-config --yes`). Skills and MCP entries are not removed by Homebrew — use `--remove-all` before uninstall when you want those gone too.
25
+ `brew uninstall` runs the formula `uninstall` hook (`configure --remove-all --yes`), which removes detected skills, MCP entries, and app config while the binary is still on PATH.
26
+
27
+ To remove app config only (keep skills/MCP), run `configure --remove-config --yes` before uninstall.
27
28
 
28
29
  ## Developer install
29
30
 
@@ -177,11 +178,11 @@ Release formulae should run:
177
178
 
178
179
  ```ruby
179
180
  def uninstall
180
- system bin/"myapp", "configure", "--remove-config", "--yes"
181
+ system bin/"myapp", "configure", "--remove-all", "--yes"
181
182
  end
182
183
  ```
183
184
 
184
- Homebrew calls this before removing the keg, so the binary is still on PATH. Safe no-op when app config was never created or `program.appConfig` is unset.
185
+ Removes detected skills, MCP entries, and app config while the binary is still on PATH. Safe no-op when nothing was installed.
185
186
 
186
187
  ## Bootstrapping a new CLI
187
188
 
@@ -10,7 +10,7 @@ Argsbarg apps distribute the **binary and shell completions** through Homebrew,
10
10
  | Skills + MCP | Formula `post_install` → `{key} configure --sync --yes` |
11
11
  | App config file | Bootstrapped as `{}` on `post_install` via `--sync` (`~/.local/lib/<key>/config.json`) |
12
12
  | App config values | User opt-in: `{key} configure` (interactive wizard when `program.appConfig` has entries) |
13
- | App config cleanup | Formula `uninstall` → `{key} configure --remove-config --yes` |
13
+ | App config cleanup | Formula `uninstall` → `{key} configure --remove-all --yes` |
14
14
 
15
15
  **Only tap-from-repo** — in-repo `Formula/` or GitHub tap. Not Homebrew core.
16
16
 
@@ -48,19 +48,27 @@ just reinstall-local # fast binary swap (`install -m 755` into Cellar; run ins
48
48
  just uninstall # undo formula + agent artifacts (app config removed by formula uninstall)
49
49
  ```
50
50
 
51
- Dev and release use the **same formula file** (`Formula/{key}.rb`, class name, install/post_install/test). `just install-local` temporarily overwrites it with a `file://` dev formula (staged under `Formula/.staging/`), runs `brew install`, then **restores** the release formula from backup. Release bumps rewrite the GitHub URL via `scripts/release.ts`.
51
+ Dev and release use the **same formula file** (`Formula/{key}.rb`). `just install-local` runs:
52
+
53
+ ```bash
54
+ bun scripts/dev-formula.ts install # back up release formula, write file:// dev formula
55
+ brew install --formula <tap>/{key} # install from the dev formula
56
+ bun scripts/dev-formula.ts reset # restore release formula
57
+ ```
58
+
59
+ If `brew install` fails, run `bun scripts/dev-formula.ts reset` manually to restore the release formula.
52
60
 
53
61
  ### Developer uninstall
54
62
 
55
63
  | Recipe | Removes |
56
64
  | --- | --- |
57
- | `just uninstall` | Formula `{key}` + tap symlink + skills/MCP; app config via formula `uninstall` |
65
+ | `just uninstall` | Formula `{key}` + tap symlink + skills/MCP + app config via formula `uninstall` |
58
66
  | `just uninstall-config` | App config file only (`configure --remove-config --yes`) |
59
- | `just uninstall-release` | Release formula from `{tap}` (keeps tap; app config via formula `uninstall`) |
60
- | `just uninstall-release-tap` | Release formula + `brew untap {tap}` (app config via formula `uninstall`) |
67
+ | `just uninstall-release` | Release formula from `{tap}` (keeps tap; agent artifacts via formula `uninstall`) |
68
+ | `just uninstall-release-tap` | Release formula + `brew untap {tap}` (agent artifacts via formula `uninstall`) |
61
69
  | `just test-release` | Install release formula and run formula test |
62
70
 
63
- End users: `<key> configure --remove-all --yes` then `brew uninstall <tap>/<key>`. `brew uninstall` also removes app config via the formula `uninstall` hook (skills/MCP are not removed automatically).
71
+ End users: `brew uninstall <tap>/<key>`. The formula `uninstall` hook runs `configure --remove-all --yes` (skills, MCP, and app config).
64
72
 
65
73
  ## Formula pattern
66
74
 
@@ -75,7 +83,7 @@ def post_install
75
83
  end
76
84
 
77
85
  def uninstall
78
- system bin/"{key}", "configure", "--remove-config", "--yes"
86
+ system bin/"{key}", "configure", "--remove-all", "--yes"
79
87
  end
80
88
  ```
81
89
 
@@ -43,7 +43,7 @@ Production CLIs with several JSON commands tend to use **codegen** so types, han
43
43
 
44
44
  ## Recommended pipeline (copy per repo)
45
45
 
46
- No shared npm package — each app copies the same **contract**. Reference implementations: **sqsp-qa-manager-poc**, **idp-trees**, **sqsp-i18n-tools-poc** (see each repo’s `docs/architecture.md` for which commands use which schema root).
46
+ No shared npm package — each app copies the same **contract**. Reference implementations: **sqsp-qa-manager-poc**, **sqsp-workspaces**, **sqsp-i18n-tools-poc** (see each repo’s `docs/architecture.md` for which commands use which schema root).
47
47
 
48
48
  ```mermaid
49
49
  flowchart LR
@@ -45,12 +45,14 @@ install: install-local
45
45
  sync-artifacts:
46
46
  just run configure --sync --yes
47
47
 
48
- # Dev install: build, dev formula (restored after brew), symlink tap, brew install
48
+ # Dev install: build, stage dev formula, brew install, restore release formula
49
49
  install-local: build
50
50
  @brew untap {{tap}} 2>/dev/null || true
51
51
  mkdir -p {{tap_parent}}
52
52
  ln -sfn '{{justfile_directory()}}' {{tap_path}}
53
- bun scripts/with-dev-formula.ts -- brew install --formula {{tap}}/{{cli_key}}
53
+ bun scripts/dev-formula.ts install
54
+ brew install --formula {{tap}}/{{cli_key}}
55
+ bun scripts/dev-formula.ts reset
54
56
  @echo ""
55
57
  @echo "Next: {{cli_key}} configure"
56
58
 
@@ -0,0 +1,97 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * Stage or restore the dev Homebrew formula for `just install-local`.
4
+ * `install` — back up release `Formula/{key}.rb` and write a `file://` dev formula.
5
+ * `reset` — restore the release formula from backup.
6
+ */
7
+
8
+ import { createHash } from "node:crypto";
9
+ import { chmodSync, copyFileSync, existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
10
+ import { join } from "node:path";
11
+ import { createIdentity } from "./create-identity.ts";
12
+ import { renderDevFormula } from "./formula-shared.ts";
13
+
14
+ const { key } = createIdentity;
15
+ const root = join(import.meta.dir, "..");
16
+ const stagingDir = join(root, "Formula", ".staging");
17
+ const stagingPath = join(stagingDir, key);
18
+ const formulaPath = join(root, "Formula", `${key}.rb`);
19
+ const backupPath = join(stagingDir, `${key}.rb.bak`);
20
+ const programPath = join(root, "src/program.ts");
21
+
22
+ type Command = "install" | "reset";
23
+
24
+ function usage(): never {
25
+ process.stderr.write("Usage: bun scripts/dev-formula.ts <install|reset>\n");
26
+ process.exit(1);
27
+ }
28
+
29
+ function parseCommand(argv: string[]): Command {
30
+ const cmd = argv[2];
31
+ if (cmd === "install" || cmd === "reset") {
32
+ return cmd;
33
+ }
34
+ usage();
35
+ }
36
+
37
+ function backupReleaseFormula(): void {
38
+ if (!existsSync(formulaPath)) {
39
+ return;
40
+ }
41
+ mkdirSync(stagingDir, { recursive: true });
42
+ copyFileSync(formulaPath, backupPath);
43
+ }
44
+
45
+ function restoreReleaseFormula(): void {
46
+ if (!existsSync(backupPath)) {
47
+ return;
48
+ }
49
+ copyFileSync(backupPath, formulaPath);
50
+ unlinkSync(backupPath);
51
+ }
52
+
53
+ function readVersion(): string {
54
+ const content = readFileSync(programPath, "utf8");
55
+ const match = /version:\s*"([^"]+)"/.exec(content);
56
+ if (!match?.[1]) {
57
+ process.stderr.write(`Could not read version from ${programPath}\n`);
58
+ process.exit(1);
59
+ }
60
+ return match[1];
61
+ }
62
+
63
+ function stageDevFormula(): void {
64
+ const distPath = join(root, "dist", key);
65
+ if (!existsSync(distPath)) {
66
+ process.stderr.write(`Missing binary: ${distPath} (run just build first)\n`);
67
+ process.exit(1);
68
+ }
69
+ mkdirSync(stagingDir, { recursive: true });
70
+ copyFileSync(distPath, stagingPath);
71
+ chmodSync(stagingPath, 0o755);
72
+
73
+ const sha256 = createHash("sha256").update(readFileSync(stagingPath)).digest("hex");
74
+ writeFileSync(formulaPath, renderDevFormula(stagingPath, readVersion(), sha256), "utf8");
75
+ }
76
+
77
+ function install(): void {
78
+ backupReleaseFormula();
79
+ stageDevFormula();
80
+ console.log(`Wrote dev formula: ${formulaPath}`);
81
+ }
82
+
83
+ function reset(): void {
84
+ if (!existsSync(backupPath)) {
85
+ console.log("No dev formula backup to restore.");
86
+ return;
87
+ }
88
+ restoreReleaseFormula();
89
+ console.log(`Restored ${formulaPath}`);
90
+ }
91
+
92
+ const command = parseCommand(process.argv);
93
+ if (command === "install") {
94
+ install();
95
+ } else {
96
+ reset();
97
+ }
@@ -24,7 +24,7 @@ export const formulaPostInstallRuby = `def post_install
24
24
  end`;
25
25
 
26
26
  export const formulaUninstallRuby = `def uninstall
27
- system bin/"${key}", "configure", "--remove-config", "--yes"
27
+ system bin/"${key}", "configure", "--remove-all", "--yes"
28
28
  end`;
29
29
 
30
30
  export const formulaCaveatsRuby = `def caveats
@@ -80,7 +80,10 @@ export function selectStaleReleaseTags(releases: ReleaseTag[]): string[] {
80
80
  export const releaseRepoSlug = releaseRepo;
81
81
 
82
82
  /** Resolves private GitHub release assets via the API at download time. */
83
- export const githubPrivateReleaseDownloadStrategyRuby = ` class GitHubPrivateReleaseDownloadStrategy < CurlDownloadStrategy
83
+ export const githubPrivateReleaseDownloadStrategyRuby = ` # Private/internal releases: default CurlDownloadStrategy cannot fetch non-public
84
+ # GitHub release assets. Resolve the asset via the API and authenticate with
85
+ # GitHub::API.credentials (set up via \`gh auth login\` or HOMEBREW_GITHUB_API_TOKEN).
86
+ class GitHubPrivateReleaseDownloadStrategy < CurlDownloadStrategy
84
87
  def initialize(url, name, version, **meta)
85
88
  super
86
89
  pattern = %r{https://github\\.com/([^/]+)/([^/]+)/releases/download/([^/]+)/(\\S+)}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "argsbarg",
3
- "version": "5.1.11",
3
+ "version": "5.1.13",
4
4
  "type": "module",
5
5
  "engines": {
6
6
  "bun": ">=1.3"
@@ -68,11 +68,10 @@ export function configureCommandNotes(program: CliProgram, _caps: CliCapabilitie
68
68
  "See what is installed:",
69
69
  ` ${app} configure --status`,
70
70
  "",
71
- "Remove agent artifacts before brew uninstall:",
72
- ` ${app} configure --remove-all --yes`,
71
+ "Uninstall:",
73
72
  ` brew uninstall <tap>/${app}`,
74
73
  "",
75
- "App config is removed automatically by the formula uninstall hook during brew uninstall.",
74
+ "The formula uninstall hook runs `configure --remove-all --yes` (skills, MCP, and app config).",
76
75
  "",
77
76
  ];
78
77
  if (program.appConfig) {
@@ -3,7 +3,7 @@ Tests for configure/configure module behavior.
3
3
  */
4
4
 
5
5
  import { afterEach, beforeEach, describe, expect, test } from "bun:test";
6
- import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
6
+ import { existsSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
7
7
  import { tmpdir } from "node:os";
8
8
  import { join } from "node:path";
9
9
  import { appConfigFileExists } from "../config/file.ts";
@@ -137,6 +137,22 @@ describe("remove plan", () => {
137
137
  const plan = buildUninstallPlan(fixture, paths, parseInstallOpts({ uninstall: "1", all: "1" }));
138
138
  expect(plan.length).toBe(0);
139
139
  });
140
+
141
+ test("configure --remove-all removes installed cursor skill", async () => {
142
+ const skillApp: CliProgram = {
143
+ ...fixture,
144
+ configure: { agentIntegration: "skill" },
145
+ };
146
+ const paths = resolveInstallPaths(skillApp);
147
+ mkdirSync(paths.cursorSkillDir, { recursive: true });
148
+ writeFileSync(join(paths.cursorSkillDir, "SKILL.md"), "# test\n", "utf8");
149
+
150
+ expect(detectInstalledArtifacts(paths, skillApp).cursorSkill).toBe(true);
151
+
152
+ const result = await new Cli(skillApp).invoke(["configure", "--remove-all", "--yes"]);
153
+ expect(result.exitCode).toBe(0);
154
+ expect(existsSync(paths.cursorSkillDir)).toBe(false);
155
+ });
140
156
  });
141
157
 
142
158
  /** Tests for app config wizard. */
@@ -121,7 +121,14 @@ function executePlan(
121
121
  if ("kind" in action && action.kind) {
122
122
  const skillTarget = skillTargetFromActionKind(action.kind);
123
123
  if (skillTarget) {
124
- changed.push(...runSkillAction(root, action.kind as InstallActionKind, opts));
124
+ if (opts.uninstall) {
125
+ const skillDir = skillDirFromUninstallSummary(action.summary, paths);
126
+ if (skillDir) {
127
+ changed.push(...uninstallSkillDir(skillDir, !!opts.dry));
128
+ }
129
+ } else {
130
+ changed.push(...runSkillAction(root, action.kind as InstallActionKind, opts));
131
+ }
125
132
  continue;
126
133
  }
127
134
  }
@@ -1,79 +0,0 @@
1
- #!/usr/bin/env bun
2
-
3
- /** Back up release formula, write dev formula, run a command, then restore. Usage: bun scripts/with-dev-formula.ts -- brew install … */
4
-
5
- import { spawnSync } from "node:child_process";
6
- import { createHash } from "node:crypto";
7
- import { chmodSync, copyFileSync, existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
8
- import { join } from "node:path";
9
- import { createIdentity } from "./create-identity.ts";
10
- import { renderDevFormula } from "./formula-shared.ts";
11
-
12
- const { key } = createIdentity;
13
- const root = join(import.meta.dir, "..");
14
- const stagingDir = join(root, "Formula", ".staging");
15
- const stagingPath = join(stagingDir, key);
16
- const formulaPath = join(root, "Formula", `${key}.rb`);
17
- const backupPath = join(stagingDir, `${key}.rb.bak`);
18
-
19
- function backupReleaseFormula(): void {
20
- if (!existsSync(formulaPath)) {
21
- return;
22
- }
23
- mkdirSync(stagingDir, { recursive: true });
24
- copyFileSync(formulaPath, backupPath);
25
- }
26
-
27
- function restoreReleaseFormula(): void {
28
- if (!existsSync(backupPath)) {
29
- return;
30
- }
31
- copyFileSync(backupPath, formulaPath);
32
- unlinkSync(backupPath);
33
- }
34
-
35
- function writeDevFormula(): void {
36
- const distPath = join(root, "dist", key);
37
- mkdirSync(stagingDir, { recursive: true });
38
- copyFileSync(distPath, stagingPath);
39
- chmodSync(stagingPath, 0o755);
40
-
41
- const sha256 = createHash("sha256").update(readFileSync(stagingPath)).digest("hex");
42
- const version = JSON.parse(readFileSync(join(root, "package.json"), "utf8")).version as string;
43
- writeFileSync(formulaPath, renderDevFormula(stagingPath, version, sha256), "utf8");
44
- }
45
-
46
- const sep = process.argv.indexOf("--");
47
- if (sep === -1 || sep === process.argv.length - 1) {
48
- process.stderr.write("Usage: bun scripts/with-dev-formula.ts -- <command...>\n");
49
- process.exit(1);
50
- }
51
-
52
- const cmd = process.argv.slice(sep + 1);
53
- const executable = cmd[0];
54
- if (!executable) {
55
- process.stderr.write("Usage: bun scripts/with-dev-formula.ts -- <command...>\n");
56
- process.exit(1);
57
- }
58
- let restored = false;
59
-
60
- function restore(): void {
61
- if (restored) {
62
- return;
63
- }
64
- restored = true;
65
- restoreReleaseFormula();
66
- }
67
-
68
- try {
69
- backupReleaseFormula();
70
- writeDevFormula();
71
- const result = spawnSync(executable, cmd.slice(1), { stdio: "inherit" });
72
- process.exitCode = result.status === null ? 1 : result.status;
73
- } finally {
74
- restore();
75
- }
76
-
77
- if (process.exitCode === 0) {
78
- console.log(`Restored ${formulaPath}`);
79
- }