skillops-cli 1.3.1 → 1.3.3

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/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.1";
13
+ var SKILLOPS_VERSION = "1.3.3";
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]*)$/;
@@ -5113,45 +5113,56 @@ function renderGlobalHelp(version, colorEnabled) {
5113
5113
  const c = colorEnabled;
5114
5114
  const bar = style.dim("\u2502", c);
5115
5115
  const banner = renderHeaderBanner(
5116
- "Framework & Library for AI Skills & Subagents",
5116
+ "Skills & Agents Library for AI Coding IDEs",
5117
5117
  {
5118
5118
  version,
5119
5119
  colorEnabled: c
5120
5120
  }
5121
5121
  );
5122
- const formatCmdSection = (pillar) => {
5123
- const specs = COMMAND_HELP_SPECS.filter((s) => s.pillar === pillar);
5124
- return specs.map((s) => {
5125
- const aliasPart = s.aliases.length > 0 ? style.dim(` (${s.aliases.join(", ")})`, c) : "";
5126
- const cmdCol = padVisible(
5127
- `${style.bold(style.cyan(s.name, c), c)}${aliasPart}`,
5128
- 23,
5129
- "left"
5130
- );
5131
- return `${bar} ${cmdCol}${s.summary}`;
5132
- }).join("\n");
5133
- };
5122
+ const coreCmds = [
5123
+ {
5124
+ cmd: "add [name...]",
5125
+ desc: "Import Skills or Agents (interactive if no name given)"
5126
+ },
5127
+ {
5128
+ cmd: "search <query>",
5129
+ desc: "Search Skills & Agents by keyword, stack, or domain"
5130
+ },
5131
+ {
5132
+ cmd: "list [query]",
5133
+ desc: "List or filter available Skills & Agents in catalog"
5134
+ },
5135
+ {
5136
+ cmd: "info <name>",
5137
+ desc: "View details and how to use a Skill or Agent"
5138
+ },
5139
+ {
5140
+ cmd: "remove <name>",
5141
+ desc: "Remove an imported Skill or Agent from your project"
5142
+ },
5143
+ {
5144
+ cmd: "init",
5145
+ desc: "Auto-detect stack & IDE and import recommended bundle"
5146
+ }
5147
+ ];
5148
+ const cmdRows = coreCmds.map(
5149
+ (item) => `${bar} ${padVisible(style.bold(style.cyan(item.cmd, c), c), 18, "left")}${item.desc}`
5150
+ ).join("\n");
5134
5151
  return [
5135
5152
  banner,
5136
- `${bar} Usage: npx skillops-cli <command> [options]`,
5137
- renderSectionHeader("Quickstart (Library + Framework)", c),
5138
- `${bar} ${style.dim("$", c)} ${style.bold(style.green("npx skillops-cli quickstart", c), c)} ${style.dim("\u2014 Detect stack, sync Skills & MCP", c)}`,
5139
- `${bar} ${style.dim("$", c)} ${style.bold(style.cyan("npx skillops-cli info <name>", c), c)} ${style.dim("\u2014 Inspect Skill/Subagent & usage", c)}`,
5140
- `${bar} ${style.dim("$", c)} ${style.bold(style.magenta("npx skillops-cli create skill <name>", c), c)} ${style.dim("\u2014 Scaffold a custom Skill", c)}`,
5141
- renderSectionHeader("1. Library of High-End Skills & Subagents", c),
5142
- formatCmdSection("library"),
5143
- renderSectionHeader(
5144
- "2. Framework for Building Custom Skills & Subagents",
5145
- c
5146
- ),
5147
- formatCmdSection("framework"),
5148
- renderSectionHeader(
5149
- "3. Universal Runtime Sync, Token Economy & Diagnostics",
5150
- c
5151
- ),
5152
- formatCmdSection("runtime"),
5153
+ `${bar} Usage: skillops <command> [options]`,
5154
+ renderSectionHeader("Commands", c),
5155
+ cmdRows,
5156
+ renderSectionHeader("Options", c),
5157
+ `${bar} ${padVisible(style.cyan("-t, --target <ide>", c), 22, "left")}AI IDE (cursor, claude, copilot, windsurf, auto)`,
5158
+ `${bar} ${padVisible(style.cyan("-p, --project <dir>", c), 22, "left")}Project folder (default: current directory)`,
5159
+ `${bar} ${padVisible(style.cyan("--all", c), 22, "left")}Import all 10 Skills & 9 Agents`,
5160
+ renderSectionHeader("Examples", c),
5161
+ `${bar} ${style.dim("$", c)} ${style.green("skillops add", c)} ${style.dim("\u2014 Interactive selector", c)}`,
5162
+ `${bar} ${style.dim("$", c)} ${style.green("skillops add bug-reproducer-tdd red-team-breaker", c)} ${style.dim("\u2014 Direct import", c)}`,
5163
+ `${bar} ${style.dim("$", c)} ${style.green("skillops list", c)} ${style.dim("\u2014 Browse catalog", c)}`,
5153
5164
  bar,
5154
- `${style.dim("\u2570\u2500", c)} ${style.bold("Global Flags:", c)} ${style.cyan("-p <dir>", c)} \xB7 ${style.cyan("-t <harnesses>", c)} \xB7 ${style.cyan("-f <table|json|md>", c)} \xB7 ${style.cyan("-h <cmd>", c)}`
5165
+ `${style.dim("\u2570\u2500", c)} ${style.dim("Run", c)} ${style.cyan("skillops <cmd> -h", c)} ${style.dim("for command options.", c)}`
5155
5166
  ].join("\n");
5156
5167
  }
5157
5168
  function renderCommandHelp(spec, version, colorEnabled) {
@@ -5176,6 +5187,341 @@ function renderCommandHelp(spec, version, colorEnabled) {
5176
5187
  return [headerLines, optionsSection, examplesSection].filter(Boolean).join("\n");
5177
5188
  }
5178
5189
 
5190
+ // src/ui/prompt.ts
5191
+ function optionMatchesTab(opt, tab) {
5192
+ if (opt.kind === "agent") {
5193
+ return tab === "agent";
5194
+ }
5195
+ return tab === "skill";
5196
+ }
5197
+ function optionMatchesQuery(opt, rawQuery) {
5198
+ const q = rawQuery.trim().toLowerCase();
5199
+ if (!q) {
5200
+ return true;
5201
+ }
5202
+ return opt.label.toLowerCase().includes(q) || opt.value.toLowerCase().includes(q) || (opt.hint ?? "").toLowerCase().includes(q);
5203
+ }
5204
+ function getFilteredTabOptions(state, tab = state.activeTab) {
5205
+ return state.options.filter(
5206
+ (opt) => optionMatchesTab(opt, tab) && optionMatchesQuery(opt, state.searchQuery)
5207
+ );
5208
+ }
5209
+ function createMultiSelectState(options) {
5210
+ const initial = /* @__PURE__ */ new Set();
5211
+ for (const opt of options) {
5212
+ if (opt.initialSelected) {
5213
+ initial.add(opt.value);
5214
+ }
5215
+ }
5216
+ const hasSkills = options.some((opt) => optionMatchesTab(opt, "skill"));
5217
+ return {
5218
+ options,
5219
+ activeTab: hasSkills ? "skill" : "agent",
5220
+ cursor: 0,
5221
+ selected: initial,
5222
+ searchMode: false,
5223
+ searchQuery: "",
5224
+ status: "active"
5225
+ };
5226
+ }
5227
+ function applyMultiSelectKey(state, key) {
5228
+ if (state.status !== "active" || state.options.length === 0) {
5229
+ return state;
5230
+ }
5231
+ if (key === "") {
5232
+ return {
5233
+ ...state,
5234
+ status: "cancelled"
5235
+ };
5236
+ }
5237
+ if (key === "\x1B") {
5238
+ if (state.searchMode || state.searchQuery.length > 0) {
5239
+ return {
5240
+ ...state,
5241
+ searchMode: false,
5242
+ searchQuery: "",
5243
+ cursor: 0
5244
+ };
5245
+ }
5246
+ return {
5247
+ ...state,
5248
+ status: "cancelled"
5249
+ };
5250
+ }
5251
+ if (key === "\r" || key === "\n") {
5252
+ return {
5253
+ ...state,
5254
+ searchMode: false,
5255
+ status: "submitted"
5256
+ };
5257
+ }
5258
+ if (key === "\x1B[D" || key === "\x1BOD" || key === "\x1B[C" || key === "\x1BOC" || key === " " || !state.searchMode && (key === "h" || key === "H" || key === "l" || key === "L")) {
5259
+ const nextTab = state.activeTab === "skill" ? "agent" : "skill";
5260
+ return {
5261
+ ...state,
5262
+ activeTab: nextTab,
5263
+ cursor: 0
5264
+ };
5265
+ }
5266
+ const visible = getFilteredTabOptions(state, state.activeTab);
5267
+ const count = visible.length;
5268
+ if (key === "\x1B[A" || key === "\x1BOA" || !state.searchMode && (key === "k" || key === "K")) {
5269
+ if (count === 0) {
5270
+ return state;
5271
+ }
5272
+ const nextCursor = (state.cursor - 1 + count) % count;
5273
+ return {
5274
+ ...state,
5275
+ cursor: nextCursor
5276
+ };
5277
+ }
5278
+ if (key === "\x1B[B" || key === "\x1BOB" || !state.searchMode && (key === "j" || key === "J")) {
5279
+ if (count === 0) {
5280
+ return state;
5281
+ }
5282
+ const nextCursor = (state.cursor + 1) % count;
5283
+ return {
5284
+ ...state,
5285
+ cursor: nextCursor
5286
+ };
5287
+ }
5288
+ if (key === " ") {
5289
+ const currentOpt = visible[state.cursor];
5290
+ if (!currentOpt) {
5291
+ return state;
5292
+ }
5293
+ const nextSelected = new Set(state.selected);
5294
+ if (nextSelected.has(currentOpt.value)) {
5295
+ nextSelected.delete(currentOpt.value);
5296
+ } else {
5297
+ nextSelected.add(currentOpt.value);
5298
+ }
5299
+ return {
5300
+ ...state,
5301
+ selected: nextSelected
5302
+ };
5303
+ }
5304
+ if (state.searchMode) {
5305
+ if (key === "\x7F" || key === "\b") {
5306
+ if (state.searchQuery.length === 0) {
5307
+ return {
5308
+ ...state,
5309
+ searchMode: false,
5310
+ cursor: 0
5311
+ };
5312
+ }
5313
+ const nextQuery = state.searchQuery.slice(0, -1);
5314
+ return {
5315
+ ...state,
5316
+ searchQuery: nextQuery,
5317
+ cursor: 0
5318
+ };
5319
+ }
5320
+ if (/^[a-zA-Z0-9_.-]$/.test(key)) {
5321
+ const nextQuery = `${state.searchQuery}${key}`;
5322
+ const nextState = {
5323
+ ...state,
5324
+ searchQuery: nextQuery,
5325
+ cursor: 0
5326
+ };
5327
+ const currentMatches = getFilteredTabOptions(nextState, state.activeTab);
5328
+ if (currentMatches.length === 0) {
5329
+ const otherTab = state.activeTab === "skill" ? "agent" : "skill";
5330
+ const otherMatches = getFilteredTabOptions(nextState, otherTab);
5331
+ if (otherMatches.length > 0) {
5332
+ return {
5333
+ ...nextState,
5334
+ activeTab: otherTab
5335
+ };
5336
+ }
5337
+ }
5338
+ return nextState;
5339
+ }
5340
+ return state;
5341
+ }
5342
+ if (key === "/") {
5343
+ return {
5344
+ ...state,
5345
+ searchMode: true,
5346
+ cursor: 0
5347
+ };
5348
+ }
5349
+ if (key === "q" || key === "Q") {
5350
+ return {
5351
+ ...state,
5352
+ status: "cancelled"
5353
+ };
5354
+ }
5355
+ if (key === "a" || key === "A") {
5356
+ if (visible.length === 0) {
5357
+ return state;
5358
+ }
5359
+ const allTabSelected = visible.every(
5360
+ (opt) => state.selected.has(opt.value)
5361
+ );
5362
+ const nextSelected = new Set(state.selected);
5363
+ if (allTabSelected) {
5364
+ for (const opt of visible) {
5365
+ nextSelected.delete(opt.value);
5366
+ }
5367
+ } else {
5368
+ for (const opt of visible) {
5369
+ nextSelected.add(opt.value);
5370
+ }
5371
+ }
5372
+ return {
5373
+ ...state,
5374
+ selected: nextSelected
5375
+ };
5376
+ }
5377
+ return state;
5378
+ }
5379
+ function renderMultiSelectFrame(title, state, options = {}) {
5380
+ const c = options.colorEnabled;
5381
+ const bar = style.dim("\u2502", c);
5382
+ const maxVisible = options.maxVisible ?? 10;
5383
+ const totalSkills = state.options.filter(
5384
+ (o) => optionMatchesTab(o, "skill")
5385
+ ).length;
5386
+ const totalAgents = state.options.filter(
5387
+ (o) => optionMatchesTab(o, "agent")
5388
+ ).length;
5389
+ const filteredSkills = getFilteredTabOptions(state, "skill");
5390
+ const filteredAgents = getFilteredTabOptions(state, "agent");
5391
+ const visible = state.activeTab === "skill" ? filteredSkills : filteredAgents;
5392
+ const total = visible.length;
5393
+ 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)}`;
5395
+ const lines = [header];
5396
+ if (options.subtitle) {
5397
+ lines.push(`${bar} ${options.subtitle}`);
5398
+ }
5399
+ lines.push(instructions, bar);
5400
+ const hasQuery = state.searchQuery.length > 0;
5401
+ const skillCountStr = hasQuery ? `${filteredSkills.length}/${totalSkills}` : `${totalSkills}`;
5402
+ 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);
5405
+ const rawQuerySlice = state.searchQuery.length > 14 ? `\u2026${state.searchQuery.slice(-13)}` : state.searchQuery;
5406
+ const searchSuffix = state.searchMode || hasQuery ? ` ${style.yellow(`/ Filter: ${rawQuerySlice}${state.searchMode ? "\u2588" : ""}`, c)}` : ` ${style.dim("(/ search)", c)}`;
5407
+ lines.push(
5408
+ `${style.dim("\u251C\u2500", c)} ${skillTabLabel} ${agentTabLabel}${searchSuffix}`,
5409
+ bar
5410
+ );
5411
+ if (total === 0) {
5412
+ const tabName = state.activeTab === "skill" ? "Skills" : "Agents";
5413
+ lines.push(
5414
+ `${bar} ${style.dim(`No ${tabName} match "${state.searchQuery}". Press Esc to clear filter.`, c)}`
5415
+ );
5416
+ } else {
5417
+ let startIdx = 0;
5418
+ if (total > maxVisible) {
5419
+ const half = Math.floor(maxVisible / 2);
5420
+ startIdx = Math.max(0, Math.min(state.cursor - half, total - maxVisible));
5421
+ }
5422
+ const endIdx = Math.min(total, startIdx + maxVisible);
5423
+ if (startIdx > 0) {
5424
+ lines.push(`${bar} ${style.dim(`\u2191 ${startIdx} more above...`, c)}`);
5425
+ }
5426
+ const maxLabelCols = Math.min(
5427
+ 26,
5428
+ visible.reduce((m, o) => Math.max(m, visibleLength(o.label)), 14)
5429
+ );
5430
+ for (let i = startIdx; i < endIdx; i++) {
5431
+ const opt = visible[i];
5432
+ if (!opt) continue;
5433
+ const isCurrent = i === state.cursor;
5434
+ const isChecked = state.selected.has(opt.value);
5435
+ const pointer = isCurrent ? style.cyan("\u276F", c) : " ";
5436
+ const checkbox = isChecked ? style.green("\u25C9", c) : style.dim("\u25EF", c);
5437
+ const labelRaw = opt.label.length > maxLabelCols ? `${opt.label.slice(0, maxLabelCols - 1)}\u2026` : opt.label;
5438
+ const labelPadded = padVisible(labelRaw, maxLabelCols + 1, "left");
5439
+ const styledLabel = isCurrent ? style.bold(labelPadded, c) : isChecked ? style.green(labelPadded, c) : labelPadded;
5440
+ const prefixWidth = 7 + maxLabelCols + 1;
5441
+ const maxHintCols = Math.max(10, 76 - prefixWidth);
5442
+ const rawHint = opt.hint ?? "";
5443
+ const shortHint = rawHint.length > maxHintCols ? `${rawHint.slice(0, maxHintCols - 1)}\u2026` : rawHint;
5444
+ const hintPart = shortHint ? ` ${style.dim(shortHint, c)}` : "";
5445
+ lines.push(`${bar} ${pointer} ${checkbox} ${styledLabel}${hintPart}`);
5446
+ }
5447
+ if (endIdx < total) {
5448
+ lines.push(
5449
+ `${bar} ${style.dim(`\u2193 ${total - endIdx} more below...`, c)}`
5450
+ );
5451
+ }
5452
+ }
5453
+ lines.push(bar);
5454
+ const selectedSkillsCount = state.options.filter(
5455
+ (o) => optionMatchesTab(o, "skill") && state.selected.has(o.value)
5456
+ ).length;
5457
+ const selectedAgentsCount = state.options.filter(
5458
+ (o) => optionMatchesTab(o, "agent") && state.selected.has(o.value)
5459
+ ).length;
5460
+ const summaryText = `${state.selected.size} selected (${selectedSkillsCount} Skills, ${selectedAgentsCount} Agents)`;
5461
+ const rawTarget = options.targetHint ?? "";
5462
+ const shortTarget = rawTarget.length > 18 ? `${rawTarget.slice(0, 17)}\u2026` : rawTarget;
5463
+ const targetSuffix = shortTarget ? ` \xB7 IDE: ${shortTarget}` : "";
5464
+ lines.push(
5465
+ `${style.dim("\u2570\u2500", c)} ${style.bold(summaryText, c)}${style.dim(`${targetSuffix} \u2014 Enter to import`, c)}`
5466
+ );
5467
+ return lines.join("\n");
5468
+ }
5469
+ async function runMultiSelectPrompt(title, options, renderOpts = {}) {
5470
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
5471
+ return null;
5472
+ }
5473
+ let state = createMultiSelectState(options);
5474
+ let renderedLineCount = 0;
5475
+ const render = () => {
5476
+ const frame = renderMultiSelectFrame(title, state, renderOpts);
5477
+ if (renderedLineCount > 0) {
5478
+ process.stdout.write(`\x1B[${renderedLineCount}A\x1B[J`);
5479
+ }
5480
+ process.stdout.write(`${frame}
5481
+ `);
5482
+ renderedLineCount = frame.split("\n").length;
5483
+ };
5484
+ return new Promise((resolve) => {
5485
+ const stdin = process.stdin;
5486
+ const wasRaw = stdin.isRaw;
5487
+ stdin.setRawMode(true);
5488
+ stdin.resume();
5489
+ stdin.setEncoding("utf-8");
5490
+ process.stdout.write("\x1B[?25l");
5491
+ render();
5492
+ const cleanup = () => {
5493
+ stdin.removeListener("data", onData);
5494
+ if (stdin.isTTY) {
5495
+ stdin.setRawMode(Boolean(wasRaw));
5496
+ }
5497
+ stdin.pause();
5498
+ process.stdout.write("\x1B[?25h");
5499
+ };
5500
+ const onData = (chunk) => {
5501
+ const key = typeof chunk === "string" ? chunk : chunk.toString("utf-8");
5502
+ state = applyMultiSelectKey(state, key);
5503
+ if (state.status === "cancelled") {
5504
+ if (renderedLineCount > 0) {
5505
+ process.stdout.write(`\x1B[${renderedLineCount}A\x1B[J`);
5506
+ }
5507
+ cleanup();
5508
+ resolve(null);
5509
+ return;
5510
+ }
5511
+ if (state.status === "submitted") {
5512
+ if (renderedLineCount > 0) {
5513
+ process.stdout.write(`\x1B[${renderedLineCount}A\x1B[J`);
5514
+ }
5515
+ cleanup();
5516
+ resolve([...state.selected]);
5517
+ return;
5518
+ }
5519
+ render();
5520
+ };
5521
+ stdin.on("data", onData);
5522
+ });
5523
+ }
5524
+
5179
5525
  // src/framework/index.ts
5180
5526
  function extractMarkdownHeadings(markdownBody) {
5181
5527
  const headings = [];
@@ -5986,6 +6332,41 @@ function resolveCatalogDir(args, repoPath, loadedConfig) {
5986
6332
  }
5987
6333
  return path10.join(PACKAGE_ROOT, "catalog");
5988
6334
  }
6335
+ function buildInteractiveCatalogOptions(catalogDir) {
6336
+ const options = [];
6337
+ const seen = /* @__PURE__ */ new Set();
6338
+ for (const skillDir of discoverSkillDirectories(catalogDir)) {
6339
+ try {
6340
+ const parsed = parseSkillDirectory(skillDir, { requireLayout: false });
6341
+ if (!seen.has(parsed.manifest.name)) {
6342
+ seen.add(parsed.manifest.name);
6343
+ options.push({
6344
+ value: parsed.manifest.name,
6345
+ label: parsed.manifest.name,
6346
+ kind: "skill",
6347
+ hint: parsed.manifest.description
6348
+ });
6349
+ }
6350
+ } catch {
6351
+ }
6352
+ }
6353
+ for (const saPath of discoverSubagentFiles(catalogDir)) {
6354
+ try {
6355
+ const parsed = parseSubagentFile(saPath);
6356
+ if (!seen.has(parsed.manifest.name)) {
6357
+ seen.add(parsed.manifest.name);
6358
+ options.push({
6359
+ value: parsed.manifest.name,
6360
+ label: parsed.manifest.name,
6361
+ kind: "agent",
6362
+ hint: parsed.manifest.description
6363
+ });
6364
+ }
6365
+ } catch {
6366
+ }
6367
+ }
6368
+ return options;
6369
+ }
5989
6370
  function runCli(argv) {
5990
6371
  const args = argv.slice(2);
5991
6372
  if (args.includes("--version") || args.includes("-V") || args[0] === "version") {
@@ -6184,6 +6565,7 @@ function runCli(argv) {
6184
6565
  return ExitCode.SUCCESS;
6185
6566
  }
6186
6567
  if (command === "list") {
6568
+ const positionals = getPositionalArgs(args);
6187
6569
  const repoPath = getFlagValue(args, "--repo", "--project", "-p") ?? process.cwd();
6188
6570
  const cfgRes = loadSkillOpsConfig(repoPath);
6189
6571
  if (cfgRes.error) {
@@ -6195,6 +6577,12 @@ function runCli(argv) {
6195
6577
  }
6196
6578
  const catalogDir = resolveCatalogDir(args, repoPath, cfgRes.config);
6197
6579
  const rawFormat = getFlagValue(args, "--format", "-f") ?? "table";
6580
+ if (positionals.length > 0) {
6581
+ const format = rawFormat === "json" || rawFormat === "markdown" ? rawFormat : "table";
6582
+ const report = searchCatalog(catalogDir, positionals.join(" "));
6583
+ process.stdout.write(formatCatalogSearchReport(report, format));
6584
+ return ExitCode.SUCCESS;
6585
+ }
6198
6586
  const summary = auditCatalogTokenBudgets(catalogDir, {
6199
6587
  requireLayout: false
6200
6588
  });
@@ -6444,7 +6832,9 @@ function runCli(argv) {
6444
6832
  const force = args.includes("--force");
6445
6833
  const rawFormat = getFlagValue(args, "--format", "-f") ?? "table";
6446
6834
  const format = rawFormat === "json" || rawFormat === "markdown" ? rawFormat : "table";
6447
- if (positionals.length === 0) {
6835
+ const importAll = args.includes("--all");
6836
+ const itemsToAdd = importAll && positionals.length === 0 ? buildInteractiveCatalogOptions(catalogDir).map((o) => o.value) : positionals;
6837
+ if (itemsToAdd.length === 0) {
6448
6838
  if (command === "add") {
6449
6839
  process.stderr.write(
6450
6840
  "Usage: npx skillops-cli add <skill-or-subagent-name> [more-names...] [options]\n"
@@ -6465,19 +6855,27 @@ function runCli(argv) {
6465
6855
  return report.passed ? ExitCode.SUCCESS : ExitCode.ERROR;
6466
6856
  }
6467
6857
  try {
6468
- let allPassed = true;
6469
- for (const itemName of positionals) {
6470
- const report = addSkillOrSubagent(repoPath, catalogDir, itemName, {
6858
+ const reports = itemsToAdd.map(
6859
+ (itemName) => addSkillOrSubagent(repoPath, catalogDir, itemName, {
6471
6860
  targets,
6472
6861
  dryRun,
6473
6862
  force
6474
- });
6475
- process.stdout.write(formatSyncReport(report, format));
6476
- if (!report.passed) {
6477
- allPassed = false;
6478
- }
6863
+ })
6864
+ );
6865
+ const firstReport = reports[0];
6866
+ if (!firstReport) {
6867
+ return ExitCode.ERROR;
6479
6868
  }
6480
- return allPassed ? ExitCode.SUCCESS : ExitCode.ERROR;
6869
+ const finalReport = reports.length === 1 ? firstReport : {
6870
+ ...firstReport,
6871
+ artifacts: reports.flatMap((r) => r.artifacts),
6872
+ drift_conflicts: [
6873
+ ...new Set(reports.flatMap((r) => r.drift_conflicts))
6874
+ ],
6875
+ passed: reports.every((r) => r.passed)
6876
+ };
6877
+ process.stdout.write(formatSyncReport(finalReport, format));
6878
+ return finalReport.passed ? ExitCode.SUCCESS : ExitCode.ERROR;
6481
6879
  } catch (err) {
6482
6880
  const msg = err instanceof Error ? err.message : String(err);
6483
6881
  process.stderr.write(`Error: ${msg}
@@ -6654,6 +7052,46 @@ function runCli(argv) {
6654
7052
  );
6655
7053
  return ExitCode.ERROR;
6656
7054
  }
7055
+ async function runCliInteractive(argv) {
7056
+ const args = argv.slice(2);
7057
+ 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) {
7059
+ const repoPath = getFlagValue(args, "--repo", "--project", "-p") ?? process.cwd();
7060
+ const cfgRes = loadSkillOpsConfig(repoPath);
7061
+ if (cfgRes.error) {
7062
+ process.stderr.write(
7063
+ `Invalid .skillops.yml at ${cfgRes.configPath}: ${cfgRes.error}
7064
+ `
7065
+ );
7066
+ return ExitCode.BUDGET_OR_CONTRACT_ERROR;
7067
+ }
7068
+ const catalogDir = resolveCatalogDir(args, repoPath, cfgRes.config);
7069
+ const targetsArg = resolveTargetsArg(args);
7070
+ let targets;
7071
+ try {
7072
+ targets = targetsArg ? resolveRuntimeTargets(targetsArg, repoPath) : cfgRes.config?.targets ?? [...CORE_RUNTIME_TARGETS];
7073
+ } catch (err) {
7074
+ const msg = err instanceof Error ? err.message : String(err);
7075
+ process.stderr.write(`Invalid --targets: ${msg}
7076
+ `);
7077
+ return ExitCode.BUDGET_OR_CONTRACT_ERROR;
7078
+ }
7079
+ const options = buildInteractiveCatalogOptions(catalogDir);
7080
+ const selected = await runMultiSelectPrompt(
7081
+ "SkillOps \u2014 Select Skills & Agents to Import",
7082
+ options,
7083
+ {
7084
+ targetHint: targets.join(", ")
7085
+ }
7086
+ );
7087
+ if (!selected || selected.length === 0) {
7088
+ process.stdout.write("No Skills or Agents selected.\n");
7089
+ return ExitCode.SUCCESS;
7090
+ }
7091
+ return runCli([...argv, ...selected]);
7092
+ }
7093
+ return runCli(argv);
7094
+ }
6657
7095
  function isDirectCliInvocation() {
6658
7096
  const entryArg = process.argv[1];
6659
7097
  if (!entryArg) {
@@ -6670,9 +7108,12 @@ function isDirectCliInvocation() {
6670
7108
  }
6671
7109
  }
6672
7110
  if (isDirectCliInvocation()) {
6673
- const code = runCli(process.argv);
6674
- process.exit(code);
7111
+ runCliInteractive(process.argv).then((code) => {
7112
+ process.exit(code);
7113
+ });
6675
7114
  }
6676
7115
  export {
6677
- runCli
7116
+ buildInteractiveCatalogOptions,
7117
+ runCli,
7118
+ runCliInteractive
6678
7119
  };