skillops-cli 1.3.3 → 1.3.5

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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # SkillOps (`npx skillops-cli@latest`)
2
2
 
3
- > **The Zero-API Framework & Benchmark-Verified Library for High-End AI Coding Skills & Context-Isolated Subagents (`v1.3.3`)**
3
+ > **The Zero-API Framework & Benchmark-Verified Library for High-End AI Coding Skills & Context-Isolated Subagents (`v1.3.4`)**
4
4
  > *Explore, search, inspect, and install benchmark-verified AI Coding Skills and Context-Isolated Subagents in 10 seconds (`npx skillops-cli@latest quickstart`), scaffold and certify your own custom Skills & Subagents (`npx skillops-cli@latest create skill <name>`), enforce Startup Token Tax budgets (`npx skillops-cli@latest budget`), and transpile natively across the Top 20 AI coding agents & IDEs — 100% offline with zero API keys.*
5
5
 
6
6
  [![CI Governance](https://img.shields.io/badge/governance-1%20Issue%20%3D%201%20PR-blue.svg)](./AGENTS.md)
package/dist/cli.js CHANGED
@@ -10,7 +10,7 @@ import YAML6 from "yaml";
10
10
  import fs from "fs";
11
11
  import YAML from "yaml";
12
12
  import { z } from "zod";
13
- var SKILLOPS_VERSION = "1.3.3";
13
+ var SKILLOPS_VERSION = "1.3.5";
14
14
  var SKILL_NAME_REGEX = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
15
15
  var SEMVER_REGEX = /^\d+\.\d+\.\d+$/;
16
16
  var FRONTMATTER_REGEX = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/;
@@ -87,7 +87,15 @@ var RuntimeTargetSchema = z.enum([
87
87
  "kiro",
88
88
  "trae",
89
89
  "amazonq",
90
- "junie"
90
+ "junie",
91
+ "gemini",
92
+ "factory",
93
+ "qwen",
94
+ "augment",
95
+ "vibe",
96
+ "warp",
97
+ "crush",
98
+ "idx"
91
99
  ]);
92
100
  var CORE_RUNTIME_TARGETS = [
93
101
  "claude",
@@ -116,7 +124,15 @@ var ALL_RUNTIME_TARGETS = [
116
124
  "kiro",
117
125
  "trae",
118
126
  "amazonq",
119
- "junie"
127
+ "junie",
128
+ "gemini",
129
+ "factory",
130
+ "qwen",
131
+ "augment",
132
+ "vibe",
133
+ "warp",
134
+ "crush",
135
+ "idx"
120
136
  ];
121
137
  var RUNTIME_HARNESS_SPECS = {
122
138
  claude: {
@@ -134,15 +150,15 @@ var RUNTIME_HARNESS_SPECS = {
134
150
  },
135
151
  antigravity: {
136
152
  id: "antigravity",
137
- displayName: "Google Antigravity / Gemini CLI",
153
+ displayName: "Google Antigravity IDE",
138
154
  vendor: "Google DeepMind",
139
155
  category: "ai-ide",
140
156
  skillFormat: "progressive-skill-dir",
141
157
  skillsDir: ".agent/skills",
142
158
  subagentsDir: ".agent/agents",
143
- detectionPaths: [".agent", ".gemini", "GEMINI.md"],
144
- alwaysOnFiles: ["GEMINI.md", ".gemini/GEMINI.md"],
145
- mcpConfigPath: ".gemini/settings.json",
159
+ detectionPaths: [".agent", "GEMINI.md"],
160
+ alwaysOnFiles: ["GEMINI.md"],
161
+ mcpConfigPath: ".agent/mcp_config.json",
146
162
  supportsMcp: true
147
163
  },
148
164
  cursor: {
@@ -387,6 +403,110 @@ var RUNTIME_HARNESS_SPECS = {
387
403
  alwaysOnFiles: [".junie/guidelines.md"],
388
404
  mcpConfigPath: null,
389
405
  supportsMcp: false
406
+ },
407
+ gemini: {
408
+ id: "gemini",
409
+ displayName: "Google Gemini CLI",
410
+ vendor: "Google",
411
+ category: "cli-agent",
412
+ skillFormat: "progressive-skill-dir",
413
+ skillsDir: ".gemini/skills",
414
+ subagentsDir: ".gemini/agents",
415
+ detectionPaths: [".gemini", "GEMINI.md"],
416
+ alwaysOnFiles: ["GEMINI.md", ".gemini/GEMINI.md", ".gemini/STYLEGUIDE.md"],
417
+ mcpConfigPath: ".gemini/settings.json",
418
+ supportsMcp: true
419
+ },
420
+ factory: {
421
+ id: "factory",
422
+ displayName: "Factory AI Droid",
423
+ vendor: "Factory AI",
424
+ category: "cli-agent",
425
+ skillFormat: "progressive-skill-dir",
426
+ skillsDir: ".factory/skills",
427
+ subagentsDir: ".factory/droids",
428
+ detectionPaths: [".factory"],
429
+ alwaysOnFiles: ["AGENTS.md", ".factory/instructions.md"],
430
+ mcpConfigPath: ".factory/mcp.json",
431
+ supportsMcp: true
432
+ },
433
+ qwen: {
434
+ id: "qwen",
435
+ displayName: "Qwen Code CLI",
436
+ vendor: "Alibaba Cloud / Qwen",
437
+ category: "cli-agent",
438
+ skillFormat: "progressive-skill-dir",
439
+ skillsDir: ".qwen/skills",
440
+ subagentsDir: ".qwen/agents",
441
+ detectionPaths: [".qwen", "QWEN.md"],
442
+ alwaysOnFiles: ["QWEN.md", "AGENTS.md"],
443
+ mcpConfigPath: ".qwen/settings.json",
444
+ supportsMcp: true
445
+ },
446
+ augment: {
447
+ id: "augment",
448
+ displayName: "Augment Code",
449
+ vendor: "Augment",
450
+ category: "ide-extension",
451
+ skillFormat: "markdown-rule",
452
+ skillsDir: ".augment/rules",
453
+ subagentsDir: ".augment/agents",
454
+ detectionPaths: [".augment", ".augment-guidelines"],
455
+ alwaysOnFiles: [".augment-guidelines", "AGENTS.md"],
456
+ mcpConfigPath: ".augment/settings.json",
457
+ supportsMcp: true
458
+ },
459
+ vibe: {
460
+ id: "vibe",
461
+ displayName: "Mistral Vibe CLI",
462
+ vendor: "Mistral AI",
463
+ category: "cli-agent",
464
+ skillFormat: "progressive-skill-dir",
465
+ skillsDir: ".vibe/skills",
466
+ subagentsDir: ".vibe/agents",
467
+ detectionPaths: [".vibe"],
468
+ alwaysOnFiles: [".vibe/config.toml", "AGENTS.md"],
469
+ mcpConfigPath: ".vibe/config.toml",
470
+ supportsMcp: true
471
+ },
472
+ warp: {
473
+ id: "warp",
474
+ displayName: "Warp AI Terminal",
475
+ vendor: "Warp",
476
+ category: "cli-agent",
477
+ skillFormat: "progressive-skill-dir",
478
+ skillsDir: ".warp/skills",
479
+ subagentsDir: ".warp/agents",
480
+ detectionPaths: [".warp", "WARP.md"],
481
+ alwaysOnFiles: ["WARP.md", "AGENTS.md"],
482
+ mcpConfigPath: null,
483
+ supportsMcp: false
484
+ },
485
+ crush: {
486
+ id: "crush",
487
+ displayName: "Charmbracelet Crush",
488
+ vendor: "Charmbracelet",
489
+ category: "cli-agent",
490
+ skillFormat: "progressive-skill-dir",
491
+ skillsDir: ".crush/skills",
492
+ subagentsDir: ".crush/agents",
493
+ detectionPaths: [".crush", ".crush.json", "CRUSH.md"],
494
+ alwaysOnFiles: ["CRUSH.md", "AGENTS.md"],
495
+ mcpConfigPath: ".crush.json",
496
+ supportsMcp: true
497
+ },
498
+ idx: {
499
+ id: "idx",
500
+ displayName: "Firebase Studio (Project IDX)",
501
+ vendor: "Google",
502
+ category: "ai-ide",
503
+ skillFormat: "markdown-rule",
504
+ skillsDir: ".idx/rules",
505
+ subagentsDir: ".idx/agents",
506
+ detectionPaths: [".idx"],
507
+ alwaysOnFiles: [".idx/airules.md", "GEMINI.md"],
508
+ mcpConfigPath: ".idx/mcp.json",
509
+ supportsMcp: true
390
510
  }
391
511
  };
392
512
  function detectInstalledHarnesses(repoPath) {
@@ -418,7 +538,7 @@ function resolveRuntimeTargets(rawSpec, repoPath, fallbackTargets = CORE_RUNTIME
418
538
  }
419
539
  };
420
540
  for (const token of tokens) {
421
- if (token === "all" || token === "*" || token === "top20") {
541
+ if (token === "all" || token === "*" || token === "top20" || token === "top28") {
422
542
  for (const t of ALL_RUNTIME_TARGETS) {
423
543
  pushUnique(t);
424
544
  }
@@ -1613,6 +1733,10 @@ var ALWAYS_ON_INSTRUCTION_CANDIDATES = [
1613
1733
  "AGENTS.md",
1614
1734
  "GEMINI.md",
1615
1735
  ".gemini/GEMINI.md",
1736
+ ".gemini/STYLEGUIDE.md",
1737
+ "QWEN.md",
1738
+ "WARP.md",
1739
+ "CRUSH.md",
1616
1740
  "AGENT.md",
1617
1741
  "CONVENTIONS.md",
1618
1742
  ".github/copilot-instructions.md",
@@ -1622,17 +1746,22 @@ var ALWAYS_ON_INSTRUCTION_CANDIDATES = [
1622
1746
  ".roorules",
1623
1747
  ".kilocoderules",
1624
1748
  ".continuerules",
1749
+ ".augment-guidelines",
1625
1750
  ".rules",
1626
1751
  ".goosehints",
1627
1752
  ".junie/guidelines.md",
1628
1753
  ".openhands/microagents/repo.md",
1629
1754
  ".trae/rules/project_rules.md",
1755
+ ".factory/instructions.md",
1756
+ ".idx/airules.md",
1757
+ ".vibe/config.toml",
1630
1758
  ".kiro/steering/product.md",
1631
1759
  ".kiro/steering/tech.md",
1632
1760
  ".kiro/steering/structure.md"
1633
1761
  ];
1634
1762
  var MCP_CONFIG_CANDIDATES = [
1635
1763
  ".mcp.json",
1764
+ ".agent/mcp_config.json",
1636
1765
  ".cursor/mcp.json",
1637
1766
  ".vscode/mcp.json",
1638
1767
  ".gemini/settings.json",
@@ -1644,7 +1773,13 @@ var MCP_CONFIG_CANDIDATES = [
1644
1773
  ".amp/settings.json",
1645
1774
  "opencode.json",
1646
1775
  ".kiro/settings/mcp.json",
1647
- ".amazonq/mcp.json"
1776
+ ".amazonq/mcp.json",
1777
+ ".factory/mcp.json",
1778
+ ".qwen/settings.json",
1779
+ ".augment/settings.json",
1780
+ ".vibe/config.toml",
1781
+ ".crush.json",
1782
+ ".idx/mcp.json"
1648
1783
  ];
1649
1784
  function countMcpServersInConfig(rawText, relPath) {
1650
1785
  if (relPath.endsWith(".toml")) {
@@ -2074,9 +2209,9 @@ function diagnoseRepository(repoPath, options = {}) {
2074
2209
  const detectedHarnesses = detectInstalledHarnesses(resolvedRepo);
2075
2210
  checks.push({
2076
2211
  id: "detected_harnesses",
2077
- title: "Detected AI Coding Harnesses (Top 20)",
2212
+ title: "Detected AI Coding Harnesses (Top 28)",
2078
2213
  status: "pass",
2079
- summary: detectedHarnesses.length > 0 ? `Detected ${detectedHarnesses.length} AI coding harness(es): ${detectedHarnesses.join(", ")}.` : "No repo-local AI harness folders detected yet (ready to sync all 20 agents)."
2214
+ summary: detectedHarnesses.length > 0 ? `Detected ${detectedHarnesses.length} AI coding harness(es): ${detectedHarnesses.join(", ")}.` : "No repo-local AI harness folders detected yet (ready to sync all 28 agents)."
2080
2215
  });
2081
2216
  const loadedCfg = loadSkillOpsConfig(resolvedRepo);
2082
2217
  const configPresent = loadedCfg.exists;
@@ -2259,7 +2394,7 @@ function formatHarnessInspectionReport(report, format = "table") {
2259
2394
  }
2260
2395
  if (format === "markdown") {
2261
2396
  const lines2 = [
2262
- `## SkillOps Universal Top-20 AI Coding Agent Harnesses (\`${report.repo_path}\`)`,
2397
+ `## SkillOps Universal Top-28 AI Coding Agent Harnesses (\`${report.repo_path}\`)`,
2263
2398
  "",
2264
2399
  `- **Supported AI Coding Harnesses:** \`${report.total_supported}\``,
2265
2400
  `- **Detected in Repository:** \`${report.detected_count}\` (${report.detected_harnesses.length > 0 ? report.detected_harnesses.join(", ") : "none"})`,
@@ -2274,7 +2409,7 @@ function formatHarnessInspectionReport(report, format = "table") {
2274
2409
  return lines2.join("\n");
2275
2410
  }
2276
2411
  const lines = [
2277
- `=== SkillOps Universal Top-20 AI Coding Harnesses (${report.repo_path}) ===`,
2412
+ `=== SkillOps Universal Top-28 AI Coding Harnesses (${report.repo_path}) ===`,
2278
2413
  `Supported Harnesses : ${report.total_supported}`,
2279
2414
  `Detected in Repo : ${report.detected_count} (${report.detected_harnesses.length > 0 ? report.detected_harnesses.join(", ") : "none"})`,
2280
2415
  "-".repeat(92),
@@ -3345,7 +3480,7 @@ var MCP_TOOLS = [
3345
3480
  },
3346
3481
  {
3347
3482
  name: "list_supported_harnesses",
3348
- description: "Inspect all 20 supported AI coding agents, IDEs, and CLI harnesses (target paths, MCP support, and repository auto-detection).",
3483
+ description: "Inspect all 28 supported AI coding agents, IDEs, and CLI harnesses (target paths, MCP support, and repository auto-detection).",
3349
3484
  inputSchema: {
3350
3485
  type: "object",
3351
3486
  properties: {
@@ -3517,7 +3652,7 @@ function startMcpStdioServer(context) {
3517
3652
  }
3518
3653
  function buildUpdatedMcpConfigContent(target, existingRaw, serverName) {
3519
3654
  const mcpArgs = ["-y", "skillops-cli@latest", "mcp"];
3520
- if (target === "codex") {
3655
+ if (target === "codex" || target === "vibe") {
3521
3656
  const tomlBlock = [
3522
3657
  `[mcp_servers.${serverName}]`,
3523
3658
  'command = "npx"',
@@ -3610,6 +3745,20 @@ ${tomlBlock}
3610
3745
  };
3611
3746
  parsedObj.mcp = mcpMap;
3612
3747
  return `${JSON.stringify(parsedObj, null, 2)}
3748
+ `;
3749
+ }
3750
+ if (target === "crush") {
3751
+ if (!("$schema" in parsedObj)) {
3752
+ parsedObj.$schema = "https://charm.land/crush.json";
3753
+ }
3754
+ const mcpMap = parsedObj.mcp && typeof parsedObj.mcp === "object" && !Array.isArray(parsedObj.mcp) ? { ...parsedObj.mcp } : {};
3755
+ mcpMap[serverName] = {
3756
+ type: "stdio",
3757
+ command: "npx",
3758
+ args: mcpArgs
3759
+ };
3760
+ parsedObj.mcp = mcpMap;
3761
+ return `${JSON.stringify(parsedObj, null, 2)}
3613
3762
  `;
3614
3763
  }
3615
3764
  const mcpServers = parsedObj.mcpServers && typeof parsedObj.mcpServers === "object" && !Array.isArray(parsedObj.mcpServers) ? { ...parsedObj.mcpServers } : {};
@@ -3860,6 +4009,24 @@ ${body}
3860
4009
  ${kiroFrontmatter}
3861
4010
  ---
3862
4011
 
4012
+ ${body}
4013
+ `
4014
+ });
4015
+ return files;
4016
+ }
4017
+ if (target === "augment") {
4018
+ const augFrontmatter = YAML4.stringify({
4019
+ type: "agent_requested",
4020
+ description: parsed.manifest.description
4021
+ }).trim();
4022
+ files.push({
4023
+ target,
4024
+ itemName: name,
4025
+ relativePath: `.augment/rules/skillops-${name}.md`,
4026
+ content: `---
4027
+ ${augFrontmatter}
4028
+ ---
4029
+
3863
4030
  ${body}
3864
4031
  `
3865
4032
  });
@@ -3893,6 +4060,7 @@ function renderSubagentFilesForRuntime(subagent, target) {
3893
4060
  const fm = YAML4.stringify({
3894
4061
  name: subagent.manifest.name,
3895
4062
  description: subagent.manifest.description,
4063
+ model: "inherit",
3896
4064
  model_tier: subagent.manifest.model_tier,
3897
4065
  tools: subagent.manifest.tools,
3898
4066
  max_output_tokens: subagent.manifest.max_output_tokens
@@ -3903,7 +4071,7 @@ ${fm}
3903
4071
 
3904
4072
  ${promptBody}
3905
4073
  `;
3906
- if (target === "claude" || target === "antigravity" || target === "codex" || target === "amp" || target === "goose" || target === "opencode") {
4074
+ if (target === "claude" || target === "antigravity" || target === "gemini" || target === "codex" || target === "amp" || target === "goose" || target === "opencode" || target === "qwen" || target === "warp" || target === "crush") {
3907
4075
  return [
3908
4076
  {
3909
4077
  target,
@@ -3913,6 +4081,44 @@ ${promptBody}
3913
4081
  }
3914
4082
  ];
3915
4083
  }
4084
+ if (target === "factory") {
4085
+ const droidFm = YAML4.stringify({
4086
+ name,
4087
+ description: subagent.manifest.description,
4088
+ model: "inherit"
4089
+ }).trim();
4090
+ return [
4091
+ {
4092
+ target,
4093
+ itemName: name,
4094
+ relativePath: `.factory/droids/skillops-agent-${name}.md`,
4095
+ content: `---
4096
+ ${droidFm}
4097
+ ---
4098
+
4099
+ ${promptBody}
4100
+ `
4101
+ }
4102
+ ];
4103
+ }
4104
+ if (target === "vibe") {
4105
+ const escapedDesc = JSON.stringify(subagent.manifest.description);
4106
+ const escapedPrompt = JSON.stringify(promptBody);
4107
+ const tomlDoc = [
4108
+ `name = "${name}"`,
4109
+ `description = ${escapedDesc}`,
4110
+ `system_prompt = ${escapedPrompt}`,
4111
+ ""
4112
+ ].join("\n");
4113
+ return [
4114
+ {
4115
+ target,
4116
+ itemName: name,
4117
+ relativePath: `.vibe/agents/skillops-agent-${name}.toml`,
4118
+ content: tomlDoc
4119
+ }
4120
+ ];
4121
+ }
3916
4122
  if (target === "copilot") {
3917
4123
  return [
3918
4124
  {
@@ -4913,7 +5119,7 @@ var COMMAND_HELP_SPECS = [
4913
5119
  name: "sync",
4914
5120
  aliases: [],
4915
5121
  pillar: "runtime",
4916
- summary: "Transpile bundle across 20 AI agents",
5122
+ summary: "Transpile bundle across 28 AI agents",
4917
5123
  usage: "npx skillops-cli sync [options]",
4918
5124
  options: [
4919
5125
  {
@@ -4944,7 +5150,7 @@ var COMMAND_HELP_SPECS = [
4944
5150
  },
4945
5151
  {
4946
5152
  command: "npx skillops-cli sync --harness all",
4947
- description: "Transpile across all 20 supported AI agents"
5153
+ description: "Transpile across all 28 supported AI agents"
4948
5154
  }
4949
5155
  ]
4950
5156
  },
@@ -5037,7 +5243,7 @@ var COMMAND_HELP_SPECS = [
5037
5243
  name: "harnesses",
5038
5244
  aliases: [],
5039
5245
  pillar: "runtime",
5040
- summary: "List 20 supported AI agents & repo status",
5246
+ summary: "List 28 supported AI agents & repo status",
5041
5247
  usage: "npx skillops-cli harnesses [options]",
5042
5248
  options: [
5043
5249
  {
@@ -5052,7 +5258,7 @@ var COMMAND_HELP_SPECS = [
5052
5258
  examples: [
5053
5259
  {
5054
5260
  command: "npx skillops-cli harnesses",
5055
- description: "View all 20 supported AI IDEs/CLI agents"
5261
+ description: "View all 28 supported AI IDEs/CLI agents"
5056
5262
  }
5057
5263
  ]
5058
5264
  },
@@ -5060,7 +5266,7 @@ var COMMAND_HELP_SPECS = [
5060
5266
  name: "mcp-setup",
5061
5267
  aliases: [],
5062
5268
  pillar: "runtime",
5063
- summary: "Wire MCP server across 13 AI IDEs & agents",
5269
+ summary: "Wire MCP server across 20 AI IDEs & agents",
5064
5270
  usage: "npx skillops-cli mcp-setup [options]",
5065
5271
  options: [
5066
5272
  {
@@ -5189,6 +5395,9 @@ function renderCommandHelp(spec, version, colorEnabled) {
5189
5395
 
5190
5396
  // src/ui/prompt.ts
5191
5397
  function optionMatchesTab(opt, tab) {
5398
+ if (opt.tab) {
5399
+ return opt.tab === tab;
5400
+ }
5192
5401
  if (opt.kind === "agent") {
5193
5402
  return tab === "agent";
5194
5403
  }
@@ -5224,7 +5433,7 @@ function createMultiSelectState(options) {
5224
5433
  status: "active"
5225
5434
  };
5226
5435
  }
5227
- function applyMultiSelectKey(state, key) {
5436
+ function applyMultiSelectKey(state, key, options = {}) {
5228
5437
  if (state.status !== "active" || state.options.length === 0) {
5229
5438
  return state;
5230
5439
  }
@@ -5249,14 +5458,29 @@ function applyMultiSelectKey(state, key) {
5249
5458
  };
5250
5459
  }
5251
5460
  if (key === "\r" || key === "\n") {
5461
+ let finalSelected = state.selected;
5462
+ if (options.selectCursorOnEmptySubmit && state.selected.size === 0) {
5463
+ const visibleOnSubmit = getFilteredTabOptions(state, state.activeTab);
5464
+ const currentOpt = visibleOnSubmit[state.cursor];
5465
+ if (currentOpt) {
5466
+ finalSelected = /* @__PURE__ */ new Set([currentOpt.value]);
5467
+ }
5468
+ }
5252
5469
  return {
5253
5470
  ...state,
5471
+ selected: finalSelected,
5254
5472
  searchMode: false,
5255
5473
  status: "submitted"
5256
5474
  };
5257
5475
  }
5258
5476
  if (key === "\x1B[D" || key === "\x1BOD" || key === "\x1B[C" || key === "\x1BOC" || key === " " || !state.searchMode && (key === "h" || key === "H" || key === "l" || key === "L")) {
5259
5477
  const nextTab = state.activeTab === "skill" ? "agent" : "skill";
5478
+ const hasNextTabOptions = state.options.some(
5479
+ (opt) => optionMatchesTab(opt, nextTab)
5480
+ );
5481
+ if (!hasNextTabOptions) {
5482
+ return state;
5483
+ }
5260
5484
  return {
5261
5485
  ...state,
5262
5486
  activeTab: nextTab,
@@ -5390,8 +5614,13 @@ function renderMultiSelectFrame(title, state, options = {}) {
5390
5614
  const filteredAgents = getFilteredTabOptions(state, "agent");
5391
5615
  const visible = state.activeTab === "skill" ? filteredSkills : filteredAgents;
5392
5616
  const total = visible.length;
5617
+ const skillTabTitle = options.tabLabels?.skill ?? "Skills";
5618
+ const agentTabTitle = options.tabLabels?.agent ?? "Agents";
5619
+ const submitVerb = options.submitActionLabel ?? "import";
5620
+ const isSinglePage = totalAgents === 0;
5393
5621
  const header = `${style.bold(style.cyan(`\u25C6 ${title}`, c), c)}`;
5394
- const instructions = state.searchMode ? `${bar} ${style.dim("\u2191/\u2193 move \xB7 \u2190/\u2192 switch tab \xB7 Space select \xB7 Esc clear search \xB7 Enter import", c)}` : `${bar} ${style.dim("\u2191/\u2193 move \xB7 \u2190/\u2192 switch tab \xB7 Space select \xB7 / search \xB7 a all \xB7 Enter import", c)}`;
5622
+ const tabHint = isSinglePage ? "" : " \xB7 \u2190/\u2192 tab";
5623
+ const instructions = state.searchMode ? `${bar} ${style.dim(`\u2191/\u2193 move${tabHint} \xB7 Space select \xB7 Esc clear \xB7 Enter ${submitVerb}`, c)}` : `${bar} ${style.dim(`\u2191/\u2193 move${tabHint} \xB7 Space select \xB7 / search \xB7 a all \xB7 Enter ${submitVerb}`, c)}`;
5395
5624
  const lines = [header];
5396
5625
  if (options.subtitle) {
5397
5626
  lines.push(`${bar} ${options.subtitle}`);
@@ -5400,16 +5629,22 @@ function renderMultiSelectFrame(title, state, options = {}) {
5400
5629
  const hasQuery = state.searchQuery.length > 0;
5401
5630
  const skillCountStr = hasQuery ? `${filteredSkills.length}/${totalSkills}` : `${totalSkills}`;
5402
5631
  const agentCountStr = hasQuery ? `${filteredAgents.length}/${totalAgents}` : `${totalAgents}`;
5403
- const skillTabLabel = state.activeTab === "skill" ? style.bold(style.cyan(`[ \u25C6 Skills (${skillCountStr}) ]`, c), c) : style.dim(`\u25CB Skills (${skillCountStr})`, c);
5404
- const agentTabLabel = state.activeTab === "agent" ? style.bold(style.magenta(`[ \u25C6 Agents (${agentCountStr}) ]`, c), c) : style.dim(`\u25CB Agents (${agentCountStr})`, c);
5632
+ const skillTabLabel = state.activeTab === "skill" ? style.bold(
5633
+ style.cyan(`[ \u25C6 ${skillTabTitle} (${skillCountStr}) ]`, c),
5634
+ c
5635
+ ) : style.dim(`\u25CB ${skillTabTitle} (${skillCountStr})`, c);
5636
+ const agentTabLabel = state.activeTab === "agent" ? style.bold(
5637
+ style.magenta(`[ \u25C6 ${agentTabTitle} (${agentCountStr}) ]`, c),
5638
+ c
5639
+ ) : style.dim(`\u25CB ${agentTabTitle} (${agentCountStr})`, c);
5405
5640
  const rawQuerySlice = state.searchQuery.length > 14 ? `\u2026${state.searchQuery.slice(-13)}` : state.searchQuery;
5406
5641
  const searchSuffix = state.searchMode || hasQuery ? ` ${style.yellow(`/ Filter: ${rawQuerySlice}${state.searchMode ? "\u2588" : ""}`, c)}` : ` ${style.dim("(/ search)", c)}`;
5407
5642
  lines.push(
5408
- `${style.dim("\u251C\u2500", c)} ${skillTabLabel} ${agentTabLabel}${searchSuffix}`,
5643
+ isSinglePage ? `${style.dim("\u251C\u2500", c)} ${skillTabLabel}${searchSuffix}` : `${style.dim("\u251C\u2500", c)} ${skillTabLabel} ${agentTabLabel}${searchSuffix}`,
5409
5644
  bar
5410
5645
  );
5411
5646
  if (total === 0) {
5412
- const tabName = state.activeTab === "skill" ? "Skills" : "Agents";
5647
+ const tabName = state.activeTab === "skill" ? skillTabTitle : agentTabTitle;
5413
5648
  lines.push(
5414
5649
  `${bar} ${style.dim(`No ${tabName} match "${state.searchQuery}". Press Esc to clear filter.`, c)}`
5415
5650
  );
@@ -5457,12 +5692,13 @@ function renderMultiSelectFrame(title, state, options = {}) {
5457
5692
  const selectedAgentsCount = state.options.filter(
5458
5693
  (o) => optionMatchesTab(o, "agent") && state.selected.has(o.value)
5459
5694
  ).length;
5460
- const summaryText = `${state.selected.size} selected (${selectedSkillsCount} Skills, ${selectedAgentsCount} Agents)`;
5695
+ const currentOptLabel = visible[state.cursor]?.label ?? "";
5696
+ const summaryText = options.tabLabels ? state.selected.size > 0 ? `${state.selected.size} selected` : options.selectCursorOnEmptySubmit && currentOptLabel ? `Target: ${currentOptLabel}` : "0 selected" : `${state.selected.size} selected (${selectedSkillsCount} ${skillTabTitle}, ${selectedAgentsCount} ${agentTabTitle})`;
5461
5697
  const rawTarget = options.targetHint ?? "";
5462
5698
  const shortTarget = rawTarget.length > 18 ? `${rawTarget.slice(0, 17)}\u2026` : rawTarget;
5463
5699
  const targetSuffix = shortTarget ? ` \xB7 IDE: ${shortTarget}` : "";
5464
5700
  lines.push(
5465
- `${style.dim("\u2570\u2500", c)} ${style.bold(summaryText, c)}${style.dim(`${targetSuffix} \u2014 Enter to import`, c)}`
5701
+ `${style.dim("\u2570\u2500", c)} ${style.bold(summaryText, c)}${style.dim(`${targetSuffix} \u2014 Enter to ${submitVerb}`, c)}`
5466
5702
  );
5467
5703
  return lines.join("\n");
5468
5704
  }
@@ -5499,7 +5735,9 @@ async function runMultiSelectPrompt(title, options, renderOpts = {}) {
5499
5735
  };
5500
5736
  const onData = (chunk) => {
5501
5737
  const key = typeof chunk === "string" ? chunk : chunk.toString("utf-8");
5502
- state = applyMultiSelectKey(state, key);
5738
+ state = applyMultiSelectKey(state, key, {
5739
+ selectCursorOnEmptySubmit: renderOpts.selectCursorOnEmptySubmit
5740
+ });
5503
5741
  if (state.status === "cancelled") {
5504
5742
  if (renderedLineCount > 0) {
5505
5743
  process.stdout.write(`\x1B[${renderedLineCount}A\x1B[J`);
@@ -6332,6 +6570,53 @@ function resolveCatalogDir(args, repoPath, loadedConfig) {
6332
6570
  }
6333
6571
  return path10.join(PACKAGE_ROOT, "catalog");
6334
6572
  }
6573
+ var POPULAR_AI_ORDER = [
6574
+ "cursor",
6575
+ "claude",
6576
+ "copilot",
6577
+ "antigravity",
6578
+ "windsurf",
6579
+ "codex",
6580
+ "gemini",
6581
+ "cline",
6582
+ "roocode",
6583
+ "kilocode",
6584
+ "zed",
6585
+ "continue",
6586
+ "aider",
6587
+ "kiro",
6588
+ "trae",
6589
+ "augment",
6590
+ "factory",
6591
+ "qwen",
6592
+ "opencode",
6593
+ "openhands",
6594
+ "amp",
6595
+ "goose",
6596
+ "warp",
6597
+ "crush",
6598
+ "vibe",
6599
+ "junie",
6600
+ "amazonq",
6601
+ "idx"
6602
+ ];
6603
+ function buildInteractiveTargetOptions() {
6604
+ const popularSet = new Set(POPULAR_AI_ORDER);
6605
+ const orderedTargets = [
6606
+ ...POPULAR_AI_ORDER,
6607
+ ...ALL_RUNTIME_TARGETS.filter((t) => !popularSet.has(t))
6608
+ ];
6609
+ return orderedTargets.map((targetId) => {
6610
+ const spec = RUNTIME_HARNESS_SPECS[targetId];
6611
+ return {
6612
+ value: spec.id,
6613
+ label: spec.id,
6614
+ kind: "ide",
6615
+ tab: "skill",
6616
+ hint: `${spec.displayName} (${spec.skillsDir})`
6617
+ };
6618
+ });
6619
+ }
6335
6620
  function buildInteractiveCatalogOptions(catalogDir) {
6336
6621
  const options = [];
6337
6622
  const seen = /* @__PURE__ */ new Set();
@@ -7055,7 +7340,7 @@ function runCli(argv) {
7055
7340
  async function runCliInteractive(argv) {
7056
7341
  const args = argv.slice(2);
7057
7342
  const command = args[0];
7058
- if (command === "add" && !args.includes("--help") && !args.includes("-h") && !args.includes("--all") && getPositionalArgs(args).length === 0 && process.stdin.isTTY && process.stdout.isTTY) {
7343
+ if (command === "add" && !args.includes("--help") && !args.includes("-h") && process.stdin.isTTY && process.stdout.isTTY) {
7059
7344
  const repoPath = getFlagValue(args, "--repo", "--project", "-p") ?? process.cwd();
7060
7345
  const cfgRes = loadSkillOpsConfig(repoPath);
7061
7346
  if (cfgRes.error) {
@@ -7066,29 +7351,57 @@ async function runCliInteractive(argv) {
7066
7351
  return ExitCode.BUDGET_OR_CONTRACT_ERROR;
7067
7352
  }
7068
7353
  const catalogDir = resolveCatalogDir(args, repoPath, cfgRes.config);
7069
- const targetsArg = resolveTargetsArg(args);
7354
+ const explicitTargetsArg = resolveTargetsArg(args);
7355
+ const positionals = getPositionalArgs(args);
7356
+ const needsCatalogPrompt = !args.includes("--all") && positionals.length === 0;
7357
+ let resolvedTargetsArg = explicitTargetsArg;
7358
+ if (!resolvedTargetsArg) {
7359
+ const targetOptions = buildInteractiveTargetOptions();
7360
+ const step1Title = needsCatalogPrompt ? "SkillOps \u2014 Step 1/2: Select Your AI IDE / Agent" : "SkillOps \u2014 Select Your AI IDE / Agent";
7361
+ const selectedTargets = await runMultiSelectPrompt(
7362
+ step1Title,
7363
+ targetOptions,
7364
+ {
7365
+ tabLabels: {
7366
+ skill: "AI IDEs & Agents",
7367
+ agent: ""
7368
+ },
7369
+ submitActionLabel: "continue",
7370
+ selectCursorOnEmptySubmit: true
7371
+ }
7372
+ );
7373
+ if (!selectedTargets || selectedTargets.length === 0) {
7374
+ process.stdout.write("No AI IDE selected.\n");
7375
+ return ExitCode.SUCCESS;
7376
+ }
7377
+ resolvedTargetsArg = selectedTargets.join(",");
7378
+ }
7070
7379
  let targets;
7071
7380
  try {
7072
- targets = targetsArg ? resolveRuntimeTargets(targetsArg, repoPath) : cfgRes.config?.targets ?? [...CORE_RUNTIME_TARGETS];
7381
+ targets = resolveRuntimeTargets(resolvedTargetsArg, repoPath);
7073
7382
  } catch (err) {
7074
7383
  const msg = err instanceof Error ? err.message : String(err);
7075
7384
  process.stderr.write(`Invalid --targets: ${msg}
7076
7385
  `);
7077
7386
  return ExitCode.BUDGET_OR_CONTRACT_ERROR;
7078
7387
  }
7079
- const options = buildInteractiveCatalogOptions(catalogDir);
7080
- const selected = await runMultiSelectPrompt(
7081
- "SkillOps \u2014 Select Skills & Agents to Import",
7082
- options,
7083
- {
7388
+ const extraArgs = [];
7389
+ if (!explicitTargetsArg) {
7390
+ extraArgs.push("-t", targets.join(","));
7391
+ }
7392
+ if (needsCatalogPrompt) {
7393
+ const options = buildInteractiveCatalogOptions(catalogDir);
7394
+ const step2Title = !explicitTargetsArg ? "SkillOps \u2014 Step 2/2: Select Skills & Agents to Import" : "SkillOps \u2014 Select Skills & Agents to Import";
7395
+ const selected = await runMultiSelectPrompt(step2Title, options, {
7084
7396
  targetHint: targets.join(", ")
7397
+ });
7398
+ if (!selected || selected.length === 0) {
7399
+ process.stdout.write("No Skills or Agents selected.\n");
7400
+ return ExitCode.SUCCESS;
7085
7401
  }
7086
- );
7087
- if (!selected || selected.length === 0) {
7088
- process.stdout.write("No Skills or Agents selected.\n");
7089
- return ExitCode.SUCCESS;
7402
+ extraArgs.push(...selected);
7090
7403
  }
7091
- return runCli([...argv, ...selected]);
7404
+ return runCli([...argv, ...extraArgs]);
7092
7405
  }
7093
7406
  return runCli(argv);
7094
7407
  }
@@ -7114,6 +7427,7 @@ if (isDirectCliInvocation()) {
7114
7427
  }
7115
7428
  export {
7116
7429
  buildInteractiveCatalogOptions,
7430
+ buildInteractiveTargetOptions,
7117
7431
  runCli,
7118
7432
  runCliInteractive
7119
7433
  };