argsbarg 6.1.10 → 6.2.0

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 (130) hide show
  1. package/CHANGELOG.md +10 -1
  2. package/README.md +43 -41
  3. package/docs/README.md +3 -2
  4. package/docs/ai-skills.md +36 -23
  5. package/docs/cli-program.md +12 -11
  6. package/docs/config-schema.md +3 -3
  7. package/docs/configure.md +12 -16
  8. package/docs/developing.md +6 -6
  9. package/docs/mcp.md +21 -57
  10. package/docs/output-schema.md +2 -2
  11. package/examples/formats.ts +5 -6
  12. package/examples/full-example/README.md +7 -69
  13. package/examples/full-example/docs/cli-schema.json +1 -1659
  14. package/examples/full-example/docs/cli.md +2 -1538
  15. package/examples/full-example/docs/http.md +0 -7
  16. package/examples/full-example/docs/mcp.md +23 -59
  17. package/examples/full-example/docs/openapi.json +2 -782
  18. package/examples/full-example/docs/skill.md +18 -14
  19. package/examples/full-example/justfile +5 -17
  20. package/examples/full-example/scripts/create-identity.ts +2 -1
  21. package/examples/full-example/src/commands/status/command.test.ts +2 -2
  22. package/examples/full-example/src/commands/status/command.ts +7 -6
  23. package/examples/full-example/src/program.ts +4 -10
  24. package/examples/full-example-json/Formula/.gitkeep +0 -0
  25. package/examples/full-example-json/Formula/full-example-json.rb +35 -0
  26. package/examples/full-example-json/README.md +27 -0
  27. package/examples/full-example-json/biome.json +22 -0
  28. package/examples/full-example-json/bun.lock +48 -0
  29. package/examples/full-example-json/docs/README.md +27 -0
  30. package/examples/full-example-json/docs/cli-schema.json +2145 -0
  31. package/examples/full-example-json/docs/cli.md +1990 -0
  32. package/examples/full-example-json/docs/http.md +92 -0
  33. package/examples/full-example-json/docs/mcp.md +116 -0
  34. package/examples/full-example-json/docs/openapi.json +1246 -0
  35. package/examples/full-example-json/docs/skill.md +57 -0
  36. package/examples/full-example-json/justfile +171 -0
  37. package/examples/full-example-json/package.json +22 -0
  38. package/examples/full-example-json/scripts/create-identity.ts +12 -0
  39. package/examples/full-example-json/scripts/dev-formula.ts +97 -0
  40. package/examples/full-example-json/scripts/formula-shared.test.ts +68 -0
  41. package/examples/full-example-json/scripts/formula-shared.ts +170 -0
  42. package/examples/full-example-json/scripts/print-identity.ts +28 -0
  43. package/examples/full-example-json/scripts/release.ts +212 -0
  44. package/examples/full-example-json/src/commands/echo/command.ts +26 -0
  45. package/examples/full-example-json/src/commands/status/command.test.ts +10 -0
  46. package/examples/full-example-json/src/commands/status/command.ts +28 -0
  47. package/examples/full-example-json/src/index.ts +10 -0
  48. package/examples/full-example-json/src/program.ts +33 -0
  49. package/examples/full-example-json/src/types/md.d.ts +4 -0
  50. package/examples/full-example-json/tsconfig.json +17 -0
  51. package/examples/minimal.ts +17 -17
  52. package/examples/nested.ts +10 -10
  53. package/examples/option-required.ts +13 -13
  54. package/examples/servers.ts +10 -10
  55. package/index.d.ts +17 -43
  56. package/package.json +1 -1
  57. package/src/cli-tool/create.test.ts +44 -68
  58. package/src/cli-tool/create.ts +81 -17
  59. package/src/cli-tool/full-example-capabilities.test.ts +33 -18
  60. package/src/cli-tool/post-create.ts +31 -17
  61. package/src/cli-tool/program.ts +16 -7
  62. package/src/cli-tool/prompt.ts +27 -0
  63. package/src/cli-tool/run-create.ts +19 -7
  64. package/src/cli-tool/schemagen/schemagen.test.ts +3 -3
  65. package/src/configure/artifacts/install-validate.test.ts +20 -33
  66. package/src/configure/artifacts/paths.ts +9 -53
  67. package/src/configure/artifacts/status.test.ts +13 -16
  68. package/src/configure/artifacts/status.ts +5 -22
  69. package/src/configure/artifacts/target-base.ts +6 -15
  70. package/src/configure/artifacts/target-effective.ts +16 -54
  71. package/src/configure/artifacts/target-mcp-json.ts +2 -5
  72. package/src/configure/artifacts/target-registry.ts +0 -7
  73. package/src/configure/artifacts/target-scope.ts +7 -17
  74. package/src/configure/artifacts/target-skill.ts +6 -15
  75. package/src/configure/artifacts/target-types.ts +6 -54
  76. package/src/configure/artifacts/targets/agents-mcp.ts +11 -0
  77. package/src/configure/artifacts/targets/configure.ts +1 -5
  78. package/src/configure/artifacts/targets/index.ts +4 -44
  79. package/src/configure/artifacts/targets/skill.ts +12 -0
  80. package/src/configure/artifacts/targets.test.ts +21 -59
  81. package/src/configure/configure.test.ts +35 -46
  82. package/src/configure/index.ts +19 -19
  83. package/src/configure/prompt.ts +2 -12
  84. package/src/core/parse.test.ts +21 -32
  85. package/src/core/types.ts +18 -44
  86. package/src/core/validate.ts +28 -45
  87. package/src/docs/docs.test.ts +4 -4
  88. package/src/docs/mcp-guide.ts +41 -71
  89. package/src/docs/resolve.ts +1 -1
  90. package/src/exports/cli.ts +1 -1
  91. package/src/index.ts +1 -1
  92. package/src/skill/generate.ts +26 -45
  93. package/src/skill/install.ts +18 -38
  94. package/src/skill/naming.ts +3 -27
  95. package/src/test/integration/config.test.ts +3 -3
  96. package/src/test/integration/mcp.test.ts +4 -4
  97. package/{examples/mcp-test.ts → src/test/mcp-integration-fixture.ts} +20 -22
  98. package/src/configure/artifacts/target-mcp-cli.ts +0 -127
  99. package/src/configure/artifacts/targets/chatgpt-mcp.ts +0 -12
  100. package/src/configure/artifacts/targets/claude-code-mcp.ts +0 -15
  101. package/src/configure/artifacts/targets/claude-desktop-mcp.ts +0 -12
  102. package/src/configure/artifacts/targets/claude-skill.ts +0 -16
  103. package/src/configure/artifacts/targets/codex-mcp.ts +0 -25
  104. package/src/configure/artifacts/targets/codex-skill.ts +0 -14
  105. package/src/configure/artifacts/targets/cursor-mcp.ts +0 -15
  106. package/src/configure/artifacts/targets/cursor-skill.ts +0 -16
  107. package/src/configure/artifacts/targets/openclaw-mcp.ts +0 -25
  108. package/src/configure/artifacts/targets/openclaw-skill.ts +0 -17
  109. package/src/configure/artifacts/targets/opencode-mcp.ts +0 -96
  110. package/src/configure/artifacts/targets/opencode-skill.ts +0 -15
  111. /package/examples/{full-example → full-example-json}/src/commands/render-json/__generated__/RenderJsonInputSchema.json +0 -0
  112. /package/examples/{full-example → full-example-json}/src/commands/render-json/__generated__/index.ts +0 -0
  113. /package/examples/{full-example → full-example-json}/src/commands/render-json/command.test.ts +0 -0
  114. /package/examples/{full-example → full-example-json}/src/commands/render-json/command.ts +0 -0
  115. /package/examples/{full-example → full-example-json}/src/commands/render-json/types.ts +0 -0
  116. /package/examples/{full-example → full-example-json}/src/commands/status/__generated__/StatusJsonOutputSchema.json +0 -0
  117. /package/examples/{full-example → full-example-json}/src/commands/status/__generated__/index.ts +0 -0
  118. /package/examples/{full-example → full-example-json}/src/commands/status/types.ts +0 -0
  119. /package/examples/{full-example → full-example-json}/src/commands/workspaces/__generated__/WorkspaceNameInputSchema.json +0 -0
  120. /package/examples/{full-example → full-example-json}/src/commands/workspaces/__generated__/index.ts +0 -0
  121. /package/examples/{full-example → full-example-json}/src/commands/workspaces/command.test.ts +0 -0
  122. /package/examples/{full-example → full-example-json}/src/commands/workspaces/command.ts +0 -0
  123. /package/examples/{full-example → full-example-json}/src/commands/workspaces/types.ts +0 -0
  124. /package/examples/{full-example → full-example-json}/src/db/index.test.ts +0 -0
  125. /package/examples/{full-example → full-example-json}/src/db/index.ts +0 -0
  126. /package/examples/{full-example → full-example-json}/src/db/migrate.test.ts +0 -0
  127. /package/examples/{full-example → full-example-json}/src/db/migrate.ts +0 -0
  128. /package/examples/{full-example → full-example-json}/src/db/migrations/001_workspaces.sql +0 -0
  129. /package/examples/{full-example → full-example-json}/src/db/tables/workspaces.ts +0 -0
  130. /package/examples/{full-example → full-example-json}/src/types/argsbarg.d.ts +0 -0
@@ -6,13 +6,14 @@ import {
6
6
  applyCreate,
7
7
  type CreateOptions,
8
8
  diffCreateDetails,
9
+ normalizeCreateTemplateId,
9
10
  parseCreateArgv,
10
11
  printCreateDiffs,
11
12
  renderCreateTree,
12
13
  resolveCreateOptions,
13
14
  } from "./create.ts";
14
15
  import { printPostCreatePlan, runPostCreate } from "./post-create.ts";
15
- import { promptConfirm, promptOptional, promptRequired } from "./prompt.ts";
16
+ import { promptConfirm, promptOptional, promptRequired, promptTemplateChoice } from "./prompt.ts";
16
17
 
17
18
  function isInteractiveTty(): boolean {
18
19
  return Boolean(process.stdin.isTTY);
@@ -22,7 +23,10 @@ function collectInteractiveOptions(
22
23
  partial: Partial<CreateOptions>,
23
24
  dir: string,
24
25
  ): { opts: CreateOptions; baseDir: string } {
25
- process.stderr.write("Argsbarg create — bootstrap a new CLI from full-example\n\n");
26
+ process.stderr.write("Argsbarg create — bootstrap a new CLI\n\n");
27
+
28
+ const templateId = partial.templateId ?? promptTemplateChoice();
29
+
26
30
  const targetDir = promptOptional("Target directory", dir) ?? dir;
27
31
  const baseDir = resolve(process.cwd(), targetDir);
28
32
  const key = promptRequired("CLI key (binary name)", partial.key);
@@ -31,6 +35,7 @@ function collectInteractiveOptions(
31
35
  const opts = resolveCreateOptions(
32
36
  {
33
37
  ...partial,
38
+ templateId,
34
39
  key,
35
40
  releaseRepo,
36
41
  force: partial.force ?? false,
@@ -38,7 +43,8 @@ function collectInteractiveOptions(
38
43
  baseDir,
39
44
  );
40
45
 
41
- process.stderr.write(`\nTarget: ${baseDir}\n`);
46
+ process.stderr.write(`\nTemplate: ${opts.templateId}\n`);
47
+ process.stderr.write(`Target: ${baseDir}\n`);
42
48
  process.stderr.write(`Key: ${opts.key} Class: ${opts.className} Tap: ${opts.tap} Release: ${opts.releaseRepo}\n`);
43
49
  process.stderr.write(`Homepage: ${opts.homepage}\n\n`);
44
50
  const tree = renderCreateTree(opts);
@@ -47,7 +53,7 @@ function collectInteractiveOptions(
47
53
  process.stderr.write(` ${rel}\n`);
48
54
  }
49
55
  process.stderr.write("\n");
50
- printPostCreatePlan();
56
+ printPostCreatePlan(opts.templateId);
51
57
  process.stderr.write("\n");
52
58
 
53
59
  if (!promptConfirm("Proceed")) {
@@ -91,7 +97,13 @@ export async function runCreate(input: Partial<CreateOptions> & { dir?: string }
91
97
  if (!partial.releaseRepo) {
92
98
  throw new Error("--release-repo is required in non-interactive mode.");
93
99
  }
94
- opts = resolveCreateOptions(partial, baseDir);
100
+ opts = resolveCreateOptions(
101
+ {
102
+ ...partial,
103
+ templateId: normalizeCreateTemplateId(partial.templateId),
104
+ },
105
+ baseDir,
106
+ );
95
107
  return runCreateApply(baseDir, opts, partial.dryRun ?? false);
96
108
  } catch (err) {
97
109
  process.stderr.write(`${err instanceof Error ? err.message : String(err)}\n`);
@@ -104,7 +116,7 @@ async function runCreateApply(baseDir: string, opts: CreateOptions, dryRun: bool
104
116
  const written = applyCreate(baseDir, { ...opts, dryRun: true, check: false });
105
117
  process.stdout.write(`Would write ${written.length} file(s) under ${baseDir}\n`);
106
118
  for (const w of written) process.stdout.write(` ${relative(baseDir, w) || w}\n`);
107
- printPostCreatePlan();
119
+ printPostCreatePlan(opts.templateId);
108
120
  return 0;
109
121
  }
110
122
 
@@ -120,7 +132,7 @@ async function runCreateApply(baseDir: string, opts: CreateOptions, dryRun: bool
120
132
  process.stdout.write(` ${relative(baseDir, w) || w}\n`);
121
133
  }
122
134
 
123
- await runPostCreate(baseDir, false);
135
+ await runPostCreate(baseDir, false, opts.templateId);
124
136
  process.stdout.write("Done.\n");
125
137
  return 0;
126
138
  }
@@ -6,7 +6,7 @@ import { discoverSchemaRoots } from "./discover-schema-roots.ts";
6
6
  import { schemaExportName, schemaJsonBasename } from "./names.ts";
7
7
  import { runSchemagen } from "./run.ts";
8
8
 
9
- const exampleRoot = join(import.meta.dir, "../../../examples/full-example");
9
+ const exampleRoot = join(import.meta.dir, "../../../examples/full-example-json");
10
10
 
11
11
  const tempRoots: string[] = [];
12
12
 
@@ -46,7 +46,7 @@ function writeSrcFile(root: string, relPath: string, body: string): void {
46
46
  }
47
47
 
48
48
  describe("schemagen", () => {
49
- test("discovers @sg types in full-example", () => {
49
+ test("discovers @sg types in full-example-json", () => {
50
50
  const roots = discoverSchemaRoots(exampleRoot);
51
51
  expect(roots.map((r) => r.typeName).sort()).toEqual(["RenderJsonInput", "StatusJsonOutput", "WorkspaceNameInput"]);
52
52
  });
@@ -56,7 +56,7 @@ describe("schemagen", () => {
56
56
  expect(schemaExportName("RenderJsonInput")).toBe("RenderJsonInputSchema");
57
57
  });
58
58
 
59
- test("runSchemagen writes __generated__ artifacts in full-example", () => {
59
+ test("runSchemagen writes __generated__ artifacts in full-example-json", () => {
60
60
  const counts = runSchemagen({ projectRoot: exampleRoot });
61
61
  expect(counts).toEqual({ schemas: 3 });
62
62
  });
@@ -29,49 +29,36 @@ describe("validateConfigureConfig", () => {
29
29
  expect(() => cliValidateProgram(program)).toThrow(/allSkills/);
30
30
  });
31
31
 
32
- test("rejects both sides of pair without both integration", () => {
33
- const program: CliProgram = {
32
+ test("rejects configure.agentIntegration", () => {
33
+ const program = {
34
34
  ...base,
35
- mcpServer: { enabled: true },
36
- configure: {
37
- agentIntegration: "mcp",
38
- targets: { cursorMcp: true, cursorSkill: true },
39
- },
40
- };
41
- expect(() => cliValidateProgram(program)).toThrow(/both/);
35
+ configure: { agentIntegration: "mcp" },
36
+ } as CliProgram;
37
+ expect(() => cliValidateProgram(program)).toThrow(/agentIntegration removed/);
42
38
  });
43
39
 
44
- test("rejects explicit MCP target in skill integration mode", () => {
45
- const program: CliProgram = {
40
+ test("rejects legacy per-host skill targets", () => {
41
+ const program = {
46
42
  ...base,
47
- configure: {
48
- agentIntegration: "skill",
49
- targets: { cursorMcp: true },
50
- },
51
- };
52
- expect(() => cliValidateProgram(program)).toThrow(/cursorMcp/);
43
+ configure: { targets: { cursorSkill: true } },
44
+ } as CliProgram;
45
+ expect(() => cliValidateProgram(program)).toThrow(/cursorSkill removed/);
53
46
  });
54
47
 
55
- test("allows explicit pair targets with both integration", () => {
56
- const program: CliProgram = {
48
+ test("rejects legacy per-host MCP targets", () => {
49
+ const program = {
57
50
  ...base,
58
51
  mcpServer: { enabled: true },
59
- configure: {
60
- agentIntegration: "both",
61
- targets: { cursorMcp: true, cursorSkill: true },
62
- },
63
- };
64
- expect(() => cliValidateProgram(program)).not.toThrow();
52
+ configure: { targets: { cursorMcp: true } },
53
+ } as CliProgram;
54
+ expect(() => cliValidateProgram(program)).toThrow(/cursorMcp removed/);
65
55
  });
66
56
 
67
- test("rejects explicit MCP target without mcpServer", () => {
68
- const program: CliProgram = {
57
+ test("rejects unknown configure.targets keys", () => {
58
+ const program = {
69
59
  ...base,
70
- configure: {
71
- agentIntegration: "both",
72
- targets: { cursorMcp: true },
73
- },
74
- };
75
- expect(() => cliValidateProgram(program)).toThrow(/mcpServer\.enabled/);
60
+ configure: { targets: { unknownKey: true } },
61
+ } as CliProgram;
62
+ expect(() => cliValidateProgram(program)).toThrow(/not a valid target key/);
76
63
  });
77
64
  });
@@ -1,25 +1,13 @@
1
1
  import { existsSync } from "node:fs";
2
2
  import { dirname, join } from "node:path";
3
3
  import type { CliProgram } from "../../core/types.ts";
4
- import { mcpServerId, sanitizeToolSegment } from "../../mcp/tools.ts";
4
+ import { mcpServerId } from "../../mcp/tools.ts";
5
5
  import { displayHomePath, userHome, xdgConfigHome } from "../../paths/host.ts";
6
- import { skillDirNameForTarget } from "../../skill/install.ts";
7
- import { resolveOpenclawConfigPath } from "./mcp-openclaw.ts";
8
- import { resolveOpenCodeConfigPathForInstall } from "./mcp-opencode.ts";
6
+ import { skillDirName } from "../../skill/naming.ts";
9
7
 
10
8
  export interface InstallPaths {
11
- cursorSkillDir: string;
12
- claudeSkillDir: string;
13
- codexSkillDir: string;
14
- opencodeSkillDir: string;
15
- openclawSkillDir: string;
16
- cursorMcpPath: string;
17
- claudeMcpPath: string;
18
- claudeDesktopMcpPath: string;
19
- opencodeMcpPath: string;
20
- chatGptMcpPath: string;
21
- codexConfigPath: string;
22
- openclawConfigPath: string;
9
+ agentsSkillDir: string;
10
+ agentsMcpPath: string;
23
11
  mcpName: string;
24
12
  skillDirName: string;
25
13
  }
@@ -31,7 +19,7 @@ export function displayInstallPath(path: string): string {
31
19
  return displayHomePath(path);
32
20
  }
33
21
 
34
- /** Resolves Claude Desktop `claude_desktop_config.json` for the current OS. */
22
+ /** Resolves Claude Desktop `claude_desktop_config.json` for the current OS (manual setup docs). */
35
23
  export function resolveClaudeDesktopMcpPath(home: string): string {
36
24
  if (process.platform === "darwin") {
37
25
  return join(home, "Library", "Application Support", "Claude", "claude_desktop_config.json");
@@ -48,47 +36,15 @@ export function claudeDesktopPresent(_home: string, configPath: string): boolean
48
36
  return existsSync(configPath) || existsSync(dirname(configPath));
49
37
  }
50
38
 
51
- /** Resolves ChatGPT desktop `chatgpt_mcp_config.json` for the current OS. */
52
- export function resolveChatGptMcpPath(home: string): string {
53
- if (process.platform === "darwin") {
54
- return join(home, "Library", "Application Support", "ChatGPT", "chatgpt_mcp_config.json");
55
- }
56
- if (process.platform === "win32") {
57
- const appData = process.env.APPDATA ?? join(home, "AppData", "Roaming");
58
- return join(appData, "OpenAI", "ChatGPT", "chatgpt_mcp_config.json");
59
- }
60
- return join(xdgConfigHome(home), "ChatGPT", "chatgpt_mcp_config.json");
61
- }
62
-
63
- /** True when ChatGPT desktop app data exists (config file or app support directory). */
64
- export function chatGptDesktopPresent(_home: string, configPath: string): boolean {
65
- return existsSync(configPath) || existsSync(dirname(configPath));
66
- }
67
-
68
39
  /** Resolves all install artifact paths for a program root. */
69
40
  export function resolveInstallPaths(root: CliProgram): InstallPaths {
70
41
  const home = userHome();
71
- const skillDirName = sanitizeToolSegment(root.key);
72
- const codexSlug = skillDirNameForTarget(root.key, "codex");
73
- const opencodeSlug = skillDirNameForTarget(root.key, "opencode");
74
- const openclawSlug = skillDirNameForTarget(root.key, "openclaw");
75
- const claudeDesktopMcpPath = resolveClaudeDesktopMcpPath(home);
76
- const chatGptMcpPath = resolveChatGptMcpPath(home);
42
+ const dirName = skillDirName(root.key);
77
43
 
78
44
  return {
79
- cursorSkillDir: join(home, ".cursor", "skills", skillDirName),
80
- claudeSkillDir: join(home, ".claude", "skills", skillDirName),
81
- codexSkillDir: join(home, ".codex", "skills", codexSlug),
82
- opencodeSkillDir: join(home, ".config", "opencode", "skills", opencodeSlug),
83
- openclawSkillDir: join(home, ".openclaw", "skills", openclawSlug),
84
- cursorMcpPath: join(home, ".cursor", "mcp.json"),
85
- claudeMcpPath: join(home, ".claude.json"),
86
- claudeDesktopMcpPath,
87
- opencodeMcpPath: resolveOpenCodeConfigPathForInstall(home),
88
- chatGptMcpPath,
89
- codexConfigPath: join(home, ".codex", "config.toml"),
90
- openclawConfigPath: resolveOpenclawConfigPath(home),
45
+ agentsSkillDir: join(home, ".agents", "skills", dirName),
46
+ agentsMcpPath: join(home, ".agents", "mcp.json"),
91
47
  mcpName: mcpServerId(root),
92
- skillDirName,
48
+ skillDirName: dirName,
93
49
  };
94
50
  }
@@ -28,8 +28,7 @@ afterEach(() => {
28
28
 
29
29
  /** Tests for resolveInstallTargetPreview. */
30
30
  describe("resolveInstallTargetPreview", () => {
31
- /** Mcp app previews MCP keys for all and mcp scopes. */
32
- test("mcp app previews MCP keys for all and mcp scopes", () => {
31
+ test("mcp app includes agentsMcp in all scope when mcpServer enabled", () => {
33
32
  const program: CliProgram = {
34
33
  key: "mcpapp",
35
34
  version: "1",
@@ -39,38 +38,36 @@ describe("resolveInstallTargetPreview", () => {
39
38
  };
40
39
  const paths = resolveInstallPaths(program);
41
40
  const preview = resolveInstallTargetPreview(program, paths);
42
- expect(preview.agentIntegration).toBe("mcp");
43
- expect(preview.all).not.toContain("app");
44
- expect(preview.all.length).toBeGreaterThan(0);
45
- expect(preview.all.every((k) => k.endsWith("Mcp") || k === "configure")).toBe(true);
41
+ expect(preview.all).toEqual(["agentsMcp"]);
42
+ expect(preview.mcp).toEqual(["agentsMcp"]);
43
+ expect(preview.skill).toEqual([]);
46
44
  });
47
45
 
48
- /** Tests that shell app previews skill keys. */
49
- test("shell app previews skill keys", () => {
46
+ test("skill app includes skill in all scope when enabled", () => {
50
47
  const program: CliProgram = {
51
48
  key: "cliapp",
52
49
  version: "1",
53
50
  description: "x",
51
+ skill: { enabled: true },
54
52
  handler: () => {},
55
53
  };
56
54
  const paths = resolveInstallPaths(program);
57
55
  const preview = resolveInstallTargetPreview(program, paths);
58
- expect(preview.agentIntegration).toBe("skill");
59
- expect(preview.all).not.toContain("app");
60
- expect(preview.all.length).toBeGreaterThan(0);
56
+ expect(preview.all).toEqual(["skill"]);
57
+ expect(preview.skill).toEqual(["skill"]);
61
58
  expect(preview.mcp).toEqual([]);
62
59
  });
63
60
  });
64
61
 
65
62
  /** Tests for printInstallStatus json. */
66
63
  describe("printInstallStatus json", () => {
67
- /** Includes agentIntegration and effective scopes. */
68
- test("includes agentIntegration and effective scopes", () => {
64
+ test("includes effective scopes", () => {
69
65
  const program: CliProgram = {
70
66
  key: "app",
71
67
  version: "1",
72
68
  description: "x",
73
69
  mcpServer: { enabled: true },
70
+ skill: { enabled: true },
74
71
  handler: () => {},
75
72
  };
76
73
  const chunks: string[] = [];
@@ -82,11 +79,11 @@ describe("printInstallStatus json", () => {
82
79
  try {
83
80
  printInstallStatus(program, { json: true });
84
81
  const parsed = JSON.parse(chunks.join("")) as {
85
- agentIntegration: string;
86
82
  effective: { all: string[]; mcp: string[]; skill: string[] };
87
83
  };
88
- expect(parsed.agentIntegration).toBe("mcp");
89
- expect(parsed.effective.all.length).toBeGreaterThan(0);
84
+ expect(parsed.effective.all).toEqual(["skill", "agentsMcp"]);
85
+ expect(parsed.effective.skill).toEqual(["skill"]);
86
+ expect(parsed.effective.mcp).toEqual(["agentsMcp"]);
90
87
  } finally {
91
88
  process.stdout.write = orig;
92
89
  }
@@ -34,7 +34,6 @@ export function printInstallStatus(root: CliProgram, opts: InstallOpts): void {
34
34
  if (opts.json) {
35
35
  const preview = resolveInstallTargetPreview(root, paths);
36
36
  const json: Record<string, unknown> = {
37
- agentIntegration: preview.agentIntegration,
38
37
  effective: {
39
38
  all: preview.all,
40
39
  mcp: preview.mcp,
@@ -42,14 +41,8 @@ export function printInstallStatus(root: CliProgram, opts: InstallOpts): void {
42
41
  },
43
42
  };
44
43
  if (status.app) json.app = status.app;
45
- if (status.cursorSkill) json.cursorSkill = status.cursorSkill;
46
- if (status.claudeSkill) json.claudeSkill = status.claudeSkill;
47
- if (status.cursorMcp) json.cursorMcp = status.cursorMcp;
48
- if (status.claudeMcp) json.claudeMcp = status.claudeMcp;
49
- if (status.claudeDesktopMcp) json.claudeDesktopMcp = status.claudeDesktopMcp;
50
- if (status.opencodeMcp) json.opencodeMcp = status.opencodeMcp;
51
- if (status.codexMcp) json.codexMcp = status.codexMcp;
52
- if (status.chatGptMcp) json.chatGptMcp = status.chatGptMcp;
44
+ if (status.skill) json.skill = status.skill;
45
+ if (status.agentsMcp) json.agentsMcp = status.agentsMcp;
53
46
  process.stdout.write(`${JSON.stringify(json, null, 2)}\n`);
54
47
  return;
55
48
  }
@@ -57,14 +50,8 @@ export function printInstallStatus(root: CliProgram, opts: InstallOpts): void {
57
50
  installOut(`Installed artifacts for ${root.key}:`, opts);
58
51
  const lines: [string, string | undefined][] = [
59
52
  ["app", status.app],
60
- ["cursor skill", status.cursorSkill],
61
- ["claude skill", status.claudeSkill],
62
- ["cursor mcp", status.cursorMcp],
63
- ["claude code mcp", status.claudeMcp],
64
- ["claude desktop mcp", status.claudeDesktopMcp],
65
- ["opencode mcp", status.opencodeMcp],
66
- ["codex mcp", status.codexMcp],
67
- ["chatgpt desktop mcp", status.chatGptMcp],
53
+ ["agent skill", status.skill],
54
+ [".agents mcp", status.agentsMcp],
68
55
  ];
69
56
  let any = false;
70
57
  for (const [label, value] of lines) {
@@ -79,10 +66,6 @@ export function printInstallStatus(root: CliProgram, opts: InstallOpts): void {
79
66
 
80
67
  const configStatus = appConfigStatus(root);
81
68
  if (configStatus) {
82
- installOut(` app config: ${configStatus.path}${configStatus.exists ? "" : " (missing)"}`, opts);
83
- for (const req of configStatus.required) {
84
- const bindingHint = req.binding && req.binding !== "missing" && req.set ? ` (${req.binding})` : "";
85
- installOut(` ${req.key}: ${req.set ? "set" : "missing"}${bindingHint}`, opts);
86
- }
69
+ installOut(` app config: ${configStatus}`, opts);
87
70
  }
88
71
  }
@@ -1,13 +1,11 @@
1
- import type { CliProgram, InstallAgentIntegration } from "../../core/types.ts";
1
+ import type { CliProgram } from "../../core/types.ts";
2
2
  import type { InstallPaths } from "./paths.ts";
3
3
  import type {
4
4
  CliInstallArtifactKey,
5
5
  DetectedSnapshot,
6
6
  InstallAction,
7
- InstallActionKind,
8
7
  InstalledArtifacts,
9
8
  InstallStatus,
10
- InstallTargetCategory,
11
9
  TargetPlanContext,
12
10
  UninstallAction,
13
11
  } from "./target-types.ts";
@@ -15,18 +13,11 @@ import type {
15
13
  /** Shared lifecycle for one install artifact. */
16
14
  export abstract class InstallTarget {
17
15
  abstract readonly key: CliInstallArtifactKey;
18
- abstract readonly actionKind: InstallActionKind;
19
- abstract readonly category: InstallTargetCategory;
20
-
21
- /** Paired MCP/skill key for agentIntegration dedupe. */
22
- readonly pairedKey?: CliInstallArtifactKey;
23
-
24
- defaultIncludedInAll(integration: InstallAgentIntegration): boolean {
25
- if (this.category === "core") return true;
26
- if (this.category === "mcp") {
27
- return integration === "mcp" || integration === "both";
28
- }
29
- return integration === "skill" || integration === "both";
16
+ abstract readonly actionKind: import("./target-types.ts").InstallActionKind;
17
+ abstract readonly category: import("./target-types.ts").InstallTargetCategory;
18
+
19
+ defaultIncludedInAll(): boolean {
20
+ return false;
30
21
  }
31
22
 
32
23
  abstract isAvailable(root: CliProgram, paths: InstallPaths): boolean;
@@ -1,27 +1,12 @@
1
- import type {
2
- CliConfigureConfig,
3
- CliConfigureTargets,
4
- CliProgram,
5
- InstallAgentIntegration,
6
- InstallTargetSpec,
7
- } from "../../core/types.ts";
1
+ import type { CliConfigureConfig, CliProgram, InstallTargetSpec } from "../../core/types.ts";
8
2
  import { resolveCapabilities } from "../../runtime/capabilities.ts";
9
- import {
10
- AGENT_PAIRS,
11
- INSTALL_ARTIFACT_KEYS,
12
- installTargetForKey,
13
- mcpServerRequiredForArtifact,
14
- } from "./target-registry.ts";
3
+ import { INSTALL_ARTIFACT_KEYS, installTargetForKey, mcpServerRequiredForArtifact } from "./target-registry.ts";
15
4
  import type { CliInstallArtifactKey, InstallPlanMode } from "./target-types.ts";
16
5
 
17
6
  export type { InstallTargetSpec, ResolvedInstallTarget } from "../../core/types.ts";
18
7
 
19
8
  export type { InstallPlanMode, InstallScope } from "./target-types.ts";
20
9
 
21
- export function resolveAgentIntegration(configure?: CliConfigureConfig, mcpEnabled = false): InstallAgentIntegration {
22
- return configure?.agentIntegration ?? (mcpEnabled ? "mcp" : "skill");
23
- }
24
-
25
10
  /** Resolves a boolean or object target spec against category defaults. */
26
11
  export function resolveInstallTargetSpec(
27
12
  spec: InstallTargetSpec | undefined,
@@ -44,56 +29,34 @@ export function resolveInstallTargetSpec(
44
29
 
45
30
  function artifactDefaults(
46
31
  key: CliInstallArtifactKey,
47
- integration: InstallAgentIntegration,
48
- mcpServerEnabled: boolean,
32
+ program?: Pick<CliProgram, "mcpServer" | "skill">,
49
33
  ): { enabled: boolean; includedInAll: boolean } {
34
+ if (key === "skill") {
35
+ const on = program?.skill?.enabled === true;
36
+ return { enabled: on, includedInAll: on };
37
+ }
38
+ if (key === "agentsMcp") {
39
+ const on = program?.mcpServer?.enabled === true;
40
+ return { enabled: on, includedInAll: on };
41
+ }
50
42
  const target = installTargetForKey(key);
51
- const includedInAll = target?.defaultIncludedInAll(integration) ?? false;
52
- if (!mcpServerRequiredForArtifact(key, mcpServerEnabled)) {
43
+ if (!mcpServerRequiredForArtifact(key, program?.mcpServer?.enabled === true)) {
53
44
  return { enabled: false, includedInAll: false };
54
45
  }
55
- return { enabled: true, includedInAll };
56
- }
57
-
58
- function applyAgentPairDedupe(
59
- out: Record<CliInstallArtifactKey, { enabled: boolean; includedInAll: boolean }>,
60
- user: CliConfigureTargets | undefined,
61
- integration: InstallAgentIntegration,
62
- ): void {
63
- if (integration === "both") return;
64
-
65
- for (const [mcpKey, skillKey] of AGENT_PAIRS) {
66
- const mcp = out[mcpKey];
67
- const skill = out[skillKey];
68
- if (!mcp.includedInAll || !skill.includedInAll) continue;
69
-
70
- const mcpKeyExplicit = user?.[mcpKey] !== undefined;
71
- const skillKeyExplicit = user?.[skillKey] !== undefined;
72
-
73
- if (mcpKeyExplicit && !skillKeyExplicit) continue;
74
- if (skillKeyExplicit && !mcpKeyExplicit) continue;
75
-
76
- if (integration === "mcp") {
77
- out[skillKey] = { ...skill, includedInAll: false };
78
- } else {
79
- out[mcpKey] = { ...mcp, includedInAll: false };
80
- }
81
- }
46
+ return { enabled: true, includedInAll: target?.defaultIncludedInAll() ?? false };
82
47
  }
83
48
 
84
49
  /** Effective per-artifact gates for install.targets. */
85
50
  export function resolveEffectiveInstallTargets(
86
51
  configure?: CliConfigureConfig,
87
- program?: Pick<CliProgram, "mcpServer">,
52
+ program?: Pick<CliProgram, "mcpServer" | "skill">,
88
53
  ): Record<CliInstallArtifactKey, { enabled: boolean; includedInAll: boolean }> {
89
- const mcpEnabled = program?.mcpServer?.enabled === true;
90
- const integration = resolveAgentIntegration(configure, mcpEnabled);
91
54
  const user = configure?.targets;
92
55
  const out = {} as Record<CliInstallArtifactKey, { enabled: boolean; includedInAll: boolean }>;
93
56
  for (const key of INSTALL_ARTIFACT_KEYS) {
94
- out[key] = resolveInstallTargetSpec(user?.[key], artifactDefaults(key, integration, mcpEnabled));
57
+ const userSpec = key === "skill" || key === "agentsMcp" ? undefined : user?.[key];
58
+ out[key] = resolveInstallTargetSpec(userSpec, artifactDefaults(key, program));
95
59
  }
96
- applyAgentPairDedupe(out, user, integration);
97
60
  return out;
98
61
  }
99
62
 
@@ -103,7 +66,6 @@ export function mcpCategoryEnabled(root: CliProgram): boolean {
103
66
  }
104
67
 
105
68
  export interface InstallTargetPreview {
106
- agentIntegration: InstallAgentIntegration;
107
69
  all: CliInstallArtifactKey[];
108
70
  mcp: CliInstallArtifactKey[];
109
71
  skill: CliInstallArtifactKey[];
@@ -23,10 +23,9 @@ export interface McpJsonHostSpec {
23
23
  key: CliInstallArtifactKey;
24
24
  actionKind: InstallActionKind;
25
25
  label: string;
26
- pairedSkillKey?: CliInstallArtifactKey;
27
26
  configPath: (paths: InstallPaths) => string;
28
- detectedKey: keyof Pick<InstalledArtifacts, "cursorMcp" | "claudeMcp" | "claudeDesktopMcp" | "chatGptMcp">;
29
- statusField: keyof Pick<InstallStatus, "cursorMcp" | "claudeMcp" | "claudeDesktopMcp" | "chatGptMcp">;
27
+ detectedKey: keyof Pick<InstalledArtifacts, "agentsMcp">;
28
+ statusField: keyof Pick<InstallStatus, "agentsMcp">;
30
29
  isAvailable: (root: CliProgram, paths: InstallPaths) => boolean;
31
30
  /** Append server name to status line (default false). */
32
31
  statusIncludesServer?: boolean;
@@ -41,7 +40,6 @@ export class McpJsonInstallTarget extends InstallTarget {
41
40
  readonly key: CliInstallArtifactKey;
42
41
  readonly actionKind: InstallActionKind;
43
42
  readonly category = "mcp" as const;
44
- readonly pairedKey?: CliInstallArtifactKey;
45
43
 
46
44
  private readonly spec: McpJsonHostSpec;
47
45
 
@@ -50,7 +48,6 @@ export class McpJsonInstallTarget extends InstallTarget {
50
48
  this.spec = spec;
51
49
  this.key = spec.key;
52
50
  this.actionKind = spec.actionKind;
53
- this.pairedKey = spec.pairedSkillKey;
54
51
  }
55
52
 
56
53
  isAvailable(root: CliProgram, paths: InstallPaths): boolean {
@@ -18,13 +18,6 @@ export const ACTION_KIND_TO_ARTIFACT: Record<InstallActionKind, CliInstallArtifa
18
18
  INSTALL_TARGETS.map((t) => [t.actionKind, t.key]),
19
19
  ) as Record<InstallActionKind, CliInstallArtifactKey>;
20
20
 
21
- /** Hosts with both MCP config and shell skill install targets. */
22
- export const AGENT_PAIRS: [CliInstallArtifactKey, CliInstallArtifactKey][] = INSTALL_TARGETS.flatMap((t) =>
23
- t.category === "mcp" && t.pairedKey !== undefined
24
- ? [[t.key, t.pairedKey] as [CliInstallArtifactKey, CliInstallArtifactKey]]
25
- : [],
26
- );
27
-
28
21
  const targetByKey = new Map(INSTALL_TARGETS.map((t) => [t.key, t]));
29
22
 
30
23
  /** Lookup a registered install target by artifact key. */
@@ -6,7 +6,6 @@ import {
6
6
  type InstallPlanMode,
7
7
  type InstallScope,
8
8
  type InstallTargetPreview,
9
- resolveAgentIntegration,
10
9
  resolveEffectiveInstallTargets,
11
10
  resolveInstallPlanMode,
12
11
  } from "./target-effective.ts";
@@ -30,18 +29,8 @@ export { mcpCategoryEnabled } from "./target-effective.ts";
30
29
  function emptyInstalledArtifacts(): InstalledArtifacts {
31
30
  return {
32
31
  app: false,
33
- cursorSkill: false,
34
- claudeSkill: false,
35
- codexSkill: false,
36
- opencodeSkill: false,
37
- openclawSkill: false,
38
- cursorMcp: false,
39
- claudeMcp: false,
40
- claudeDesktopMcp: false,
41
- opencodeMcp: false,
42
- codexMcp: false,
43
- openclawMcp: false,
44
- chatGptMcp: false,
32
+ skill: false,
33
+ agentsMcp: false,
45
34
  };
46
35
  }
47
36
 
@@ -75,6 +64,7 @@ export function buildDetectedSnapshot(root: CliProgram, paths: InstallPaths): De
75
64
  }
76
65
 
77
66
  function targetExplicitlyConfigured(user: CliConfigureTargets | undefined, key: CliInstallArtifactKey): boolean {
67
+ if (key === "skill" || key === "agentsMcp") return false;
78
68
  return user?.[key] !== undefined;
79
69
  }
80
70
 
@@ -101,6 +91,8 @@ export function isArtifactInScope(
101
91
  root: CliProgram,
102
92
  targets?: CliConfigureTargets,
103
93
  ): boolean {
94
+ const userTargets = targets ?? root.configure?.targets;
95
+
104
96
  if (mode === "uninstall-all") {
105
97
  return true;
106
98
  }
@@ -119,10 +111,10 @@ export function isArtifactInScope(
119
111
 
120
112
  let inScope = false;
121
113
  if (scope.skill) {
122
- inScope = inScope || agentCategoryInScope(key, SKILL_KEYS, "skill", effective, targets, root);
114
+ inScope = inScope || agentCategoryInScope(key, SKILL_KEYS, "skill", effective, userTargets, root);
123
115
  }
124
116
  if (scope.mcp) {
125
- inScope = inScope || agentCategoryInScope(key, MCP_KEYS, "mcp", effective, targets, root);
117
+ inScope = inScope || agentCategoryInScope(key, MCP_KEYS, "mcp", effective, userTargets, root);
126
118
  }
127
119
  if (scope.configure && key === "configure") {
128
120
  inScope = true;
@@ -225,13 +217,11 @@ export function detectedForArtifact(key: CliInstallArtifactKey, detected: Detect
225
217
  /** Resolved artifact keys for `--all`, `--mcp`, and `--skill` (availability-gated). */
226
218
  export function resolveInstallTargetPreview(program: CliProgram, paths: InstallPaths): InstallTargetPreview {
227
219
  const effective = resolveEffectiveInstallTargets(program.configure, program);
228
- const integration = resolveAgentIntegration(program.configure, program.mcpServer?.enabled === true);
229
220
 
230
221
  const keysForScope = (scope: InstallScope, mode: InstallPlanMode): CliInstallArtifactKey[] =>
231
222
  INSTALL_ARTIFACT_KEYS.filter((key) => shouldIncludeArtifact(key, program, paths, scope, mode, effective));
232
223
 
233
224
  return {
234
- agentIntegration: integration,
235
225
  all: keysForScope({ all: true }, "install-all"),
236
226
  mcp: keysForScope({ mcp: true }, "install-scoped"),
237
227
  skill: keysForScope({ skill: true }, "install-scoped"),