argsbarg 4.0.4 → 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 (147) hide show
  1. package/CHANGELOG.md +77 -1
  2. package/README.md +91 -85
  3. package/docs/README.md +6 -6
  4. package/docs/ai-skills.md +8 -5
  5. package/docs/bundled-docs.md +1 -1
  6. package/docs/cli-program.md +9 -7
  7. package/docs/config-schema.md +37 -13
  8. package/docs/developing.md +8 -8
  9. package/docs/distribution-homebrew.md +103 -0
  10. package/docs/install.md +143 -106
  11. package/docs/mcp.md +23 -12
  12. package/docs/output-schema.md +1 -1
  13. package/examples/full-example/Formula/.gitkeep +0 -0
  14. package/examples/full-example/README.md +98 -0
  15. package/examples/full-example/biome.json +22 -0
  16. package/examples/{consumer-app → full-example}/bun.lock +2 -0
  17. package/examples/full-example/justfile +134 -0
  18. package/examples/{consumer-app → full-example}/package.json +10 -3
  19. package/examples/{consumer-app → full-example}/schemas/generated/app-config.json +1 -1
  20. package/examples/{consumer-app → full-example}/schemas/generated/status.json +1 -1
  21. package/examples/full-example/scripts/create-identity.ts +11 -0
  22. package/examples/full-example/scripts/formula-shared.ts +73 -0
  23. package/examples/full-example/scripts/gen-dev-formula.ts +26 -0
  24. package/examples/full-example/scripts/print-identity.ts +27 -0
  25. package/examples/full-example/src/commands/echo/command.ts +21 -0
  26. package/examples/full-example/src/commands/status/command.test.ts +10 -0
  27. package/examples/full-example/src/commands/status/command.ts +36 -0
  28. package/examples/{consumer-app → full-example}/src/commands/status/types.ts +1 -1
  29. package/examples/full-example/src/index.ts +10 -0
  30. package/examples/full-example/src/program.ts +57 -0
  31. package/examples/{consumer-app → full-example}/src/types.ts +1 -1
  32. package/examples/mcp-test.ts +8 -24
  33. package/examples/nested.ts +1 -3
  34. package/index.d.ts +81 -65
  35. package/package.json +2 -2
  36. package/src/builtins/builtins.test.ts +37 -22
  37. package/src/builtins/completion-group.ts +17 -15
  38. package/src/builtins/config.test.ts +31 -25
  39. package/src/builtins/config.ts +4 -3
  40. package/src/builtins/dispatch.ts +25 -1
  41. package/src/builtins/install.ts +45 -82
  42. package/src/builtins/mcp.ts +1 -1
  43. package/src/builtins/registry.ts +2 -0
  44. package/src/builtins/uninstall.ts +80 -0
  45. package/src/capabilities.ts +5 -7
  46. package/src/cli-tool/cli-smoke.test.ts +19 -0
  47. package/src/cli-tool/create.test.ts +119 -0
  48. package/src/cli-tool/create.ts +380 -0
  49. package/{examples/consumer-app/capabilities.test.ts → src/cli-tool/full-example-capabilities.test.ts} +15 -14
  50. package/src/cli-tool/main.ts +8 -0
  51. package/src/cli-tool/post-create.ts +111 -0
  52. package/src/cli-tool/program.ts +82 -0
  53. package/src/cli-tool/prompt.ts +28 -0
  54. package/src/cli-tool/run-create.ts +149 -0
  55. package/src/config/bootstrap.ts +174 -66
  56. package/src/config/context.test.ts +22 -36
  57. package/src/config/context.ts +5 -4
  58. package/src/config/file.test.ts +66 -56
  59. package/src/config/file.ts +33 -25
  60. package/src/config/resolve.test.ts +192 -1
  61. package/src/config/resolve.ts +92 -13
  62. package/src/config.integration.test.ts +17 -10
  63. package/src/docs/api-guide.test.ts +4 -5
  64. package/src/docs/docs.test.ts +2 -1
  65. package/src/docs/mcp-guide.ts +7 -8
  66. package/src/hidden-mcpb.test.ts +41 -1
  67. package/src/index.ts +7 -10
  68. package/src/install/binary-placement.test.ts +101 -0
  69. package/src/install/binary-placement.ts +47 -0
  70. package/src/install/detect-installed.ts +2 -97
  71. package/src/install/index.ts +239 -168
  72. package/src/install/install-validate.test.ts +61 -0
  73. package/src/install/install.test.ts +170 -90
  74. package/src/install/mcp-openclaw.test.ts +40 -0
  75. package/src/install/mcp-openclaw.ts +106 -0
  76. package/src/install/normalize-uninstall.ts +11 -0
  77. package/src/install/normalize.ts +20 -0
  78. package/src/install/paths.ts +18 -26
  79. package/src/install/plan.ts +40 -261
  80. package/src/install/shell.ts +0 -14
  81. package/src/install/status.test.ts +85 -0
  82. package/src/install/status.ts +22 -15
  83. package/src/install/target-base.ts +93 -0
  84. package/src/install/target-detect.ts +20 -0
  85. package/src/install/target-effective.ts +129 -0
  86. package/src/install/target-mcp-cli.ts +149 -0
  87. package/src/install/target-mcp-json.ts +130 -0
  88. package/src/install/target-plan-build.ts +67 -0
  89. package/src/install/target-registry.ts +57 -0
  90. package/src/install/target-scope.ts +253 -0
  91. package/src/install/target-skill.ts +104 -0
  92. package/src/install/target-types.ts +129 -0
  93. package/src/install/targets/app.ts +60 -0
  94. package/src/install/targets/chatgpt-mcp.ts +12 -0
  95. package/src/install/targets/claude-code-mcp.ts +15 -0
  96. package/src/install/targets/claude-desktop-mcp.ts +12 -0
  97. package/src/install/targets/claude-skill.ts +16 -0
  98. package/src/install/targets/codex-mcp.ts +25 -0
  99. package/src/install/targets/codex-skill.ts +14 -0
  100. package/src/install/targets/configure.ts +63 -0
  101. package/src/install/targets/cursor-mcp.ts +15 -0
  102. package/src/install/targets/cursor-skill.ts +16 -0
  103. package/src/install/targets/index.ts +50 -0
  104. package/src/install/targets/openclaw-mcp.ts +25 -0
  105. package/src/install/targets/openclaw-skill.ts +17 -0
  106. package/src/install/targets/opencode-mcp.ts +101 -0
  107. package/src/install/targets/opencode-skill.ts +15 -0
  108. package/src/install/targets.test.ts +118 -0
  109. package/src/install/uninstall.ts +16 -152
  110. package/src/invoke.test.ts +7 -1
  111. package/src/mcp/bundle.ts +16 -4
  112. package/src/mcp/claude.test.ts +14 -1
  113. package/src/mcp/claude.ts +11 -4
  114. package/src/mcp/env.test.ts +92 -0
  115. package/src/mcp/env.ts +15 -14
  116. package/src/mcp/zip.test.ts +17 -0
  117. package/src/mcp/zip.ts +62 -9
  118. package/src/mcp.integration.test.ts +1 -1
  119. package/src/parse.test.ts +14 -2
  120. package/src/paths/host.ts +11 -11
  121. package/src/paths/remove-empty-dir.ts +13 -0
  122. package/src/prompt.ts +10 -0
  123. package/src/schema.ts +9 -1
  124. package/src/skill/generate.ts +18 -4
  125. package/src/skill/install.ts +33 -6
  126. package/src/skill/naming.ts +28 -0
  127. package/src/types.ts +86 -7
  128. package/src/validate.ts +73 -9
  129. package/docs/templates/cursor/rules/cli-program.mdc +0 -31
  130. package/examples/config-app/main.ts +0 -20
  131. package/examples/config-app/program.ts +0 -81
  132. package/examples/config-app/schema.ts +0 -37
  133. package/examples/config-app/types.ts +0 -19
  134. package/examples/consumer-app/README.md +0 -57
  135. package/examples/consumer-app/src/main.ts +0 -15
  136. package/examples/consumer-app/src/program.ts +0 -108
  137. package/src/install/binary.ts +0 -94
  138. package/src/install/completions.ts +0 -56
  139. package/src/install/update.test.ts +0 -108
  140. package/src/install/update.ts +0 -57
  141. /package/examples/{consumer-app → full-example}/schemas/configSchemas.ts +0 -0
  142. /package/examples/{consumer-app → full-example}/schemas/outputSchemas.ts +0 -0
  143. /package/examples/{consumer-app → full-example}/scripts/schemagen/discover-schema-roots.test.ts +0 -0
  144. /package/examples/{consumer-app → full-example}/scripts/schemagen/discover-schema-roots.ts +0 -0
  145. /package/examples/{consumer-app → full-example}/scripts/schemagen/naming.ts +0 -0
  146. /package/examples/{consumer-app → full-example}/scripts/schemagen.ts +0 -0
  147. /package/examples/{consumer-app → full-example}/tsconfig.json +0 -0
@@ -6,56 +6,51 @@ export function installBuiltinOptions(root: CliProgram): CliOption[] {
6
6
  const opts: CliOption[] = [
7
7
  {
8
8
  name: "all",
9
- description: "Install binary, completions, skills, and MCP config (when enabled).",
9
+ description: "Install agent skills and MCP config (default artifact set for this app).",
10
10
  kind: CliOptionKind.Presence,
11
11
  },
12
12
  {
13
- name: "bin",
14
- description: "Copy this binary to your install directory (default ~/.local/bin).",
15
- kind: CliOptionKind.Presence,
16
- },
17
- {
18
- name: "completions",
13
+ name: "skill",
19
14
  description:
20
- "Install bash, zsh, and fish tab-completion scripts (each skipped silently if that shell is not on PATH).",
15
+ "Install agent skills for Cursor, Claude, and other supported AI tools on this machine.",
21
16
  kind: CliOptionKind.Presence,
22
17
  },
23
- {
24
- name: "skill",
25
- description: "Install Cursor and Claude Code skills when ~/.cursor or ~/.claude exists.",
18
+ ];
19
+
20
+ if (resolveCapabilities(root).mcp) {
21
+ opts.push({
22
+ name: "mcp",
23
+ description:
24
+ "Add MCP server configuration for Cursor, Claude Code, and other supported agents.",
26
25
  kind: CliOptionKind.Presence,
27
- },
28
- {
29
- name: "reinstall",
30
- description: "Reinstall artifacts already on disk (always includes the binary).",
26
+ });
27
+ }
28
+
29
+ if (root.appConfig) {
30
+ opts.push({
31
+ name: "configure",
32
+ description: "Run the interactive configuration wizard.",
31
33
  kind: CliOptionKind.Presence,
32
- },
33
- {
34
- name: "from",
35
- description: "Binary to copy (default: running executable). Used with --reinstall.",
36
- kind: CliOptionKind.String,
37
- },
34
+ });
35
+ }
36
+
37
+ opts.push(
38
38
  {
39
39
  name: "status",
40
40
  description: "Print what is currently installed (read-only).",
41
41
  kind: CliOptionKind.Presence,
42
42
  },
43
43
  {
44
- name: "uninstall",
44
+ name: "reinstall",
45
45
  description:
46
- "Remove installed artifacts (use --all or scoped flags; skips targets not on disk).",
46
+ "Refresh installed agent artifacts (skills, MCP). Used by Homebrew post_install.",
47
47
  kind: CliOptionKind.Presence,
48
48
  },
49
- {
50
- name: "prefix",
51
- description:
52
- "Install directory for the binary (default ~/.local/bin; overrides INSTALL_PREFIX).",
53
- kind: CliOptionKind.String,
54
- },
55
49
  {
56
50
  name: "yes",
57
51
  description: "Skip the confirmation prompt.",
58
52
  kind: CliOptionKind.Presence,
53
+ shortName: "y",
59
54
  },
60
55
  {
61
56
  name: "dry",
@@ -64,46 +59,10 @@ export function installBuiltinOptions(root: CliProgram): CliOption[] {
64
59
  },
65
60
  {
66
61
  name: "json",
67
- description: "Print changed paths (install/update/uninstall) or status JSON on stdout.",
68
- kind: CliOptionKind.Presence,
69
- },
70
- {
71
- name: "quiet",
72
- description:
73
- "Suppress informational output (requires --yes, --json, --reinstall, or --update).",
62
+ description: "Print changed paths (install/reinstall) or status JSON on stdout.",
74
63
  kind: CliOptionKind.Presence,
75
64
  },
76
- ];
77
-
78
- if (resolveCapabilities(root).mcp) {
79
- opts.splice(4, 0, {
80
- name: "mcp",
81
- description: "Add or update MCP server entries in Cursor and Claude config files.",
82
- kind: CliOptionKind.Presence,
83
- });
84
- }
85
-
86
- if (resolveCapabilities(root).update) {
87
- const statusIdx = opts.findIndex((o) => o.name === "status");
88
- opts.splice(statusIdx, 0, {
89
- name: "update",
90
- description: "Download the latest release and reinstall installed artifacts.",
91
- kind: CliOptionKind.Presence,
92
- });
93
- }
94
-
95
- if (root.appConfig) {
96
- opts.push({
97
- name: "configure",
98
- description: "Interactively write app config (~/.config/<key>/config).",
99
- kind: CliOptionKind.Presence,
100
- });
101
- opts.push({
102
- name: "config",
103
- description: "Remove app config file (with --uninstall).",
104
- kind: CliOptionKind.Presence,
105
- });
106
- }
65
+ );
107
66
 
108
67
  return opts;
109
68
  }
@@ -112,32 +71,36 @@ export function installBuiltinOptions(root: CliProgram): CliOption[] {
112
71
  export function cliBuiltinInstallCommand(root: CliProgram): CliLeaf {
113
72
  const app = root.key;
114
73
  const notesLines = [
115
- "First-time setup:",
116
- ` ${app} install --all --yes`,
74
+ "Install the binary via Homebrew (tap-from-repo), then refresh agent artifacts:",
75
+ ` brew tap <org>/<repo>`,
76
+ ` brew install <tap>/${app}`,
117
77
  "",
118
- "Refresh after upgrading:",
119
- ` ${app} install --reinstall`,
120
- ];
121
- if (resolveCapabilities(root).update) {
122
- notesLines.push("", "Upgrade to latest release:", ` ${app} install --update`);
123
- }
124
- notesLines.push(
78
+ "Homebrew post_install runs:",
79
+ ` ${app} install --reinstall --yes`,
80
+ "",
81
+ "Configure separately (interactive):",
82
+ ` ${app} install --configure`,
83
+ "",
84
+ "Upgrade:",
85
+ ` brew upgrade ${app}`,
86
+ "",
87
+ "Shell completions are installed by Homebrew during brew install.",
88
+ "See: https://docs.brew.sh/Shell-Completion",
125
89
  "",
126
90
  "See what is installed:",
127
91
  ` ${app} install --status`,
128
92
  "",
129
- "Remove everything installed with --all:",
130
- ` ${app} install --uninstall --all --yes`,
93
+ "Remove agent artifacts:",
94
+ ` ${app} uninstall --yes`,
131
95
  "",
132
96
  "Use --dry to preview changes without writing files.",
133
97
  "Use --json for machine-readable output.",
134
- );
98
+ ];
135
99
  return {
136
100
  key: "install",
137
- description:
138
- "Install the binary, shell completions, agent skills, and MCP config to your user environment.",
139
- notes: notesLines.join("\n"),
101
+ description: "Install agent skills and MCP config for this app (binary via Homebrew).",
140
102
  options: installBuiltinOptions(root),
103
+ notes: notesLines.join("\n"),
141
104
  handler: () => {},
142
105
  };
143
106
  }
@@ -28,7 +28,7 @@ export function cliBuiltinMcpCommand(program: CliProgram): CliRouter {
28
28
 
29
29
  const bundle: CliLeaf = {
30
30
  key: "bundle",
31
- description: "Pack Claude Desktop `.mcpb` and Claude Code plugin zip from dist/<key>.",
31
+ description: "Pack dist MCP artifacts (`.mcpb`, Claude Code plugin zip) from dist/<key>.",
32
32
  handler: () => {},
33
33
  };
34
34
 
@@ -5,6 +5,7 @@ import { cliBuiltinCompletionGroup } from "./completion-group.ts";
5
5
  import { cliBuiltinConfigGroupIfEnabled } from "./config.ts";
6
6
  import { cliBuiltinInstallCommand } from "./install.ts";
7
7
  import { cliBuiltinMcpCommand } from "./mcp.ts";
8
+ import { cliBuiltinUninstallCommand } from "./uninstall.ts";
8
9
  import { cliBuiltinVersionCommand } from "./version.ts";
9
10
 
10
11
  type BuiltinFactory = (program: CliProgram) => CliNode | null;
@@ -30,6 +31,7 @@ export function resolveBuiltins(program: CliProgram, caps: CliCapabilities): Cli
30
31
  pushBuiltin(builtins, program, () => cliBuiltinVersionCommand());
31
32
  if (caps.install) {
32
33
  pushBuiltin(builtins, program, (p) => cliBuiltinInstallCommand(p));
34
+ pushBuiltin(builtins, program, (p) => cliBuiltinUninstallCommand(p));
33
35
  }
34
36
  pushBuiltin(builtins, program, (p) => cliBuiltinDocsGroupIfEnabled(p) ?? null);
35
37
  if (caps.mcp) {
@@ -0,0 +1,80 @@
1
+ import { resolveCapabilities } from "../capabilities.ts";
2
+ import { type CliLeaf, type CliOption, CliOptionKind, type CliProgram } from "../types.ts";
3
+
4
+ /** Uninstall command options (dynamic: `--mcp` only when MCP is enabled). */
5
+ export function uninstallBuiltinOptions(root: CliProgram): CliOption[] {
6
+ const opts: CliOption[] = [
7
+ {
8
+ name: "all",
9
+ description: "Remove all detected agent artifacts (default when no scope flags are given).",
10
+ kind: CliOptionKind.Presence,
11
+ },
12
+ {
13
+ name: "skill",
14
+ description: "Remove agent skills only.",
15
+ kind: CliOptionKind.Presence,
16
+ },
17
+ ];
18
+
19
+ if (resolveCapabilities(root).mcp) {
20
+ opts.push({
21
+ name: "mcp",
22
+ description: "Remove MCP server configuration only.",
23
+ kind: CliOptionKind.Presence,
24
+ });
25
+ }
26
+
27
+ if (root.appConfig) {
28
+ opts.push({
29
+ name: "configure",
30
+ description: "Remove the app config file only.",
31
+ kind: CliOptionKind.Presence,
32
+ });
33
+ }
34
+
35
+ opts.push(
36
+ {
37
+ name: "yes",
38
+ description: "Skip the confirmation prompt.",
39
+ kind: CliOptionKind.Presence,
40
+ shortName: "y",
41
+ },
42
+ {
43
+ name: "dry",
44
+ description: "Show what would change without writing files.",
45
+ kind: CliOptionKind.Presence,
46
+ },
47
+ {
48
+ name: "json",
49
+ description: "Print changed paths on stdout.",
50
+ kind: CliOptionKind.Presence,
51
+ },
52
+ );
53
+
54
+ return opts;
55
+ }
56
+
57
+ /** Builds the `uninstall` built-in command. */
58
+ export function cliBuiltinUninstallCommand(root: CliProgram): CliLeaf {
59
+ const app = root.key;
60
+ const notesLines = [
61
+ "Remove agent artifacts installed by this app (skills, MCP, config).",
62
+ "Run before `brew uninstall` while the CLI is still on PATH:",
63
+ "",
64
+ ` ${app} uninstall --yes`,
65
+ ` brew uninstall <tap>/${app}`,
66
+ "",
67
+ "Remove app config only:",
68
+ ` ${app} uninstall --configure --yes`,
69
+ "",
70
+ "Use --dry to preview changes without writing files.",
71
+ "Use --json for machine-readable output.",
72
+ ];
73
+ return {
74
+ key: "uninstall",
75
+ description: "Remove agent skills, MCP config, and app config for this app.",
76
+ options: uninstallBuiltinOptions(root),
77
+ notes: notesLines.join("\n"),
78
+ handler: () => {},
79
+ };
80
+ }
@@ -12,7 +12,6 @@ export interface CliCapabilities {
12
12
  mcp: boolean;
13
13
  install: boolean;
14
14
  docs: boolean;
15
- update: boolean;
16
15
  configCommands: boolean;
17
16
  }
18
17
 
@@ -24,7 +23,6 @@ export function resolveCapabilities(program: CliProgram): CliCapabilities {
24
23
  mcp: program.mcpServer?.enabled === true,
25
24
  install,
26
25
  docs: program.docs?.enabled === true,
27
- update: install && typeof program.install?.updateGetLatest === "function",
28
26
  configCommands: configCommandsEnabled(program),
29
27
  };
30
28
  }
@@ -33,7 +31,7 @@ export function resolveCapabilities(program: CliProgram): CliCapabilities {
33
31
  export function reservedCommandNames(caps: CliCapabilities): string[] {
34
32
  const names = ["completion", "version"];
35
33
  if (caps.install) {
36
- names.push("install");
34
+ names.push("install", "uninstall");
37
35
  }
38
36
  if (caps.docs) {
39
37
  names.push("docs");
@@ -65,13 +63,13 @@ export type CapabilityFeature = "mcp" | "install" | "docs" | "config";
65
63
  export function capabilityDeniedMessage(feature: CapabilityFeature): string {
66
64
  switch (feature) {
67
65
  case "mcp":
68
- return "MCP is not enabled. Set mcpServer: { enabled: true } on the program root.\n";
66
+ return "MCP is not available for this app.\n";
69
67
  case "install":
70
- return "install is disabled. Remove install.enabled: false from the program root.\n";
68
+ return "Install is not available for this app.\n";
71
69
  case "docs":
72
- return "docs is not enabled. Set docs: { enabled: true } on the program root.\n";
70
+ return "Documentation commands are not available for this app.\n";
73
71
  case "config":
74
- return "config commands are disabled. Set program.appConfig or enable config.commands.\n";
72
+ return "Configuration commands are not available for this app.\n";
75
73
  }
76
74
  }
77
75
 
@@ -0,0 +1,19 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import { spawnSync } from "node:child_process";
3
+ import { join } from "node:path";
4
+
5
+ const main = join(import.meta.dir, "main.ts");
6
+
7
+ describe("argsbarg cli-tool", () => {
8
+ test("version subcommand prints version", () => {
9
+ const proc = spawnSync("bun", [main, "version"], { encoding: "utf8" });
10
+ expect(proc.status).toBe(0);
11
+ expect(proc.stdout.trim().length).toBeGreaterThan(0);
12
+ });
13
+
14
+ test("completion subcommand emits bash script", () => {
15
+ const proc = spawnSync("bun", [main, "completion", "bash"], { encoding: "utf8" });
16
+ expect(proc.status).toBe(0);
17
+ expect(proc.stdout).toContain("complete");
18
+ });
19
+ });
@@ -0,0 +1,119 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import { mkdtempSync, rmSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ import {
6
+ applyCreate,
7
+ classNameFromKey,
8
+ diffCreate,
9
+ diffCreateDetails,
10
+ renderCreateTree,
11
+ substituteTemplateContent,
12
+ } from "./create.ts";
13
+
14
+ describe("argsbarg create", () => {
15
+ test("substitutes {key} tokens", () => {
16
+ const out = substituteTemplateContent(
17
+ "key={key} class={className} env={envPrefix}_API_TOKEN tap={tap} org={tapOrg}",
18
+ {
19
+ key: "my-cli",
20
+ className: "MyCli",
21
+ tap: "org/my-cli",
22
+ homepage: "https://github.com/org/my-cli",
23
+ releaseRepo: "org/my-cli",
24
+ desc: "My CLI",
25
+ force: false,
26
+ dryRun: false,
27
+ check: false,
28
+ diff: false,
29
+ yes: false,
30
+ devTemplate: false,
31
+ },
32
+ );
33
+ expect(out).toContain("my-cli");
34
+ expect(out).toContain("MyCli");
35
+ expect(out).toContain("MY_CLI_API_TOKEN");
36
+ expect(out).toContain("org/my-cli");
37
+ expect(out).not.toContain("{key}");
38
+ });
39
+
40
+ test("classNameFromKey", () => {
41
+ expect(classNameFromKey("sqsp-i18n")).toBe("SqspI18n");
42
+ });
43
+
44
+ test("renderCreateTree includes justfile and create-identity", () => {
45
+ const tree = renderCreateTree({
46
+ key: "testapp",
47
+ className: "Testapp",
48
+ tap: "local/testapp",
49
+ homepage: "https://example.com",
50
+ releaseRepo: "example/testapp",
51
+ desc: "Test",
52
+ force: false,
53
+ dryRun: false,
54
+ check: false,
55
+ diff: false,
56
+ yes: false,
57
+ devTemplate: false,
58
+ });
59
+ expect(tree.has("justfile")).toBe(true);
60
+ expect(tree.has("biome.json")).toBe(true);
61
+ expect(tree.has(".cursor/rules/code.mdc")).toBe(true);
62
+ expect(tree.has("scripts/create-identity.ts")).toBe(true);
63
+ const identity = tree.get("scripts/create-identity.ts");
64
+ expect(identity).toContain('key: "testapp"');
65
+ const formula = tree.get("scripts/formula-shared.ts");
66
+ expect(formula).toContain("create-identity.ts");
67
+ });
68
+
69
+ test("--check detects drift", () => {
70
+ const dir = mkdtempSync(join(tmpdir(), "argsbarg-create-"));
71
+ try {
72
+ applyCreate(dir, {
73
+ key: "testapp",
74
+ className: "Testapp",
75
+ tap: "local/testapp",
76
+ homepage: "https://example.com",
77
+ releaseRepo: "example/testapp",
78
+ desc: "Test",
79
+ force: true,
80
+ dryRun: false,
81
+ check: false,
82
+ diff: false,
83
+ yes: false,
84
+ devTemplate: false,
85
+ });
86
+ expect(diffCreate(dir, { key: "testapp", className: "Testapp" })).toEqual([]);
87
+ } finally {
88
+ rmSync(dir, { recursive: true, force: true });
89
+ }
90
+ });
91
+
92
+ test("--check infers options from create-identity.ts", () => {
93
+ const dir = mkdtempSync(join(tmpdir(), "argsbarg-create-"));
94
+ try {
95
+ applyCreate(dir, {
96
+ key: "testapp",
97
+ className: "Testapp",
98
+ tap: "local/testapp",
99
+ homepage: "https://example.com",
100
+ releaseRepo: "example/testapp",
101
+ desc: "Test",
102
+ force: true,
103
+ dryRun: false,
104
+ check: false,
105
+ diff: false,
106
+ yes: false,
107
+ devTemplate: false,
108
+ });
109
+ expect(diffCreate(dir, { check: true })).toEqual([]);
110
+ } finally {
111
+ rmSync(dir, { recursive: true, force: true });
112
+ }
113
+ });
114
+
115
+ test("--diff captures drift details", () => {
116
+ const drifts = diffCreateDetails("/nonexistent", { key: "x" });
117
+ expect(drifts.length).toBeGreaterThan(0);
118
+ });
119
+ });