ccski 2.3.2 → 2.5.0
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 +57 -9
- package/dist/cli.mjs +89 -5
- package/dist/cli.mjs.map +1 -1
- package/dist/{esm-CWBkGBbQ.mjs → esm-C5JN5Xg3.mjs} +2 -2
- package/dist/{esm-CWBkGBbQ.mjs.map → esm-C5JN5Xg3.mjs.map} +1 -1
- package/dist/index.d.mts +93 -26
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +2 -2
- package/dist/{server-DwuqhC__.mjs → workflow-install-KvZzb83I.mjs} +455 -312
- package/dist/workflow-install-KvZzb83I.mjs.map +1 -0
- package/package.json +1 -1
- package/dist/server-DwuqhC__.mjs.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { S as __toESM, _ as isSpaceKey, a as useRef, b as __commonJS, c as esm_default$1, d as useState, f as withPointer, g as isNumberKey, h as isEnterKey, i as useKeypress, l as require_yoctocolors_cjs, m as isDownKey, n as breakLines, o as usePrefix, p as ValidationError$1, r as readlineWidth, s as makeTheme, t as createPrompt, u as useEffect, x as __require, y as isUpKey } from "./create-prompt-CIJV_96B.mjs";
|
|
2
|
-
import { cpSync, existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, statSync } from "node:fs";
|
|
2
|
+
import { cpSync, existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { TextDecoder } from "node:util";
|
|
4
4
|
import { homedir, tmpdir } from "node:os";
|
|
5
5
|
import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
@@ -14088,7 +14088,10 @@ function parseSkillDir(value) {
|
|
|
14088
14088
|
}
|
|
14089
14089
|
function buildRegistryOptions(argv$1, extras = {}) {
|
|
14090
14090
|
const options = {};
|
|
14091
|
-
if (
|
|
14091
|
+
if (argv$1.customDirs?.length) {
|
|
14092
|
+
options.customDirs = argv$1.customDirs;
|
|
14093
|
+
options.customProvider = argv$1.customProvider ?? "file";
|
|
14094
|
+
} else if (Array.isArray(argv$1.skillDir)) {
|
|
14092
14095
|
options.customDirs = argv$1.skillDir.map(parseSkillDir).map((p) => ({
|
|
14093
14096
|
path: p.path,
|
|
14094
14097
|
scope: p.scope ?? "other"
|
|
@@ -14105,28 +14108,6 @@ function buildRegistryOptions(argv$1, extras = {}) {
|
|
|
14105
14108
|
};
|
|
14106
14109
|
}
|
|
14107
14110
|
|
|
14108
|
-
//#endregion
|
|
14109
|
-
//#region src/api/filters.ts
|
|
14110
|
-
function resolveFilters(options, parseOptions = {}) {
|
|
14111
|
-
const includeArgs = options.include;
|
|
14112
|
-
const { includes, excludes } = parseFilters(!includeArgs?.length && options.all ? ["all"] : includeArgs, options.exclude, parseOptions);
|
|
14113
|
-
const state = options.disabled ? "disabled" : options.all ? "all" : "enabled";
|
|
14114
|
-
return {
|
|
14115
|
-
includes,
|
|
14116
|
-
excludes,
|
|
14117
|
-
state,
|
|
14118
|
-
includeDisabled: state === "all" || state === "disabled"
|
|
14119
|
-
};
|
|
14120
|
-
}
|
|
14121
|
-
|
|
14122
|
-
//#endregion
|
|
14123
|
-
//#region src/api/list.ts
|
|
14124
|
-
async function listSkills(options = {}) {
|
|
14125
|
-
const registry = new SkillRegistry(buildRegistryOptions(options, { includeDisabled: resolveFilters(options).includeDisabled }));
|
|
14126
|
-
const { includes, excludes, state } = resolveFilters(options, { providers: providerNamesFromSkills(registry.getAll()) });
|
|
14127
|
-
return applyFilters(registry.getAll(), includes, excludes, state).sort((a, b) => a.name.localeCompare(b.name));
|
|
14128
|
-
}
|
|
14129
|
-
|
|
14130
14111
|
//#endregion
|
|
14131
14112
|
//#region src/utils/resolution.ts
|
|
14132
14113
|
function matchesName(skill, target) {
|
|
@@ -14156,6 +14137,20 @@ function resolveSelectors(skills, selectors) {
|
|
|
14156
14137
|
return selected;
|
|
14157
14138
|
}
|
|
14158
14139
|
|
|
14140
|
+
//#endregion
|
|
14141
|
+
//#region src/api/filters.ts
|
|
14142
|
+
function resolveFilters(options, parseOptions = {}) {
|
|
14143
|
+
const includeArgs = options.include;
|
|
14144
|
+
const { includes, excludes } = parseFilters(!includeArgs?.length && options.all ? ["all"] : includeArgs, options.exclude, parseOptions);
|
|
14145
|
+
const state = options.disabled ? "disabled" : options.all ? "all" : "enabled";
|
|
14146
|
+
return {
|
|
14147
|
+
includes,
|
|
14148
|
+
excludes,
|
|
14149
|
+
state,
|
|
14150
|
+
includeDisabled: state === "all" || state === "disabled"
|
|
14151
|
+
};
|
|
14152
|
+
}
|
|
14153
|
+
|
|
14159
14154
|
//#endregion
|
|
14160
14155
|
//#region src/api/info.ts
|
|
14161
14156
|
async function getSkillInfo(options) {
|
|
@@ -14182,94 +14177,6 @@ async function getSkillInfo(options) {
|
|
|
14182
14177
|
};
|
|
14183
14178
|
}
|
|
14184
14179
|
|
|
14185
|
-
//#endregion
|
|
14186
|
-
//#region src/api/search.ts
|
|
14187
|
-
function buildSearchContext(options) {
|
|
14188
|
-
const registry = new SkillRegistry(buildRegistryOptions(options, { includeDisabled: resolveFilters(options).includeDisabled }));
|
|
14189
|
-
const { includes, excludes, state } = resolveFilters(options, { providers: providerNamesFromSkills(registry.getAll()) });
|
|
14190
|
-
const skills = applyFilters(registry.getAll(), includes, excludes, state);
|
|
14191
|
-
const ranked = rankStrings(skills.map((skill) => `${skill.name} ${skill.description}`), options.query);
|
|
14192
|
-
const picked = ranked.length > 0 ? ranked.map((i$4) => skills[i$4]) : skills;
|
|
14193
|
-
return {
|
|
14194
|
-
registry,
|
|
14195
|
-
matches: options.content ? picked.filter((skill) => {
|
|
14196
|
-
const resolved = resolveSkill(skills, skill.name);
|
|
14197
|
-
const content = registry.load(`${resolved.provider}:${resolved.name}`).content;
|
|
14198
|
-
return containsCaseInsensitive(content, options.query);
|
|
14199
|
-
}) : picked.filter((skill) => containsCaseInsensitive(`${skill.name} ${skill.description}`, options.query))
|
|
14200
|
-
};
|
|
14201
|
-
}
|
|
14202
|
-
function searchSkillsDetailed(options) {
|
|
14203
|
-
const { matches } = buildSearchContext(options);
|
|
14204
|
-
return matches;
|
|
14205
|
-
}
|
|
14206
|
-
async function searchSkills(options) {
|
|
14207
|
-
return searchSkillsDetailed(options).map((skill) => ({
|
|
14208
|
-
name: skill.name,
|
|
14209
|
-
description: skill.description,
|
|
14210
|
-
location: skill.location,
|
|
14211
|
-
provider: skill.provider,
|
|
14212
|
-
disabled: skill.disabled ?? false,
|
|
14213
|
-
path: skill.path
|
|
14214
|
-
}));
|
|
14215
|
-
}
|
|
14216
|
-
|
|
14217
|
-
//#endregion
|
|
14218
|
-
//#region src/api/validate.ts
|
|
14219
|
-
async function validateSkill(options) {
|
|
14220
|
-
const registry = new SkillRegistry(buildRegistryOptions(options));
|
|
14221
|
-
const { includes, excludes, state } = resolveFilters(options, { providers: providerNamesFromSkills(registry.getAll()) });
|
|
14222
|
-
const filtered = applyFilters(registry.getAll(), includes, excludes, state);
|
|
14223
|
-
const target = resolve(options.path);
|
|
14224
|
-
const skillFile = resolveSkillFile(target);
|
|
14225
|
-
if (!skillFile) throw new Error(`Could not find SKILL.md at ${target}`);
|
|
14226
|
-
const result = validateSkillFile(skillFile);
|
|
14227
|
-
const provider = inferProvider(skillFile, filtered);
|
|
14228
|
-
const codexIssues = [];
|
|
14229
|
-
const codexWarnings = [];
|
|
14230
|
-
if (result.success && provider === "codex") try {
|
|
14231
|
-
const parsed = parseSkillFile(skillFile);
|
|
14232
|
-
codexIssues.push(...codexRuleViolations(parsed.frontmatter.name, parsed.frontmatter.description));
|
|
14233
|
-
if (lstatSync(skillFile).isSymbolicLink()) codexWarnings.push(`SKILL.md is a symlink (${skillFile})`);
|
|
14234
|
-
if (lstatSync(resolve(skillFile, "..")).isSymbolicLink()) codexWarnings.push(`Skill directory is a symlink (${resolve(skillFile, "..")})`);
|
|
14235
|
-
} catch {}
|
|
14236
|
-
const errors = [...result.errors, ...codexIssues];
|
|
14237
|
-
const warnings = [...result.suggestions, ...codexWarnings];
|
|
14238
|
-
return {
|
|
14239
|
-
file: skillFile,
|
|
14240
|
-
success: errors.length === 0,
|
|
14241
|
-
errors,
|
|
14242
|
-
warnings
|
|
14243
|
-
};
|
|
14244
|
-
}
|
|
14245
|
-
function resolveSkillFile(input) {
|
|
14246
|
-
if (!existsSync(input)) return null;
|
|
14247
|
-
const stats = statSync(input);
|
|
14248
|
-
if (stats.isDirectory()) {
|
|
14249
|
-
const candidate = join(input, "SKILL.md");
|
|
14250
|
-
return existsSync(candidate) ? candidate : null;
|
|
14251
|
-
}
|
|
14252
|
-
if (stats.isFile() && input.toLowerCase().endsWith("skill.md")) return input;
|
|
14253
|
-
return null;
|
|
14254
|
-
}
|
|
14255
|
-
function inferProvider(skillPath, filtered) {
|
|
14256
|
-
const lower = skillPath.toLowerCase();
|
|
14257
|
-
const match = filtered.find((s) => s.path === resolve(skillPath, ".."));
|
|
14258
|
-
if (match) return match.provider;
|
|
14259
|
-
const provider = lower.split("/").find((segment) => segment.startsWith("."))?.slice(1);
|
|
14260
|
-
if (provider === "agent" || provider === "agents") return "agents";
|
|
14261
|
-
if (provider && lower.includes(`/.${provider}/skills`)) return provider;
|
|
14262
|
-
return "file";
|
|
14263
|
-
}
|
|
14264
|
-
function codexRuleViolations(name, description) {
|
|
14265
|
-
const issues = [];
|
|
14266
|
-
if (name.length > 100) issues.push("Codex rule: name exceeds 100 characters");
|
|
14267
|
-
if (description.length > 500) issues.push("Codex rule: description exceeds 500 characters");
|
|
14268
|
-
if (name.includes("\n")) issues.push("Codex rule: name must be single-line");
|
|
14269
|
-
if (description.includes("\n")) issues.push("Codex rule: description must be single-line");
|
|
14270
|
-
return issues;
|
|
14271
|
-
}
|
|
14272
|
-
|
|
14273
14180
|
//#endregion
|
|
14274
14181
|
//#region src/cli/prompts/commandBuilder.ts
|
|
14275
14182
|
/**
|
|
@@ -14398,7 +14305,7 @@ var InteractiveCommandBuilder = class InteractiveCommandBuilder {
|
|
|
14398
14305
|
console.log(`${tone.bold("Command:")}`);
|
|
14399
14306
|
console.log(` ${tone.accent(this.buildFull())}`);
|
|
14400
14307
|
console.log();
|
|
14401
|
-
const { confirm } = await import("./esm-
|
|
14308
|
+
const { confirm } = await import("./esm-C5JN5Xg3.mjs");
|
|
14402
14309
|
return confirm({
|
|
14403
14310
|
message: "Proceed?",
|
|
14404
14311
|
default: true
|
|
@@ -15752,8 +15659,10 @@ function buildPreview(selection, destinations) {
|
|
|
15752
15659
|
}
|
|
15753
15660
|
async function installSkills(options, output = silentOutput$1) {
|
|
15754
15661
|
try {
|
|
15755
|
-
|
|
15756
|
-
const
|
|
15662
|
+
if (!options.source) throw new Error("installSkills requires a source. Use installCcskiWorkflow for workflow setup.");
|
|
15663
|
+
const source = options.source;
|
|
15664
|
+
const parsed = parseGitUrl(source);
|
|
15665
|
+
const resolvedSource = parsed?.repo ?? source;
|
|
15757
15666
|
const resolvedBranch = options.branch ?? parsed?.branch;
|
|
15758
15667
|
const resolvedPath = options.path ?? (parsed?.type === "blob" ? parsed.path : void 0);
|
|
15759
15668
|
const cmdBuilder = new InteractiveCommandBuilder("ccski install");
|
|
@@ -15762,14 +15671,14 @@ async function installSkills(options, output = silentOutput$1) {
|
|
|
15762
15671
|
if (resolvedPath) cmdBuilder.addArg("path", resolvedPath);
|
|
15763
15672
|
if (options.force || options.override) cmdBuilder.addFlag("force");
|
|
15764
15673
|
const destinations = await resolveDestinations(options, cmdBuilder, output);
|
|
15765
|
-
const materialized = await materializeSource(
|
|
15674
|
+
const materialized = await materializeSource(source, {
|
|
15766
15675
|
...options.mode ? { mode: options.mode } : {},
|
|
15767
15676
|
...options.branch ? { branch: options.branch } : {},
|
|
15768
15677
|
...options.timeout ? { timeout: options.timeout } : {}
|
|
15769
15678
|
}, output);
|
|
15770
15679
|
const explicitPath = options.path ?? materialized.useHint;
|
|
15771
15680
|
if (materialized.useHint && !resolvedPath) cmdBuilder.addArg("path", materialized.useHint);
|
|
15772
|
-
const sourceArgs = buildSourceArgs(
|
|
15681
|
+
const sourceArgs = buildSourceArgs(source, materialized.branch ?? options.branch, explicitPath, materialized.repo);
|
|
15773
15682
|
const sourceLabel = formatSourceLabel(sourceArgs.source, sourceArgs.branch, sourceArgs.path);
|
|
15774
15683
|
const targets = resolveInstallTargets(materialized.base, explicitPath, sourceLabel);
|
|
15775
15684
|
if (targets.length === 0) throw new Error(`No skills found for ${sourceLabel}. Resolved directory: ${materialized.base}`);
|
|
@@ -15831,199 +15740,11 @@ async function installSkills(options, output = silentOutput$1) {
|
|
|
15831
15740
|
}
|
|
15832
15741
|
|
|
15833
15742
|
//#endregion
|
|
15834
|
-
//#region src/api/
|
|
15835
|
-
|
|
15836
|
-
|
|
15837
|
-
const
|
|
15838
|
-
|
|
15839
|
-
const skills = applyFilters(registry.getAll(), includes, excludes, "all");
|
|
15840
|
-
const candidates = mode === "disable" ? skills.filter((s) => !s.disabled) : skills.filter((s) => s.disabled);
|
|
15841
|
-
if (candidates.length === 0) return {
|
|
15842
|
-
mode,
|
|
15843
|
-
results: [],
|
|
15844
|
-
succeeded: 0,
|
|
15845
|
-
skipped: 0,
|
|
15846
|
-
failed: 0
|
|
15847
|
-
};
|
|
15848
|
-
const cmdBuilder = new InteractiveCommandBuilder(`ccski ${mode}`);
|
|
15849
|
-
if (options.include?.length) cmdBuilder.addArg("include", options.include);
|
|
15850
|
-
if (options.exclude?.length) cmdBuilder.addArg("exclude", options.exclude);
|
|
15851
|
-
const force = options.force === true || options.override === true;
|
|
15852
|
-
if (force) cmdBuilder.addFlag("force");
|
|
15853
|
-
const selected = await pickSkills(mode, candidates, options, cmdBuilder);
|
|
15854
|
-
const conflicts = detectConflicts(selected);
|
|
15855
|
-
if (options.interactive && selected.length > 0 && !options.yes) {
|
|
15856
|
-
if (!await confirmToggle(mode, cmdBuilder, selected, conflicts, force, output)) throw new ToggleCancelledError(mode);
|
|
15857
|
-
}
|
|
15858
|
-
const results = [];
|
|
15859
|
-
for (const skill of selected) results.push(executeToggle(mode, skill, force));
|
|
15860
|
-
return {
|
|
15861
|
-
mode,
|
|
15862
|
-
results,
|
|
15863
|
-
succeeded: results.filter((r) => r.status === `${mode}d`).length,
|
|
15864
|
-
skipped: results.filter((r) => r.status === "skipped").length,
|
|
15865
|
-
failed: results.filter((r) => r.status === "failed").length
|
|
15866
|
-
};
|
|
15867
|
-
}
|
|
15868
|
-
var MultiSelectError = class extends Error {
|
|
15869
|
-
constructor(message, listing) {
|
|
15870
|
-
super(message);
|
|
15871
|
-
this.listing = listing;
|
|
15872
|
-
this.name = "MultiSelectError";
|
|
15873
|
-
}
|
|
15874
|
-
};
|
|
15875
|
-
var ToggleCancelledError = class extends Error {
|
|
15876
|
-
constructor(mode) {
|
|
15877
|
-
super(`${mode} cancelled`);
|
|
15878
|
-
this.mode = mode;
|
|
15879
|
-
this.name = "ToggleCancelledError";
|
|
15880
|
-
}
|
|
15881
|
-
};
|
|
15882
|
-
function detectConflicts(skills) {
|
|
15883
|
-
const conflicts = [];
|
|
15884
|
-
for (const skill of skills) {
|
|
15885
|
-
const skillFile = join(skill.path, "SKILL.md");
|
|
15886
|
-
const disabledFile = join(skill.path, ".SKILL.md");
|
|
15887
|
-
const hasSkill = existsSync(skillFile);
|
|
15888
|
-
const hasDisabled = existsSync(disabledFile);
|
|
15889
|
-
if (hasSkill && hasDisabled) conflicts.push({
|
|
15890
|
-
skill: skill.name,
|
|
15891
|
-
path: skill.path,
|
|
15892
|
-
reason: "Both SKILL.md and .SKILL.md exist"
|
|
15893
|
-
});
|
|
15894
|
-
}
|
|
15895
|
-
return conflicts;
|
|
15896
|
-
}
|
|
15897
|
-
function executeToggle(mode, skill, force) {
|
|
15898
|
-
const skillFile = join(skill.path, "SKILL.md");
|
|
15899
|
-
const disabledFile = join(skill.path, ".SKILL.md");
|
|
15900
|
-
const hasSkill = existsSync(skillFile);
|
|
15901
|
-
const hasDisabled = existsSync(disabledFile);
|
|
15902
|
-
try {
|
|
15903
|
-
if (mode === "disable") {
|
|
15904
|
-
if (!hasSkill && hasDisabled) return {
|
|
15905
|
-
skill: skill.name,
|
|
15906
|
-
path: skill.path,
|
|
15907
|
-
status: "skipped",
|
|
15908
|
-
error: "Already disabled"
|
|
15909
|
-
};
|
|
15910
|
-
if (hasSkill && hasDisabled && !force) return {
|
|
15911
|
-
skill: skill.name,
|
|
15912
|
-
path: skill.path,
|
|
15913
|
-
status: "skipped",
|
|
15914
|
-
error: "Both files exist, use --force"
|
|
15915
|
-
};
|
|
15916
|
-
if (hasDisabled && force) rmSync(disabledFile);
|
|
15917
|
-
if (hasSkill) renameSync(skillFile, disabledFile);
|
|
15918
|
-
return {
|
|
15919
|
-
skill: skill.name,
|
|
15920
|
-
path: skill.path,
|
|
15921
|
-
status: "disabled"
|
|
15922
|
-
};
|
|
15923
|
-
}
|
|
15924
|
-
if (hasSkill && !hasDisabled) return {
|
|
15925
|
-
skill: skill.name,
|
|
15926
|
-
path: skill.path,
|
|
15927
|
-
status: "skipped",
|
|
15928
|
-
error: "Already enabled"
|
|
15929
|
-
};
|
|
15930
|
-
if (hasSkill && hasDisabled && !force) return {
|
|
15931
|
-
skill: skill.name,
|
|
15932
|
-
path: skill.path,
|
|
15933
|
-
status: "skipped",
|
|
15934
|
-
error: "Both files exist, use --force"
|
|
15935
|
-
};
|
|
15936
|
-
if (hasSkill && force) rmSync(skillFile);
|
|
15937
|
-
if (!hasDisabled) return {
|
|
15938
|
-
skill: skill.name,
|
|
15939
|
-
path: skill.path,
|
|
15940
|
-
status: "failed",
|
|
15941
|
-
error: "No .SKILL.md found"
|
|
15942
|
-
};
|
|
15943
|
-
renameSync(disabledFile, skillFile);
|
|
15944
|
-
return {
|
|
15945
|
-
skill: skill.name,
|
|
15946
|
-
path: skill.path,
|
|
15947
|
-
status: "enabled"
|
|
15948
|
-
};
|
|
15949
|
-
} catch (err) {
|
|
15950
|
-
return {
|
|
15951
|
-
skill: skill.name,
|
|
15952
|
-
path: skill.path,
|
|
15953
|
-
status: "failed",
|
|
15954
|
-
error: err instanceof Error ? err.message : String(err)
|
|
15955
|
-
};
|
|
15956
|
-
}
|
|
15957
|
-
}
|
|
15958
|
-
async function confirmToggle(mode, cmdBuilder, selected, conflicts, force, output) {
|
|
15959
|
-
output.log();
|
|
15960
|
-
output.log(heading(`${capitalize(mode)} Summary`));
|
|
15961
|
-
output.log();
|
|
15962
|
-
output.log(`${tone.bold("Skills:")} ${selected.length} selected`);
|
|
15963
|
-
for (const skill of selected) output.log(` ${tone.primary("•")} ${skill.name} ${dim(`(${skill.location})`)}`);
|
|
15964
|
-
output.log();
|
|
15965
|
-
if (conflicts.length > 0) {
|
|
15966
|
-
if (force) output.log(`${tone.warning("Will overwrite:")} ${conflicts.length} skill(s) with conflicts`);
|
|
15967
|
-
else output.log(`${tone.warning("Will skip:")} ${conflicts.length} skill(s) with conflicts`);
|
|
15968
|
-
for (const c of conflicts.slice(0, 5)) output.log(` ${tone.warning("•")} ${c.skill}: ${dim(c.reason)}`);
|
|
15969
|
-
if (conflicts.length > 5) output.log(dim(` ... and ${conflicts.length - 5} more`));
|
|
15970
|
-
output.log();
|
|
15971
|
-
}
|
|
15972
|
-
output.log(`${tone.bold("Command:")}`);
|
|
15973
|
-
output.log(` ${tone.accent(cmdBuilder.buildFull())}`);
|
|
15974
|
-
output.log();
|
|
15975
|
-
const { confirm } = await import("./esm-CWBkGBbQ.mjs");
|
|
15976
|
-
return confirm({
|
|
15977
|
-
message: "Proceed?",
|
|
15978
|
-
default: true
|
|
15979
|
-
});
|
|
15980
|
-
}
|
|
15981
|
-
async function pickSkills(mode, candidates, options, cmdBuilder) {
|
|
15982
|
-
const selectors = parseNames(options);
|
|
15983
|
-
const listing = `${heading(mode === "disable" ? "Enabled skills" : "Disabled skills")} (${candidates.length})\n${renderList(skillsToListItems(candidates))}`;
|
|
15984
|
-
if (options.all) {
|
|
15985
|
-
cmdBuilder.addFlag("all");
|
|
15986
|
-
return candidates;
|
|
15987
|
-
}
|
|
15988
|
-
if (selectors.length > 0) {
|
|
15989
|
-
cmdBuilder.addArg("names", selectors, { positional: true });
|
|
15990
|
-
return resolveSelectors(candidates, selectors);
|
|
15991
|
-
}
|
|
15992
|
-
if (options.interactive) {
|
|
15993
|
-
if (!process.stdin.isTTY || !process.stdout.isTTY) throw new MultiSelectError("Interactive mode requires a TTY.", listing);
|
|
15994
|
-
cmdBuilder.addArg("names", candidates.map((c) => c.name), {
|
|
15995
|
-
positional: true,
|
|
15996
|
-
totalChoices: candidates.length,
|
|
15997
|
-
shortRender: (values, total) => {
|
|
15998
|
-
if (total && values.length === total && values.length > 1) return ["--all"];
|
|
15999
|
-
if (values.length <= 3) return values;
|
|
16000
|
-
return [...values.slice(0, 3), `... (+${values.length - 3} more)`];
|
|
16001
|
-
}
|
|
16002
|
-
});
|
|
16003
|
-
const names$1 = await promptMultiSelect({
|
|
16004
|
-
message: mode === "disable" ? "Select skills to disable" : "Select skills to enable",
|
|
16005
|
-
choices: candidates.map((skill) => ({
|
|
16006
|
-
value: skill.name,
|
|
16007
|
-
label: formatSkillChoiceLabel(skill),
|
|
16008
|
-
description: skill.description,
|
|
16009
|
-
checked: false
|
|
16010
|
-
})),
|
|
16011
|
-
defaultChecked: false,
|
|
16012
|
-
commandBuilder: cmdBuilder,
|
|
16013
|
-
commandArgKey: "names"
|
|
16014
|
-
});
|
|
16015
|
-
if (!Array.isArray(names$1) || names$1.length === 0) throw new MultiSelectError("No skills selected.", listing);
|
|
16016
|
-
cmdBuilder.updateArg("names", names$1);
|
|
16017
|
-
const pickedSet = new Set(names$1.map((n) => n.toLowerCase()));
|
|
16018
|
-
return candidates.filter((c) => skillAliases(c).some((alias) => pickedSet.has(alias)));
|
|
16019
|
-
}
|
|
16020
|
-
throw new MultiSelectError("Multiple skills available. Provide names, use --all, or enable interactive mode (-i).", listing);
|
|
16021
|
-
}
|
|
16022
|
-
function parseNames(options) {
|
|
16023
|
-
return [].concat(Array.isArray(options.names) ? options.names : []).flatMap((item) => item.split(/[\\/,]/)).map((s) => s.trim()).filter(Boolean);
|
|
16024
|
-
}
|
|
16025
|
-
function capitalize(value) {
|
|
16026
|
-
return value.charAt(0).toUpperCase() + value.slice(1);
|
|
15743
|
+
//#region src/api/list.ts
|
|
15744
|
+
async function listSkills(options = {}) {
|
|
15745
|
+
const registry = new SkillRegistry(buildRegistryOptions(options, { includeDisabled: resolveFilters(options).includeDisabled }));
|
|
15746
|
+
const { includes, excludes, state } = resolveFilters(options, { providers: providerNamesFromSkills(registry.getAll()) });
|
|
15747
|
+
return applyFilters(registry.getAll(), includes, excludes, state).sort((a, b) => a.name.localeCompare(b.name));
|
|
16027
15748
|
}
|
|
16028
15749
|
|
|
16029
15750
|
//#endregion
|
|
@@ -34594,5 +34315,427 @@ function formatSkillContent(skill) {
|
|
|
34594
34315
|
}
|
|
34595
34316
|
|
|
34596
34317
|
//#endregion
|
|
34597
|
-
|
|
34598
|
-
|
|
34318
|
+
//#region src/api/search.ts
|
|
34319
|
+
function buildSearchContext(options) {
|
|
34320
|
+
const registry = new SkillRegistry(buildRegistryOptions(options, { includeDisabled: resolveFilters(options).includeDisabled }));
|
|
34321
|
+
const { includes, excludes, state } = resolveFilters(options, { providers: providerNamesFromSkills(registry.getAll()) });
|
|
34322
|
+
const skills = applyFilters(registry.getAll(), includes, excludes, state);
|
|
34323
|
+
const ranked = rankStrings(skills.map((skill) => `${skill.name} ${skill.description}`), options.query);
|
|
34324
|
+
const picked = ranked.length > 0 ? ranked.map((i$4) => skills[i$4]) : skills;
|
|
34325
|
+
return {
|
|
34326
|
+
registry,
|
|
34327
|
+
matches: options.content ? picked.filter((skill) => {
|
|
34328
|
+
const resolved = resolveSkill(skills, skill.name);
|
|
34329
|
+
const content = registry.load(`${resolved.provider}:${resolved.name}`).content;
|
|
34330
|
+
return containsCaseInsensitive(content, options.query);
|
|
34331
|
+
}) : picked.filter((skill) => containsCaseInsensitive(`${skill.name} ${skill.description}`, options.query))
|
|
34332
|
+
};
|
|
34333
|
+
}
|
|
34334
|
+
function searchSkillsDetailed(options) {
|
|
34335
|
+
const { matches } = buildSearchContext(options);
|
|
34336
|
+
return matches;
|
|
34337
|
+
}
|
|
34338
|
+
async function searchSkills(options) {
|
|
34339
|
+
return searchSkillsDetailed(options).map((skill) => ({
|
|
34340
|
+
name: skill.name,
|
|
34341
|
+
description: skill.description,
|
|
34342
|
+
location: skill.location,
|
|
34343
|
+
provider: skill.provider,
|
|
34344
|
+
disabled: skill.disabled ?? false,
|
|
34345
|
+
path: skill.path
|
|
34346
|
+
}));
|
|
34347
|
+
}
|
|
34348
|
+
|
|
34349
|
+
//#endregion
|
|
34350
|
+
//#region src/api/toggle.ts
|
|
34351
|
+
const silentOutput = { log: () => {} };
|
|
34352
|
+
async function toggleSkills(mode, options, output = silentOutput) {
|
|
34353
|
+
const registry = new SkillRegistry(buildRegistryOptions(options, { includeDisabled: true }));
|
|
34354
|
+
const { includes, excludes } = parseFilters(options.include, options.exclude, { providers: providerNamesFromSkills(registry.getAll()) });
|
|
34355
|
+
const skills = applyFilters(registry.getAll(), includes, excludes, "all");
|
|
34356
|
+
const candidates = mode === "disable" ? skills.filter((s) => !s.disabled) : skills.filter((s) => s.disabled);
|
|
34357
|
+
if (candidates.length === 0) return {
|
|
34358
|
+
mode,
|
|
34359
|
+
results: [],
|
|
34360
|
+
succeeded: 0,
|
|
34361
|
+
skipped: 0,
|
|
34362
|
+
failed: 0
|
|
34363
|
+
};
|
|
34364
|
+
const cmdBuilder = new InteractiveCommandBuilder(`ccski ${mode}`);
|
|
34365
|
+
if (options.include?.length) cmdBuilder.addArg("include", options.include);
|
|
34366
|
+
if (options.exclude?.length) cmdBuilder.addArg("exclude", options.exclude);
|
|
34367
|
+
const force = options.force === true || options.override === true;
|
|
34368
|
+
if (force) cmdBuilder.addFlag("force");
|
|
34369
|
+
const selected = await pickSkills(mode, candidates, options, cmdBuilder);
|
|
34370
|
+
const conflicts = detectConflicts(selected);
|
|
34371
|
+
if (options.interactive && selected.length > 0 && !options.yes) {
|
|
34372
|
+
if (!await confirmToggle(mode, cmdBuilder, selected, conflicts, force, output)) throw new ToggleCancelledError(mode);
|
|
34373
|
+
}
|
|
34374
|
+
const results = [];
|
|
34375
|
+
for (const skill of selected) results.push(executeToggle(mode, skill, force));
|
|
34376
|
+
return {
|
|
34377
|
+
mode,
|
|
34378
|
+
results,
|
|
34379
|
+
succeeded: results.filter((r) => r.status === `${mode}d`).length,
|
|
34380
|
+
skipped: results.filter((r) => r.status === "skipped").length,
|
|
34381
|
+
failed: results.filter((r) => r.status === "failed").length
|
|
34382
|
+
};
|
|
34383
|
+
}
|
|
34384
|
+
var MultiSelectError = class extends Error {
|
|
34385
|
+
constructor(message, listing) {
|
|
34386
|
+
super(message);
|
|
34387
|
+
this.listing = listing;
|
|
34388
|
+
this.name = "MultiSelectError";
|
|
34389
|
+
}
|
|
34390
|
+
};
|
|
34391
|
+
var ToggleCancelledError = class extends Error {
|
|
34392
|
+
constructor(mode) {
|
|
34393
|
+
super(`${mode} cancelled`);
|
|
34394
|
+
this.mode = mode;
|
|
34395
|
+
this.name = "ToggleCancelledError";
|
|
34396
|
+
}
|
|
34397
|
+
};
|
|
34398
|
+
function detectConflicts(skills) {
|
|
34399
|
+
const conflicts = [];
|
|
34400
|
+
for (const skill of skills) {
|
|
34401
|
+
const skillFile = join(skill.path, "SKILL.md");
|
|
34402
|
+
const disabledFile = join(skill.path, ".SKILL.md");
|
|
34403
|
+
const hasSkill = existsSync(skillFile);
|
|
34404
|
+
const hasDisabled = existsSync(disabledFile);
|
|
34405
|
+
if (hasSkill && hasDisabled) conflicts.push({
|
|
34406
|
+
skill: skill.name,
|
|
34407
|
+
path: skill.path,
|
|
34408
|
+
reason: "Both SKILL.md and .SKILL.md exist"
|
|
34409
|
+
});
|
|
34410
|
+
}
|
|
34411
|
+
return conflicts;
|
|
34412
|
+
}
|
|
34413
|
+
function executeToggle(mode, skill, force) {
|
|
34414
|
+
const skillFile = join(skill.path, "SKILL.md");
|
|
34415
|
+
const disabledFile = join(skill.path, ".SKILL.md");
|
|
34416
|
+
const hasSkill = existsSync(skillFile);
|
|
34417
|
+
const hasDisabled = existsSync(disabledFile);
|
|
34418
|
+
try {
|
|
34419
|
+
if (mode === "disable") {
|
|
34420
|
+
if (!hasSkill && hasDisabled) return {
|
|
34421
|
+
skill: skill.name,
|
|
34422
|
+
path: skill.path,
|
|
34423
|
+
status: "skipped",
|
|
34424
|
+
error: "Already disabled"
|
|
34425
|
+
};
|
|
34426
|
+
if (hasSkill && hasDisabled && !force) return {
|
|
34427
|
+
skill: skill.name,
|
|
34428
|
+
path: skill.path,
|
|
34429
|
+
status: "skipped",
|
|
34430
|
+
error: "Both files exist, use --force"
|
|
34431
|
+
};
|
|
34432
|
+
if (hasDisabled && force) rmSync(disabledFile);
|
|
34433
|
+
if (hasSkill) renameSync(skillFile, disabledFile);
|
|
34434
|
+
return {
|
|
34435
|
+
skill: skill.name,
|
|
34436
|
+
path: skill.path,
|
|
34437
|
+
status: "disabled"
|
|
34438
|
+
};
|
|
34439
|
+
}
|
|
34440
|
+
if (hasSkill && !hasDisabled) return {
|
|
34441
|
+
skill: skill.name,
|
|
34442
|
+
path: skill.path,
|
|
34443
|
+
status: "skipped",
|
|
34444
|
+
error: "Already enabled"
|
|
34445
|
+
};
|
|
34446
|
+
if (hasSkill && hasDisabled && !force) return {
|
|
34447
|
+
skill: skill.name,
|
|
34448
|
+
path: skill.path,
|
|
34449
|
+
status: "skipped",
|
|
34450
|
+
error: "Both files exist, use --force"
|
|
34451
|
+
};
|
|
34452
|
+
if (hasSkill && force) rmSync(skillFile);
|
|
34453
|
+
if (!hasDisabled) return {
|
|
34454
|
+
skill: skill.name,
|
|
34455
|
+
path: skill.path,
|
|
34456
|
+
status: "failed",
|
|
34457
|
+
error: "No .SKILL.md found"
|
|
34458
|
+
};
|
|
34459
|
+
renameSync(disabledFile, skillFile);
|
|
34460
|
+
return {
|
|
34461
|
+
skill: skill.name,
|
|
34462
|
+
path: skill.path,
|
|
34463
|
+
status: "enabled"
|
|
34464
|
+
};
|
|
34465
|
+
} catch (err) {
|
|
34466
|
+
return {
|
|
34467
|
+
skill: skill.name,
|
|
34468
|
+
path: skill.path,
|
|
34469
|
+
status: "failed",
|
|
34470
|
+
error: err instanceof Error ? err.message : String(err)
|
|
34471
|
+
};
|
|
34472
|
+
}
|
|
34473
|
+
}
|
|
34474
|
+
async function confirmToggle(mode, cmdBuilder, selected, conflicts, force, output) {
|
|
34475
|
+
output.log();
|
|
34476
|
+
output.log(heading(`${capitalize(mode)} Summary`));
|
|
34477
|
+
output.log();
|
|
34478
|
+
output.log(`${tone.bold("Skills:")} ${selected.length} selected`);
|
|
34479
|
+
for (const skill of selected) output.log(` ${tone.primary("•")} ${skill.name} ${dim(`(${skill.location})`)}`);
|
|
34480
|
+
output.log();
|
|
34481
|
+
if (conflicts.length > 0) {
|
|
34482
|
+
if (force) output.log(`${tone.warning("Will overwrite:")} ${conflicts.length} skill(s) with conflicts`);
|
|
34483
|
+
else output.log(`${tone.warning("Will skip:")} ${conflicts.length} skill(s) with conflicts`);
|
|
34484
|
+
for (const c of conflicts.slice(0, 5)) output.log(` ${tone.warning("•")} ${c.skill}: ${dim(c.reason)}`);
|
|
34485
|
+
if (conflicts.length > 5) output.log(dim(` ... and ${conflicts.length - 5} more`));
|
|
34486
|
+
output.log();
|
|
34487
|
+
}
|
|
34488
|
+
output.log(`${tone.bold("Command:")}`);
|
|
34489
|
+
output.log(` ${tone.accent(cmdBuilder.buildFull())}`);
|
|
34490
|
+
output.log();
|
|
34491
|
+
const { confirm } = await import("./esm-C5JN5Xg3.mjs");
|
|
34492
|
+
return confirm({
|
|
34493
|
+
message: "Proceed?",
|
|
34494
|
+
default: true
|
|
34495
|
+
});
|
|
34496
|
+
}
|
|
34497
|
+
async function pickSkills(mode, candidates, options, cmdBuilder) {
|
|
34498
|
+
const selectors = parseNames(options);
|
|
34499
|
+
const listing = `${heading(mode === "disable" ? "Enabled skills" : "Disabled skills")} (${candidates.length})\n${renderList(skillsToListItems(candidates))}`;
|
|
34500
|
+
if (options.all) {
|
|
34501
|
+
cmdBuilder.addFlag("all");
|
|
34502
|
+
return candidates;
|
|
34503
|
+
}
|
|
34504
|
+
if (selectors.length > 0) {
|
|
34505
|
+
cmdBuilder.addArg("names", selectors, { positional: true });
|
|
34506
|
+
return resolveSelectors(candidates, selectors);
|
|
34507
|
+
}
|
|
34508
|
+
if (options.interactive) {
|
|
34509
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) throw new MultiSelectError("Interactive mode requires a TTY.", listing);
|
|
34510
|
+
cmdBuilder.addArg("names", candidates.map((c) => c.name), {
|
|
34511
|
+
positional: true,
|
|
34512
|
+
totalChoices: candidates.length,
|
|
34513
|
+
shortRender: (values, total) => {
|
|
34514
|
+
if (total && values.length === total && values.length > 1) return ["--all"];
|
|
34515
|
+
if (values.length <= 3) return values;
|
|
34516
|
+
return [...values.slice(0, 3), `... (+${values.length - 3} more)`];
|
|
34517
|
+
}
|
|
34518
|
+
});
|
|
34519
|
+
const names$1 = await promptMultiSelect({
|
|
34520
|
+
message: mode === "disable" ? "Select skills to disable" : "Select skills to enable",
|
|
34521
|
+
choices: candidates.map((skill) => ({
|
|
34522
|
+
value: skill.name,
|
|
34523
|
+
label: formatSkillChoiceLabel(skill),
|
|
34524
|
+
description: skill.description,
|
|
34525
|
+
checked: false
|
|
34526
|
+
})),
|
|
34527
|
+
defaultChecked: false,
|
|
34528
|
+
commandBuilder: cmdBuilder,
|
|
34529
|
+
commandArgKey: "names"
|
|
34530
|
+
});
|
|
34531
|
+
if (!Array.isArray(names$1) || names$1.length === 0) throw new MultiSelectError("No skills selected.", listing);
|
|
34532
|
+
cmdBuilder.updateArg("names", names$1);
|
|
34533
|
+
const pickedSet = new Set(names$1.map((n) => n.toLowerCase()));
|
|
34534
|
+
return candidates.filter((c) => skillAliases(c).some((alias) => pickedSet.has(alias)));
|
|
34535
|
+
}
|
|
34536
|
+
throw new MultiSelectError("Multiple skills available. Provide names, use --all, or enable interactive mode (-i).", listing);
|
|
34537
|
+
}
|
|
34538
|
+
function parseNames(options) {
|
|
34539
|
+
return [].concat(Array.isArray(options.names) ? options.names : []).flatMap((item) => item.split(/[\\/,]/)).map((s) => s.trim()).filter(Boolean);
|
|
34540
|
+
}
|
|
34541
|
+
function capitalize(value) {
|
|
34542
|
+
return value.charAt(0).toUpperCase() + value.slice(1);
|
|
34543
|
+
}
|
|
34544
|
+
|
|
34545
|
+
//#endregion
|
|
34546
|
+
//#region src/api/validate.ts
|
|
34547
|
+
async function validateSkill(options) {
|
|
34548
|
+
const registry = new SkillRegistry(buildRegistryOptions(options));
|
|
34549
|
+
const { includes, excludes, state } = resolveFilters(options, { providers: providerNamesFromSkills(registry.getAll()) });
|
|
34550
|
+
const filtered = applyFilters(registry.getAll(), includes, excludes, state);
|
|
34551
|
+
const target = resolve(options.path);
|
|
34552
|
+
const skillFile = resolveSkillFile(target);
|
|
34553
|
+
if (!skillFile) throw new Error(`Could not find SKILL.md at ${target}`);
|
|
34554
|
+
const result = validateSkillFile(skillFile);
|
|
34555
|
+
const provider = inferProvider(skillFile, filtered);
|
|
34556
|
+
const codexIssues = [];
|
|
34557
|
+
const codexWarnings = [];
|
|
34558
|
+
if (result.success && provider === "codex") try {
|
|
34559
|
+
const parsed = parseSkillFile(skillFile);
|
|
34560
|
+
codexIssues.push(...codexRuleViolations(parsed.frontmatter.name, parsed.frontmatter.description));
|
|
34561
|
+
if (lstatSync(skillFile).isSymbolicLink()) codexWarnings.push(`SKILL.md is a symlink (${skillFile})`);
|
|
34562
|
+
if (lstatSync(resolve(skillFile, "..")).isSymbolicLink()) codexWarnings.push(`Skill directory is a symlink (${resolve(skillFile, "..")})`);
|
|
34563
|
+
} catch {}
|
|
34564
|
+
const errors = [...result.errors, ...codexIssues];
|
|
34565
|
+
const warnings = [...result.suggestions, ...codexWarnings];
|
|
34566
|
+
return {
|
|
34567
|
+
file: skillFile,
|
|
34568
|
+
success: errors.length === 0,
|
|
34569
|
+
errors,
|
|
34570
|
+
warnings
|
|
34571
|
+
};
|
|
34572
|
+
}
|
|
34573
|
+
function resolveSkillFile(input) {
|
|
34574
|
+
if (!existsSync(input)) return null;
|
|
34575
|
+
const stats = statSync(input);
|
|
34576
|
+
if (stats.isDirectory()) {
|
|
34577
|
+
const candidate = join(input, "SKILL.md");
|
|
34578
|
+
return existsSync(candidate) ? candidate : null;
|
|
34579
|
+
}
|
|
34580
|
+
if (stats.isFile() && input.toLowerCase().endsWith("skill.md")) return input;
|
|
34581
|
+
return null;
|
|
34582
|
+
}
|
|
34583
|
+
function inferProvider(skillPath, filtered) {
|
|
34584
|
+
const lower = skillPath.toLowerCase();
|
|
34585
|
+
const match = filtered.find((s) => s.path === resolve(skillPath, ".."));
|
|
34586
|
+
if (match) return match.provider;
|
|
34587
|
+
const provider = lower.split("/").find((segment) => segment.startsWith("."))?.slice(1);
|
|
34588
|
+
if (provider === "agent" || provider === "agents") return "agents";
|
|
34589
|
+
if (provider && lower.includes(`/.${provider}/skills`)) return provider;
|
|
34590
|
+
return "file";
|
|
34591
|
+
}
|
|
34592
|
+
function codexRuleViolations(name, description) {
|
|
34593
|
+
const issues = [];
|
|
34594
|
+
if (name.length > 100) issues.push("Codex rule: name exceeds 100 characters");
|
|
34595
|
+
if (description.length > 500) issues.push("Codex rule: description exceeds 500 characters");
|
|
34596
|
+
if (name.includes("\n")) issues.push("Codex rule: name must be single-line");
|
|
34597
|
+
if (description.includes("\n")) issues.push("Codex rule: description must be single-line");
|
|
34598
|
+
return issues;
|
|
34599
|
+
}
|
|
34600
|
+
|
|
34601
|
+
//#endregion
|
|
34602
|
+
//#region src/api/workflow-install.ts
|
|
34603
|
+
const WORKFLOW_BLOCK = `<workflow name="ccski">
|
|
34604
|
+
When starting a new context, run \`bunx ccski list --no-color\` to discover extra local skills available on this machine, especially shared \`agents:user\` skills and agent-specific skills such as \`claude:user\`, \`codex:user\`, and \`gemini:user\`.
|
|
34605
|
+
If the user names a skill, or a task clearly matches a skill that is not already exposed in the current session, do not assume the skill is unavailable. First run \`bunx ccski list --no-color\`, then run \`bunx ccski info <name>\` and load the \`SKILL.md\` path reported by the output.
|
|
34606
|
+
\`ccski\` may emit compatibility warnings while reading local agent or plugin metadata, such as \`installed_plugins.json\`. These warnings do not necessarily block \`list\` or \`info\`; prefer \`--no-color\` for readable output and downstream parsing.
|
|
34607
|
+
</workflow>`;
|
|
34608
|
+
const WORKFLOW_BLOCK_PATTERN = /<workflow\s+name=["']ccski["'][^>]*>[\s\S]*?<\/workflow>/m;
|
|
34609
|
+
const AGENT_INSTRUCTION_TARGETS = [
|
|
34610
|
+
{
|
|
34611
|
+
id: "codex",
|
|
34612
|
+
label: "Codex",
|
|
34613
|
+
aliases: ["openai-codex", "codex-cli"],
|
|
34614
|
+
userPath: [".codex", "AGENTS.md"],
|
|
34615
|
+
projectPath: ["AGENTS.md"]
|
|
34616
|
+
},
|
|
34617
|
+
{
|
|
34618
|
+
id: "claude-code",
|
|
34619
|
+
label: "Claude Code",
|
|
34620
|
+
aliases: ["claude", "claudecode"],
|
|
34621
|
+
userPath: [".claude", "CLAUDE.md"],
|
|
34622
|
+
projectPath: ["CLAUDE.md"]
|
|
34623
|
+
},
|
|
34624
|
+
{
|
|
34625
|
+
id: "gemini",
|
|
34626
|
+
label: "Gemini CLI",
|
|
34627
|
+
aliases: ["gemini-cli"],
|
|
34628
|
+
userPath: [".gemini", "GEMINI.md"],
|
|
34629
|
+
projectPath: ["GEMINI.md"]
|
|
34630
|
+
},
|
|
34631
|
+
{
|
|
34632
|
+
id: "opencode",
|
|
34633
|
+
label: "OpenCode",
|
|
34634
|
+
aliases: ["open-code"],
|
|
34635
|
+
userPath: [
|
|
34636
|
+
".config",
|
|
34637
|
+
"opencode",
|
|
34638
|
+
"AGENTS.md"
|
|
34639
|
+
],
|
|
34640
|
+
projectPath: ["AGENTS.md"]
|
|
34641
|
+
}
|
|
34642
|
+
];
|
|
34643
|
+
function getCcskiWorkflowBlock() {
|
|
34644
|
+
return WORKFLOW_BLOCK;
|
|
34645
|
+
}
|
|
34646
|
+
function resolveAgentInstructionTarget(name) {
|
|
34647
|
+
const normalized = name.trim().toLowerCase();
|
|
34648
|
+
return AGENT_INSTRUCTION_TARGETS.find((target) => target.id === normalized || target.aliases.some((alias) => alias.toLowerCase() === normalized)) ?? null;
|
|
34649
|
+
}
|
|
34650
|
+
function listAgentInstructionTargets() {
|
|
34651
|
+
return AGENT_INSTRUCTION_TARGETS;
|
|
34652
|
+
}
|
|
34653
|
+
function installCcskiWorkflow(options = {}) {
|
|
34654
|
+
const scope = options.scope ?? "user";
|
|
34655
|
+
const userDir = resolve(options.userDir ?? homedir());
|
|
34656
|
+
const projectDir = resolve(options.projectDir ?? process.cwd());
|
|
34657
|
+
const results = resolveRequestedTargets(options.agents).map((target) => installTargetWorkflow({
|
|
34658
|
+
target,
|
|
34659
|
+
scope,
|
|
34660
|
+
userDir,
|
|
34661
|
+
projectDir,
|
|
34662
|
+
dryRun: options.dryRun === true
|
|
34663
|
+
}));
|
|
34664
|
+
return {
|
|
34665
|
+
scope,
|
|
34666
|
+
dryRun: options.dryRun === true,
|
|
34667
|
+
results,
|
|
34668
|
+
installed: results.filter((entry) => entry.status === "installed").length,
|
|
34669
|
+
updated: results.filter((entry) => entry.status === "updated").length,
|
|
34670
|
+
unchanged: results.filter((entry) => entry.status === "unchanged").length,
|
|
34671
|
+
failed: results.filter((entry) => entry.status === "failed").length
|
|
34672
|
+
};
|
|
34673
|
+
}
|
|
34674
|
+
function resolveRequestedTargets(agents) {
|
|
34675
|
+
if (!agents || agents.length === 0) return [...AGENT_INSTRUCTION_TARGETS];
|
|
34676
|
+
const resolved = [];
|
|
34677
|
+
for (const agent of agents.flatMap((value) => value.split(","))) {
|
|
34678
|
+
const trimmed = agent.trim();
|
|
34679
|
+
if (!trimmed) continue;
|
|
34680
|
+
if (trimmed.toLowerCase() === "all") {
|
|
34681
|
+
for (const target$1 of AGENT_INSTRUCTION_TARGETS) appendUniqueTarget(resolved, target$1);
|
|
34682
|
+
continue;
|
|
34683
|
+
}
|
|
34684
|
+
const target = resolveAgentInstructionTarget(trimmed);
|
|
34685
|
+
if (!target) {
|
|
34686
|
+
const known = AGENT_INSTRUCTION_TARGETS.map((entry) => entry.id).join(", ");
|
|
34687
|
+
throw new Error(`Unknown agent target '${trimmed}'. Known targets: ${known}.`);
|
|
34688
|
+
}
|
|
34689
|
+
appendUniqueTarget(resolved, target);
|
|
34690
|
+
}
|
|
34691
|
+
if (resolved.length === 0) throw new Error("No agent targets selected.");
|
|
34692
|
+
return resolved;
|
|
34693
|
+
}
|
|
34694
|
+
function appendUniqueTarget(collection, target) {
|
|
34695
|
+
if (!collection.some((entry) => entry.id === target.id)) collection.push(target);
|
|
34696
|
+
}
|
|
34697
|
+
function installTargetWorkflow(options) {
|
|
34698
|
+
const targetPath = resolveTargetPath(options);
|
|
34699
|
+
try {
|
|
34700
|
+
const current = existsSync(targetPath) ? readFileSync(targetPath, "utf8") : "";
|
|
34701
|
+
const next = upsertWorkflowBlock(current);
|
|
34702
|
+
const status$1 = current === next ? "unchanged" : current.trim().length === 0 ? "installed" : "updated";
|
|
34703
|
+
if (!options.dryRun && status$1 !== "unchanged") {
|
|
34704
|
+
mkdirSync(dirname(targetPath), { recursive: true });
|
|
34705
|
+
writeFileSync(targetPath, next, "utf8");
|
|
34706
|
+
}
|
|
34707
|
+
return {
|
|
34708
|
+
agent: options.target.id,
|
|
34709
|
+
label: options.target.label,
|
|
34710
|
+
scope: options.scope,
|
|
34711
|
+
path: targetPath,
|
|
34712
|
+
status: status$1
|
|
34713
|
+
};
|
|
34714
|
+
} catch (err) {
|
|
34715
|
+
return {
|
|
34716
|
+
agent: options.target.id,
|
|
34717
|
+
label: options.target.label,
|
|
34718
|
+
scope: options.scope,
|
|
34719
|
+
path: targetPath,
|
|
34720
|
+
status: "failed",
|
|
34721
|
+
error: err instanceof Error ? err.message : String(err)
|
|
34722
|
+
};
|
|
34723
|
+
}
|
|
34724
|
+
}
|
|
34725
|
+
function resolveTargetPath(options) {
|
|
34726
|
+
const segments = options.scope === "user" ? options.target.userPath : options.target.projectPath;
|
|
34727
|
+
return join(options.scope === "user" ? options.userDir : options.projectDir, ...segments);
|
|
34728
|
+
}
|
|
34729
|
+
function upsertWorkflowBlock(content) {
|
|
34730
|
+
const normalized = normalizeNewline(content);
|
|
34731
|
+
if (WORKFLOW_BLOCK_PATTERN.test(normalized)) return normalized.replace(WORKFLOW_BLOCK_PATTERN, WORKFLOW_BLOCK);
|
|
34732
|
+
const prefix = normalized.trimEnd();
|
|
34733
|
+
return prefix.length > 0 ? `${prefix}\n\n${WORKFLOW_BLOCK}\n` : `${WORKFLOW_BLOCK}\n`;
|
|
34734
|
+
}
|
|
34735
|
+
function normalizeNewline(content) {
|
|
34736
|
+
return content.replace(/\r\n/g, "\n");
|
|
34737
|
+
}
|
|
34738
|
+
|
|
34739
|
+
//#endregion
|
|
34740
|
+
export { BUILT_IN_SKILL_PROVIDERS as $, colors$1 as A, success as B, resolveSkill as C, parseFilters as D, computeDuplicateGroups as E, heading as F, discoverSkills as G, warn as H, highlight as I, getDefaultSkillDirectories as J, loadSkill as K, info as L, duplicateBadge as M, error$1 as N, SkillRegistry as O, formatBytes as P, validateSkillFile as Q, renderList as R, getSkillInfo as S, applyFilters as T, containsCaseInsensitive as U, tone as V, rankStrings as W, providerNamesFromSkills as X, compareSkillProviders as Y, parseSkillFile as Z, installSkillDir as _, resolveAgentInstructionTarget as a, AmbiguousSkillNameError as at, promptMultiSelect as b, ToggleCancelledError as c, SkillNotFoundError as ct, searchSkillsDetailed as d, SKILL_SOURCE_PRIORITIES as et, startMCPServer as f, createConsoleInstallOutput as g, MultiSkillSelectionError as h, listAgentInstructionTargets as i, SkillFrontmatterSchema as it, dim as j, formatSkillLabel as k, toggleSkills as l, ValidationError$2 as lt, InstallCancelledError as m, getCcskiWorkflowBlock as n, InstalledPluginsSchema as nt, validateSkill as o, CcskiError as ot, listSkills as p, scanSkillDirectory as q, installCcskiWorkflow as r, PluginEntrySchema as rt, MultiSelectError as s, ParseError as st, AGENT_INSTRUCTION_TARGETS as t, ClaudeSettingsSchema as tt, searchSkills as u, diagnosticToWarning as ut, installSkills as v, buildRegistryOptions as w, Separator as x, registerInstallCleanupHandlers as y, setColorEnabled as z };
|
|
34741
|
+
//# sourceMappingURL=workflow-install-KvZzb83I.mjs.map
|