skillops-cli 1.3.2 → 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 +1 -1
- package/dist/cli.js +289 -62
- package/dist/index.d.ts +28 -7
- package/dist/index.js +218 -48
- package/package.json +1 -1
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
|
+
> **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
|
[](./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.
|
|
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]*)$/;
|
|
@@ -5124,6 +5124,10 @@ function renderGlobalHelp(version, colorEnabled) {
|
|
|
5124
5124
|
cmd: "add [name...]",
|
|
5125
5125
|
desc: "Import Skills or Agents (interactive if no name given)"
|
|
5126
5126
|
},
|
|
5127
|
+
{
|
|
5128
|
+
cmd: "search <query>",
|
|
5129
|
+
desc: "Search Skills & Agents by keyword, stack, or domain"
|
|
5130
|
+
},
|
|
5127
5131
|
{
|
|
5128
5132
|
cmd: "list [query]",
|
|
5129
5133
|
desc: "List or filter available Skills & Agents in catalog"
|
|
@@ -5184,6 +5188,27 @@ function renderCommandHelp(spec, version, colorEnabled) {
|
|
|
5184
5188
|
}
|
|
5185
5189
|
|
|
5186
5190
|
// src/ui/prompt.ts
|
|
5191
|
+
function optionMatchesTab(opt, tab) {
|
|
5192
|
+
if (opt.tab) {
|
|
5193
|
+
return opt.tab === tab;
|
|
5194
|
+
}
|
|
5195
|
+
if (opt.kind === "agent") {
|
|
5196
|
+
return tab === "agent";
|
|
5197
|
+
}
|
|
5198
|
+
return tab === "skill";
|
|
5199
|
+
}
|
|
5200
|
+
function optionMatchesQuery(opt, rawQuery) {
|
|
5201
|
+
const q = rawQuery.trim().toLowerCase();
|
|
5202
|
+
if (!q) {
|
|
5203
|
+
return true;
|
|
5204
|
+
}
|
|
5205
|
+
return opt.label.toLowerCase().includes(q) || opt.value.toLowerCase().includes(q) || (opt.hint ?? "").toLowerCase().includes(q);
|
|
5206
|
+
}
|
|
5207
|
+
function getFilteredTabOptions(state, tab = state.activeTab) {
|
|
5208
|
+
return state.options.filter(
|
|
5209
|
+
(opt) => optionMatchesTab(opt, tab) && optionMatchesQuery(opt, state.searchQuery)
|
|
5210
|
+
);
|
|
5211
|
+
}
|
|
5187
5212
|
function createMultiSelectState(options) {
|
|
5188
5213
|
const initial = /* @__PURE__ */ new Set();
|
|
5189
5214
|
for (const opt of options) {
|
|
@@ -5191,38 +5216,81 @@ function createMultiSelectState(options) {
|
|
|
5191
5216
|
initial.add(opt.value);
|
|
5192
5217
|
}
|
|
5193
5218
|
}
|
|
5219
|
+
const hasSkills = options.some((opt) => optionMatchesTab(opt, "skill"));
|
|
5194
5220
|
return {
|
|
5195
5221
|
options,
|
|
5222
|
+
activeTab: hasSkills ? "skill" : "agent",
|
|
5196
5223
|
cursor: 0,
|
|
5197
5224
|
selected: initial,
|
|
5225
|
+
searchMode: false,
|
|
5226
|
+
searchQuery: "",
|
|
5198
5227
|
status: "active"
|
|
5199
5228
|
};
|
|
5200
5229
|
}
|
|
5201
|
-
function applyMultiSelectKey(state, key) {
|
|
5230
|
+
function applyMultiSelectKey(state, key, options = {}) {
|
|
5202
5231
|
if (state.status !== "active" || state.options.length === 0) {
|
|
5203
5232
|
return state;
|
|
5204
5233
|
}
|
|
5205
|
-
|
|
5206
|
-
|
|
5234
|
+
if (key === "") {
|
|
5235
|
+
return {
|
|
5236
|
+
...state,
|
|
5237
|
+
status: "cancelled"
|
|
5238
|
+
};
|
|
5239
|
+
}
|
|
5240
|
+
if (key === "\x1B") {
|
|
5241
|
+
if (state.searchMode || state.searchQuery.length > 0) {
|
|
5242
|
+
return {
|
|
5243
|
+
...state,
|
|
5244
|
+
searchMode: false,
|
|
5245
|
+
searchQuery: "",
|
|
5246
|
+
cursor: 0
|
|
5247
|
+
};
|
|
5248
|
+
}
|
|
5207
5249
|
return {
|
|
5208
5250
|
...state,
|
|
5209
5251
|
status: "cancelled"
|
|
5210
5252
|
};
|
|
5211
5253
|
}
|
|
5212
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
|
+
}
|
|
5213
5263
|
return {
|
|
5214
5264
|
...state,
|
|
5265
|
+
selected: finalSelected,
|
|
5266
|
+
searchMode: false,
|
|
5215
5267
|
status: "submitted"
|
|
5216
5268
|
};
|
|
5217
5269
|
}
|
|
5218
|
-
if (key === "\x1B[
|
|
5270
|
+
if (key === "\x1B[D" || key === "\x1BOD" || key === "\x1B[C" || key === "\x1BOC" || key === " " || !state.searchMode && (key === "h" || key === "H" || key === "l" || key === "L")) {
|
|
5271
|
+
const nextTab = state.activeTab === "skill" ? "agent" : "skill";
|
|
5272
|
+
return {
|
|
5273
|
+
...state,
|
|
5274
|
+
activeTab: nextTab,
|
|
5275
|
+
cursor: 0
|
|
5276
|
+
};
|
|
5277
|
+
}
|
|
5278
|
+
const visible = getFilteredTabOptions(state, state.activeTab);
|
|
5279
|
+
const count = visible.length;
|
|
5280
|
+
if (key === "\x1B[A" || key === "\x1BOA" || !state.searchMode && (key === "k" || key === "K")) {
|
|
5281
|
+
if (count === 0) {
|
|
5282
|
+
return state;
|
|
5283
|
+
}
|
|
5219
5284
|
const nextCursor = (state.cursor - 1 + count) % count;
|
|
5220
5285
|
return {
|
|
5221
5286
|
...state,
|
|
5222
5287
|
cursor: nextCursor
|
|
5223
5288
|
};
|
|
5224
5289
|
}
|
|
5225
|
-
if (key === "\x1B[B" || key === "\x1BOB" || key === "j" || key === "J") {
|
|
5290
|
+
if (key === "\x1B[B" || key === "\x1BOB" || !state.searchMode && (key === "j" || key === "J")) {
|
|
5291
|
+
if (count === 0) {
|
|
5292
|
+
return state;
|
|
5293
|
+
}
|
|
5226
5294
|
const nextCursor = (state.cursor + 1) % count;
|
|
5227
5295
|
return {
|
|
5228
5296
|
...state,
|
|
@@ -5230,7 +5298,7 @@ function applyMultiSelectKey(state, key) {
|
|
|
5230
5298
|
};
|
|
5231
5299
|
}
|
|
5232
5300
|
if (key === " ") {
|
|
5233
|
-
const currentOpt =
|
|
5301
|
+
const currentOpt = visible[state.cursor];
|
|
5234
5302
|
if (!currentOpt) {
|
|
5235
5303
|
return state;
|
|
5236
5304
|
}
|
|
@@ -5245,11 +5313,71 @@ function applyMultiSelectKey(state, key) {
|
|
|
5245
5313
|
selected: nextSelected
|
|
5246
5314
|
};
|
|
5247
5315
|
}
|
|
5316
|
+
if (state.searchMode) {
|
|
5317
|
+
if (key === "\x7F" || key === "\b") {
|
|
5318
|
+
if (state.searchQuery.length === 0) {
|
|
5319
|
+
return {
|
|
5320
|
+
...state,
|
|
5321
|
+
searchMode: false,
|
|
5322
|
+
cursor: 0
|
|
5323
|
+
};
|
|
5324
|
+
}
|
|
5325
|
+
const nextQuery = state.searchQuery.slice(0, -1);
|
|
5326
|
+
return {
|
|
5327
|
+
...state,
|
|
5328
|
+
searchQuery: nextQuery,
|
|
5329
|
+
cursor: 0
|
|
5330
|
+
};
|
|
5331
|
+
}
|
|
5332
|
+
if (/^[a-zA-Z0-9_.-]$/.test(key)) {
|
|
5333
|
+
const nextQuery = `${state.searchQuery}${key}`;
|
|
5334
|
+
const nextState = {
|
|
5335
|
+
...state,
|
|
5336
|
+
searchQuery: nextQuery,
|
|
5337
|
+
cursor: 0
|
|
5338
|
+
};
|
|
5339
|
+
const currentMatches = getFilteredTabOptions(nextState, state.activeTab);
|
|
5340
|
+
if (currentMatches.length === 0) {
|
|
5341
|
+
const otherTab = state.activeTab === "skill" ? "agent" : "skill";
|
|
5342
|
+
const otherMatches = getFilteredTabOptions(nextState, otherTab);
|
|
5343
|
+
if (otherMatches.length > 0) {
|
|
5344
|
+
return {
|
|
5345
|
+
...nextState,
|
|
5346
|
+
activeTab: otherTab
|
|
5347
|
+
};
|
|
5348
|
+
}
|
|
5349
|
+
}
|
|
5350
|
+
return nextState;
|
|
5351
|
+
}
|
|
5352
|
+
return state;
|
|
5353
|
+
}
|
|
5354
|
+
if (key === "/") {
|
|
5355
|
+
return {
|
|
5356
|
+
...state,
|
|
5357
|
+
searchMode: true,
|
|
5358
|
+
cursor: 0
|
|
5359
|
+
};
|
|
5360
|
+
}
|
|
5361
|
+
if (key === "q" || key === "Q") {
|
|
5362
|
+
return {
|
|
5363
|
+
...state,
|
|
5364
|
+
status: "cancelled"
|
|
5365
|
+
};
|
|
5366
|
+
}
|
|
5248
5367
|
if (key === "a" || key === "A") {
|
|
5249
|
-
|
|
5250
|
-
|
|
5251
|
-
|
|
5252
|
-
|
|
5368
|
+
if (visible.length === 0) {
|
|
5369
|
+
return state;
|
|
5370
|
+
}
|
|
5371
|
+
const allTabSelected = visible.every(
|
|
5372
|
+
(opt) => state.selected.has(opt.value)
|
|
5373
|
+
);
|
|
5374
|
+
const nextSelected = new Set(state.selected);
|
|
5375
|
+
if (allTabSelected) {
|
|
5376
|
+
for (const opt of visible) {
|
|
5377
|
+
nextSelected.delete(opt.value);
|
|
5378
|
+
}
|
|
5379
|
+
} else {
|
|
5380
|
+
for (const opt of visible) {
|
|
5253
5381
|
nextSelected.add(opt.value);
|
|
5254
5382
|
}
|
|
5255
5383
|
}
|
|
@@ -5264,54 +5392,93 @@ function renderMultiSelectFrame(title, state, options = {}) {
|
|
|
5264
5392
|
const c = options.colorEnabled;
|
|
5265
5393
|
const bar = style.dim("\u2502", c);
|
|
5266
5394
|
const maxVisible = options.maxVisible ?? 10;
|
|
5267
|
-
const
|
|
5395
|
+
const totalSkills = state.options.filter(
|
|
5396
|
+
(o) => optionMatchesTab(o, "skill")
|
|
5397
|
+
).length;
|
|
5398
|
+
const totalAgents = state.options.filter(
|
|
5399
|
+
(o) => optionMatchesTab(o, "agent")
|
|
5400
|
+
).length;
|
|
5401
|
+
const filteredSkills = getFilteredTabOptions(state, "skill");
|
|
5402
|
+
const filteredAgents = getFilteredTabOptions(state, "agent");
|
|
5403
|
+
const visible = state.activeTab === "skill" ? filteredSkills : filteredAgents;
|
|
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";
|
|
5268
5408
|
const header = `${style.bold(style.cyan(`\u25C6 ${title}`, c), c)}`;
|
|
5269
|
-
const instructions = `${bar} ${style.dim(
|
|
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)}`;
|
|
5270
5410
|
const lines = [header];
|
|
5271
5411
|
if (options.subtitle) {
|
|
5272
5412
|
lines.push(`${bar} ${options.subtitle}`);
|
|
5273
5413
|
}
|
|
5274
5414
|
lines.push(instructions, bar);
|
|
5275
|
-
|
|
5276
|
-
|
|
5277
|
-
|
|
5278
|
-
|
|
5279
|
-
}
|
|
5280
|
-
const
|
|
5281
|
-
|
|
5282
|
-
|
|
5283
|
-
|
|
5284
|
-
|
|
5285
|
-
26,
|
|
5286
|
-
state.options.reduce((m, o) => Math.max(m, visibleLength(o.label)), 14)
|
|
5415
|
+
const hasQuery = state.searchQuery.length > 0;
|
|
5416
|
+
const skillCountStr = hasQuery ? `${filteredSkills.length}/${totalSkills}` : `${totalSkills}`;
|
|
5417
|
+
const agentCountStr = hasQuery ? `${filteredAgents.length}/${totalAgents}` : `${totalAgents}`;
|
|
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);
|
|
5420
|
+
const rawQuerySlice = state.searchQuery.length > 14 ? `\u2026${state.searchQuery.slice(-13)}` : state.searchQuery;
|
|
5421
|
+
const searchSuffix = state.searchMode || hasQuery ? ` ${style.yellow(`/ Filter: ${rawQuerySlice}${state.searchMode ? "\u2588" : ""}`, c)}` : ` ${style.dim("(/ search)", c)}`;
|
|
5422
|
+
lines.push(
|
|
5423
|
+
`${style.dim("\u251C\u2500", c)} ${skillTabLabel} ${agentTabLabel}${searchSuffix}`,
|
|
5424
|
+
bar
|
|
5287
5425
|
);
|
|
5288
|
-
|
|
5289
|
-
const
|
|
5290
|
-
if (!opt) continue;
|
|
5291
|
-
const isCurrent = i === state.cursor;
|
|
5292
|
-
const isChecked = state.selected.has(opt.value);
|
|
5293
|
-
const pointer = isCurrent ? style.cyan("\u276F", c) : " ";
|
|
5294
|
-
const checkbox = isChecked ? style.green("\u25FC", c) : style.dim("\u25FB", c);
|
|
5295
|
-
const kindTag = opt.kind === "skill" ? style.cyan("skill ", c) : opt.kind === "agent" ? style.magenta("agent ", c) : "";
|
|
5296
|
-
const labelRaw = opt.label.length > maxLabelCols ? `${opt.label.slice(0, maxLabelCols - 1)}\u2026` : opt.label;
|
|
5297
|
-
const labelPadded = padVisible(labelRaw, maxLabelCols + 1, "left");
|
|
5298
|
-
const styledLabel = isCurrent ? style.bold(labelPadded, c) : isChecked ? style.green(labelPadded, c) : labelPadded;
|
|
5299
|
-
const prefixWidth = 6 + (opt.kind === "skill" || opt.kind === "agent" ? 6 : 0) + maxLabelCols + 1;
|
|
5300
|
-
const maxHintCols = Math.max(10, 75 - prefixWidth);
|
|
5301
|
-
const rawHint = opt.hint ?? "";
|
|
5302
|
-
const shortHint = rawHint.length > maxHintCols ? `${rawHint.slice(0, maxHintCols - 1)}\u2026` : rawHint;
|
|
5303
|
-
const hintPart = shortHint ? ` ${style.dim(shortHint, c)}` : "";
|
|
5426
|
+
if (total === 0) {
|
|
5427
|
+
const tabName = state.activeTab === "skill" ? skillTabTitle : agentTabTitle;
|
|
5304
5428
|
lines.push(
|
|
5305
|
-
`${bar} ${
|
|
5429
|
+
`${bar} ${style.dim(`No ${tabName} match "${state.searchQuery}". Press Esc to clear filter.`, c)}`
|
|
5306
5430
|
);
|
|
5307
|
-
}
|
|
5308
|
-
|
|
5309
|
-
|
|
5431
|
+
} else {
|
|
5432
|
+
let startIdx = 0;
|
|
5433
|
+
if (total > maxVisible) {
|
|
5434
|
+
const half = Math.floor(maxVisible / 2);
|
|
5435
|
+
startIdx = Math.max(0, Math.min(state.cursor - half, total - maxVisible));
|
|
5436
|
+
}
|
|
5437
|
+
const endIdx = Math.min(total, startIdx + maxVisible);
|
|
5438
|
+
if (startIdx > 0) {
|
|
5439
|
+
lines.push(`${bar} ${style.dim(`\u2191 ${startIdx} more above...`, c)}`);
|
|
5440
|
+
}
|
|
5441
|
+
const maxLabelCols = Math.min(
|
|
5442
|
+
26,
|
|
5443
|
+
visible.reduce((m, o) => Math.max(m, visibleLength(o.label)), 14)
|
|
5444
|
+
);
|
|
5445
|
+
for (let i = startIdx; i < endIdx; i++) {
|
|
5446
|
+
const opt = visible[i];
|
|
5447
|
+
if (!opt) continue;
|
|
5448
|
+
const isCurrent = i === state.cursor;
|
|
5449
|
+
const isChecked = state.selected.has(opt.value);
|
|
5450
|
+
const pointer = isCurrent ? style.cyan("\u276F", c) : " ";
|
|
5451
|
+
const checkbox = isChecked ? style.green("\u25C9", c) : style.dim("\u25EF", c);
|
|
5452
|
+
const labelRaw = opt.label.length > maxLabelCols ? `${opt.label.slice(0, maxLabelCols - 1)}\u2026` : opt.label;
|
|
5453
|
+
const labelPadded = padVisible(labelRaw, maxLabelCols + 1, "left");
|
|
5454
|
+
const styledLabel = isCurrent ? style.bold(labelPadded, c) : isChecked ? style.green(labelPadded, c) : labelPadded;
|
|
5455
|
+
const prefixWidth = 7 + maxLabelCols + 1;
|
|
5456
|
+
const maxHintCols = Math.max(10, 76 - prefixWidth);
|
|
5457
|
+
const rawHint = opt.hint ?? "";
|
|
5458
|
+
const shortHint = rawHint.length > maxHintCols ? `${rawHint.slice(0, maxHintCols - 1)}\u2026` : rawHint;
|
|
5459
|
+
const hintPart = shortHint ? ` ${style.dim(shortHint, c)}` : "";
|
|
5460
|
+
lines.push(`${bar} ${pointer} ${checkbox} ${styledLabel}${hintPart}`);
|
|
5461
|
+
}
|
|
5462
|
+
if (endIdx < total) {
|
|
5463
|
+
lines.push(
|
|
5464
|
+
`${bar} ${style.dim(`\u2193 ${total - endIdx} more below...`, c)}`
|
|
5465
|
+
);
|
|
5466
|
+
}
|
|
5310
5467
|
}
|
|
5311
5468
|
lines.push(bar);
|
|
5312
|
-
const
|
|
5469
|
+
const selectedSkillsCount = state.options.filter(
|
|
5470
|
+
(o) => optionMatchesTab(o, "skill") && state.selected.has(o.value)
|
|
5471
|
+
).length;
|
|
5472
|
+
const selectedAgentsCount = state.options.filter(
|
|
5473
|
+
(o) => optionMatchesTab(o, "agent") && state.selected.has(o.value)
|
|
5474
|
+
).length;
|
|
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})`;
|
|
5477
|
+
const rawTarget = options.targetHint ?? "";
|
|
5478
|
+
const shortTarget = rawTarget.length > 18 ? `${rawTarget.slice(0, 17)}\u2026` : rawTarget;
|
|
5479
|
+
const targetSuffix = shortTarget ? ` \xB7 IDE: ${shortTarget}` : "";
|
|
5313
5480
|
lines.push(
|
|
5314
|
-
`${style.dim("\u2570\u2500", c)} ${style.bold(
|
|
5481
|
+
`${style.dim("\u2570\u2500", c)} ${style.bold(summaryText, c)}${style.dim(`${targetSuffix} \u2014 Enter to ${submitVerb}`, c)}`
|
|
5315
5482
|
);
|
|
5316
5483
|
return lines.join("\n");
|
|
5317
5484
|
}
|
|
@@ -5348,7 +5515,9 @@ async function runMultiSelectPrompt(title, options, renderOpts = {}) {
|
|
|
5348
5515
|
};
|
|
5349
5516
|
const onData = (chunk) => {
|
|
5350
5517
|
const key = typeof chunk === "string" ? chunk : chunk.toString("utf-8");
|
|
5351
|
-
state = applyMultiSelectKey(state, key
|
|
5518
|
+
state = applyMultiSelectKey(state, key, {
|
|
5519
|
+
selectCursorOnEmptySubmit: renderOpts.selectCursorOnEmptySubmit
|
|
5520
|
+
});
|
|
5352
5521
|
if (state.status === "cancelled") {
|
|
5353
5522
|
if (renderedLineCount > 0) {
|
|
5354
5523
|
process.stdout.write(`\x1B[${renderedLineCount}A\x1B[J`);
|
|
@@ -6181,6 +6350,35 @@ function resolveCatalogDir(args, repoPath, loadedConfig) {
|
|
|
6181
6350
|
}
|
|
6182
6351
|
return path10.join(PACKAGE_ROOT, "catalog");
|
|
6183
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
|
+
}
|
|
6184
6382
|
function buildInteractiveCatalogOptions(catalogDir) {
|
|
6185
6383
|
const options = [];
|
|
6186
6384
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -6904,7 +7102,7 @@ function runCli(argv) {
|
|
|
6904
7102
|
async function runCliInteractive(argv) {
|
|
6905
7103
|
const args = argv.slice(2);
|
|
6906
7104
|
const command = args[0];
|
|
6907
|
-
if (command === "add" && !args.includes("--help") && !args.includes("-h") &&
|
|
7105
|
+
if (command === "add" && !args.includes("--help") && !args.includes("-h") && process.stdin.isTTY && process.stdout.isTTY) {
|
|
6908
7106
|
const repoPath = getFlagValue(args, "--repo", "--project", "-p") ?? process.cwd();
|
|
6909
7107
|
const cfgRes = loadSkillOpsConfig(repoPath);
|
|
6910
7108
|
if (cfgRes.error) {
|
|
@@ -6915,29 +7113,57 @@ async function runCliInteractive(argv) {
|
|
|
6915
7113
|
return ExitCode.BUDGET_OR_CONTRACT_ERROR;
|
|
6916
7114
|
}
|
|
6917
7115
|
const catalogDir = resolveCatalogDir(args, repoPath, cfgRes.config);
|
|
6918
|
-
const
|
|
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
|
+
}
|
|
6919
7141
|
let targets;
|
|
6920
7142
|
try {
|
|
6921
|
-
targets =
|
|
7143
|
+
targets = resolveRuntimeTargets(resolvedTargetsArg, repoPath);
|
|
6922
7144
|
} catch (err) {
|
|
6923
7145
|
const msg = err instanceof Error ? err.message : String(err);
|
|
6924
7146
|
process.stderr.write(`Invalid --targets: ${msg}
|
|
6925
7147
|
`);
|
|
6926
7148
|
return ExitCode.BUDGET_OR_CONTRACT_ERROR;
|
|
6927
7149
|
}
|
|
6928
|
-
const
|
|
6929
|
-
|
|
6930
|
-
"
|
|
6931
|
-
|
|
6932
|
-
|
|
6933
|
-
|
|
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, {
|
|
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;
|
|
6934
7163
|
}
|
|
6935
|
-
|
|
6936
|
-
if (!selected || selected.length === 0) {
|
|
6937
|
-
process.stdout.write("No Skills or Agents selected.\n");
|
|
6938
|
-
return ExitCode.SUCCESS;
|
|
7164
|
+
extraArgs.push(...selected);
|
|
6939
7165
|
}
|
|
6940
|
-
return runCli([...argv, ...
|
|
7166
|
+
return runCli([...argv, ...extraArgs]);
|
|
6941
7167
|
}
|
|
6942
7168
|
return runCli(argv);
|
|
6943
7169
|
}
|
|
@@ -6963,6 +7189,7 @@ if (isDirectCliInvocation()) {
|
|
|
6963
7189
|
}
|
|
6964
7190
|
export {
|
|
6965
7191
|
buildInteractiveCatalogOptions,
|
|
7192
|
+
buildInteractiveTargetOptions,
|
|
6966
7193
|
runCli,
|
|
6967
7194
|
runCliInteractive
|
|
6968
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.
|
|
11
|
+
declare const SKILLOPS_VERSION = "1.3.4";
|
|
12
12
|
/**
|
|
13
13
|
* Count whitespace-delimited words in text.
|
|
14
14
|
*/
|
|
@@ -2176,38 +2176,59 @@ declare function renderCommandHelp(spec: CommandHelpSpec, version: string, color
|
|
|
2176
2176
|
/**
|
|
2177
2177
|
* Zero-dependency interactive terminal multi-select prompt (`src/ui/prompt.ts`).
|
|
2178
2178
|
*
|
|
2179
|
-
* Supports
|
|
2180
|
-
*
|
|
2179
|
+
* Supports:
|
|
2180
|
+
* - Horizontal tabs (`←`/`→` or `Tab`) separating `Skills` and `Agents`
|
|
2181
|
+
* - Vertical navigation (`↑`/`↓` or `j`/`k`)
|
|
2182
|
+
* - Real-time keyword filtering (`/` to search, `Backspace` to edit, `Esc` to clear)
|
|
2183
|
+
* - `Space` to toggle selection across tabs, `a` to toggle all in current tab, `Enter` to confirm
|
|
2181
2184
|
*/
|
|
2185
|
+
type MultiSelectTab = "skill" | "agent";
|
|
2182
2186
|
interface MultiSelectOption {
|
|
2183
2187
|
readonly value: string;
|
|
2184
2188
|
readonly label: string;
|
|
2185
2189
|
readonly kind?: "skill" | "agent" | "ide";
|
|
2190
|
+
readonly tab?: MultiSelectTab;
|
|
2186
2191
|
readonly hint?: string;
|
|
2187
2192
|
readonly initialSelected?: boolean;
|
|
2188
2193
|
}
|
|
2189
2194
|
interface MultiSelectState {
|
|
2190
2195
|
readonly options: readonly MultiSelectOption[];
|
|
2196
|
+
readonly activeTab: MultiSelectTab;
|
|
2191
2197
|
readonly cursor: number;
|
|
2192
2198
|
readonly selected: ReadonlySet<string>;
|
|
2199
|
+
readonly searchMode: boolean;
|
|
2200
|
+
readonly searchQuery: string;
|
|
2193
2201
|
readonly status: "active" | "submitted" | "cancelled";
|
|
2194
2202
|
}
|
|
2203
|
+
interface ApplyMultiSelectKeyOptions {
|
|
2204
|
+
readonly selectCursorOnEmptySubmit?: boolean;
|
|
2205
|
+
}
|
|
2195
2206
|
interface RenderMultiSelectOptions {
|
|
2196
2207
|
readonly subtitle?: string;
|
|
2197
2208
|
readonly targetHint?: string;
|
|
2209
|
+
readonly tabLabels?: {
|
|
2210
|
+
readonly skill: string;
|
|
2211
|
+
readonly agent: string;
|
|
2212
|
+
};
|
|
2213
|
+
readonly submitActionLabel?: string;
|
|
2214
|
+
readonly selectCursorOnEmptySubmit?: boolean;
|
|
2198
2215
|
readonly maxVisible?: number;
|
|
2199
2216
|
readonly colorEnabled?: boolean;
|
|
2200
2217
|
}
|
|
2218
|
+
/**
|
|
2219
|
+
* Return the filtered options for a given tab (`skill` or `agent`) and active `searchQuery`.
|
|
2220
|
+
*/
|
|
2221
|
+
declare function getFilteredTabOptions(state: MultiSelectState, tab?: MultiSelectTab): MultiSelectOption[];
|
|
2201
2222
|
/**
|
|
2202
2223
|
* Initialize a deterministic multi-select state from a list of options.
|
|
2203
2224
|
*/
|
|
2204
2225
|
declare function createMultiSelectState(options: readonly MultiSelectOption[]): MultiSelectState;
|
|
2205
2226
|
/**
|
|
2206
|
-
* Pure state reducer for keyboard input in the multi-select TUI.
|
|
2227
|
+
* Pure state reducer for keyboard input in the tabbed + searchable multi-select TUI.
|
|
2207
2228
|
*/
|
|
2208
|
-
declare function applyMultiSelectKey(state: MultiSelectState, key: string): MultiSelectState;
|
|
2229
|
+
declare function applyMultiSelectKey(state: MultiSelectState, key: string, options?: ApplyMultiSelectKeyOptions): MultiSelectState;
|
|
2209
2230
|
/**
|
|
2210
|
-
* Render a single frame of the multi-select tree prompt (guaranteed <= 78 columns).
|
|
2231
|
+
* Render a single frame of the tabbed + searchable multi-select tree prompt (guaranteed <= 78 columns).
|
|
2211
2232
|
*/
|
|
2212
2233
|
declare function renderMultiSelectFrame(title: string, state: MultiSelectState, options?: RenderMultiSelectOptions): string;
|
|
2213
2234
|
/**
|
|
@@ -2335,4 +2356,4 @@ declare const DEFAULT_VALID_COMMANDS: readonly string[];
|
|
|
2335
2356
|
*/
|
|
2336
2357
|
declare function suggestCommand(unknownCommand: string, validCommands?: readonly string[]): string | null;
|
|
2337
2358
|
|
|
2338
|
-
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 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, 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.
|
|
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]*)$/;
|
|
@@ -5142,6 +5142,10 @@ function renderGlobalHelp(version, colorEnabled) {
|
|
|
5142
5142
|
cmd: "add [name...]",
|
|
5143
5143
|
desc: "Import Skills or Agents (interactive if no name given)"
|
|
5144
5144
|
},
|
|
5145
|
+
{
|
|
5146
|
+
cmd: "search <query>",
|
|
5147
|
+
desc: "Search Skills & Agents by keyword, stack, or domain"
|
|
5148
|
+
},
|
|
5145
5149
|
{
|
|
5146
5150
|
cmd: "list [query]",
|
|
5147
5151
|
desc: "List or filter available Skills & Agents in catalog"
|
|
@@ -5202,6 +5206,27 @@ function renderCommandHelp(spec, version, colorEnabled) {
|
|
|
5202
5206
|
}
|
|
5203
5207
|
|
|
5204
5208
|
// src/ui/prompt.ts
|
|
5209
|
+
function optionMatchesTab(opt, tab) {
|
|
5210
|
+
if (opt.tab) {
|
|
5211
|
+
return opt.tab === tab;
|
|
5212
|
+
}
|
|
5213
|
+
if (opt.kind === "agent") {
|
|
5214
|
+
return tab === "agent";
|
|
5215
|
+
}
|
|
5216
|
+
return tab === "skill";
|
|
5217
|
+
}
|
|
5218
|
+
function optionMatchesQuery(opt, rawQuery) {
|
|
5219
|
+
const q = rawQuery.trim().toLowerCase();
|
|
5220
|
+
if (!q) {
|
|
5221
|
+
return true;
|
|
5222
|
+
}
|
|
5223
|
+
return opt.label.toLowerCase().includes(q) || opt.value.toLowerCase().includes(q) || (opt.hint ?? "").toLowerCase().includes(q);
|
|
5224
|
+
}
|
|
5225
|
+
function getFilteredTabOptions(state, tab = state.activeTab) {
|
|
5226
|
+
return state.options.filter(
|
|
5227
|
+
(opt) => optionMatchesTab(opt, tab) && optionMatchesQuery(opt, state.searchQuery)
|
|
5228
|
+
);
|
|
5229
|
+
}
|
|
5205
5230
|
function createMultiSelectState(options) {
|
|
5206
5231
|
const initial = /* @__PURE__ */ new Set();
|
|
5207
5232
|
for (const opt of options) {
|
|
@@ -5209,38 +5234,81 @@ function createMultiSelectState(options) {
|
|
|
5209
5234
|
initial.add(opt.value);
|
|
5210
5235
|
}
|
|
5211
5236
|
}
|
|
5237
|
+
const hasSkills = options.some((opt) => optionMatchesTab(opt, "skill"));
|
|
5212
5238
|
return {
|
|
5213
5239
|
options,
|
|
5240
|
+
activeTab: hasSkills ? "skill" : "agent",
|
|
5214
5241
|
cursor: 0,
|
|
5215
5242
|
selected: initial,
|
|
5243
|
+
searchMode: false,
|
|
5244
|
+
searchQuery: "",
|
|
5216
5245
|
status: "active"
|
|
5217
5246
|
};
|
|
5218
5247
|
}
|
|
5219
|
-
function applyMultiSelectKey(state, key) {
|
|
5248
|
+
function applyMultiSelectKey(state, key, options = {}) {
|
|
5220
5249
|
if (state.status !== "active" || state.options.length === 0) {
|
|
5221
5250
|
return state;
|
|
5222
5251
|
}
|
|
5223
|
-
|
|
5224
|
-
|
|
5252
|
+
if (key === "") {
|
|
5253
|
+
return {
|
|
5254
|
+
...state,
|
|
5255
|
+
status: "cancelled"
|
|
5256
|
+
};
|
|
5257
|
+
}
|
|
5258
|
+
if (key === "\x1B") {
|
|
5259
|
+
if (state.searchMode || state.searchQuery.length > 0) {
|
|
5260
|
+
return {
|
|
5261
|
+
...state,
|
|
5262
|
+
searchMode: false,
|
|
5263
|
+
searchQuery: "",
|
|
5264
|
+
cursor: 0
|
|
5265
|
+
};
|
|
5266
|
+
}
|
|
5225
5267
|
return {
|
|
5226
5268
|
...state,
|
|
5227
5269
|
status: "cancelled"
|
|
5228
5270
|
};
|
|
5229
5271
|
}
|
|
5230
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
|
+
}
|
|
5231
5281
|
return {
|
|
5232
5282
|
...state,
|
|
5283
|
+
selected: finalSelected,
|
|
5284
|
+
searchMode: false,
|
|
5233
5285
|
status: "submitted"
|
|
5234
5286
|
};
|
|
5235
5287
|
}
|
|
5236
|
-
if (key === "\x1B[
|
|
5288
|
+
if (key === "\x1B[D" || key === "\x1BOD" || key === "\x1B[C" || key === "\x1BOC" || key === " " || !state.searchMode && (key === "h" || key === "H" || key === "l" || key === "L")) {
|
|
5289
|
+
const nextTab = state.activeTab === "skill" ? "agent" : "skill";
|
|
5290
|
+
return {
|
|
5291
|
+
...state,
|
|
5292
|
+
activeTab: nextTab,
|
|
5293
|
+
cursor: 0
|
|
5294
|
+
};
|
|
5295
|
+
}
|
|
5296
|
+
const visible = getFilteredTabOptions(state, state.activeTab);
|
|
5297
|
+
const count = visible.length;
|
|
5298
|
+
if (key === "\x1B[A" || key === "\x1BOA" || !state.searchMode && (key === "k" || key === "K")) {
|
|
5299
|
+
if (count === 0) {
|
|
5300
|
+
return state;
|
|
5301
|
+
}
|
|
5237
5302
|
const nextCursor = (state.cursor - 1 + count) % count;
|
|
5238
5303
|
return {
|
|
5239
5304
|
...state,
|
|
5240
5305
|
cursor: nextCursor
|
|
5241
5306
|
};
|
|
5242
5307
|
}
|
|
5243
|
-
if (key === "\x1B[B" || key === "\x1BOB" || key === "j" || key === "J") {
|
|
5308
|
+
if (key === "\x1B[B" || key === "\x1BOB" || !state.searchMode && (key === "j" || key === "J")) {
|
|
5309
|
+
if (count === 0) {
|
|
5310
|
+
return state;
|
|
5311
|
+
}
|
|
5244
5312
|
const nextCursor = (state.cursor + 1) % count;
|
|
5245
5313
|
return {
|
|
5246
5314
|
...state,
|
|
@@ -5248,7 +5316,7 @@ function applyMultiSelectKey(state, key) {
|
|
|
5248
5316
|
};
|
|
5249
5317
|
}
|
|
5250
5318
|
if (key === " ") {
|
|
5251
|
-
const currentOpt =
|
|
5319
|
+
const currentOpt = visible[state.cursor];
|
|
5252
5320
|
if (!currentOpt) {
|
|
5253
5321
|
return state;
|
|
5254
5322
|
}
|
|
@@ -5263,11 +5331,71 @@ function applyMultiSelectKey(state, key) {
|
|
|
5263
5331
|
selected: nextSelected
|
|
5264
5332
|
};
|
|
5265
5333
|
}
|
|
5334
|
+
if (state.searchMode) {
|
|
5335
|
+
if (key === "\x7F" || key === "\b") {
|
|
5336
|
+
if (state.searchQuery.length === 0) {
|
|
5337
|
+
return {
|
|
5338
|
+
...state,
|
|
5339
|
+
searchMode: false,
|
|
5340
|
+
cursor: 0
|
|
5341
|
+
};
|
|
5342
|
+
}
|
|
5343
|
+
const nextQuery = state.searchQuery.slice(0, -1);
|
|
5344
|
+
return {
|
|
5345
|
+
...state,
|
|
5346
|
+
searchQuery: nextQuery,
|
|
5347
|
+
cursor: 0
|
|
5348
|
+
};
|
|
5349
|
+
}
|
|
5350
|
+
if (/^[a-zA-Z0-9_.-]$/.test(key)) {
|
|
5351
|
+
const nextQuery = `${state.searchQuery}${key}`;
|
|
5352
|
+
const nextState = {
|
|
5353
|
+
...state,
|
|
5354
|
+
searchQuery: nextQuery,
|
|
5355
|
+
cursor: 0
|
|
5356
|
+
};
|
|
5357
|
+
const currentMatches = getFilteredTabOptions(nextState, state.activeTab);
|
|
5358
|
+
if (currentMatches.length === 0) {
|
|
5359
|
+
const otherTab = state.activeTab === "skill" ? "agent" : "skill";
|
|
5360
|
+
const otherMatches = getFilteredTabOptions(nextState, otherTab);
|
|
5361
|
+
if (otherMatches.length > 0) {
|
|
5362
|
+
return {
|
|
5363
|
+
...nextState,
|
|
5364
|
+
activeTab: otherTab
|
|
5365
|
+
};
|
|
5366
|
+
}
|
|
5367
|
+
}
|
|
5368
|
+
return nextState;
|
|
5369
|
+
}
|
|
5370
|
+
return state;
|
|
5371
|
+
}
|
|
5372
|
+
if (key === "/") {
|
|
5373
|
+
return {
|
|
5374
|
+
...state,
|
|
5375
|
+
searchMode: true,
|
|
5376
|
+
cursor: 0
|
|
5377
|
+
};
|
|
5378
|
+
}
|
|
5379
|
+
if (key === "q" || key === "Q") {
|
|
5380
|
+
return {
|
|
5381
|
+
...state,
|
|
5382
|
+
status: "cancelled"
|
|
5383
|
+
};
|
|
5384
|
+
}
|
|
5266
5385
|
if (key === "a" || key === "A") {
|
|
5267
|
-
|
|
5268
|
-
|
|
5269
|
-
|
|
5270
|
-
|
|
5386
|
+
if (visible.length === 0) {
|
|
5387
|
+
return state;
|
|
5388
|
+
}
|
|
5389
|
+
const allTabSelected = visible.every(
|
|
5390
|
+
(opt) => state.selected.has(opt.value)
|
|
5391
|
+
);
|
|
5392
|
+
const nextSelected = new Set(state.selected);
|
|
5393
|
+
if (allTabSelected) {
|
|
5394
|
+
for (const opt of visible) {
|
|
5395
|
+
nextSelected.delete(opt.value);
|
|
5396
|
+
}
|
|
5397
|
+
} else {
|
|
5398
|
+
for (const opt of visible) {
|
|
5271
5399
|
nextSelected.add(opt.value);
|
|
5272
5400
|
}
|
|
5273
5401
|
}
|
|
@@ -5282,54 +5410,93 @@ function renderMultiSelectFrame(title, state, options = {}) {
|
|
|
5282
5410
|
const c = options.colorEnabled;
|
|
5283
5411
|
const bar = style.dim("\u2502", c);
|
|
5284
5412
|
const maxVisible = options.maxVisible ?? 10;
|
|
5285
|
-
const
|
|
5413
|
+
const totalSkills = state.options.filter(
|
|
5414
|
+
(o) => optionMatchesTab(o, "skill")
|
|
5415
|
+
).length;
|
|
5416
|
+
const totalAgents = state.options.filter(
|
|
5417
|
+
(o) => optionMatchesTab(o, "agent")
|
|
5418
|
+
).length;
|
|
5419
|
+
const filteredSkills = getFilteredTabOptions(state, "skill");
|
|
5420
|
+
const filteredAgents = getFilteredTabOptions(state, "agent");
|
|
5421
|
+
const visible = state.activeTab === "skill" ? filteredSkills : filteredAgents;
|
|
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";
|
|
5286
5426
|
const header = `${style.bold(style.cyan(`\u25C6 ${title}`, c), c)}`;
|
|
5287
|
-
const instructions = `${bar} ${style.dim(
|
|
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)}`;
|
|
5288
5428
|
const lines = [header];
|
|
5289
5429
|
if (options.subtitle) {
|
|
5290
5430
|
lines.push(`${bar} ${options.subtitle}`);
|
|
5291
5431
|
}
|
|
5292
5432
|
lines.push(instructions, bar);
|
|
5293
|
-
|
|
5294
|
-
|
|
5295
|
-
|
|
5296
|
-
|
|
5297
|
-
}
|
|
5298
|
-
const
|
|
5299
|
-
|
|
5300
|
-
|
|
5301
|
-
|
|
5302
|
-
|
|
5303
|
-
26,
|
|
5304
|
-
state.options.reduce((m, o) => Math.max(m, visibleLength(o.label)), 14)
|
|
5433
|
+
const hasQuery = state.searchQuery.length > 0;
|
|
5434
|
+
const skillCountStr = hasQuery ? `${filteredSkills.length}/${totalSkills}` : `${totalSkills}`;
|
|
5435
|
+
const agentCountStr = hasQuery ? `${filteredAgents.length}/${totalAgents}` : `${totalAgents}`;
|
|
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);
|
|
5438
|
+
const rawQuerySlice = state.searchQuery.length > 14 ? `\u2026${state.searchQuery.slice(-13)}` : state.searchQuery;
|
|
5439
|
+
const searchSuffix = state.searchMode || hasQuery ? ` ${style.yellow(`/ Filter: ${rawQuerySlice}${state.searchMode ? "\u2588" : ""}`, c)}` : ` ${style.dim("(/ search)", c)}`;
|
|
5440
|
+
lines.push(
|
|
5441
|
+
`${style.dim("\u251C\u2500", c)} ${skillTabLabel} ${agentTabLabel}${searchSuffix}`,
|
|
5442
|
+
bar
|
|
5305
5443
|
);
|
|
5306
|
-
|
|
5307
|
-
const
|
|
5308
|
-
if (!opt) continue;
|
|
5309
|
-
const isCurrent = i === state.cursor;
|
|
5310
|
-
const isChecked = state.selected.has(opt.value);
|
|
5311
|
-
const pointer = isCurrent ? style.cyan("\u276F", c) : " ";
|
|
5312
|
-
const checkbox = isChecked ? style.green("\u25FC", c) : style.dim("\u25FB", c);
|
|
5313
|
-
const kindTag = opt.kind === "skill" ? style.cyan("skill ", c) : opt.kind === "agent" ? style.magenta("agent ", c) : "";
|
|
5314
|
-
const labelRaw = opt.label.length > maxLabelCols ? `${opt.label.slice(0, maxLabelCols - 1)}\u2026` : opt.label;
|
|
5315
|
-
const labelPadded = padVisible(labelRaw, maxLabelCols + 1, "left");
|
|
5316
|
-
const styledLabel = isCurrent ? style.bold(labelPadded, c) : isChecked ? style.green(labelPadded, c) : labelPadded;
|
|
5317
|
-
const prefixWidth = 6 + (opt.kind === "skill" || opt.kind === "agent" ? 6 : 0) + maxLabelCols + 1;
|
|
5318
|
-
const maxHintCols = Math.max(10, 75 - prefixWidth);
|
|
5319
|
-
const rawHint = opt.hint ?? "";
|
|
5320
|
-
const shortHint = rawHint.length > maxHintCols ? `${rawHint.slice(0, maxHintCols - 1)}\u2026` : rawHint;
|
|
5321
|
-
const hintPart = shortHint ? ` ${style.dim(shortHint, c)}` : "";
|
|
5444
|
+
if (total === 0) {
|
|
5445
|
+
const tabName = state.activeTab === "skill" ? skillTabTitle : agentTabTitle;
|
|
5322
5446
|
lines.push(
|
|
5323
|
-
`${bar} ${
|
|
5447
|
+
`${bar} ${style.dim(`No ${tabName} match "${state.searchQuery}". Press Esc to clear filter.`, c)}`
|
|
5324
5448
|
);
|
|
5325
|
-
}
|
|
5326
|
-
|
|
5327
|
-
|
|
5449
|
+
} else {
|
|
5450
|
+
let startIdx = 0;
|
|
5451
|
+
if (total > maxVisible) {
|
|
5452
|
+
const half = Math.floor(maxVisible / 2);
|
|
5453
|
+
startIdx = Math.max(0, Math.min(state.cursor - half, total - maxVisible));
|
|
5454
|
+
}
|
|
5455
|
+
const endIdx = Math.min(total, startIdx + maxVisible);
|
|
5456
|
+
if (startIdx > 0) {
|
|
5457
|
+
lines.push(`${bar} ${style.dim(`\u2191 ${startIdx} more above...`, c)}`);
|
|
5458
|
+
}
|
|
5459
|
+
const maxLabelCols = Math.min(
|
|
5460
|
+
26,
|
|
5461
|
+
visible.reduce((m, o) => Math.max(m, visibleLength(o.label)), 14)
|
|
5462
|
+
);
|
|
5463
|
+
for (let i = startIdx; i < endIdx; i++) {
|
|
5464
|
+
const opt = visible[i];
|
|
5465
|
+
if (!opt) continue;
|
|
5466
|
+
const isCurrent = i === state.cursor;
|
|
5467
|
+
const isChecked = state.selected.has(opt.value);
|
|
5468
|
+
const pointer = isCurrent ? style.cyan("\u276F", c) : " ";
|
|
5469
|
+
const checkbox = isChecked ? style.green("\u25C9", c) : style.dim("\u25EF", c);
|
|
5470
|
+
const labelRaw = opt.label.length > maxLabelCols ? `${opt.label.slice(0, maxLabelCols - 1)}\u2026` : opt.label;
|
|
5471
|
+
const labelPadded = padVisible(labelRaw, maxLabelCols + 1, "left");
|
|
5472
|
+
const styledLabel = isCurrent ? style.bold(labelPadded, c) : isChecked ? style.green(labelPadded, c) : labelPadded;
|
|
5473
|
+
const prefixWidth = 7 + maxLabelCols + 1;
|
|
5474
|
+
const maxHintCols = Math.max(10, 76 - prefixWidth);
|
|
5475
|
+
const rawHint = opt.hint ?? "";
|
|
5476
|
+
const shortHint = rawHint.length > maxHintCols ? `${rawHint.slice(0, maxHintCols - 1)}\u2026` : rawHint;
|
|
5477
|
+
const hintPart = shortHint ? ` ${style.dim(shortHint, c)}` : "";
|
|
5478
|
+
lines.push(`${bar} ${pointer} ${checkbox} ${styledLabel}${hintPart}`);
|
|
5479
|
+
}
|
|
5480
|
+
if (endIdx < total) {
|
|
5481
|
+
lines.push(
|
|
5482
|
+
`${bar} ${style.dim(`\u2193 ${total - endIdx} more below...`, c)}`
|
|
5483
|
+
);
|
|
5484
|
+
}
|
|
5328
5485
|
}
|
|
5329
5486
|
lines.push(bar);
|
|
5330
|
-
const
|
|
5487
|
+
const selectedSkillsCount = state.options.filter(
|
|
5488
|
+
(o) => optionMatchesTab(o, "skill") && state.selected.has(o.value)
|
|
5489
|
+
).length;
|
|
5490
|
+
const selectedAgentsCount = state.options.filter(
|
|
5491
|
+
(o) => optionMatchesTab(o, "agent") && state.selected.has(o.value)
|
|
5492
|
+
).length;
|
|
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})`;
|
|
5495
|
+
const rawTarget = options.targetHint ?? "";
|
|
5496
|
+
const shortTarget = rawTarget.length > 18 ? `${rawTarget.slice(0, 17)}\u2026` : rawTarget;
|
|
5497
|
+
const targetSuffix = shortTarget ? ` \xB7 IDE: ${shortTarget}` : "";
|
|
5331
5498
|
lines.push(
|
|
5332
|
-
`${style.dim("\u2570\u2500", c)} ${style.bold(
|
|
5499
|
+
`${style.dim("\u2570\u2500", c)} ${style.bold(summaryText, c)}${style.dim(`${targetSuffix} \u2014 Enter to ${submitVerb}`, c)}`
|
|
5333
5500
|
);
|
|
5334
5501
|
return lines.join("\n");
|
|
5335
5502
|
}
|
|
@@ -5366,7 +5533,9 @@ async function runMultiSelectPrompt(title, options, renderOpts = {}) {
|
|
|
5366
5533
|
};
|
|
5367
5534
|
const onData = (chunk) => {
|
|
5368
5535
|
const key = typeof chunk === "string" ? chunk : chunk.toString("utf-8");
|
|
5369
|
-
state = applyMultiSelectKey(state, key
|
|
5536
|
+
state = applyMultiSelectKey(state, key, {
|
|
5537
|
+
selectCursorOnEmptySubmit: renderOpts.selectCursorOnEmptySubmit
|
|
5538
|
+
});
|
|
5370
5539
|
if (state.status === "cancelled") {
|
|
5371
5540
|
if (renderedLineCount > 0) {
|
|
5372
5541
|
process.stdout.write(`\x1B[${renderedLineCount}A\x1B[J`);
|
|
@@ -6185,6 +6354,7 @@ export {
|
|
|
6185
6354
|
formatScaffoldReport,
|
|
6186
6355
|
formatSyncReport,
|
|
6187
6356
|
formatTokenEconomyReport,
|
|
6357
|
+
getFilteredTabOptions,
|
|
6188
6358
|
handleMcpJsonRpcRequest,
|
|
6189
6359
|
inspectCatalogItem,
|
|
6190
6360
|
inspectHarnesses,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "skillops-cli",
|
|
3
|
-
"version": "1.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",
|