ccski 2.3.2 → 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";
@@ -14105,28 +14105,6 @@ function buildRegistryOptions(argv$1, extras = {}) {
14105
14105
  };
14106
14106
  }
14107
14107
 
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
14108
  //#endregion
14131
14109
  //#region src/utils/resolution.ts
14132
14110
  function matchesName(skill, target) {
@@ -14156,6 +14134,20 @@ function resolveSelectors(skills, selectors) {
14156
14134
  return selected;
14157
14135
  }
14158
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
+
14159
14151
  //#endregion
14160
14152
  //#region src/api/info.ts
14161
14153
  async function getSkillInfo(options) {
@@ -14182,94 +14174,6 @@ async function getSkillInfo(options) {
14182
14174
  };
14183
14175
  }
14184
14176
 
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
14177
  //#endregion
14274
14178
  //#region src/cli/prompts/commandBuilder.ts
14275
14179
  /**
@@ -14398,7 +14302,7 @@ var InteractiveCommandBuilder = class InteractiveCommandBuilder {
14398
14302
  console.log(`${tone.bold("Command:")}`);
14399
14303
  console.log(` ${tone.accent(this.buildFull())}`);
14400
14304
  console.log();
14401
- const { confirm } = await import("./esm-CWBkGBbQ.mjs");
14305
+ const { confirm } = await import("./esm-BdE_kIOe.mjs");
14402
14306
  return confirm({
14403
14307
  message: "Proceed?",
14404
14308
  default: true
@@ -15752,8 +15656,10 @@ function buildPreview(selection, destinations) {
15752
15656
  }
15753
15657
  async function installSkills(options, output = silentOutput$1) {
15754
15658
  try {
15755
- const parsed = parseGitUrl(options.source);
15756
- 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;
15757
15663
  const resolvedBranch = options.branch ?? parsed?.branch;
15758
15664
  const resolvedPath = options.path ?? (parsed?.type === "blob" ? parsed.path : void 0);
15759
15665
  const cmdBuilder = new InteractiveCommandBuilder("ccski install");
@@ -15762,14 +15668,14 @@ async function installSkills(options, output = silentOutput$1) {
15762
15668
  if (resolvedPath) cmdBuilder.addArg("path", resolvedPath);
15763
15669
  if (options.force || options.override) cmdBuilder.addFlag("force");
15764
15670
  const destinations = await resolveDestinations(options, cmdBuilder, output);
15765
- const materialized = await materializeSource(options.source, {
15671
+ const materialized = await materializeSource(source, {
15766
15672
  ...options.mode ? { mode: options.mode } : {},
15767
15673
  ...options.branch ? { branch: options.branch } : {},
15768
15674
  ...options.timeout ? { timeout: options.timeout } : {}
15769
15675
  }, output);
15770
15676
  const explicitPath = options.path ?? materialized.useHint;
15771
15677
  if (materialized.useHint && !resolvedPath) cmdBuilder.addArg("path", materialized.useHint);
15772
- const sourceArgs = buildSourceArgs(options.source, materialized.branch ?? options.branch, explicitPath, materialized.repo);
15678
+ const sourceArgs = buildSourceArgs(source, materialized.branch ?? options.branch, explicitPath, materialized.repo);
15773
15679
  const sourceLabel = formatSourceLabel(sourceArgs.source, sourceArgs.branch, sourceArgs.path);
15774
15680
  const targets = resolveInstallTargets(materialized.base, explicitPath, sourceLabel);
15775
15681
  if (targets.length === 0) throw new Error(`No skills found for ${sourceLabel}. Resolved directory: ${materialized.base}`);
@@ -15831,199 +15737,11 @@ async function installSkills(options, output = silentOutput$1) {
15831
15737
  }
15832
15738
 
15833
15739
  //#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);
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));
16027
15745
  }
16028
15746
 
16029
15747
  //#endregion
@@ -34594,5 +34312,427 @@ function formatSkillContent(skill) {
34594
34312
  }
34595
34313
 
34596
34314
  //#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
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