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