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.
@@ -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 (Array.isArray(argv$1.skillDir)) {
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-CWBkGBbQ.mjs");
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
- const parsed = parseGitUrl(options.source);
15756
- const resolvedSource = parsed?.repo ?? options.source;
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(options.source, {
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(options.source, materialized.branch ?? options.branch, explicitPath, materialized.repo);
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/toggle.ts
15835
- const silentOutput = { log: () => {} };
15836
- async function toggleSkills(mode, options, output = silentOutput) {
15837
- const registry = new SkillRegistry(buildRegistryOptions(options, { includeDisabled: true }));
15838
- const { includes, excludes } = parseFilters(options.include, options.exclude, { providers: providerNamesFromSkills(registry.getAll()) });
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
- export { AmbiguousSkillNameError as $, highlight as A, loadSkill as B, formatSkillLabel as C, error$1 as D, duplicateBadge as E, tone as F, parseSkillFile as G, getDefaultSkillDirectories as H, warn as I, SKILL_SOURCE_PRIORITIES as J, validateSkillFile as K, containsCaseInsensitive as L, renderList as M, setColorEnabled as N, formatBytes as O, success as P, SkillFrontmatterSchema as Q, rankStrings as R, SkillRegistry as S, dim as T, compareSkillProviders as U, scanSkillDirectory as V, providerNamesFromSkills as W, InstalledPluginsSchema as X, ClaudeSettingsSchema as Y, PluginEntrySchema as Z, listSkills as _, InstallCancelledError as a, computeDuplicateGroups as b, installSkillDir as c, Separator as d, CcskiError as et, validateSkill as f, resolveSkill as g, getSkillInfo as h, toggleSkills as i, diagnosticToWarning as it, info as j, heading as k, installSkills as l, searchSkillsDetailed as m, MultiSelectError as n, SkillNotFoundError as nt, MultiSkillSelectionError as o, searchSkills as p, BUILT_IN_SKILL_PROVIDERS as q, ToggleCancelledError as r, ValidationError$2 as rt, createConsoleInstallOutput as s, startMCPServer as t, ParseError as tt, registerInstallCleanupHandlers as u, buildRegistryOptions as v, colors$1 as w, parseFilters as x, applyFilters as y, discoverSkills as z };
34598
- //# sourceMappingURL=server-DwuqhC__.mjs.map
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