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/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.3";
|
|
12
12
|
/**
|
|
13
13
|
* Count whitespace-delimited words in text.
|
|
14
14
|
*/
|
|
@@ -2165,7 +2165,7 @@ declare const COMMAND_HELP_SPECS: readonly CommandHelpSpec[];
|
|
|
2165
2165
|
*/
|
|
2166
2166
|
declare function findCommandHelpSpec(commandOrAlias: string): CommandHelpSpec | undefined;
|
|
2167
2167
|
/**
|
|
2168
|
-
* Render the compact, emoji-free
|
|
2168
|
+
* Render the compact, emoji-free global help tree (`npx skillops-cli --help`) focused on the 5 core commands.
|
|
2169
2169
|
*/
|
|
2170
2170
|
declare function renderGlobalHelp(version: string, colorEnabled?: boolean): string;
|
|
2171
2171
|
/**
|
|
@@ -2173,6 +2173,60 @@ declare function renderGlobalHelp(version: string, colorEnabled?: boolean): stri
|
|
|
2173
2173
|
*/
|
|
2174
2174
|
declare function renderCommandHelp(spec: CommandHelpSpec, version: string, colorEnabled?: boolean): string;
|
|
2175
2175
|
|
|
2176
|
+
/**
|
|
2177
|
+
* Zero-dependency interactive terminal multi-select prompt (`src/ui/prompt.ts`).
|
|
2178
|
+
*
|
|
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
|
|
2184
|
+
*/
|
|
2185
|
+
type MultiSelectTab = "skill" | "agent";
|
|
2186
|
+
interface MultiSelectOption {
|
|
2187
|
+
readonly value: string;
|
|
2188
|
+
readonly label: string;
|
|
2189
|
+
readonly kind?: "skill" | "agent" | "ide";
|
|
2190
|
+
readonly hint?: string;
|
|
2191
|
+
readonly initialSelected?: boolean;
|
|
2192
|
+
}
|
|
2193
|
+
interface MultiSelectState {
|
|
2194
|
+
readonly options: readonly MultiSelectOption[];
|
|
2195
|
+
readonly activeTab: MultiSelectTab;
|
|
2196
|
+
readonly cursor: number;
|
|
2197
|
+
readonly selected: ReadonlySet<string>;
|
|
2198
|
+
readonly searchMode: boolean;
|
|
2199
|
+
readonly searchQuery: string;
|
|
2200
|
+
readonly status: "active" | "submitted" | "cancelled";
|
|
2201
|
+
}
|
|
2202
|
+
interface RenderMultiSelectOptions {
|
|
2203
|
+
readonly subtitle?: string;
|
|
2204
|
+
readonly targetHint?: string;
|
|
2205
|
+
readonly maxVisible?: number;
|
|
2206
|
+
readonly colorEnabled?: boolean;
|
|
2207
|
+
}
|
|
2208
|
+
/**
|
|
2209
|
+
* Return the filtered options for a given tab (`skill` or `agent`) and active `searchQuery`.
|
|
2210
|
+
*/
|
|
2211
|
+
declare function getFilteredTabOptions(state: MultiSelectState, tab?: MultiSelectTab): MultiSelectOption[];
|
|
2212
|
+
/**
|
|
2213
|
+
* Initialize a deterministic multi-select state from a list of options.
|
|
2214
|
+
*/
|
|
2215
|
+
declare function createMultiSelectState(options: readonly MultiSelectOption[]): MultiSelectState;
|
|
2216
|
+
/**
|
|
2217
|
+
* Pure state reducer for keyboard input in the tabbed + searchable multi-select TUI.
|
|
2218
|
+
*/
|
|
2219
|
+
declare function applyMultiSelectKey(state: MultiSelectState, key: string): MultiSelectState;
|
|
2220
|
+
/**
|
|
2221
|
+
* Render a single frame of the tabbed + searchable multi-select tree prompt (guaranteed <= 78 columns).
|
|
2222
|
+
*/
|
|
2223
|
+
declare function renderMultiSelectFrame(title: string, state: MultiSelectState, options?: RenderMultiSelectOptions): string;
|
|
2224
|
+
/**
|
|
2225
|
+
* Run an interactive multi-select prompt in a TTY terminal.
|
|
2226
|
+
* Returns the array of selected values, or `null` if cancelled by user.
|
|
2227
|
+
*/
|
|
2228
|
+
declare function runMultiSelectPrompt(title: string, options: readonly MultiSelectOption[], renderOpts?: RenderMultiSelectOptions): Promise<string[] | null>;
|
|
2229
|
+
|
|
2176
2230
|
/**
|
|
2177
2231
|
* SkillOps Zero-Dependency Terminal UI (TUI) Aesthetic Engine.
|
|
2178
2232
|
*
|
|
@@ -2292,4 +2346,4 @@ declare const DEFAULT_VALID_COMMANDS: readonly string[];
|
|
|
2292
2346
|
*/
|
|
2293
2347
|
declare function suggestCommand(unknownCommand: string, validCommands?: readonly string[]): string | null;
|
|
2294
2348
|
|
|
2295
|
-
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 NextStepHint, type ParseSkillOptions, type ParsedSkillDirectory, type ParsedSubagentFile, type QuickstartOptions, type QuickstartReport, RUNTIME_HARNESS_SPECS, type RemoveOptions, 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, auditCatalog, auditCatalogTokenBudgets, auditSkillTokenBudget, auditSubagentTokenBudget, classifyCollisionSeverity, cleanSyncedArtifacts, compileSkillBundle, computeCollisionMatrix, computeFeatureCosine, computeIdfWeights, computeSha256, countWords, createCollisionPair, createCollisionReport, 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, renderNextSteps, renderProgressBar, renderSectionHeader, renderStep, renderTable, renderTokenBar, resolveRuntimeTargets, routeRepository, runQuickstart, scaffoldFrameworkItem, scoreSkillAgainstProfile, searchCatalog, setupMcpForHarnesses, splitFrontmatter, startMcpStdioServer, stripAnsi, stripCodexAgentsBlock, stripNegativeTriggerClauses, style, suggestCommand, syncRepository, tokenizeRoutingText, visibleLength };
|
|
2349
|
+
export { ALL_RUNTIME_TARGETS, type AddOptions, type AnalyzeTokenEconomyOptions, type AuditCatalogOptions, type BadgeType, type BenchmarkArm, BenchmarkArmSchema, type BenchmarkDelta, BenchmarkDeltaSchema, type BoxOptions, COMMAND_HELP_SPECS, CORE_RUNTIME_TARGETS, type CatalogEvalSummary, CatalogEvalSummarySchema, type CatalogItemInfo, type CatalogSearchMatch, type CatalogSearchReport, type CatalogTokenBudgetSummary, type CatalogTrack, CatalogTrackSchema, type CleanOptions, type CollisionPair, CollisionPairSchema, type CollisionReport, CollisionReportSchema, type CollisionSeverity, CollisionSeveritySchema, type ColorOverrideOptions, type CommandHelpSpec, type CommandOptionSpec, DEFAULT_VALID_COMMANDS, type DiagnoseRepositoryOptions, type DoctorCheckItem, type DoctorReport, type EvalAssertion, EvalAssertionSchema, type EvalCase, EvalCaseSchema, ExitCode, type ExitCodeValue, type HarnessCategory, type HarnessInspectionEntry, type HarnessInspectionReport, type HarnessSkillFormat, type JsonRpcRequest, type JsonRpcResponse, type LoadedSkillOpsConfigResult, type LockfileArtifactDiagnostic, type McpConfigAction, type McpServerContext, type McpSetupOptions, type McpSetupReport, type MultiSelectOption, type MultiSelectState, type MultiSelectTab, type NextStepHint, type ParseSkillOptions, type ParsedSkillDirectory, type ParsedSubagentFile, type QuickstartOptions, type QuickstartReport, RUNTIME_HARNESS_SPECS, type RemoveOptions, type RenderMultiSelectOptions, RepoFingerprinter, type RepoProfile, RepoProfileSchema, type RouteOptions, type RoutedBundle, RoutedBundleSchema, type RoutingSignals, RoutingSignalsSchema, type RuntimeHarnessSpec, type RuntimeTarget, RuntimeTargetSchema, SKILLOPS_VERSION, type ScaffoldOptions, type ScaffoldResult, type ScoredSkill, ScoredSkillSchema, type ScriptEntrypoint, ScriptEntrypointSchema, type SkillBenchmark, SkillBenchmarkSchema, type SkillEntrypoints, SkillEntrypointsSchema, type SkillEvalSuite, SkillEvalSuiteSchema, type SkillManifest, type SkillManifestInput, SkillManifestSchema, type SkillOpsConfig, SkillOpsConfigSchema, type SkillOpsDetectedStackConfig, SkillOpsDetectedStackConfigSchema, type SkillOpsTokenEconomyConfig, SkillOpsTokenEconomyConfigSchema, type SkillTokenBudgetAudit, type SkippedSkill, SkippedSkillSchema, type SubagentManifest, SubagentManifestSchema, type SubagentTokenBudgetAudit, type SyncLockEntry, SyncLockEntrySchema, type SyncLockfile, SyncLockfileSchema, type SyncOptions, type SyncReport, SyncReportSchema, type SyncedArtifact, SyncedArtifactSchema, type TableOptions, type TokenBarOptions, type TokenEconomyReport, TokenEconomyReportSchema, type TokenFileCategory, TokenFileCategorySchema, type TokenFileEntry, TokenFileEntrySchema, type TokenWasteHotspot, TokenWasteHotspotSchema, addSkillOrSubagent, analyzeRepoTokenEconomy, applyMultiSelectKey, auditCatalog, auditCatalogTokenBudgets, auditSkillTokenBudget, auditSubagentTokenBudget, classifyCollisionSeverity, cleanSyncedArtifacts, compileSkillBundle, computeCollisionMatrix, computeFeatureCosine, computeIdfWeights, computeSha256, countWords, createCollisionPair, createCollisionReport, createMultiSelectState, createSkillManifest, createSubagentManifest, detectInstalledHarnesses, diagnoseRepository, discoverSkillDirectories, discoverSubagentFiles, estimateTokenCount, evaluateCatalog, evaluateSkillDirectory, extractSkillFeatures, findCommandHelpSpec, fingerprintRepository, formatBadge, formatCatalogItemInfo, formatCatalogSearchReport, formatDoctorReport, formatEvalSummary, formatHarnessInspectionReport, formatMcpSetupReport, formatQuickstartReport, formatRoutedBundle, formatScaffoldReport, formatSyncReport, formatTokenEconomyReport, getFilteredTabOptions, handleMcpJsonRpcRequest, inspectCatalogItem, inspectHarnesses, isColorEnabled, levenshteinDistance, loadSkillOpsConfig, normalizePackageName, padVisible, parseSkillDirectory, parseSkillMdFile, parseSubagentFile, parseSubagentMdFile, removeSkillOrSubagent, renderAiUsageGuide, renderBox, renderCodexAgentsIndex, renderCommandHelp, renderGlobalHelp, renderHeaderBanner, renderMultiSelectFrame, renderNextSteps, renderProgressBar, renderSectionHeader, renderStep, renderTable, renderTokenBar, resolveRuntimeTargets, routeRepository, runMultiSelectPrompt, runQuickstart, scaffoldFrameworkItem, scoreSkillAgainstProfile, searchCatalog, setupMcpForHarnesses, splitFrontmatter, startMcpStdioServer, stripAnsi, stripCodexAgentsBlock, stripNegativeTriggerClauses, style, suggestCommand, syncRepository, tokenizeRoutingText, visibleLength };
|
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.3";
|
|
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]*)$/;
|
|
@@ -5131,45 +5131,56 @@ function renderGlobalHelp(version, colorEnabled) {
|
|
|
5131
5131
|
const c = colorEnabled;
|
|
5132
5132
|
const bar = style.dim("\u2502", c);
|
|
5133
5133
|
const banner = renderHeaderBanner(
|
|
5134
|
-
"
|
|
5134
|
+
"Skills & Agents Library for AI Coding IDEs",
|
|
5135
5135
|
{
|
|
5136
5136
|
version,
|
|
5137
5137
|
colorEnabled: c
|
|
5138
5138
|
}
|
|
5139
5139
|
);
|
|
5140
|
-
const
|
|
5141
|
-
|
|
5142
|
-
|
|
5143
|
-
|
|
5144
|
-
|
|
5145
|
-
|
|
5146
|
-
|
|
5147
|
-
|
|
5148
|
-
|
|
5149
|
-
|
|
5150
|
-
|
|
5151
|
-
|
|
5140
|
+
const coreCmds = [
|
|
5141
|
+
{
|
|
5142
|
+
cmd: "add [name...]",
|
|
5143
|
+
desc: "Import Skills or Agents (interactive if no name given)"
|
|
5144
|
+
},
|
|
5145
|
+
{
|
|
5146
|
+
cmd: "search <query>",
|
|
5147
|
+
desc: "Search Skills & Agents by keyword, stack, or domain"
|
|
5148
|
+
},
|
|
5149
|
+
{
|
|
5150
|
+
cmd: "list [query]",
|
|
5151
|
+
desc: "List or filter available Skills & Agents in catalog"
|
|
5152
|
+
},
|
|
5153
|
+
{
|
|
5154
|
+
cmd: "info <name>",
|
|
5155
|
+
desc: "View details and how to use a Skill or Agent"
|
|
5156
|
+
},
|
|
5157
|
+
{
|
|
5158
|
+
cmd: "remove <name>",
|
|
5159
|
+
desc: "Remove an imported Skill or Agent from your project"
|
|
5160
|
+
},
|
|
5161
|
+
{
|
|
5162
|
+
cmd: "init",
|
|
5163
|
+
desc: "Auto-detect stack & IDE and import recommended bundle"
|
|
5164
|
+
}
|
|
5165
|
+
];
|
|
5166
|
+
const cmdRows = coreCmds.map(
|
|
5167
|
+
(item) => `${bar} ${padVisible(style.bold(style.cyan(item.cmd, c), c), 18, "left")}${item.desc}`
|
|
5168
|
+
).join("\n");
|
|
5152
5169
|
return [
|
|
5153
5170
|
banner,
|
|
5154
|
-
`${bar} Usage:
|
|
5155
|
-
renderSectionHeader("
|
|
5156
|
-
|
|
5157
|
-
|
|
5158
|
-
`${bar} ${
|
|
5159
|
-
|
|
5160
|
-
|
|
5161
|
-
renderSectionHeader(
|
|
5162
|
-
|
|
5163
|
-
|
|
5164
|
-
),
|
|
5165
|
-
formatCmdSection("framework"),
|
|
5166
|
-
renderSectionHeader(
|
|
5167
|
-
"3. Universal Runtime Sync, Token Economy & Diagnostics",
|
|
5168
|
-
c
|
|
5169
|
-
),
|
|
5170
|
-
formatCmdSection("runtime"),
|
|
5171
|
+
`${bar} Usage: skillops <command> [options]`,
|
|
5172
|
+
renderSectionHeader("Commands", c),
|
|
5173
|
+
cmdRows,
|
|
5174
|
+
renderSectionHeader("Options", c),
|
|
5175
|
+
`${bar} ${padVisible(style.cyan("-t, --target <ide>", c), 22, "left")}AI IDE (cursor, claude, copilot, windsurf, auto)`,
|
|
5176
|
+
`${bar} ${padVisible(style.cyan("-p, --project <dir>", c), 22, "left")}Project folder (default: current directory)`,
|
|
5177
|
+
`${bar} ${padVisible(style.cyan("--all", c), 22, "left")}Import all 10 Skills & 9 Agents`,
|
|
5178
|
+
renderSectionHeader("Examples", c),
|
|
5179
|
+
`${bar} ${style.dim("$", c)} ${style.green("skillops add", c)} ${style.dim("\u2014 Interactive selector", c)}`,
|
|
5180
|
+
`${bar} ${style.dim("$", c)} ${style.green("skillops add bug-reproducer-tdd red-team-breaker", c)} ${style.dim("\u2014 Direct import", c)}`,
|
|
5181
|
+
`${bar} ${style.dim("$", c)} ${style.green("skillops list", c)} ${style.dim("\u2014 Browse catalog", c)}`,
|
|
5171
5182
|
bar,
|
|
5172
|
-
`${style.dim("\u2570\u2500", c)} ${style.
|
|
5183
|
+
`${style.dim("\u2570\u2500", c)} ${style.dim("Run", c)} ${style.cyan("skillops <cmd> -h", c)} ${style.dim("for command options.", c)}`
|
|
5173
5184
|
].join("\n");
|
|
5174
5185
|
}
|
|
5175
5186
|
function renderCommandHelp(spec, version, colorEnabled) {
|
|
@@ -5194,6 +5205,341 @@ function renderCommandHelp(spec, version, colorEnabled) {
|
|
|
5194
5205
|
return [headerLines, optionsSection, examplesSection].filter(Boolean).join("\n");
|
|
5195
5206
|
}
|
|
5196
5207
|
|
|
5208
|
+
// src/ui/prompt.ts
|
|
5209
|
+
function optionMatchesTab(opt, tab) {
|
|
5210
|
+
if (opt.kind === "agent") {
|
|
5211
|
+
return tab === "agent";
|
|
5212
|
+
}
|
|
5213
|
+
return tab === "skill";
|
|
5214
|
+
}
|
|
5215
|
+
function optionMatchesQuery(opt, rawQuery) {
|
|
5216
|
+
const q = rawQuery.trim().toLowerCase();
|
|
5217
|
+
if (!q) {
|
|
5218
|
+
return true;
|
|
5219
|
+
}
|
|
5220
|
+
return opt.label.toLowerCase().includes(q) || opt.value.toLowerCase().includes(q) || (opt.hint ?? "").toLowerCase().includes(q);
|
|
5221
|
+
}
|
|
5222
|
+
function getFilteredTabOptions(state, tab = state.activeTab) {
|
|
5223
|
+
return state.options.filter(
|
|
5224
|
+
(opt) => optionMatchesTab(opt, tab) && optionMatchesQuery(opt, state.searchQuery)
|
|
5225
|
+
);
|
|
5226
|
+
}
|
|
5227
|
+
function createMultiSelectState(options) {
|
|
5228
|
+
const initial = /* @__PURE__ */ new Set();
|
|
5229
|
+
for (const opt of options) {
|
|
5230
|
+
if (opt.initialSelected) {
|
|
5231
|
+
initial.add(opt.value);
|
|
5232
|
+
}
|
|
5233
|
+
}
|
|
5234
|
+
const hasSkills = options.some((opt) => optionMatchesTab(opt, "skill"));
|
|
5235
|
+
return {
|
|
5236
|
+
options,
|
|
5237
|
+
activeTab: hasSkills ? "skill" : "agent",
|
|
5238
|
+
cursor: 0,
|
|
5239
|
+
selected: initial,
|
|
5240
|
+
searchMode: false,
|
|
5241
|
+
searchQuery: "",
|
|
5242
|
+
status: "active"
|
|
5243
|
+
};
|
|
5244
|
+
}
|
|
5245
|
+
function applyMultiSelectKey(state, key) {
|
|
5246
|
+
if (state.status !== "active" || state.options.length === 0) {
|
|
5247
|
+
return state;
|
|
5248
|
+
}
|
|
5249
|
+
if (key === "") {
|
|
5250
|
+
return {
|
|
5251
|
+
...state,
|
|
5252
|
+
status: "cancelled"
|
|
5253
|
+
};
|
|
5254
|
+
}
|
|
5255
|
+
if (key === "\x1B") {
|
|
5256
|
+
if (state.searchMode || state.searchQuery.length > 0) {
|
|
5257
|
+
return {
|
|
5258
|
+
...state,
|
|
5259
|
+
searchMode: false,
|
|
5260
|
+
searchQuery: "",
|
|
5261
|
+
cursor: 0
|
|
5262
|
+
};
|
|
5263
|
+
}
|
|
5264
|
+
return {
|
|
5265
|
+
...state,
|
|
5266
|
+
status: "cancelled"
|
|
5267
|
+
};
|
|
5268
|
+
}
|
|
5269
|
+
if (key === "\r" || key === "\n") {
|
|
5270
|
+
return {
|
|
5271
|
+
...state,
|
|
5272
|
+
searchMode: false,
|
|
5273
|
+
status: "submitted"
|
|
5274
|
+
};
|
|
5275
|
+
}
|
|
5276
|
+
if (key === "\x1B[D" || key === "\x1BOD" || key === "\x1B[C" || key === "\x1BOC" || key === " " || !state.searchMode && (key === "h" || key === "H" || key === "l" || key === "L")) {
|
|
5277
|
+
const nextTab = state.activeTab === "skill" ? "agent" : "skill";
|
|
5278
|
+
return {
|
|
5279
|
+
...state,
|
|
5280
|
+
activeTab: nextTab,
|
|
5281
|
+
cursor: 0
|
|
5282
|
+
};
|
|
5283
|
+
}
|
|
5284
|
+
const visible = getFilteredTabOptions(state, state.activeTab);
|
|
5285
|
+
const count = visible.length;
|
|
5286
|
+
if (key === "\x1B[A" || key === "\x1BOA" || !state.searchMode && (key === "k" || key === "K")) {
|
|
5287
|
+
if (count === 0) {
|
|
5288
|
+
return state;
|
|
5289
|
+
}
|
|
5290
|
+
const nextCursor = (state.cursor - 1 + count) % count;
|
|
5291
|
+
return {
|
|
5292
|
+
...state,
|
|
5293
|
+
cursor: nextCursor
|
|
5294
|
+
};
|
|
5295
|
+
}
|
|
5296
|
+
if (key === "\x1B[B" || key === "\x1BOB" || !state.searchMode && (key === "j" || key === "J")) {
|
|
5297
|
+
if (count === 0) {
|
|
5298
|
+
return state;
|
|
5299
|
+
}
|
|
5300
|
+
const nextCursor = (state.cursor + 1) % count;
|
|
5301
|
+
return {
|
|
5302
|
+
...state,
|
|
5303
|
+
cursor: nextCursor
|
|
5304
|
+
};
|
|
5305
|
+
}
|
|
5306
|
+
if (key === " ") {
|
|
5307
|
+
const currentOpt = visible[state.cursor];
|
|
5308
|
+
if (!currentOpt) {
|
|
5309
|
+
return state;
|
|
5310
|
+
}
|
|
5311
|
+
const nextSelected = new Set(state.selected);
|
|
5312
|
+
if (nextSelected.has(currentOpt.value)) {
|
|
5313
|
+
nextSelected.delete(currentOpt.value);
|
|
5314
|
+
} else {
|
|
5315
|
+
nextSelected.add(currentOpt.value);
|
|
5316
|
+
}
|
|
5317
|
+
return {
|
|
5318
|
+
...state,
|
|
5319
|
+
selected: nextSelected
|
|
5320
|
+
};
|
|
5321
|
+
}
|
|
5322
|
+
if (state.searchMode) {
|
|
5323
|
+
if (key === "\x7F" || key === "\b") {
|
|
5324
|
+
if (state.searchQuery.length === 0) {
|
|
5325
|
+
return {
|
|
5326
|
+
...state,
|
|
5327
|
+
searchMode: false,
|
|
5328
|
+
cursor: 0
|
|
5329
|
+
};
|
|
5330
|
+
}
|
|
5331
|
+
const nextQuery = state.searchQuery.slice(0, -1);
|
|
5332
|
+
return {
|
|
5333
|
+
...state,
|
|
5334
|
+
searchQuery: nextQuery,
|
|
5335
|
+
cursor: 0
|
|
5336
|
+
};
|
|
5337
|
+
}
|
|
5338
|
+
if (/^[a-zA-Z0-9_.-]$/.test(key)) {
|
|
5339
|
+
const nextQuery = `${state.searchQuery}${key}`;
|
|
5340
|
+
const nextState = {
|
|
5341
|
+
...state,
|
|
5342
|
+
searchQuery: nextQuery,
|
|
5343
|
+
cursor: 0
|
|
5344
|
+
};
|
|
5345
|
+
const currentMatches = getFilteredTabOptions(nextState, state.activeTab);
|
|
5346
|
+
if (currentMatches.length === 0) {
|
|
5347
|
+
const otherTab = state.activeTab === "skill" ? "agent" : "skill";
|
|
5348
|
+
const otherMatches = getFilteredTabOptions(nextState, otherTab);
|
|
5349
|
+
if (otherMatches.length > 0) {
|
|
5350
|
+
return {
|
|
5351
|
+
...nextState,
|
|
5352
|
+
activeTab: otherTab
|
|
5353
|
+
};
|
|
5354
|
+
}
|
|
5355
|
+
}
|
|
5356
|
+
return nextState;
|
|
5357
|
+
}
|
|
5358
|
+
return state;
|
|
5359
|
+
}
|
|
5360
|
+
if (key === "/") {
|
|
5361
|
+
return {
|
|
5362
|
+
...state,
|
|
5363
|
+
searchMode: true,
|
|
5364
|
+
cursor: 0
|
|
5365
|
+
};
|
|
5366
|
+
}
|
|
5367
|
+
if (key === "q" || key === "Q") {
|
|
5368
|
+
return {
|
|
5369
|
+
...state,
|
|
5370
|
+
status: "cancelled"
|
|
5371
|
+
};
|
|
5372
|
+
}
|
|
5373
|
+
if (key === "a" || key === "A") {
|
|
5374
|
+
if (visible.length === 0) {
|
|
5375
|
+
return state;
|
|
5376
|
+
}
|
|
5377
|
+
const allTabSelected = visible.every(
|
|
5378
|
+
(opt) => state.selected.has(opt.value)
|
|
5379
|
+
);
|
|
5380
|
+
const nextSelected = new Set(state.selected);
|
|
5381
|
+
if (allTabSelected) {
|
|
5382
|
+
for (const opt of visible) {
|
|
5383
|
+
nextSelected.delete(opt.value);
|
|
5384
|
+
}
|
|
5385
|
+
} else {
|
|
5386
|
+
for (const opt of visible) {
|
|
5387
|
+
nextSelected.add(opt.value);
|
|
5388
|
+
}
|
|
5389
|
+
}
|
|
5390
|
+
return {
|
|
5391
|
+
...state,
|
|
5392
|
+
selected: nextSelected
|
|
5393
|
+
};
|
|
5394
|
+
}
|
|
5395
|
+
return state;
|
|
5396
|
+
}
|
|
5397
|
+
function renderMultiSelectFrame(title, state, options = {}) {
|
|
5398
|
+
const c = options.colorEnabled;
|
|
5399
|
+
const bar = style.dim("\u2502", c);
|
|
5400
|
+
const maxVisible = options.maxVisible ?? 10;
|
|
5401
|
+
const totalSkills = state.options.filter(
|
|
5402
|
+
(o) => optionMatchesTab(o, "skill")
|
|
5403
|
+
).length;
|
|
5404
|
+
const totalAgents = state.options.filter(
|
|
5405
|
+
(o) => optionMatchesTab(o, "agent")
|
|
5406
|
+
).length;
|
|
5407
|
+
const filteredSkills = getFilteredTabOptions(state, "skill");
|
|
5408
|
+
const filteredAgents = getFilteredTabOptions(state, "agent");
|
|
5409
|
+
const visible = state.activeTab === "skill" ? filteredSkills : filteredAgents;
|
|
5410
|
+
const total = visible.length;
|
|
5411
|
+
const header = `${style.bold(style.cyan(`\u25C6 ${title}`, c), c)}`;
|
|
5412
|
+
const instructions = state.searchMode ? `${bar} ${style.dim("\u2191/\u2193 move \xB7 \u2190/\u2192 switch tab \xB7 Space select \xB7 Esc clear search \xB7 Enter import", c)}` : `${bar} ${style.dim("\u2191/\u2193 move \xB7 \u2190/\u2192 switch tab \xB7 Space select \xB7 / search \xB7 a all \xB7 Enter import", c)}`;
|
|
5413
|
+
const lines = [header];
|
|
5414
|
+
if (options.subtitle) {
|
|
5415
|
+
lines.push(`${bar} ${options.subtitle}`);
|
|
5416
|
+
}
|
|
5417
|
+
lines.push(instructions, bar);
|
|
5418
|
+
const hasQuery = state.searchQuery.length > 0;
|
|
5419
|
+
const skillCountStr = hasQuery ? `${filteredSkills.length}/${totalSkills}` : `${totalSkills}`;
|
|
5420
|
+
const agentCountStr = hasQuery ? `${filteredAgents.length}/${totalAgents}` : `${totalAgents}`;
|
|
5421
|
+
const skillTabLabel = state.activeTab === "skill" ? style.bold(style.cyan(`[ \u25C6 Skills (${skillCountStr}) ]`, c), c) : style.dim(`\u25CB Skills (${skillCountStr})`, c);
|
|
5422
|
+
const agentTabLabel = state.activeTab === "agent" ? style.bold(style.magenta(`[ \u25C6 Agents (${agentCountStr}) ]`, c), c) : style.dim(`\u25CB Agents (${agentCountStr})`, c);
|
|
5423
|
+
const rawQuerySlice = state.searchQuery.length > 14 ? `\u2026${state.searchQuery.slice(-13)}` : state.searchQuery;
|
|
5424
|
+
const searchSuffix = state.searchMode || hasQuery ? ` ${style.yellow(`/ Filter: ${rawQuerySlice}${state.searchMode ? "\u2588" : ""}`, c)}` : ` ${style.dim("(/ search)", c)}`;
|
|
5425
|
+
lines.push(
|
|
5426
|
+
`${style.dim("\u251C\u2500", c)} ${skillTabLabel} ${agentTabLabel}${searchSuffix}`,
|
|
5427
|
+
bar
|
|
5428
|
+
);
|
|
5429
|
+
if (total === 0) {
|
|
5430
|
+
const tabName = state.activeTab === "skill" ? "Skills" : "Agents";
|
|
5431
|
+
lines.push(
|
|
5432
|
+
`${bar} ${style.dim(`No ${tabName} match "${state.searchQuery}". Press Esc to clear filter.`, c)}`
|
|
5433
|
+
);
|
|
5434
|
+
} else {
|
|
5435
|
+
let startIdx = 0;
|
|
5436
|
+
if (total > maxVisible) {
|
|
5437
|
+
const half = Math.floor(maxVisible / 2);
|
|
5438
|
+
startIdx = Math.max(0, Math.min(state.cursor - half, total - maxVisible));
|
|
5439
|
+
}
|
|
5440
|
+
const endIdx = Math.min(total, startIdx + maxVisible);
|
|
5441
|
+
if (startIdx > 0) {
|
|
5442
|
+
lines.push(`${bar} ${style.dim(`\u2191 ${startIdx} more above...`, c)}`);
|
|
5443
|
+
}
|
|
5444
|
+
const maxLabelCols = Math.min(
|
|
5445
|
+
26,
|
|
5446
|
+
visible.reduce((m, o) => Math.max(m, visibleLength(o.label)), 14)
|
|
5447
|
+
);
|
|
5448
|
+
for (let i = startIdx; i < endIdx; i++) {
|
|
5449
|
+
const opt = visible[i];
|
|
5450
|
+
if (!opt) continue;
|
|
5451
|
+
const isCurrent = i === state.cursor;
|
|
5452
|
+
const isChecked = state.selected.has(opt.value);
|
|
5453
|
+
const pointer = isCurrent ? style.cyan("\u276F", c) : " ";
|
|
5454
|
+
const checkbox = isChecked ? style.green("\u25C9", c) : style.dim("\u25EF", c);
|
|
5455
|
+
const labelRaw = opt.label.length > maxLabelCols ? `${opt.label.slice(0, maxLabelCols - 1)}\u2026` : opt.label;
|
|
5456
|
+
const labelPadded = padVisible(labelRaw, maxLabelCols + 1, "left");
|
|
5457
|
+
const styledLabel = isCurrent ? style.bold(labelPadded, c) : isChecked ? style.green(labelPadded, c) : labelPadded;
|
|
5458
|
+
const prefixWidth = 7 + maxLabelCols + 1;
|
|
5459
|
+
const maxHintCols = Math.max(10, 76 - prefixWidth);
|
|
5460
|
+
const rawHint = opt.hint ?? "";
|
|
5461
|
+
const shortHint = rawHint.length > maxHintCols ? `${rawHint.slice(0, maxHintCols - 1)}\u2026` : rawHint;
|
|
5462
|
+
const hintPart = shortHint ? ` ${style.dim(shortHint, c)}` : "";
|
|
5463
|
+
lines.push(`${bar} ${pointer} ${checkbox} ${styledLabel}${hintPart}`);
|
|
5464
|
+
}
|
|
5465
|
+
if (endIdx < total) {
|
|
5466
|
+
lines.push(
|
|
5467
|
+
`${bar} ${style.dim(`\u2193 ${total - endIdx} more below...`, c)}`
|
|
5468
|
+
);
|
|
5469
|
+
}
|
|
5470
|
+
}
|
|
5471
|
+
lines.push(bar);
|
|
5472
|
+
const selectedSkillsCount = state.options.filter(
|
|
5473
|
+
(o) => optionMatchesTab(o, "skill") && state.selected.has(o.value)
|
|
5474
|
+
).length;
|
|
5475
|
+
const selectedAgentsCount = state.options.filter(
|
|
5476
|
+
(o) => optionMatchesTab(o, "agent") && state.selected.has(o.value)
|
|
5477
|
+
).length;
|
|
5478
|
+
const summaryText = `${state.selected.size} selected (${selectedSkillsCount} Skills, ${selectedAgentsCount} Agents)`;
|
|
5479
|
+
const rawTarget = options.targetHint ?? "";
|
|
5480
|
+
const shortTarget = rawTarget.length > 18 ? `${rawTarget.slice(0, 17)}\u2026` : rawTarget;
|
|
5481
|
+
const targetSuffix = shortTarget ? ` \xB7 IDE: ${shortTarget}` : "";
|
|
5482
|
+
lines.push(
|
|
5483
|
+
`${style.dim("\u2570\u2500", c)} ${style.bold(summaryText, c)}${style.dim(`${targetSuffix} \u2014 Enter to import`, c)}`
|
|
5484
|
+
);
|
|
5485
|
+
return lines.join("\n");
|
|
5486
|
+
}
|
|
5487
|
+
async function runMultiSelectPrompt(title, options, renderOpts = {}) {
|
|
5488
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
5489
|
+
return null;
|
|
5490
|
+
}
|
|
5491
|
+
let state = createMultiSelectState(options);
|
|
5492
|
+
let renderedLineCount = 0;
|
|
5493
|
+
const render = () => {
|
|
5494
|
+
const frame = renderMultiSelectFrame(title, state, renderOpts);
|
|
5495
|
+
if (renderedLineCount > 0) {
|
|
5496
|
+
process.stdout.write(`\x1B[${renderedLineCount}A\x1B[J`);
|
|
5497
|
+
}
|
|
5498
|
+
process.stdout.write(`${frame}
|
|
5499
|
+
`);
|
|
5500
|
+
renderedLineCount = frame.split("\n").length;
|
|
5501
|
+
};
|
|
5502
|
+
return new Promise((resolve) => {
|
|
5503
|
+
const stdin = process.stdin;
|
|
5504
|
+
const wasRaw = stdin.isRaw;
|
|
5505
|
+
stdin.setRawMode(true);
|
|
5506
|
+
stdin.resume();
|
|
5507
|
+
stdin.setEncoding("utf-8");
|
|
5508
|
+
process.stdout.write("\x1B[?25l");
|
|
5509
|
+
render();
|
|
5510
|
+
const cleanup = () => {
|
|
5511
|
+
stdin.removeListener("data", onData);
|
|
5512
|
+
if (stdin.isTTY) {
|
|
5513
|
+
stdin.setRawMode(Boolean(wasRaw));
|
|
5514
|
+
}
|
|
5515
|
+
stdin.pause();
|
|
5516
|
+
process.stdout.write("\x1B[?25h");
|
|
5517
|
+
};
|
|
5518
|
+
const onData = (chunk) => {
|
|
5519
|
+
const key = typeof chunk === "string" ? chunk : chunk.toString("utf-8");
|
|
5520
|
+
state = applyMultiSelectKey(state, key);
|
|
5521
|
+
if (state.status === "cancelled") {
|
|
5522
|
+
if (renderedLineCount > 0) {
|
|
5523
|
+
process.stdout.write(`\x1B[${renderedLineCount}A\x1B[J`);
|
|
5524
|
+
}
|
|
5525
|
+
cleanup();
|
|
5526
|
+
resolve(null);
|
|
5527
|
+
return;
|
|
5528
|
+
}
|
|
5529
|
+
if (state.status === "submitted") {
|
|
5530
|
+
if (renderedLineCount > 0) {
|
|
5531
|
+
process.stdout.write(`\x1B[${renderedLineCount}A\x1B[J`);
|
|
5532
|
+
}
|
|
5533
|
+
cleanup();
|
|
5534
|
+
resolve([...state.selected]);
|
|
5535
|
+
return;
|
|
5536
|
+
}
|
|
5537
|
+
render();
|
|
5538
|
+
};
|
|
5539
|
+
stdin.on("data", onData);
|
|
5540
|
+
});
|
|
5541
|
+
}
|
|
5542
|
+
|
|
5197
5543
|
// src/framework/index.ts
|
|
5198
5544
|
function extractMarkdownHeadings(markdownBody) {
|
|
5199
5545
|
const headings = [];
|
|
@@ -5950,6 +6296,7 @@ export {
|
|
|
5950
6296
|
TokenWasteHotspotSchema,
|
|
5951
6297
|
addSkillOrSubagent,
|
|
5952
6298
|
analyzeRepoTokenEconomy,
|
|
6299
|
+
applyMultiSelectKey,
|
|
5953
6300
|
auditCatalog,
|
|
5954
6301
|
auditCatalogTokenBudgets,
|
|
5955
6302
|
auditSkillTokenBudget,
|
|
@@ -5964,6 +6311,7 @@ export {
|
|
|
5964
6311
|
countWords,
|
|
5965
6312
|
createCollisionPair,
|
|
5966
6313
|
createCollisionReport,
|
|
6314
|
+
createMultiSelectState,
|
|
5967
6315
|
createSkillManifest,
|
|
5968
6316
|
createSubagentManifest,
|
|
5969
6317
|
detectInstalledHarnesses,
|
|
@@ -5988,6 +6336,7 @@ export {
|
|
|
5988
6336
|
formatScaffoldReport,
|
|
5989
6337
|
formatSyncReport,
|
|
5990
6338
|
formatTokenEconomyReport,
|
|
6339
|
+
getFilteredTabOptions,
|
|
5991
6340
|
handleMcpJsonRpcRequest,
|
|
5992
6341
|
inspectCatalogItem,
|
|
5993
6342
|
inspectHarnesses,
|
|
@@ -6007,6 +6356,7 @@ export {
|
|
|
6007
6356
|
renderCommandHelp,
|
|
6008
6357
|
renderGlobalHelp,
|
|
6009
6358
|
renderHeaderBanner,
|
|
6359
|
+
renderMultiSelectFrame,
|
|
6010
6360
|
renderNextSteps,
|
|
6011
6361
|
renderProgressBar,
|
|
6012
6362
|
renderSectionHeader,
|
|
@@ -6015,6 +6365,7 @@ export {
|
|
|
6015
6365
|
renderTokenBar,
|
|
6016
6366
|
resolveRuntimeTargets,
|
|
6017
6367
|
routeRepository,
|
|
6368
|
+
runMultiSelectPrompt,
|
|
6018
6369
|
runQuickstart,
|
|
6019
6370
|
scaffoldFrameworkItem,
|
|
6020
6371
|
scoreSkillAgainstProfile,
|