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/README.md +52 -49
- package/dist/cli.js +486 -45
- package/dist/index.d.ts +57 -3
- package/dist/index.js +383 -32
- package/package.json +84 -84
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.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
|
-
"
|
|
5116
|
+
"Skills & Agents Library for AI Coding IDEs",
|
|
5117
5117
|
{
|
|
5118
5118
|
version,
|
|
5119
5119
|
colorEnabled: c
|
|
5120
5120
|
}
|
|
5121
5121
|
);
|
|
5122
|
-
const
|
|
5123
|
-
|
|
5124
|
-
|
|
5125
|
-
|
|
5126
|
-
|
|
5127
|
-
|
|
5128
|
-
|
|
5129
|
-
|
|
5130
|
-
|
|
5131
|
-
|
|
5132
|
-
|
|
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:
|
|
5137
|
-
renderSectionHeader("
|
|
5138
|
-
|
|
5139
|
-
|
|
5140
|
-
`${bar} ${
|
|
5141
|
-
|
|
5142
|
-
|
|
5143
|
-
renderSectionHeader(
|
|
5144
|
-
|
|
5145
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
6469
|
-
|
|
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
|
-
|
|
6476
|
-
|
|
6477
|
-
|
|
6478
|
-
|
|
6863
|
+
})
|
|
6864
|
+
);
|
|
6865
|
+
const firstReport = reports[0];
|
|
6866
|
+
if (!firstReport) {
|
|
6867
|
+
return ExitCode.ERROR;
|
|
6479
6868
|
}
|
|
6480
|
-
|
|
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
|
-
|
|
6674
|
-
|
|
7111
|
+
runCliInteractive(process.argv).then((code) => {
|
|
7112
|
+
process.exit(code);
|
|
7113
|
+
});
|
|
6675
7114
|
}
|
|
6676
7115
|
export {
|
|
6677
|
-
|
|
7116
|
+
buildInteractiveCatalogOptions,
|
|
7117
|
+
runCli,
|
|
7118
|
+
runCliInteractive
|
|
6678
7119
|
};
|