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.
Files changed (103) hide show
  1. package/CHANGELOG.md +48 -1
  2. package/README.md +90 -84
  3. package/docs/README.md +6 -6
  4. package/docs/bundled-docs.md +1 -1
  5. package/docs/cli-program.md +5 -3
  6. package/docs/config-schema.md +36 -9
  7. package/docs/developing.md +7 -7
  8. package/docs/distribution-homebrew.md +103 -0
  9. package/docs/install.md +105 -189
  10. package/docs/mcp.md +2 -3
  11. package/docs/output-schema.md +1 -1
  12. package/examples/full-example/Formula/.gitkeep +0 -0
  13. package/examples/full-example/README.md +98 -0
  14. package/examples/full-example/biome.json +22 -0
  15. package/examples/{consumer-app → full-example}/bun.lock +2 -0
  16. package/examples/full-example/justfile +134 -0
  17. package/examples/{consumer-app → full-example}/package.json +10 -3
  18. package/examples/{consumer-app → full-example}/schemas/generated/app-config.json +1 -1
  19. package/examples/{consumer-app → full-example}/schemas/generated/status.json +1 -1
  20. package/examples/full-example/scripts/create-identity.ts +11 -0
  21. package/examples/full-example/scripts/formula-shared.ts +73 -0
  22. package/examples/full-example/scripts/gen-dev-formula.ts +26 -0
  23. package/examples/full-example/scripts/print-identity.ts +27 -0
  24. package/examples/full-example/src/commands/echo/command.ts +21 -0
  25. package/examples/full-example/src/commands/status/command.test.ts +10 -0
  26. package/examples/full-example/src/commands/status/command.ts +36 -0
  27. package/examples/{consumer-app → full-example}/src/commands/status/types.ts +1 -1
  28. package/examples/full-example/src/index.ts +10 -0
  29. package/examples/full-example/src/program.ts +57 -0
  30. package/examples/{consumer-app → full-example}/src/types.ts +1 -1
  31. package/examples/nested.ts +1 -3
  32. package/index.d.ts +27 -66
  33. package/package.json +2 -2
  34. package/src/builtins/builtins.test.ts +22 -23
  35. package/src/builtins/completion-group.ts +17 -15
  36. package/src/builtins/dispatch.ts +25 -1
  37. package/src/builtins/install.ts +22 -52
  38. package/src/builtins/registry.ts +2 -0
  39. package/src/builtins/uninstall.ts +80 -0
  40. package/src/capabilities.ts +1 -3
  41. package/src/cli-tool/cli-smoke.test.ts +19 -0
  42. package/src/cli-tool/create.test.ts +119 -0
  43. package/src/cli-tool/create.ts +380 -0
  44. package/{examples/consumer-app/capabilities.test.ts → src/cli-tool/full-example-capabilities.test.ts} +15 -14
  45. package/src/cli-tool/main.ts +8 -0
  46. package/src/cli-tool/post-create.ts +111 -0
  47. package/src/cli-tool/program.ts +82 -0
  48. package/src/cli-tool/prompt.ts +28 -0
  49. package/src/cli-tool/run-create.ts +149 -0
  50. package/src/cli.ts +0 -2
  51. package/src/config/bootstrap.ts +16 -9
  52. package/src/config/resolve.test.ts +167 -0
  53. package/src/config/resolve.ts +50 -6
  54. package/src/docs/api-guide.test.ts +4 -5
  55. package/src/docs/docs.test.ts +2 -1
  56. package/src/docs/mcp-guide.ts +7 -8
  57. package/src/index.ts +3 -10
  58. package/src/install/binary-placement.test.ts +101 -0
  59. package/src/install/binary-placement.ts +47 -0
  60. package/src/install/index.ts +117 -123
  61. package/src/install/install.test.ts +89 -105
  62. package/src/install/normalize-uninstall.ts +11 -0
  63. package/src/install/normalize.ts +4 -19
  64. package/src/install/paths.ts +0 -22
  65. package/src/install/plan.ts +14 -6
  66. package/src/install/shell.ts +0 -14
  67. package/src/install/status.test.ts +6 -6
  68. package/src/install/status.ts +0 -6
  69. package/src/install/target-effective.ts +0 -2
  70. package/src/install/target-scope.ts +15 -28
  71. package/src/install/target-types.ts +0 -16
  72. package/src/install/targets/app.ts +19 -28
  73. package/src/install/targets/configure.ts +5 -1
  74. package/src/install/targets/index.ts +0 -3
  75. package/src/install/targets.test.ts +24 -42
  76. package/src/mcp/env.test.ts +92 -0
  77. package/src/mcp/env.ts +15 -14
  78. package/src/parse.test.ts +3 -2
  79. package/src/prompt.ts +10 -0
  80. package/src/schema.ts +9 -1
  81. package/src/types.ts +27 -9
  82. package/src/validate.ts +5 -11
  83. package/docs/templates/cursor/rules/cli-program.mdc +0 -31
  84. package/examples/config-app/main.ts +0 -20
  85. package/examples/config-app/program.ts +0 -78
  86. package/examples/config-app/schema.ts +0 -37
  87. package/examples/config-app/types.ts +0 -19
  88. package/examples/consumer-app/README.md +0 -56
  89. package/examples/consumer-app/src/main.ts +0 -15
  90. package/examples/consumer-app/src/program.ts +0 -108
  91. package/src/install/app.ts +0 -94
  92. package/src/install/bootstrap.ts +0 -22
  93. package/src/install/completions.ts +0 -56
  94. package/src/install/targets/completions.ts +0 -133
  95. package/src/install/update.test.ts +0 -123
  96. package/src/install/update.ts +0 -54
  97. /package/examples/{consumer-app → full-example}/schemas/configSchemas.ts +0 -0
  98. /package/examples/{consumer-app → full-example}/schemas/outputSchemas.ts +0 -0
  99. /package/examples/{consumer-app → full-example}/scripts/schemagen/discover-schema-roots.test.ts +0 -0
  100. /package/examples/{consumer-app → full-example}/scripts/schemagen/discover-schema-roots.ts +0 -0
  101. /package/examples/{consumer-app → full-example}/scripts/schemagen/naming.ts +0 -0
  102. /package/examples/{consumer-app → full-example}/scripts/schemagen.ts +0 -0
  103. /package/examples/{consumer-app → full-example}/tsconfig.json +0 -0
@@ -1,123 +0,0 @@
1
- import { afterEach, beforeEach, expect, test } from "bun:test";
2
- import {
3
- chmodSync,
4
- existsSync,
5
- mkdirSync,
6
- mkdtempSync,
7
- readFileSync,
8
- rmSync,
9
- writeFileSync,
10
- } from "node:fs";
11
- import { tmpdir } from "node:os";
12
- import { join } from "node:path";
13
- import { cliPresentationRoot } from "../builtins/presentation.ts";
14
- import { Cli } from "../index.ts";
15
- import type { CliProgram, CliUpdateArtifact } from "../types.ts";
16
- import { cliValidateProgram } from "../validate.ts";
17
- import { parseInstallOpts, runInstallMutation } from "./index.ts";
18
-
19
- let home: string;
20
- let prevHome: string | undefined;
21
- let prevExecPath: string;
22
-
23
- beforeEach(() => {
24
- home = mkdtempSync(join(tmpdir(), "argsbarg-update-"));
25
- prevHome = process.env.HOME;
26
- process.env.HOME = home;
27
- prevExecPath = process.execPath;
28
- });
29
-
30
- afterEach(() => {
31
- if (prevHome === undefined) delete process.env.HOME;
32
- else process.env.HOME = prevHome;
33
- process.execPath = prevExecPath;
34
- rmSync(home, { recursive: true, force: true });
35
- });
36
-
37
- function fixtureWithUpdate(hook: () => Promise<CliUpdateArtifact>): CliProgram {
38
- return {
39
- key: "testapp",
40
- version: "1.0.0",
41
- description: "Test",
42
- install: { updateGetLatest: hook },
43
- commands: [{ key: "run", description: "Run", handler: () => {} }],
44
- };
45
- }
46
-
47
- test("user update command is allowed when updateGetLatest is set", () => {
48
- const root: CliProgram = {
49
- ...fixtureWithUpdate(async () => ({ path: process.execPath })),
50
- commands: [{ key: "update", description: "Custom update", handler: () => {} }],
51
- };
52
- expect(() => cliValidateProgram(root)).not.toThrow();
53
- });
54
-
55
- test("presentation exposes install --update when hook is set", () => {
56
- const root = fixtureWithUpdate(async () => ({ path: process.execPath }));
57
- const presentation = cliPresentationRoot(root);
58
- const install = presentation.commands.find((c) => c.key === "install");
59
- expect(install?.options?.some((o) => o.name === "update")).toBe(true);
60
- expect(presentation.commands.some((c) => c.key === "update")).toBe(false);
61
- });
62
-
63
- test("parseInstallOpts treats --update separately from --reinstall", () => {
64
- expect(parseInstallOpts({ update: "1" }).update).toBe(true);
65
- expect(parseInstallOpts({ update: "1" }).reinstall).toBe(false);
66
- });
67
-
68
- test("runInstallMutation honors --from for binary copy", async () => {
69
- const root: CliProgram = {
70
- key: "testapp",
71
- version: "1.0.0",
72
- description: "Test",
73
- handler: () => {},
74
- };
75
- const source = join(home, "new-binary");
76
- writeFileSync(source, "#!/bin/sh\necho hi\n", "utf8");
77
- chmodSync(source, 0o755);
78
-
79
- const bindir = join(home, ".local", "bin");
80
- mkdirSync(bindir, { recursive: true });
81
- writeFileSync(join(bindir, "testapp"), "old\n", "utf8");
82
-
83
- const { changed } = await runInstallMutation(root, {
84
- reinstall: "1",
85
- yes: "1",
86
- from: source,
87
- });
88
-
89
- const dest = join(home, ".local", "bin", "testapp");
90
- expect(changed).toContain(dest);
91
- expect(readFileSync(dest, "utf8")).toContain("echo hi");
92
- });
93
-
94
- test("Cli.invoke install --update uses hook and reinstalls", async () => {
95
- const source = join(home, "new-binary");
96
- writeFileSync(source, "#!/bin/sh\necho hi\n", "utf8");
97
- chmodSync(source, 0o755);
98
-
99
- const root = fixtureWithUpdate(async () => ({
100
- path: source,
101
- version: "2.0.0",
102
- }));
103
-
104
- const bindir = join(home, ".local", "bin");
105
- mkdirSync(bindir, { recursive: true });
106
- writeFileSync(join(bindir, "testapp"), "old\n", "utf8");
107
-
108
- const result = await new Cli(root).invoke(["install", "--update"]);
109
- expect(result.exitCode).toBe(0);
110
- expect(result.stdout).toContain("Updated testapp 1.0.0 → 2.0.0");
111
- expect(existsSync(join(home, ".local", "bin", "testapp"))).toBe(true);
112
- });
113
-
114
- test("Cli.invoke install --update reports already current", async () => {
115
- const root = fixtureWithUpdate(async () => ({
116
- path: process.execPath,
117
- version: "1.0.0",
118
- }));
119
-
120
- const result = await new Cli(root).invoke(["install", "--update"]);
121
- expect(result.exitCode).toBe(0);
122
- expect(result.stdout).toContain("Already at v1.0.0");
123
- });
@@ -1,54 +0,0 @@
1
- import { existsSync } from "node:fs";
2
- import type { CliProgram } from "../types.ts";
3
- import { runInstallMutation } from "./index.ts";
4
- import { installErr } from "./status.ts";
5
-
6
- /** Downloads the latest release and reinstalls installed artifacts (`myapp install --update`). */
7
- export async function cliUpdate(root: CliProgram): Promise<never> {
8
- const hook = root.install?.updateGetLatest;
9
- if (!hook) {
10
- installErr("Remote updates are not supported by this app.");
11
- process.exit(1);
12
- }
13
-
14
- let artifact: Awaited<ReturnType<typeof hook>>;
15
- try {
16
- artifact = await hook({ version: root.version });
17
- } catch (err) {
18
- const message = err instanceof Error ? err.message : String(err);
19
- installErr(message);
20
- process.exit(1);
21
- }
22
-
23
- if (!artifact.path || !existsSync(artifact.path)) {
24
- installErr("Update download did not return a valid app binary.");
25
- process.exit(1);
26
- }
27
-
28
- if (artifact.version !== undefined && artifact.version === root.version) {
29
- process.stdout.write(`Already at v${root.version}\n`);
30
- await artifact.cleanup?.();
31
- process.exit(0);
32
- }
33
-
34
- const currentVersion = root.version;
35
- try {
36
- await runInstallMutation(root, {
37
- reinstall: "1",
38
- yes: "1",
39
- from: artifact.path,
40
- });
41
- } catch (err) {
42
- await artifact.cleanup?.();
43
- installErr(err instanceof Error ? err.message : String(err));
44
- process.exit(1);
45
- }
46
-
47
- await artifact.cleanup?.();
48
-
49
- if (artifact.version !== undefined && artifact.version !== currentVersion) {
50
- process.stdout.write(`Updated ${root.key} ${currentVersion} → ${artifact.version}\n`);
51
- }
52
-
53
- process.exit(0);
54
- }