skillops-cli 1.3.3 → 1.3.4

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.4";
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]*)$/;
@@ -5189,6 +5189,9 @@ function renderCommandHelp(spec, version, colorEnabled) {
5189
5189
 
5190
5190
  // src/ui/prompt.ts
5191
5191
  function optionMatchesTab(opt, tab) {
5192
+ if (opt.tab) {
5193
+ return opt.tab === tab;
5194
+ }
5192
5195
  if (opt.kind === "agent") {
5193
5196
  return tab === "agent";
5194
5197
  }
@@ -5224,7 +5227,7 @@ function createMultiSelectState(options) {
5224
5227
  status: "active"
5225
5228
  };
5226
5229
  }
5227
- function applyMultiSelectKey(state, key) {
5230
+ function applyMultiSelectKey(state, key, options = {}) {
5228
5231
  if (state.status !== "active" || state.options.length === 0) {
5229
5232
  return state;
5230
5233
  }
@@ -5249,8 +5252,17 @@ function applyMultiSelectKey(state, key) {
5249
5252
  };
5250
5253
  }
5251
5254
  if (key === "\r" || key === "\n") {
5255
+ let finalSelected = state.selected;
5256
+ if (options.selectCursorOnEmptySubmit && state.selected.size === 0) {
5257
+ const visibleOnSubmit = getFilteredTabOptions(state, state.activeTab);
5258
+ const currentOpt = visibleOnSubmit[state.cursor];
5259
+ if (currentOpt) {
5260
+ finalSelected = /* @__PURE__ */ new Set([currentOpt.value]);
5261
+ }
5262
+ }
5252
5263
  return {
5253
5264
  ...state,
5265
+ selected: finalSelected,
5254
5266
  searchMode: false,
5255
5267
  status: "submitted"
5256
5268
  };
@@ -5390,8 +5402,11 @@ function renderMultiSelectFrame(title, state, options = {}) {
5390
5402
  const filteredAgents = getFilteredTabOptions(state, "agent");
5391
5403
  const visible = state.activeTab === "skill" ? filteredSkills : filteredAgents;
5392
5404
  const total = visible.length;
5405
+ const skillTabTitle = options.tabLabels?.skill ?? "Skills";
5406
+ const agentTabTitle = options.tabLabels?.agent ?? "Agents";
5407
+ const submitVerb = options.submitActionLabel ?? "import";
5393
5408
  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)}`;
5409
+ const instructions = state.searchMode ? `${bar} ${style.dim(`\u2191/\u2193 move \xB7 \u2190/\u2192 tab \xB7 Space select \xB7 Esc clear \xB7 Enter ${submitVerb}`, c)}` : `${bar} ${style.dim(`\u2191/\u2193 move \xB7 \u2190/\u2192 tab \xB7 Space select \xB7 / search \xB7 a all \xB7 Enter ${submitVerb}`, c)}`;
5395
5410
  const lines = [header];
5396
5411
  if (options.subtitle) {
5397
5412
  lines.push(`${bar} ${options.subtitle}`);
@@ -5400,8 +5415,8 @@ function renderMultiSelectFrame(title, state, options = {}) {
5400
5415
  const hasQuery = state.searchQuery.length > 0;
5401
5416
  const skillCountStr = hasQuery ? `${filteredSkills.length}/${totalSkills}` : `${totalSkills}`;
5402
5417
  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);
5418
+ const skillTabLabel = state.activeTab === "skill" ? style.bold(style.cyan(`[ \u25C6 ${skillTabTitle} (${skillCountStr}) ]`, c), c) : style.dim(`\u25CB ${skillTabTitle} (${skillCountStr})`, c);
5419
+ const agentTabLabel = state.activeTab === "agent" ? style.bold(style.magenta(`[ \u25C6 ${agentTabTitle} (${agentCountStr}) ]`, c), c) : style.dim(`\u25CB ${agentTabTitle} (${agentCountStr})`, c);
5405
5420
  const rawQuerySlice = state.searchQuery.length > 14 ? `\u2026${state.searchQuery.slice(-13)}` : state.searchQuery;
5406
5421
  const searchSuffix = state.searchMode || hasQuery ? ` ${style.yellow(`/ Filter: ${rawQuerySlice}${state.searchMode ? "\u2588" : ""}`, c)}` : ` ${style.dim("(/ search)", c)}`;
5407
5422
  lines.push(
@@ -5409,7 +5424,7 @@ function renderMultiSelectFrame(title, state, options = {}) {
5409
5424
  bar
5410
5425
  );
5411
5426
  if (total === 0) {
5412
- const tabName = state.activeTab === "skill" ? "Skills" : "Agents";
5427
+ const tabName = state.activeTab === "skill" ? skillTabTitle : agentTabTitle;
5413
5428
  lines.push(
5414
5429
  `${bar} ${style.dim(`No ${tabName} match "${state.searchQuery}". Press Esc to clear filter.`, c)}`
5415
5430
  );
@@ -5457,12 +5472,13 @@ function renderMultiSelectFrame(title, state, options = {}) {
5457
5472
  const selectedAgentsCount = state.options.filter(
5458
5473
  (o) => optionMatchesTab(o, "agent") && state.selected.has(o.value)
5459
5474
  ).length;
5460
- const summaryText = `${state.selected.size} selected (${selectedSkillsCount} Skills, ${selectedAgentsCount} Agents)`;
5475
+ const currentOptLabel = visible[state.cursor]?.label ?? "";
5476
+ 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
5477
  const rawTarget = options.targetHint ?? "";
5462
5478
  const shortTarget = rawTarget.length > 18 ? `${rawTarget.slice(0, 17)}\u2026` : rawTarget;
5463
5479
  const targetSuffix = shortTarget ? ` \xB7 IDE: ${shortTarget}` : "";
5464
5480
  lines.push(
5465
- `${style.dim("\u2570\u2500", c)} ${style.bold(summaryText, c)}${style.dim(`${targetSuffix} \u2014 Enter to import`, c)}`
5481
+ `${style.dim("\u2570\u2500", c)} ${style.bold(summaryText, c)}${style.dim(`${targetSuffix} \u2014 Enter to ${submitVerb}`, c)}`
5466
5482
  );
5467
5483
  return lines.join("\n");
5468
5484
  }
@@ -5499,7 +5515,9 @@ async function runMultiSelectPrompt(title, options, renderOpts = {}) {
5499
5515
  };
5500
5516
  const onData = (chunk) => {
5501
5517
  const key = typeof chunk === "string" ? chunk : chunk.toString("utf-8");
5502
- state = applyMultiSelectKey(state, key);
5518
+ state = applyMultiSelectKey(state, key, {
5519
+ selectCursorOnEmptySubmit: renderOpts.selectCursorOnEmptySubmit
5520
+ });
5503
5521
  if (state.status === "cancelled") {
5504
5522
  if (renderedLineCount > 0) {
5505
5523
  process.stdout.write(`\x1B[${renderedLineCount}A\x1B[J`);
@@ -6332,6 +6350,35 @@ function resolveCatalogDir(args, repoPath, loadedConfig) {
6332
6350
  }
6333
6351
  return path10.join(PACKAGE_ROOT, "catalog");
6334
6352
  }
6353
+ var POPULAR_AI_ORDER = [
6354
+ "cursor",
6355
+ "claude",
6356
+ "antigravity",
6357
+ "copilot",
6358
+ "windsurf",
6359
+ "codex",
6360
+ "cline",
6361
+ "roocode",
6362
+ "kilocode",
6363
+ "zed"
6364
+ ];
6365
+ function buildInteractiveTargetOptions() {
6366
+ const popularSet = new Set(POPULAR_AI_ORDER);
6367
+ const orderedTargets = [
6368
+ ...POPULAR_AI_ORDER,
6369
+ ...ALL_RUNTIME_TARGETS.filter((t) => !popularSet.has(t))
6370
+ ];
6371
+ return orderedTargets.map((targetId, index) => {
6372
+ const spec = RUNTIME_HARNESS_SPECS[targetId];
6373
+ return {
6374
+ value: spec.id,
6375
+ label: spec.id,
6376
+ kind: "ide",
6377
+ tab: index < 10 ? "skill" : "agent",
6378
+ hint: `${spec.displayName} (${spec.skillsDir})`
6379
+ };
6380
+ });
6381
+ }
6335
6382
  function buildInteractiveCatalogOptions(catalogDir) {
6336
6383
  const options = [];
6337
6384
  const seen = /* @__PURE__ */ new Set();
@@ -7055,7 +7102,7 @@ function runCli(argv) {
7055
7102
  async function runCliInteractive(argv) {
7056
7103
  const args = argv.slice(2);
7057
7104
  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) {
7105
+ if (command === "add" && !args.includes("--help") && !args.includes("-h") && process.stdin.isTTY && process.stdout.isTTY) {
7059
7106
  const repoPath = getFlagValue(args, "--repo", "--project", "-p") ?? process.cwd();
7060
7107
  const cfgRes = loadSkillOpsConfig(repoPath);
7061
7108
  if (cfgRes.error) {
@@ -7066,29 +7113,57 @@ async function runCliInteractive(argv) {
7066
7113
  return ExitCode.BUDGET_OR_CONTRACT_ERROR;
7067
7114
  }
7068
7115
  const catalogDir = resolveCatalogDir(args, repoPath, cfgRes.config);
7069
- const targetsArg = resolveTargetsArg(args);
7116
+ const explicitTargetsArg = resolveTargetsArg(args);
7117
+ const positionals = getPositionalArgs(args);
7118
+ const needsCatalogPrompt = !args.includes("--all") && positionals.length === 0;
7119
+ let resolvedTargetsArg = explicitTargetsArg;
7120
+ if (!resolvedTargetsArg) {
7121
+ const targetOptions = buildInteractiveTargetOptions();
7122
+ const step1Title = needsCatalogPrompt ? "SkillOps \u2014 Step 1/2: Select Your AI IDE / Agent" : "SkillOps \u2014 Select Your AI IDE / Agent";
7123
+ const selectedTargets = await runMultiSelectPrompt(
7124
+ step1Title,
7125
+ targetOptions,
7126
+ {
7127
+ tabLabels: {
7128
+ skill: "Popular AIs",
7129
+ agent: "More AIs"
7130
+ },
7131
+ submitActionLabel: "continue",
7132
+ selectCursorOnEmptySubmit: true
7133
+ }
7134
+ );
7135
+ if (!selectedTargets || selectedTargets.length === 0) {
7136
+ process.stdout.write("No AI IDE selected.\n");
7137
+ return ExitCode.SUCCESS;
7138
+ }
7139
+ resolvedTargetsArg = selectedTargets.join(",");
7140
+ }
7070
7141
  let targets;
7071
7142
  try {
7072
- targets = targetsArg ? resolveRuntimeTargets(targetsArg, repoPath) : cfgRes.config?.targets ?? [...CORE_RUNTIME_TARGETS];
7143
+ targets = resolveRuntimeTargets(resolvedTargetsArg, repoPath);
7073
7144
  } catch (err) {
7074
7145
  const msg = err instanceof Error ? err.message : String(err);
7075
7146
  process.stderr.write(`Invalid --targets: ${msg}
7076
7147
  `);
7077
7148
  return ExitCode.BUDGET_OR_CONTRACT_ERROR;
7078
7149
  }
7079
- const options = buildInteractiveCatalogOptions(catalogDir);
7080
- const selected = await runMultiSelectPrompt(
7081
- "SkillOps \u2014 Select Skills & Agents to Import",
7082
- options,
7083
- {
7150
+ const extraArgs = [];
7151
+ if (!explicitTargetsArg) {
7152
+ extraArgs.push("-t", targets.join(","));
7153
+ }
7154
+ if (needsCatalogPrompt) {
7155
+ const options = buildInteractiveCatalogOptions(catalogDir);
7156
+ const step2Title = !explicitTargetsArg ? "SkillOps \u2014 Step 2/2: Select Skills & Agents to Import" : "SkillOps \u2014 Select Skills & Agents to Import";
7157
+ const selected = await runMultiSelectPrompt(step2Title, options, {
7084
7158
  targetHint: targets.join(", ")
7159
+ });
7160
+ if (!selected || selected.length === 0) {
7161
+ process.stdout.write("No Skills or Agents selected.\n");
7162
+ return ExitCode.SUCCESS;
7085
7163
  }
7086
- );
7087
- if (!selected || selected.length === 0) {
7088
- process.stdout.write("No Skills or Agents selected.\n");
7089
- return ExitCode.SUCCESS;
7164
+ extraArgs.push(...selected);
7090
7165
  }
7091
- return runCli([...argv, ...selected]);
7166
+ return runCli([...argv, ...extraArgs]);
7092
7167
  }
7093
7168
  return runCli(argv);
7094
7169
  }
@@ -7114,6 +7189,7 @@ if (isDirectCliInvocation()) {
7114
7189
  }
7115
7190
  export {
7116
7191
  buildInteractiveCatalogOptions,
7192
+ buildInteractiveTargetOptions,
7117
7193
  runCli,
7118
7194
  runCliInteractive
7119
7195
  };
package/dist/index.d.ts CHANGED
@@ -8,7 +8,7 @@ import { z } from 'zod';
8
8
  * telemetry and Multi-Runtime Transpilation lockfiles.
9
9
  */
10
10
 
11
- declare const SKILLOPS_VERSION = "1.3.3";
11
+ declare const SKILLOPS_VERSION = "1.3.4";
12
12
  /**
13
13
  * Count whitespace-delimited words in text.
14
14
  */
@@ -2187,6 +2187,7 @@ interface MultiSelectOption {
2187
2187
  readonly value: string;
2188
2188
  readonly label: string;
2189
2189
  readonly kind?: "skill" | "agent" | "ide";
2190
+ readonly tab?: MultiSelectTab;
2190
2191
  readonly hint?: string;
2191
2192
  readonly initialSelected?: boolean;
2192
2193
  }
@@ -2199,9 +2200,18 @@ interface MultiSelectState {
2199
2200
  readonly searchQuery: string;
2200
2201
  readonly status: "active" | "submitted" | "cancelled";
2201
2202
  }
2203
+ interface ApplyMultiSelectKeyOptions {
2204
+ readonly selectCursorOnEmptySubmit?: boolean;
2205
+ }
2202
2206
  interface RenderMultiSelectOptions {
2203
2207
  readonly subtitle?: string;
2204
2208
  readonly targetHint?: string;
2209
+ readonly tabLabels?: {
2210
+ readonly skill: string;
2211
+ readonly agent: string;
2212
+ };
2213
+ readonly submitActionLabel?: string;
2214
+ readonly selectCursorOnEmptySubmit?: boolean;
2205
2215
  readonly maxVisible?: number;
2206
2216
  readonly colorEnabled?: boolean;
2207
2217
  }
@@ -2216,7 +2226,7 @@ declare function createMultiSelectState(options: readonly MultiSelectOption[]):
2216
2226
  /**
2217
2227
  * Pure state reducer for keyboard input in the tabbed + searchable multi-select TUI.
2218
2228
  */
2219
- declare function applyMultiSelectKey(state: MultiSelectState, key: string): MultiSelectState;
2229
+ declare function applyMultiSelectKey(state: MultiSelectState, key: string, options?: ApplyMultiSelectKeyOptions): MultiSelectState;
2220
2230
  /**
2221
2231
  * Render a single frame of the tabbed + searchable multi-select tree prompt (guaranteed <= 78 columns).
2222
2232
  */
@@ -2346,4 +2356,4 @@ declare const DEFAULT_VALID_COMMANDS: readonly string[];
2346
2356
  */
2347
2357
  declare function suggestCommand(unknownCommand: string, validCommands?: readonly string[]): string | null;
2348
2358
 
2349
- export { ALL_RUNTIME_TARGETS, type AddOptions, type AnalyzeTokenEconomyOptions, type AuditCatalogOptions, type BadgeType, type BenchmarkArm, BenchmarkArmSchema, type BenchmarkDelta, BenchmarkDeltaSchema, type BoxOptions, COMMAND_HELP_SPECS, CORE_RUNTIME_TARGETS, type CatalogEvalSummary, CatalogEvalSummarySchema, type CatalogItemInfo, type CatalogSearchMatch, type CatalogSearchReport, type CatalogTokenBudgetSummary, type CatalogTrack, CatalogTrackSchema, type CleanOptions, type CollisionPair, CollisionPairSchema, type CollisionReport, CollisionReportSchema, type CollisionSeverity, CollisionSeveritySchema, type ColorOverrideOptions, type CommandHelpSpec, type CommandOptionSpec, DEFAULT_VALID_COMMANDS, type DiagnoseRepositoryOptions, type DoctorCheckItem, type DoctorReport, type EvalAssertion, EvalAssertionSchema, type EvalCase, EvalCaseSchema, ExitCode, type ExitCodeValue, type HarnessCategory, type HarnessInspectionEntry, type HarnessInspectionReport, type HarnessSkillFormat, type JsonRpcRequest, type JsonRpcResponse, type LoadedSkillOpsConfigResult, type LockfileArtifactDiagnostic, type McpConfigAction, type McpServerContext, type McpSetupOptions, type McpSetupReport, type MultiSelectOption, type MultiSelectState, type MultiSelectTab, type NextStepHint, type ParseSkillOptions, type ParsedSkillDirectory, type ParsedSubagentFile, type QuickstartOptions, type QuickstartReport, RUNTIME_HARNESS_SPECS, type RemoveOptions, type RenderMultiSelectOptions, RepoFingerprinter, type RepoProfile, RepoProfileSchema, type RouteOptions, type RoutedBundle, RoutedBundleSchema, type RoutingSignals, RoutingSignalsSchema, type RuntimeHarnessSpec, type RuntimeTarget, RuntimeTargetSchema, SKILLOPS_VERSION, type ScaffoldOptions, type ScaffoldResult, type ScoredSkill, ScoredSkillSchema, type ScriptEntrypoint, ScriptEntrypointSchema, type SkillBenchmark, SkillBenchmarkSchema, type SkillEntrypoints, SkillEntrypointsSchema, type SkillEvalSuite, SkillEvalSuiteSchema, type SkillManifest, type SkillManifestInput, SkillManifestSchema, type SkillOpsConfig, SkillOpsConfigSchema, type SkillOpsDetectedStackConfig, SkillOpsDetectedStackConfigSchema, type SkillOpsTokenEconomyConfig, SkillOpsTokenEconomyConfigSchema, type SkillTokenBudgetAudit, type SkippedSkill, SkippedSkillSchema, type SubagentManifest, SubagentManifestSchema, type SubagentTokenBudgetAudit, type SyncLockEntry, SyncLockEntrySchema, type SyncLockfile, SyncLockfileSchema, type SyncOptions, type SyncReport, SyncReportSchema, type SyncedArtifact, SyncedArtifactSchema, type TableOptions, type TokenBarOptions, type TokenEconomyReport, TokenEconomyReportSchema, type TokenFileCategory, TokenFileCategorySchema, type TokenFileEntry, TokenFileEntrySchema, type TokenWasteHotspot, TokenWasteHotspotSchema, addSkillOrSubagent, analyzeRepoTokenEconomy, applyMultiSelectKey, auditCatalog, auditCatalogTokenBudgets, auditSkillTokenBudget, auditSubagentTokenBudget, classifyCollisionSeverity, cleanSyncedArtifacts, compileSkillBundle, computeCollisionMatrix, computeFeatureCosine, computeIdfWeights, computeSha256, countWords, createCollisionPair, createCollisionReport, createMultiSelectState, createSkillManifest, createSubagentManifest, detectInstalledHarnesses, diagnoseRepository, discoverSkillDirectories, discoverSubagentFiles, estimateTokenCount, evaluateCatalog, evaluateSkillDirectory, extractSkillFeatures, findCommandHelpSpec, fingerprintRepository, formatBadge, formatCatalogItemInfo, formatCatalogSearchReport, formatDoctorReport, formatEvalSummary, formatHarnessInspectionReport, formatMcpSetupReport, formatQuickstartReport, formatRoutedBundle, formatScaffoldReport, formatSyncReport, formatTokenEconomyReport, getFilteredTabOptions, handleMcpJsonRpcRequest, inspectCatalogItem, inspectHarnesses, isColorEnabled, levenshteinDistance, loadSkillOpsConfig, normalizePackageName, padVisible, parseSkillDirectory, parseSkillMdFile, parseSubagentFile, parseSubagentMdFile, removeSkillOrSubagent, renderAiUsageGuide, renderBox, renderCodexAgentsIndex, renderCommandHelp, renderGlobalHelp, renderHeaderBanner, renderMultiSelectFrame, renderNextSteps, renderProgressBar, renderSectionHeader, renderStep, renderTable, renderTokenBar, resolveRuntimeTargets, routeRepository, runMultiSelectPrompt, runQuickstart, scaffoldFrameworkItem, scoreSkillAgainstProfile, searchCatalog, setupMcpForHarnesses, splitFrontmatter, startMcpStdioServer, stripAnsi, stripCodexAgentsBlock, stripNegativeTriggerClauses, style, suggestCommand, syncRepository, tokenizeRoutingText, visibleLength };
2359
+ export { ALL_RUNTIME_TARGETS, type AddOptions, type AnalyzeTokenEconomyOptions, type ApplyMultiSelectKeyOptions, type AuditCatalogOptions, type BadgeType, type BenchmarkArm, BenchmarkArmSchema, type BenchmarkDelta, BenchmarkDeltaSchema, type BoxOptions, COMMAND_HELP_SPECS, CORE_RUNTIME_TARGETS, type CatalogEvalSummary, CatalogEvalSummarySchema, type CatalogItemInfo, type CatalogSearchMatch, type CatalogSearchReport, type CatalogTokenBudgetSummary, type CatalogTrack, CatalogTrackSchema, type CleanOptions, type CollisionPair, CollisionPairSchema, type CollisionReport, CollisionReportSchema, type CollisionSeverity, CollisionSeveritySchema, type ColorOverrideOptions, type CommandHelpSpec, type CommandOptionSpec, DEFAULT_VALID_COMMANDS, type DiagnoseRepositoryOptions, type DoctorCheckItem, type DoctorReport, type EvalAssertion, EvalAssertionSchema, type EvalCase, EvalCaseSchema, ExitCode, type ExitCodeValue, type HarnessCategory, type HarnessInspectionEntry, type HarnessInspectionReport, type HarnessSkillFormat, type JsonRpcRequest, type JsonRpcResponse, type LoadedSkillOpsConfigResult, type LockfileArtifactDiagnostic, type McpConfigAction, type McpServerContext, type McpSetupOptions, type McpSetupReport, type MultiSelectOption, type MultiSelectState, type MultiSelectTab, type NextStepHint, type ParseSkillOptions, type ParsedSkillDirectory, type ParsedSubagentFile, type QuickstartOptions, type QuickstartReport, RUNTIME_HARNESS_SPECS, type RemoveOptions, type RenderMultiSelectOptions, RepoFingerprinter, type RepoProfile, RepoProfileSchema, type RouteOptions, type RoutedBundle, RoutedBundleSchema, type RoutingSignals, RoutingSignalsSchema, type RuntimeHarnessSpec, type RuntimeTarget, RuntimeTargetSchema, SKILLOPS_VERSION, type ScaffoldOptions, type ScaffoldResult, type ScoredSkill, ScoredSkillSchema, type ScriptEntrypoint, ScriptEntrypointSchema, type SkillBenchmark, SkillBenchmarkSchema, type SkillEntrypoints, SkillEntrypointsSchema, type SkillEvalSuite, SkillEvalSuiteSchema, type SkillManifest, type SkillManifestInput, SkillManifestSchema, type SkillOpsConfig, SkillOpsConfigSchema, type SkillOpsDetectedStackConfig, SkillOpsDetectedStackConfigSchema, type SkillOpsTokenEconomyConfig, SkillOpsTokenEconomyConfigSchema, type SkillTokenBudgetAudit, type SkippedSkill, SkippedSkillSchema, type SubagentManifest, SubagentManifestSchema, type SubagentTokenBudgetAudit, type SyncLockEntry, SyncLockEntrySchema, type SyncLockfile, SyncLockfileSchema, type SyncOptions, type SyncReport, SyncReportSchema, type SyncedArtifact, SyncedArtifactSchema, type TableOptions, type TokenBarOptions, type TokenEconomyReport, TokenEconomyReportSchema, type TokenFileCategory, TokenFileCategorySchema, type TokenFileEntry, TokenFileEntrySchema, type TokenWasteHotspot, TokenWasteHotspotSchema, addSkillOrSubagent, analyzeRepoTokenEconomy, applyMultiSelectKey, auditCatalog, auditCatalogTokenBudgets, auditSkillTokenBudget, auditSubagentTokenBudget, classifyCollisionSeverity, cleanSyncedArtifacts, compileSkillBundle, computeCollisionMatrix, computeFeatureCosine, computeIdfWeights, computeSha256, countWords, createCollisionPair, createCollisionReport, createMultiSelectState, createSkillManifest, createSubagentManifest, detectInstalledHarnesses, diagnoseRepository, discoverSkillDirectories, discoverSubagentFiles, estimateTokenCount, evaluateCatalog, evaluateSkillDirectory, extractSkillFeatures, findCommandHelpSpec, fingerprintRepository, formatBadge, formatCatalogItemInfo, formatCatalogSearchReport, formatDoctorReport, formatEvalSummary, formatHarnessInspectionReport, formatMcpSetupReport, formatQuickstartReport, formatRoutedBundle, formatScaffoldReport, formatSyncReport, formatTokenEconomyReport, getFilteredTabOptions, handleMcpJsonRpcRequest, inspectCatalogItem, inspectHarnesses, isColorEnabled, levenshteinDistance, loadSkillOpsConfig, normalizePackageName, padVisible, parseSkillDirectory, parseSkillMdFile, parseSubagentFile, parseSubagentMdFile, removeSkillOrSubagent, renderAiUsageGuide, renderBox, renderCodexAgentsIndex, renderCommandHelp, renderGlobalHelp, renderHeaderBanner, renderMultiSelectFrame, renderNextSteps, renderProgressBar, renderSectionHeader, renderStep, renderTable, renderTokenBar, resolveRuntimeTargets, routeRepository, runMultiSelectPrompt, runQuickstart, scaffoldFrameworkItem, scoreSkillAgainstProfile, searchCatalog, setupMcpForHarnesses, splitFrontmatter, startMcpStdioServer, stripAnsi, stripCodexAgentsBlock, stripNegativeTriggerClauses, style, suggestCommand, syncRepository, tokenizeRoutingText, visibleLength };
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import fs from "fs";
3
3
  import YAML from "yaml";
4
4
  import { z } from "zod";
5
- var SKILLOPS_VERSION = "1.3.3";
5
+ var SKILLOPS_VERSION = "1.3.4";
6
6
  var SKILL_NAME_REGEX = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
7
7
  var SEMVER_REGEX = /^\d+\.\d+\.\d+$/;
8
8
  var FRONTMATTER_REGEX = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/;
@@ -5207,6 +5207,9 @@ function renderCommandHelp(spec, version, colorEnabled) {
5207
5207
 
5208
5208
  // src/ui/prompt.ts
5209
5209
  function optionMatchesTab(opt, tab) {
5210
+ if (opt.tab) {
5211
+ return opt.tab === tab;
5212
+ }
5210
5213
  if (opt.kind === "agent") {
5211
5214
  return tab === "agent";
5212
5215
  }
@@ -5242,7 +5245,7 @@ function createMultiSelectState(options) {
5242
5245
  status: "active"
5243
5246
  };
5244
5247
  }
5245
- function applyMultiSelectKey(state, key) {
5248
+ function applyMultiSelectKey(state, key, options = {}) {
5246
5249
  if (state.status !== "active" || state.options.length === 0) {
5247
5250
  return state;
5248
5251
  }
@@ -5267,8 +5270,17 @@ function applyMultiSelectKey(state, key) {
5267
5270
  };
5268
5271
  }
5269
5272
  if (key === "\r" || key === "\n") {
5273
+ let finalSelected = state.selected;
5274
+ if (options.selectCursorOnEmptySubmit && state.selected.size === 0) {
5275
+ const visibleOnSubmit = getFilteredTabOptions(state, state.activeTab);
5276
+ const currentOpt = visibleOnSubmit[state.cursor];
5277
+ if (currentOpt) {
5278
+ finalSelected = /* @__PURE__ */ new Set([currentOpt.value]);
5279
+ }
5280
+ }
5270
5281
  return {
5271
5282
  ...state,
5283
+ selected: finalSelected,
5272
5284
  searchMode: false,
5273
5285
  status: "submitted"
5274
5286
  };
@@ -5408,8 +5420,11 @@ function renderMultiSelectFrame(title, state, options = {}) {
5408
5420
  const filteredAgents = getFilteredTabOptions(state, "agent");
5409
5421
  const visible = state.activeTab === "skill" ? filteredSkills : filteredAgents;
5410
5422
  const total = visible.length;
5423
+ const skillTabTitle = options.tabLabels?.skill ?? "Skills";
5424
+ const agentTabTitle = options.tabLabels?.agent ?? "Agents";
5425
+ const submitVerb = options.submitActionLabel ?? "import";
5411
5426
  const header = `${style.bold(style.cyan(`\u25C6 ${title}`, c), c)}`;
5412
- 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)}`;
5427
+ const instructions = state.searchMode ? `${bar} ${style.dim(`\u2191/\u2193 move \xB7 \u2190/\u2192 tab \xB7 Space select \xB7 Esc clear \xB7 Enter ${submitVerb}`, c)}` : `${bar} ${style.dim(`\u2191/\u2193 move \xB7 \u2190/\u2192 tab \xB7 Space select \xB7 / search \xB7 a all \xB7 Enter ${submitVerb}`, c)}`;
5413
5428
  const lines = [header];
5414
5429
  if (options.subtitle) {
5415
5430
  lines.push(`${bar} ${options.subtitle}`);
@@ -5418,8 +5433,8 @@ function renderMultiSelectFrame(title, state, options = {}) {
5418
5433
  const hasQuery = state.searchQuery.length > 0;
5419
5434
  const skillCountStr = hasQuery ? `${filteredSkills.length}/${totalSkills}` : `${totalSkills}`;
5420
5435
  const agentCountStr = hasQuery ? `${filteredAgents.length}/${totalAgents}` : `${totalAgents}`;
5421
- const skillTabLabel = state.activeTab === "skill" ? style.bold(style.cyan(`[ \u25C6 Skills (${skillCountStr}) ]`, c), c) : style.dim(`\u25CB Skills (${skillCountStr})`, c);
5422
- const agentTabLabel = state.activeTab === "agent" ? style.bold(style.magenta(`[ \u25C6 Agents (${agentCountStr}) ]`, c), c) : style.dim(`\u25CB Agents (${agentCountStr})`, c);
5436
+ const skillTabLabel = state.activeTab === "skill" ? style.bold(style.cyan(`[ \u25C6 ${skillTabTitle} (${skillCountStr}) ]`, c), c) : style.dim(`\u25CB ${skillTabTitle} (${skillCountStr})`, c);
5437
+ const agentTabLabel = state.activeTab === "agent" ? style.bold(style.magenta(`[ \u25C6 ${agentTabTitle} (${agentCountStr}) ]`, c), c) : style.dim(`\u25CB ${agentTabTitle} (${agentCountStr})`, c);
5423
5438
  const rawQuerySlice = state.searchQuery.length > 14 ? `\u2026${state.searchQuery.slice(-13)}` : state.searchQuery;
5424
5439
  const searchSuffix = state.searchMode || hasQuery ? ` ${style.yellow(`/ Filter: ${rawQuerySlice}${state.searchMode ? "\u2588" : ""}`, c)}` : ` ${style.dim("(/ search)", c)}`;
5425
5440
  lines.push(
@@ -5427,7 +5442,7 @@ function renderMultiSelectFrame(title, state, options = {}) {
5427
5442
  bar
5428
5443
  );
5429
5444
  if (total === 0) {
5430
- const tabName = state.activeTab === "skill" ? "Skills" : "Agents";
5445
+ const tabName = state.activeTab === "skill" ? skillTabTitle : agentTabTitle;
5431
5446
  lines.push(
5432
5447
  `${bar} ${style.dim(`No ${tabName} match "${state.searchQuery}". Press Esc to clear filter.`, c)}`
5433
5448
  );
@@ -5475,12 +5490,13 @@ function renderMultiSelectFrame(title, state, options = {}) {
5475
5490
  const selectedAgentsCount = state.options.filter(
5476
5491
  (o) => optionMatchesTab(o, "agent") && state.selected.has(o.value)
5477
5492
  ).length;
5478
- const summaryText = `${state.selected.size} selected (${selectedSkillsCount} Skills, ${selectedAgentsCount} Agents)`;
5493
+ const currentOptLabel = visible[state.cursor]?.label ?? "";
5494
+ 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})`;
5479
5495
  const rawTarget = options.targetHint ?? "";
5480
5496
  const shortTarget = rawTarget.length > 18 ? `${rawTarget.slice(0, 17)}\u2026` : rawTarget;
5481
5497
  const targetSuffix = shortTarget ? ` \xB7 IDE: ${shortTarget}` : "";
5482
5498
  lines.push(
5483
- `${style.dim("\u2570\u2500", c)} ${style.bold(summaryText, c)}${style.dim(`${targetSuffix} \u2014 Enter to import`, c)}`
5499
+ `${style.dim("\u2570\u2500", c)} ${style.bold(summaryText, c)}${style.dim(`${targetSuffix} \u2014 Enter to ${submitVerb}`, c)}`
5484
5500
  );
5485
5501
  return lines.join("\n");
5486
5502
  }
@@ -5517,7 +5533,9 @@ async function runMultiSelectPrompt(title, options, renderOpts = {}) {
5517
5533
  };
5518
5534
  const onData = (chunk) => {
5519
5535
  const key = typeof chunk === "string" ? chunk : chunk.toString("utf-8");
5520
- state = applyMultiSelectKey(state, key);
5536
+ state = applyMultiSelectKey(state, key, {
5537
+ selectCursorOnEmptySubmit: renderOpts.selectCursorOnEmptySubmit
5538
+ });
5521
5539
  if (state.status === "cancelled") {
5522
5540
  if (renderedLineCount > 0) {
5523
5541
  process.stdout.write(`\x1B[${renderedLineCount}A\x1B[J`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skillops-cli",
3
- "version": "1.3.3",
3
+ "version": "1.3.4",
4
4
  "description": "SkillOps — The Zero-API Framework & Benchmark-Verified Library for High-End AI Coding Skills & Context-Isolated Subagents with Universal Top-20 AI Agent Transpilation & Token Economy Auditing.",
5
5
  "type": "module",
6
6
  "license": "MIT",