@crustjs/skills 0.0.20 → 0.0.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -75,7 +75,7 @@ Prefer readonly commands before mutating project state.
75
75
  });
76
76
  ```
77
77
 
78
- The plugin automatically updates already-installed skills when the version changes, checking both project and global paths for the current working directory. First-time installation is done via the interactive `skill` subcommand (or `skill update` for update-only flows), or programmatically using the exported primitives.
78
+ The plugin automatically updates already-installed skills when the version changes, checking both project and global paths for the current working directory. If the current working directory is the home directory, `project` scope is normalized to `global` so installs, updates, and status checks use the global skill locations. First-time installation is done via the interactive `skill` subcommand (or `skill update` for update-only flows), or programmatically using the exported primitives.
79
79
 
80
80
  Generated bundles are written once to a canonical store (`.crust/skills` for project scope, `~/.crust/skills` for global scope) and then installed into agent paths via symlink or copy depending on `installMode`.
81
81
 
@@ -118,7 +118,7 @@ runMain(app);
118
118
  If auto-update does not appear to work:
119
119
 
120
120
  - Ensure plugin is passed to `runMain(..., { plugins: [...] })`.
121
- - Ensure at least one supported agent is detected. Auto-update checks both project and global install paths.
121
+ - Ensure at least one supported agent is detected. Auto-update checks both project and global install paths, with home-directory `project` scope treated as `global`.
122
122
  - Check for existing conflicting skill directories without `crust.json`.
123
123
 
124
124
  ## Recommended Export Pattern
@@ -302,6 +302,8 @@ resolveCanonicalSkillPath("global", "my-cli");
302
302
  // → "~/.crust/skills/my-cli"
303
303
  ```
304
304
 
305
+ When `process.cwd()` is the home directory, `resolveCanonicalSkillPath("project", ...)` returns the same global path as `resolveCanonicalSkillPath("global", ...)`.
306
+
305
307
  ### `isValidSkillName(name)`
306
308
 
307
309
  Validates a skill name against the [Agent Skills spec](https://agentskills.io/specification) pattern: 1–64 lowercase alphanumeric characters and hyphens, no leading/trailing/consecutive hyphens.
package/dist/index.d.ts CHANGED
@@ -84,7 +84,7 @@ interface SkillMeta {
84
84
  type AgentTarget = "amp" | "adal" | "antigravity" | "augment" | "claude-code" | "cline" | "codebuddy" | "codex" | "command-code" | "continue" | "cortex" | "crush" | "cursor" | "droid" | "gemini-cli" | "github-copilot" | "goose" | "iflow-cli" | "junie" | "kilo" | "kimi-cli" | "kiro-cli" | "kode" | "mcpjam" | "mistral-vibe" | "mux" | "neovate" | "opencode" | "openclaw" | "openhands" | "pi" | "pochi" | "qoder" | "qwen-code" | "replit" | "roo" | "trae" | "trae-cn" | "windsurf" | "zencoder";
85
85
  /** Agent install class used by interactive skill management UX. */
86
86
  type AgentClass = "universal" | "additional";
87
- /** Installation scope — global (home directory) or project (cwd). */
87
+ /** Installation scope — global (home directory) or project (cwd, except home dir which normalizes to global). */
88
88
  type Scope = "global" | "project";
89
89
  /** Installation strategy for agent skill output paths. */
90
90
  type SkillInstallMode = "auto" | "symlink" | "copy";
@@ -127,12 +127,14 @@ interface GenerateOptions {
127
127
  * - `"copy"`: write full copies directly into each agent path.
128
128
  *
129
129
  * Canonical bundles are always generated once under `.crust/skills` (project)
130
- * or `~/.crust/skills` (global).
130
+ * or `~/.crust/skills` (global). When `process.cwd()` is the home directory,
131
+ * project scope is normalized to the global location.
131
132
  * @default "auto"
132
133
  */
133
134
  installMode?: SkillInstallMode;
134
135
  /**
135
136
  * Installation scope — global (home directory) or project (cwd).
137
+ * When `process.cwd()` is the home directory, `"project"` is treated as `"global"`.
136
138
  * @default "global"
137
139
  */
138
140
  scope?: Scope;
@@ -182,6 +184,7 @@ interface UninstallOptions {
182
184
  agents: AgentTarget[];
183
185
  /**
184
186
  * Installation scope to uninstall from.
187
+ * When `process.cwd()` is the home directory, `"project"` is treated as `"global"`.
185
188
  * @default "global"
186
189
  */
187
190
  scope?: Scope;
@@ -203,6 +206,7 @@ interface StatusOptions {
203
206
  agents: AgentTarget[];
204
207
  /**
205
208
  * Installation scope to check.
209
+ * When `process.cwd()` is the home directory, `"project"` is treated as `"global"`.
206
210
  * @default "global"
207
211
  */
208
212
  scope?: Scope;
@@ -244,6 +248,8 @@ interface StatusResult {
244
248
  * prompted to choose `project` or `global`.
245
249
  * - If `defaultScope` is not set and the terminal is non-interactive, scope
246
250
  * falls back to `"global"`.
251
+ * - When `process.cwd()` is the home directory, `"project"` is normalized to
252
+ * `"global"` for path resolution and update/status messaging.
247
253
  */
248
254
  interface SkillPluginOptions {
249
255
  /** Skill version string — compared against the installed crust.json */
@@ -253,6 +259,7 @@ interface SkillPluginOptions {
253
259
  *
254
260
  * When omitted, interactive commands prompt for scope in TTY mode.
255
261
  * Non-interactive mode falls back to "global".
262
+ * When `process.cwd()` is the home directory, `"project"` behaves as `"global"`.
256
263
  */
257
264
  defaultScope?: Scope;
258
265
  /**
package/dist/index.js CHANGED
@@ -1,11 +1,11 @@
1
1
  // @bun
2
- import{accessSync as aQ,constants as nQ}from"fs";import{homedir as e}from"os";import{delimiter as tQ,join as Y}from"path";var k=Y(".agents","skills"),eQ=Y(".crust","skills");function VQ(Q){if(Q!==e())return Y(Q,".config");let Z=process.env.XDG_CONFIG_HOME?.trim();return Z&&Z.length>0?Z:Y(Q,".config")}function j(Q){return Y(Q,".agents","skills")}function QZ(Q){return Y(Q,".crust","skills")}var C={amp:{label:"Amp",class:"universal",projectSkillsDir:k,globalSkillsDir:j},adal:{label:"AdaL",class:"additional",projectSkillsDir:Y(".adal","skills"),globalSkillsDir:(Q)=>Y(Q,".adal","skills"),detectCommands:["adal"]},antigravity:{label:"Antigravity",class:"additional",projectSkillsDir:Y(".agent","skills"),globalSkillsDir:(Q)=>Y(Q,".gemini","antigravity","skills"),detectCommands:["antigravity"]},augment:{label:"Augment",class:"additional",projectSkillsDir:Y(".augment","skills"),globalSkillsDir:(Q)=>Y(Q,".augment","skills"),detectCommands:["augment"]},"claude-code":{label:"Claude Code",class:"additional",projectSkillsDir:Y(".claude","skills"),globalSkillsDir:(Q)=>Y(process.env.CLAUDE_CONFIG_DIR?.trim()||Y(Q,".claude"),"skills"),detectCommands:["claude","claude-code"]},cline:{label:"Cline",class:"universal",projectSkillsDir:k,globalSkillsDir:j},codebuddy:{label:"CodeBuddy",class:"additional",projectSkillsDir:Y(".codebuddy","skills"),globalSkillsDir:(Q)=>Y(Q,".codebuddy","skills"),detectCommands:["codebuddy"]},codex:{label:"Codex",class:"universal",projectSkillsDir:k,globalSkillsDir:j},"command-code":{label:"Command Code",class:"additional",projectSkillsDir:Y(".commandcode","skills"),globalSkillsDir:(Q)=>Y(Q,".commandcode","skills"),detectCommands:["command-code","commandcode"]},continue:{label:"Continue",class:"additional",projectSkillsDir:Y(".continue","skills"),globalSkillsDir:(Q)=>Y(Q,".continue","skills"),detectCommands:["continue"]},cortex:{label:"Cortex Code",class:"additional",projectSkillsDir:Y(".cortex","skills"),globalSkillsDir:(Q)=>Y(Q,".snowflake","cortex","skills"),detectCommands:["cortex"]},crush:{label:"Crush",class:"additional",projectSkillsDir:Y(".crush","skills"),globalSkillsDir:(Q)=>Y(VQ(Q),"crush","skills"),detectCommands:["crush"]},cursor:{label:"Cursor",class:"universal",projectSkillsDir:k,globalSkillsDir:j},droid:{label:"Droid",class:"additional",projectSkillsDir:Y(".factory","skills"),globalSkillsDir:(Q)=>Y(Q,".factory","skills"),detectCommands:["droid"]},"gemini-cli":{label:"Gemini CLI",class:"universal",projectSkillsDir:k,globalSkillsDir:j},"github-copilot":{label:"GitHub Copilot",class:"universal",projectSkillsDir:k,globalSkillsDir:j},goose:{label:"Goose",class:"additional",projectSkillsDir:Y(".goose","skills"),globalSkillsDir:(Q)=>Y(VQ(Q),"goose","skills"),detectCommands:["goose"]},"iflow-cli":{label:"iFlow CLI",class:"additional",projectSkillsDir:Y(".iflow","skills"),globalSkillsDir:(Q)=>Y(Q,".iflow","skills"),detectCommands:["iflow","iflow-cli"]},junie:{label:"Junie",class:"additional",projectSkillsDir:Y(".junie","skills"),globalSkillsDir:(Q)=>Y(Q,".junie","skills"),detectCommands:["junie"]},kilo:{label:"Kilo Code",class:"additional",projectSkillsDir:Y(".kilocode","skills"),globalSkillsDir:(Q)=>Y(Q,".kilocode","skills"),detectCommands:["kilo","kilocode"]},"kimi-cli":{label:"Kimi Code CLI",class:"universal",projectSkillsDir:k,globalSkillsDir:j},"kiro-cli":{label:"Kiro CLI",class:"additional",projectSkillsDir:Y(".kiro","skills"),globalSkillsDir:(Q)=>Y(Q,".kiro","skills"),detectCommands:["kiro","kiro-cli"]},kode:{label:"Kode",class:"additional",projectSkillsDir:Y(".kode","skills"),globalSkillsDir:(Q)=>Y(Q,".kode","skills"),detectCommands:["kode"]},mcpjam:{label:"MCPJam",class:"additional",projectSkillsDir:Y(".mcpjam","skills"),globalSkillsDir:(Q)=>Y(Q,".mcpjam","skills"),detectCommands:["mcpjam"]},"mistral-vibe":{label:"Mistral Vibe",class:"additional",projectSkillsDir:Y(".vibe","skills"),globalSkillsDir:(Q)=>Y(Q,".vibe","skills"),detectCommands:["mistral-vibe","vibe"]},mux:{label:"Mux",class:"additional",projectSkillsDir:Y(".mux","skills"),globalSkillsDir:(Q)=>Y(Q,".mux","skills"),detectCommands:["mux"]},neovate:{label:"Neovate",class:"additional",projectSkillsDir:Y(".neovate","skills"),globalSkillsDir:(Q)=>Y(Q,".neovate","skills"),detectCommands:["neovate"]},opencode:{label:"OpenCode",class:"universal",projectSkillsDir:k,globalSkillsDir:j},openclaw:{label:"OpenClaw",class:"additional",projectSkillsDir:"skills",globalSkillsDir:(Q)=>Y(Q,".openclaw","skills"),detectCommands:["openclaw"]},openhands:{label:"OpenHands",class:"additional",projectSkillsDir:Y(".openhands","skills"),globalSkillsDir:(Q)=>Y(Q,".openhands","skills"),detectCommands:["openhands"]},pi:{label:"Pi",class:"additional",projectSkillsDir:Y(".pi","skills"),globalSkillsDir:(Q)=>Y(Q,".pi","agent","skills"),detectCommands:["pi"]},pochi:{label:"Pochi",class:"additional",projectSkillsDir:Y(".pochi","skills"),globalSkillsDir:(Q)=>Y(Q,".pochi","skills"),detectCommands:["pochi"]},qoder:{label:"Qoder",class:"additional",projectSkillsDir:Y(".qoder","skills"),globalSkillsDir:(Q)=>Y(Q,".qoder","skills"),detectCommands:["qoder"]},"qwen-code":{label:"Qwen Code",class:"additional",projectSkillsDir:Y(".qwen","skills"),globalSkillsDir:(Q)=>Y(Q,".qwen","skills"),detectCommands:["qwen","qwen-code"]},replit:{label:"Replit",class:"universal",projectSkillsDir:k,globalSkillsDir:j},roo:{label:"Roo Code",class:"additional",projectSkillsDir:Y(".roo","skills"),globalSkillsDir:(Q)=>Y(Q,".roo","skills"),detectCommands:["roo","roo-code"]},trae:{label:"Trae",class:"additional",projectSkillsDir:Y(".trae","skills"),globalSkillsDir:(Q)=>Y(Q,".trae","skills"),detectCommands:["trae"]},"trae-cn":{label:"Trae CN",class:"additional",projectSkillsDir:Y(".trae","skills"),globalSkillsDir:(Q)=>Y(Q,".trae-cn","skills"),detectCommands:["trae-cn","trae"]},windsurf:{label:"Windsurf",class:"additional",projectSkillsDir:Y(".windsurf","skills"),globalSkillsDir:(Q)=>Y(Q,".codeium","windsurf","skills"),detectCommands:["windsurf"]},zencoder:{label:"Zencoder",class:"additional",projectSkillsDir:Y(".zencoder","skills"),globalSkillsDir:(Q)=>Y(Q,".zencoder","skills"),detectCommands:["zencoder"]}},u=Object.keys(C),y=Object.fromEntries(u.map((Q)=>[Q,C[Q].label]));function S(){return u.filter((Q)=>C[Q].class==="universal")}function h(){return u.filter((Q)=>C[Q].class==="additional")}function ZZ(Q){return C[Q].class==="universal"}async function QQ(Q){let Z=typeof Q==="string"?{home:Q}:Q??{},X=Z.cwd??process.cwd(),$=Z.commandChecker??((H)=>Promise.resolve(XZ(H))),W=[];for(let H of h()){let B=C[H].detectCommands??[],z=!1;for(let J of B)if(await $(J,X)){z=!0;break}if(z)W.push(H)}return W}function O(Q,Z,X){let $=C[Q];if(Z==="project")return Y(process.cwd(),$.projectSkillsDir,X);return Y($.globalSkillsDir(e()),X)}function P(Q,Z){if(Q==="project")return Y(process.cwd(),eQ,Z);return Y(QZ(e()),Z)}function XZ(Q){let X=(process.env.PATH??"").split(tQ).filter((H)=>H.length>0),$=process.platform==="win32",W=$?(process.env.PATHEXT??".EXE;.CMD;.BAT;.COM").split(";").filter((H)=>H.length>0):[];for(let H of X){if(!$&&MQ(Y(H,Q)))return!0;if($){for(let B of W)if(MQ(Y(H,Q+B)))return!0}}return!1}function MQ(Q){try{return aQ(Q,nQ.X_OK),!0}catch{return!1}}import{Crust as $Z}from"@crustjs/core";function s(Q){if(Q===void 0)return[];return(Array.isArray(Q)?Q:[Q]).flatMap((X)=>X.split(/\r?\n/)).map((X)=>X.trim()).filter((X)=>X.length>0)}function TQ(Q){let Z=Q?.trim();if(!Z)return[];return Z.split(/\r?\n/)}function ZQ(Q){return Q.length>0}var _Q=Symbol("crust.skill.commandAnnotations");function WZ(Q){return Q instanceof $Z?Q._node:Q}function HZ(Q,Z){let X=WZ(Q),$=s(typeof Z==="string"||Array.isArray(Z)?Z:Z.instructions??[]);if($.length===0)return Q;let W=XQ(X)?.instructions??[],H=[...new Set([...W,...$])];return Object.defineProperty(X,_Q,{value:{instructions:H},enumerable:!0,configurable:!0}),Q}function XQ(Q){let Z=Q[_Q];if(!Z?.instructions||Z.instructions.length===0)return;return{instructions:[...Z.instructions]}}class I extends Error{name="SkillConflictError";details;constructor(Q){let Z=`Skill conflict for agent "${Q.agent}": directory "${Q.outputDir}" already exists but was not created by Crust (no crust.json found). Delete or rename the conflicting skill to resolve.`;super(Z);this.details=Q}}import{lstat as NZ,mkdir as hQ,readlink as LZ,realpath as PZ,rm as m,symlink as CZ,writeFile as yZ}from"fs/promises";import{dirname as pQ,join as SQ}from"path";function bQ(Q){return OQ(Q,[])}function OQ(Q,Z){let X=YZ(Q.meta.name),$=[...Z,X],W=qZ(Q.args),H=JZ(Q.effectiveFlags),B=KZ(Q.subCommands,$),z=XQ(Q);return{name:X,path:$,description:Q.meta.description,usage:Q.meta.usage,instructions:z?.instructions,runnable:typeof Q.run==="function",args:W,flags:H,children:B}}function YZ(Q){return Q.trim().toLowerCase()}function qZ(Q){if(!Q||Q.length===0)return[];return Q.map(BZ)}function BZ(Q){let Z={name:Q.name,type:Q.type,required:Q.required===!0,variadic:Q.variadic===!0};if(Q.description!==void 0)Z.description=Q.description;if(Q.default!==void 0)Z.default=RQ(Q.default);return Z}function JZ(Q){if(!Q)return[];return Object.keys(Q).sort().map((X)=>{return zZ(X,Q[X])})}function zZ(Q,Z){let X={name:Q,type:Z.type,required:Z.required===!0,multiple:Z.multiple===!0,short:Z.short,aliases:Z.aliases?[...Z.aliases].sort():[]};if(Z.description!==void 0)X.description=Z.description;if(Z.default!==void 0)X.default=RQ(Z.default);return X}function KZ(Q,Z){return Object.keys(Q).sort().map(($)=>{return OQ(Q[$],Z)})}function RQ(Q){if(Array.isArray(Q))return JSON.stringify(Q);return String(Q)}function c(Q){if(/[:#[\]{}&*!|>'"`,@?\\]|^\s|\s$|^---|[\n\r]/.test(Q))return`"${Q.replace(/\\/g,"\\\\").replace(/"/g,"\\\"").replace(/\n/g,"\\n").replace(/\r/g,"\\r")}"`;return Q}function EQ(Q){return Q.replace(/(?<!\\)\|/g,"\\|")}function kQ(Q,Z){let X=[],$=jQ(Q);X.push({path:"SKILL.md",content:xZ(Q,Z,$)});for(let W of $){let H=D(W),B=W.children.length>0?UZ(W,Q):wZ(W,Q);X.push({path:H,content:B})}return X}function jQ(Q){let Z=[Q];for(let X of Q.children)Z.push(...jQ(X));return Z}function D(Q){if(Q.path.length<=1)return`commands/${Q.name}.md`;return`commands/${Q.path.slice(1).join("/")}.md`}function WQ(Q){return Q.path.join(" ")}function $Q(Q,Z){let X=Q.split("/").slice(0,-1),$=Z.split("/"),W=0;while(W<X.length&&W<$.length&&X[W]===$[W])W++;let H=X.length-W,B=$.slice(W);if(H===0)return B.join("/");return[...Array.from({length:H},()=>".."),...B].join("/")}function xZ(Q,Z,X){let $=[];if($.push("---"),$.push(`name: ${c(Z.name)}`),$.push(`description: ${c(Z.description)}`),Z.license)$.push(`license: ${c(Z.license)}`);if(Z.compatibility)$.push(`compatibility: ${c(Z.compatibility)}`);if(Z.disableModelInvocation)$.push("disable-model-invocation: true");if(Z.allowedTools)$.push(`allowed-tools: ${c(Z.allowedTools)}`);if($.push("metadata:"),$.push(` version: "${Z.version}"`),$.push("---"),$.push(""),$.push(`# ${Z.name}`),$.push(""),Q.description)$.push(Q.description),$.push("");$.push(`You should use this skill when you need accurate help with \`${Z.name}\` commands, including command selection, syntax, arguments, flags, defaults, and subcommands.`),$.push("");let W=EZ(Z.instructions);if($.push("## How to Use This Skill"),$.push(""),$.push("1. You must find the command that best matches the user's task from the Command Reference below."),$.push("2. You must check the `Type` column before suggesting execution: `runnable` and `runnable, group` commands can be executed, while `group` commands are organizational only."),$.push("3. You should read only the linked file or files you need from `commands/`."),$.push("4. You must read a command's file before answering a command-specific question or suggesting that command."),$.push("5. You must treat the command file as the source of truth for usage, arguments, flags, aliases, and defaults."),$.push("6. If a flag, argument, alias, or default is not documented there, you must say it is not documented instead of guessing."),$.push(""),ZQ(W))$.push("## General Guidance"),$.push(""),$.push(...W),$.push("");if($.push("## Command Reference"),$.push(""),$.push("You should use this table to locate the command file you need."),$.push(""),$.push(...GZ(X)),$.push(""),Q.runnable){$.push("## Usage"),$.push("");let H=D(Q);$.push(`The root command is directly executable. You should see [${Q.name}](${H}) for usage details.`),$.push("")}return $.join(`
3
- `)}function GZ(Q){let Z=[];Z.push("| Command | Type | Documentation |"),Z.push("| ------- | ---- | ------------- |");for(let X of Q){let $=WQ(X),W=D(X),H=FZ(X);Z.push(`| \`${$}\` | ${H} | [${W}](${W}) |`)}return Z}function FZ(Q){if(Q.runnable&&Q.children.length>0)return"runnable, group";if(Q.runnable)return"runnable";return"group"}function wZ(Q,Z){let X=[];return X.push(...IQ(Q)),X.push(...NQ(Q)),X.push(...LQ(Q)),X.push(...CQ(Q,Z)),X.join(`
4
- `)}function UZ(Q,Z){let X=[],$=D(Q);if(X.push(...IQ(Q)),X.push(...NQ(Q)),Q.runnable)X.push(...LQ(Q));return X.push(...MZ(Q,$)),X.push(...CQ(Q,Z)),X.join(`
5
- `)}function VZ(Q){let Z=[...Q.path];for(let X of Q.args)if(X.variadic)Z.push(X.required?`<${X.name}...>`:`[${X.name}...]`);else Z.push(X.required?`<${X.name}>`:`[${X.name}]`);if(Q.flags.length>0)Z.push("[options]");return Z.join(" ")}function IQ(Q){let Z=[`# \`${WQ(Q)}\``,""];if(Q.description)Z.push(Q.description,"");return Z}function NQ(Q){let Z=Q.instructions??[];if(!ZQ(Z))return[];return["## Command Instructions","",...PQ(Z),""]}function LQ(Q){let Z=["## Usage","","```",Q.usage??VZ(Q),"```",""];if(Q.args.length>0)Z.push("## Arguments","",...TZ(Q.args),"");if(Q.flags.length>0)Z.push("## Flags","",...bZ(Q.flags),"");return Z.push("## Command Documentation Authority","","You must treat only the arguments, flags, options, aliases, and defaults documented in this file as supported for this command.","You must not infer or invent additional command-line options.",""),Z}function MZ(Q,Z){let X=["## Subcommands",""];for(let $ of Q.children){let W=D($),H=$Q(Z,W),B=$.description?` - ${$.description}`:"";X.push(`- [\`${$.name}\`](${H})${B}`)}return X.push(""),X}function TZ(Q){let Z=[];Z.push("| Argument | Type | Required | Description |"),Z.push("| -------- | ---- | -------- | ----------- |");for(let X of Q){let $=X.variadic?`${X.name}...`:X.name,W=X.required?"Yes":"No",H=EQ(_Z(X));Z.push(`| \`${$}\` | ${X.type} | ${W} | ${H} |`)}return Z}function _Z(Q){let Z=[];if(Q.description)Z.push(Q.description);if(Q.default!==void 0)Z.push(`Default: \`${Q.default}\``);return Z.join(". ")||"-"}function bZ(Q){let Z=[];Z.push("| Flag | Type | Required | Description |"),Z.push("| ---- | ---- | -------- | ----------- |");for(let X of Q){let $=OZ(X),W=X.required?"Yes":"No",H=EQ(RZ(X));Z.push(`| ${$} | ${X.type} | ${W} | ${H} |`)}return Z}function OZ(Q){let Z=[`\`--${Q.name}\``];if(Q.short)Z.push(`\`-${Q.short}\``);for(let X of Q.aliases)Z.push(`\`--${X}\``);return Z.join(", ")}function RZ(Q){let Z=[];if(Q.description)Z.push(Q.description);if(Q.multiple)Z.push("Can be specified multiple times");if(Q.default!==void 0)Z.push(`Default: \`${Q.default}\``);return Z.join(". ")||"-"}function PQ(Q){return Q.map((Z)=>`- ${Z}`)}function EZ(Q){if(typeof Q==="string")return TQ(Q);return PQ(s(Q))}function CQ(Q,Z){let X=[],$=D(Q);if(X.push("---"),X.push(""),Q.path.length>1){let H=Q.path.slice(0,-1),B=yQ(Z,H);if(B){let z=D(B),J=$Q($,z),K=WQ(B);X.push(`Parent: [\`${K}\`](${J})`),X.push("")}}let W=$Q($,"SKILL.md");return X.push(`[Skill Overview](${W})`),X.push(""),X}function yQ(Q,Z){if(kZ(Q.path,Z))return Q;for(let X of Q.children){let $=yQ(X,Z);if($)return $}return}function kZ(Q,Z){if(Q.length!==Z.length)return!1;for(let X=0;X<Q.length;X++)if(Q[X]!==Z[X])return!1;return!0}import{readFile as jZ}from"fs/promises";import{join as IZ}from"path";var HQ="crust.json";async function A(Q){try{let Z=await jZ(IZ(Q,HQ),"utf-8"),X=JSON.parse(Z);if(typeof X==="object"&&X!==null&&"version"in X&&typeof X.version==="string")return X.version;return null}catch{return null}}var SZ="auto",gQ=/^[a-z0-9]+(-[a-z0-9]+)*$/;function uQ(Q){return Q.length>=1&&Q.length<=64&&gQ.test(Q)}function i(Q){return Q}function BQ(Q){return Q.startsWith("use-")?Q:`use-${Q}`}async function p(Q){let{command:Z,meta:X,agents:$,scope:W="global",clean:H=!0,force:B=!1,installMode:z=SZ}=Q,J=i(X.name),K=BQ(X.name);if(!uQ(J))throw Error(`Invalid skill name "${J}": must be 1\u201364 lowercase `+`alphanumeric characters and hyphens, no leading/trailing/consecutive hyphens. Pattern: ${gQ.source}`);let x=$[0];if(!x)return{agents:[]};let G={...X,name:J},U=bQ(Z),N=kQ(U,G),M=pZ(G),b=[...N,...M].sort((q,F)=>q.path<F.path?-1:q.path>F.path?1:0),R=b.map((q)=>q.path),T=new Map;for(let q of $){let F=O(q,W,G.name),w=T.get(F);if(w)w.push(q);else T.set(F,[q])}let _=P(W,G.name),E=P(W,K),L=new Map;for(let[q,F]of T){let w=F[0];if(!w)continue;L.set(q,await KQ({outputDir:q,legacyOutputDir:O(w,W,K),canonicalOutputDir:_,legacyCanonicalOutputDir:E}))}let l=await A(_);if((await zQ(_,_)).exists&&l===null&&!B)throw new I({agent:x,outputDir:_});let v=l!==G.version;if(v){if(H)await xQ(_);await cQ(_,b)}let f=[];for(let[q,F]of T){let w=F[0];if(!w)continue;let V=L.get(q);if(!V)continue;if(V.current.inspection.exists&&!V.current.isCrustManaged&&!B)throw new I({agent:w,outputDir:q});let n=await AZ({outputDir:q,canonicalOutputDir:_,allFiles:b,clean:H,installMode:z,inspection:V.current.inspection,installedVersion:V.preferredVersion,currentVersion:G.version}),iQ=await fZ(V),t=DZ({installedVersion:V.preferredVersion,currentVersion:G.version,canonicalChanged:v,pathChanged:n||iQ||V.preferredOutputDir!==q});for(let oQ of F)f.push({agent:oQ,outputDir:q,files:t==="up-to-date"?[]:R,status:t,previousVersion:t==="updated"?V.preferredVersion??void 0:void 0})}{let q=await A(E);if(E!==_&&q!==null&&!await qQ(K,W))await m(E,{recursive:!0,force:!0})}return{agents:f}}async function JQ(Q){let{name:Z,agents:X,scope:$="global"}=Q,W=i(Z),H=BQ(Z),B=P($,W),z=P($,H),J=[],K=new Map;for(let x of X){let G=O(x,$,W),U=K.get(G);if(U)U.push(x);else K.set(G,[x])}for(let[x,G]of K){let U=G[0];if(!U)continue;let N=O(U,$,H),M=await KQ({outputDir:x,legacyOutputDir:N,canonicalOutputDir:B,legacyCanonicalOutputDir:z}),b=await YQ(M.current),R=M.legacy.outputDir!==M.current.outputDir?await YQ(M.legacy):!1,T=b||R,_=b?x:R?N:x;for(let E of G)J.push({agent:E,outputDir:_,status:T?"removed":"not-found"})}if(await A(B)!==null&&!await qQ(W,$))await m(B,{recursive:!0,force:!0});{let x=await A(z);if(z!==B&&x!==null&&!await qQ(H,$))await m(z,{recursive:!0,force:!0})}return{agents:J}}async function d(Q){let{name:Z,agents:X,scope:$="global"}=Q,W=i(Z),H=BQ(Z),B=[],z=new Map;for(let J of X){let K=O(J,$,W),x=z.get(K);if(x)x.push(J);else z.set(K,[J])}for(let[J,K]of z){let x=K[0];if(!x)continue;let G=O(x,$,H),U=P($,W),N=P($,H),M=await KQ({outputDir:J,legacyOutputDir:G,canonicalOutputDir:U,legacyCanonicalOutputDir:N}),b=M.preferredOutputDir??J,R=M.preferredVersion;for(let T of K)B.push({agent:T,outputDir:b,installed:R!==null,version:R??void 0})}return{agents:B}}function DZ(Q){let{installedVersion:Z,currentVersion:X,canonicalChanged:$,pathChanged:W}=Q;if(Z===null)return"installed";if(Z===X&&!$&&!W)return"up-to-date";return"updated"}async function AZ(Q){let{outputDir:Z,canonicalOutputDir:X,allFiles:$,clean:W,installMode:H,inspection:B,installedVersion:z,currentVersion:J}=Q;if(H==="copy")return DQ({outputDir:Z,allFiles:$,clean:W,inspection:B,installedVersion:z,currentVersion:J});try{return await vZ({outputDir:Z,canonicalOutputDir:X,inspection:B})}catch(K){if(H==="symlink")throw Error(`Failed to create symlink at "${Z}" (installMode: symlink).`,{cause:K});let x=await zQ(Z,X);return DQ({outputDir:Z,allFiles:$,clean:W,inspection:x,installedVersion:z,currentVersion:J})}}async function DQ(Q){let{outputDir:Z,allFiles:X,clean:$,inspection:W,installedVersion:H,currentVersion:B}=Q;if(!(!W.exists||W.isSymlink||H!==B))return!1;if(W.isSymlink||$)await xQ(Z);return await cQ(Z,X),!0}async function vZ(Q){let{outputDir:Z,canonicalOutputDir:X,inspection:$}=Q;if($.exists&&$.isSymlink&&$.pointsToCanonical)return!1;if($.exists)await xQ(Z);return await hZ(X,Z),!0}async function zQ(Q,Z){let X;try{X=await NZ(Q)}catch{return{exists:!1,isSymlink:!1,pointsToCanonical:!1}}let $=process.platform==="win32"&&X.isDirectory()&&await fQ(Q)!==null;if(!(X.isSymbolicLink()||$))return{exists:!0,isSymlink:!1,pointsToCanonical:!1};let[H,B,z]=await Promise.all([vQ(Q),vQ(Z),fQ(Q)]);return{exists:!0,isSymlink:!0,pointsToCanonical:H!==null&&B!==null&&H===B||z===Z}}async function AQ(Q,Z){let[X,$]=await Promise.all([A(Q),zQ(Q,Z)]),W=X!==null||$.exists&&$.isSymlink&&$.pointsToCanonical;return{outputDir:Q,version:X,inspection:$,isCrustManaged:W}}async function KQ(Q){let{outputDir:Z,legacyOutputDir:X,canonicalOutputDir:$,legacyCanonicalOutputDir:W}=Q,H=await AQ(Z,$),B=X===Z?H:await AQ(X,W);if(H.isCrustManaged)return{current:H,legacy:B,preferredVersion:H.version,preferredOutputDir:H.outputDir};if(B.isCrustManaged)return{current:H,legacy:B,preferredVersion:B.version,preferredOutputDir:B.outputDir};return{current:H,legacy:B,preferredVersion:null,preferredOutputDir:null}}async function YQ(Q){if(!Q.isCrustManaged||!Q.inspection.exists)return!1;return await m(Q.outputDir,{recursive:!0,force:!0}),!0}async function fZ(Q){if(Q.legacy.outputDir===Q.current.outputDir)return!1;return YQ(Q.legacy)}async function vQ(Q){try{return await PZ(Q)}catch{return null}}async function fQ(Q){try{return await LZ(Q)}catch{return null}}async function hZ(Q,Z){await hQ(pQ(Z),{recursive:!0});let X=process.platform==="win32"?"junction":"dir";await CZ(Q,Z,X)}async function qQ(Q,Z){let X=new Set;for(let $ of u)X.add(O($,Z,Q));for(let $ of X)if(await A($)!==null)return!0;return!1}function pZ(Q){let Z={name:Q.name,description:Q.description,version:Q.version};return[{path:HQ,content:`${JSON.stringify(Z,null,"\t")}
6
- `}]}async function xQ(Q){await m(Q,{recursive:!0,force:!0})}async function cQ(Q,Z){let X=new Set;for(let W of Z){let H=SQ(Q,W.path),B=pQ(H);X.add(B)}let $=[...X].sort();for(let W of $)await hQ(W,{recursive:!0});for(let W of Z){let H=SQ(Q,W.path);await yZ(H,W.content,"utf-8")}}import{Crust as dQ,VALIDATION_MODE_ENV as gZ}from"@crustjs/core";import{confirm as uZ,multiselect as cZ,select as mZ,spinner as r}from"@crustjs/prompts";import{bold as a,dim as g,yellow as rQ}from"@crustjs/style";var dZ="skill",lQ="global",o="__universal__";function rZ(Q){return Q==="global"||Q==="project"}async function sQ(Q,Z){if(Q!==void 0){if(!rZ(Q))throw Error(`Invalid --scope value: ${String(Q)}. Expected "project" or "global".`);return Q}if(Z.defaultScope)return Z.defaultScope;return mZ({message:"Select scope",choices:[{label:"Project",value:"project"},{label:"Global",value:"global"}],default:lQ})}function GQ(Q){let Z=new Set(S()),X=[];if(Q.some(($)=>Z.has($)))X.push("Universal");for(let $ of Q){if(Z.has($))continue;X.push(y[$])}return X}function mQ(Q){let Z=new Set(S()),X=[],$=Q.find((W)=>Z.has(W.agent));if($)X.push({label:"Universal",outputDir:$.outputDir});for(let W of Q){if(Z.has(W.agent))continue;X.push({label:y[W.agent],outputDir:W.outputDir})}return X}function FQ(Q,Z){return{name:Q.meta.name,description:Q.meta.description??"",version:Z.version,instructions:Z.instructions,license:Z.license,allowedTools:Z.allowedTools,compatibility:Z.compatibility,disableModelInvocation:Z.disableModelInvocation}}function wQ(Q,Z,X,$){if(!$.installed)return!1;let W=O(Q,Z,X.name);return $.version!==X.version||$.outputDir!==W}async function lZ(Q,Z){let X=[...S(),...h()];if(X.length===0)return;let $=FQ(Q,Z),W=["project","global"];for(let H of W){let z=(await d({name:$.name,agents:X,scope:H})).agents.filter((J)=>wQ(J.agent,H,$,J));if(z.length===0)continue;try{await r({message:`Updating ${H} skills...`,task:async({updateMessage:J})=>{let K=await p({command:Q,meta:$,agents:z.map((U)=>U.agent),scope:H,installMode:Z.installMode}),x=K.agents.filter((U)=>U.status==="updated").map((U)=>U.agent),G=GQ(x);if(G.length>0)J(`Updated skill "${$.name}" to v${$.version} for ${G.join(", ")} (${H})`);return K}})}catch(J){if(J instanceof I)console.warn(rQ(`Skill conflict: "${J.details.outputDir}" already exists but was not created by ${$.name}. Skipping auto-update for ${H}. Delete or rename the conflicting skill to resolve.`));else throw J}}}function sZ(Q){let Z;return{name:"skills",async setup(X,$){Z=X.rootCommand;let W=Q.command??dZ;if($.addSubCommand(Z,W,iZ(Z,Q,W)),process.env[gZ]==="1")return;if(X.argv[0]===W)return;if(Q.autoUpdate!==!1)await lZ(Z,Q)}}}function iZ(Q,Z,X){let $=oZ(Q,Z);return new dQ(X).meta({description:"Manage agent skill installations"}).flags({scope:{type:"string",description:"Install scope (project or global)"},all:{type:"boolean",description:"Install for all detected agents non-interactively (universal + detected)"}}).run(async(W)=>{let H=FQ(Q,Z),B=W.flags.all===!0,z=B?Z.defaultScope??lQ:await sQ(W.flags.scope,Z),J=await QQ(),K=S(),x=h(),G=await d({name:H.name,agents:[...K,...x],scope:z}),U=new Set(G.agents.filter((q)=>q.installed).map((q)=>q.agent)),N=new Set(J),M=new Map(G.agents.map((q)=>[q.agent,q])),b=x.filter((q)=>{if(N.has(q))return!0;return M.get(q)?.installed===!0}),R=b.filter((q)=>U.has(q)),T=[];if(K.length>0){let q=K[0];if(!q)throw Error("Expected at least one universal agent");let w=M.get(q)?.outputDir??"path unavailable";T.push({label:"Universal",value:o,hint:w});let V=K.map((n)=>y[n]).join(", ");console.log(g(`Agents supporting universal skills: ${V}`))}for(let q of b){let w=M.get(q)?.outputDir??"path unavailable";T.push({label:y[q],value:q,hint:w})}let _=K.length>0&&K.every((q)=>U.has(q)),E=[...R.filter((q)=>!K.includes(q))];if(_)E.unshift(o);let L;if(B)L=[...K,...b];else{let q=T.length===0?[]:await cZ({message:"Select agents to install skills for",choices:T,default:E,required:!1}),F=new Set(q.filter((w)=>w!==o));if(q.includes(o))for(let w of K)F.add(w);L=[...F]}let l=L.filter((q)=>!U.has(q)),UQ=L.filter((q)=>{let F=M.get(q);return F!==void 0&&wQ(q,z,H,F)}),v=[...U].filter((q)=>!L.includes(q)),f=[...l,...UQ];if(f.length>0)try{let q=await r({message:"Installing skills...",task:async()=>p({command:Q,meta:H,agents:f,scope:z,installMode:Z.installMode})});console.log(`
7
- ${a(`Installed "${H.name}" v${H.version}`)}`);for(let F of mQ(q.agents))console.log(g(` ${F.label} \u2192 ${F.outputDir}`))}catch(q){if(q instanceof I)if(B?!0:await uZ({message:`"${q.details.outputDir}" already exists but was not created by Crust. Overwrite?`,default:!1})){let w=await r({message:"Overwriting skill...",task:async()=>p({command:Q,meta:H,agents:[q.details.agent],scope:z,force:!0,installMode:Z.installMode})});console.log(`
8
- ${a(`Installed "${H.name}" v${H.version}`)}`);for(let V of mQ(w.agents))console.log(g(` ${V.label} \u2192 ${V.outputDir}`))}else console.log(g(`
9
- Skipped ${y[q.details.agent]}`));else throw q}if(v.length>0){let F=(await r({message:"Removing skills...",task:async()=>JQ({name:H.name,agents:v,scope:z})})).agents.filter((V)=>V.status==="removed").map((V)=>V.agent),w=GQ(F);if(w.length>0)console.log(`
10
- ${a(`Removed from ${w.join(", ")}`)}`)}if(f.length===0&&v.length===0)console.log(g("No changes."))}).command($)._node}function oZ(Q,Z){return new dQ("update").meta({description:"Update installed skills to latest version"}).flags({scope:{type:"string",description:"Update scope (project or global)"}}).run(async(X)=>{let $=await sQ(X.flags.scope,Z),W=[...S(),...h()],H=FQ(Q,Z),z=(await d({name:H.name,agents:W,scope:$})).agents.filter((J)=>wQ(J.agent,$,H,J));if(z.length===0){console.log(g(`No updates needed (${$}).`));return}try{let K=(await r({message:`Updating ${$} skills...`,task:async()=>p({command:Q,meta:H,agents:z.map((G)=>G.agent),scope:$,installMode:Z.installMode})})).agents.filter((G)=>G.status==="updated").map((G)=>G.agent),x=GQ(K);if(x.length>0)console.log(`
11
- ${a(`Updated "${H.name}" to v${H.version} for ${x.join(", ")} (${$})`)}`)}catch(J){if(J instanceof I)console.warn(rQ(`Skipped ${y[J.details.agent]}: "${J.details.outputDir}" already exists but was not created by ${H.name}. Delete or rename the conflicting directory to resolve.`));else throw J}})}export{JQ as uninstallSkill,d as skillStatus,sZ as skillPlugin,i as resolveSkillName,P as resolveCanonicalSkillPath,uQ as isValidSkillName,ZZ as isUniversalAgent,S as getUniversalAgents,h as getAdditionalAgents,p as generateSkill,QQ as detectInstalledAgents,HZ as annotate,I as SkillConflictError};
2
+ import{accessSync as nQ,constants as tQ}from"fs";import{homedir as i}from"os";import{delimiter as eQ,join as Y}from"path";var E=Y(".agents","skills"),QZ=Y(".crust","skills");function MQ(Q){if(Q!==i())return Y(Q,".config");let Z=process.env.XDG_CONFIG_HOME?.trim();return Z&&Z.length>0?Z:Y(Q,".config")}function j(Q){return Y(Q,".agents","skills")}function ZZ(Q){return Y(Q,".crust","skills")}function u(Q){return Q==="project"&&process.cwd()===i()?"global":Q}var C={amp:{label:"Amp",class:"universal",projectSkillsDir:E,globalSkillsDir:j},adal:{label:"AdaL",class:"additional",projectSkillsDir:Y(".adal","skills"),globalSkillsDir:(Q)=>Y(Q,".adal","skills"),detectCommands:["adal"]},antigravity:{label:"Antigravity",class:"additional",projectSkillsDir:Y(".agent","skills"),globalSkillsDir:(Q)=>Y(Q,".gemini","antigravity","skills"),detectCommands:["antigravity"]},augment:{label:"Augment",class:"additional",projectSkillsDir:Y(".augment","skills"),globalSkillsDir:(Q)=>Y(Q,".augment","skills"),detectCommands:["augment"]},"claude-code":{label:"Claude Code",class:"additional",projectSkillsDir:Y(".claude","skills"),globalSkillsDir:(Q)=>Y(process.env.CLAUDE_CONFIG_DIR?.trim()||Y(Q,".claude"),"skills"),detectCommands:["claude","claude-code"]},cline:{label:"Cline",class:"universal",projectSkillsDir:E,globalSkillsDir:j},codebuddy:{label:"CodeBuddy",class:"additional",projectSkillsDir:Y(".codebuddy","skills"),globalSkillsDir:(Q)=>Y(Q,".codebuddy","skills"),detectCommands:["codebuddy"]},codex:{label:"Codex",class:"universal",projectSkillsDir:E,globalSkillsDir:j},"command-code":{label:"Command Code",class:"additional",projectSkillsDir:Y(".commandcode","skills"),globalSkillsDir:(Q)=>Y(Q,".commandcode","skills"),detectCommands:["command-code","commandcode"]},continue:{label:"Continue",class:"additional",projectSkillsDir:Y(".continue","skills"),globalSkillsDir:(Q)=>Y(Q,".continue","skills"),detectCommands:["continue"]},cortex:{label:"Cortex Code",class:"additional",projectSkillsDir:Y(".cortex","skills"),globalSkillsDir:(Q)=>Y(Q,".snowflake","cortex","skills"),detectCommands:["cortex"]},crush:{label:"Crush",class:"additional",projectSkillsDir:Y(".crush","skills"),globalSkillsDir:(Q)=>Y(MQ(Q),"crush","skills"),detectCommands:["crush"]},cursor:{label:"Cursor",class:"universal",projectSkillsDir:E,globalSkillsDir:j},droid:{label:"Droid",class:"additional",projectSkillsDir:Y(".factory","skills"),globalSkillsDir:(Q)=>Y(Q,".factory","skills"),detectCommands:["droid"]},"gemini-cli":{label:"Gemini CLI",class:"universal",projectSkillsDir:E,globalSkillsDir:j},"github-copilot":{label:"GitHub Copilot",class:"universal",projectSkillsDir:E,globalSkillsDir:j},goose:{label:"Goose",class:"additional",projectSkillsDir:Y(".goose","skills"),globalSkillsDir:(Q)=>Y(MQ(Q),"goose","skills"),detectCommands:["goose"]},"iflow-cli":{label:"iFlow CLI",class:"additional",projectSkillsDir:Y(".iflow","skills"),globalSkillsDir:(Q)=>Y(Q,".iflow","skills"),detectCommands:["iflow","iflow-cli"]},junie:{label:"Junie",class:"additional",projectSkillsDir:Y(".junie","skills"),globalSkillsDir:(Q)=>Y(Q,".junie","skills"),detectCommands:["junie"]},kilo:{label:"Kilo Code",class:"additional",projectSkillsDir:Y(".kilocode","skills"),globalSkillsDir:(Q)=>Y(Q,".kilocode","skills"),detectCommands:["kilo","kilocode"]},"kimi-cli":{label:"Kimi Code CLI",class:"universal",projectSkillsDir:E,globalSkillsDir:j},"kiro-cli":{label:"Kiro CLI",class:"additional",projectSkillsDir:Y(".kiro","skills"),globalSkillsDir:(Q)=>Y(Q,".kiro","skills"),detectCommands:["kiro","kiro-cli"]},kode:{label:"Kode",class:"additional",projectSkillsDir:Y(".kode","skills"),globalSkillsDir:(Q)=>Y(Q,".kode","skills"),detectCommands:["kode"]},mcpjam:{label:"MCPJam",class:"additional",projectSkillsDir:Y(".mcpjam","skills"),globalSkillsDir:(Q)=>Y(Q,".mcpjam","skills"),detectCommands:["mcpjam"]},"mistral-vibe":{label:"Mistral Vibe",class:"additional",projectSkillsDir:Y(".vibe","skills"),globalSkillsDir:(Q)=>Y(Q,".vibe","skills"),detectCommands:["mistral-vibe","vibe"]},mux:{label:"Mux",class:"additional",projectSkillsDir:Y(".mux","skills"),globalSkillsDir:(Q)=>Y(Q,".mux","skills"),detectCommands:["mux"]},neovate:{label:"Neovate",class:"additional",projectSkillsDir:Y(".neovate","skills"),globalSkillsDir:(Q)=>Y(Q,".neovate","skills"),detectCommands:["neovate"]},opencode:{label:"OpenCode",class:"universal",projectSkillsDir:E,globalSkillsDir:j},openclaw:{label:"OpenClaw",class:"additional",projectSkillsDir:"skills",globalSkillsDir:(Q)=>Y(Q,".openclaw","skills"),detectCommands:["openclaw"]},openhands:{label:"OpenHands",class:"additional",projectSkillsDir:Y(".openhands","skills"),globalSkillsDir:(Q)=>Y(Q,".openhands","skills"),detectCommands:["openhands"]},pi:{label:"Pi",class:"additional",projectSkillsDir:Y(".pi","skills"),globalSkillsDir:(Q)=>Y(Q,".pi","agent","skills"),detectCommands:["pi"]},pochi:{label:"Pochi",class:"additional",projectSkillsDir:Y(".pochi","skills"),globalSkillsDir:(Q)=>Y(Q,".pochi","skills"),detectCommands:["pochi"]},qoder:{label:"Qoder",class:"additional",projectSkillsDir:Y(".qoder","skills"),globalSkillsDir:(Q)=>Y(Q,".qoder","skills"),detectCommands:["qoder"]},"qwen-code":{label:"Qwen Code",class:"additional",projectSkillsDir:Y(".qwen","skills"),globalSkillsDir:(Q)=>Y(Q,".qwen","skills"),detectCommands:["qwen","qwen-code"]},replit:{label:"Replit",class:"universal",projectSkillsDir:E,globalSkillsDir:j},roo:{label:"Roo Code",class:"additional",projectSkillsDir:Y(".roo","skills"),globalSkillsDir:(Q)=>Y(Q,".roo","skills"),detectCommands:["roo","roo-code"]},trae:{label:"Trae",class:"additional",projectSkillsDir:Y(".trae","skills"),globalSkillsDir:(Q)=>Y(Q,".trae","skills"),detectCommands:["trae"]},"trae-cn":{label:"Trae CN",class:"additional",projectSkillsDir:Y(".trae","skills"),globalSkillsDir:(Q)=>Y(Q,".trae-cn","skills"),detectCommands:["trae-cn","trae"]},windsurf:{label:"Windsurf",class:"additional",projectSkillsDir:Y(".windsurf","skills"),globalSkillsDir:(Q)=>Y(Q,".codeium","windsurf","skills"),detectCommands:["windsurf"]},zencoder:{label:"Zencoder",class:"additional",projectSkillsDir:Y(".zencoder","skills"),globalSkillsDir:(Q)=>Y(Q,".zencoder","skills"),detectCommands:["zencoder"]}},c=Object.keys(C),y=Object.fromEntries(c.map((Q)=>[Q,C[Q].label]));function D(){return c.filter((Q)=>C[Q].class==="universal")}function h(){return c.filter((Q)=>C[Q].class==="additional")}function XZ(Q){return C[Q].class==="universal"}async function ZQ(Q){let Z=typeof Q==="string"?{home:Q}:Q??{},X=Z.cwd??process.cwd(),$=Z.commandChecker??((H)=>Promise.resolve($Z(H))),W=[];for(let H of h()){let B=C[H].detectCommands??[],K=!1;for(let z of B)if(await $(z,X)){K=!0;break}if(K)W.push(H)}return W}function O(Q,Z,X){let $=u(Z),W=C[Q];if($==="project")return Y(process.cwd(),W.projectSkillsDir,X);return Y(W.globalSkillsDir(i()),X)}function P(Q,Z){if(u(Q)==="project")return Y(process.cwd(),QZ,Z);return Y(ZZ(i()),Z)}function $Z(Q){let X=(process.env.PATH??"").split(eQ).filter((H)=>H.length>0),$=process.platform==="win32",W=$?(process.env.PATHEXT??".EXE;.CMD;.BAT;.COM").split(";").filter((H)=>H.length>0):[];for(let H of X){if(!$&&TQ(Y(H,Q)))return!0;if($){for(let B of W)if(TQ(Y(H,Q+B)))return!0}}return!1}function TQ(Q){try{return nQ(Q,tQ.X_OK),!0}catch{return!1}}import{Crust as WZ}from"@crustjs/core";function o(Q){if(Q===void 0)return[];return(Array.isArray(Q)?Q:[Q]).flatMap((X)=>X.split(/\r?\n/)).map((X)=>X.trim()).filter((X)=>X.length>0)}function _Q(Q){let Z=Q?.trim();if(!Z)return[];return Z.split(/\r?\n/)}function XQ(Q){return Q.length>0}var bQ=Symbol("crust.skill.commandAnnotations");function HZ(Q){return Q instanceof WZ?Q._node:Q}function YZ(Q,Z){let X=HZ(Q),$=o(typeof Z==="string"||Array.isArray(Z)?Z:Z.instructions??[]);if($.length===0)return Q;let W=$Q(X)?.instructions??[],H=[...new Set([...W,...$])];return Object.defineProperty(X,bQ,{value:{instructions:H},enumerable:!0,configurable:!0}),Q}function $Q(Q){let Z=Q[bQ];if(!Z?.instructions||Z.instructions.length===0)return;return{instructions:[...Z.instructions]}}class I extends Error{name="SkillConflictError";details;constructor(Q){let Z=`Skill conflict for agent "${Q.agent}": directory "${Q.outputDir}" already exists but was not created by Crust (no crust.json found). Delete or rename the conflicting skill to resolve.`;super(Z);this.details=Q}}import{lstat as LZ,mkdir as pQ,readlink as PZ,realpath as CZ,rm as d,symlink as yZ,writeFile as DZ}from"fs/promises";import{dirname as gQ,join as SQ}from"path";function OQ(Q){return RQ(Q,[])}function RQ(Q,Z){let X=qZ(Q.meta.name),$=[...Z,X],W=BZ(Q.args),H=zZ(Q.effectiveFlags),B=xZ(Q.subCommands,$),K=$Q(Q);return{name:X,path:$,description:Q.meta.description,usage:Q.meta.usage,instructions:K?.instructions,runnable:typeof Q.run==="function",args:W,flags:H,children:B}}function qZ(Q){return Q.trim().toLowerCase()}function BZ(Q){if(!Q||Q.length===0)return[];return Q.map(JZ)}function JZ(Q){let Z={name:Q.name,type:Q.type,required:Q.required===!0,variadic:Q.variadic===!0};if(Q.description!==void 0)Z.description=Q.description;if(Q.default!==void 0)Z.default=kQ(Q.default);return Z}function zZ(Q){if(!Q)return[];return Object.keys(Q).sort().map((X)=>{return KZ(X,Q[X])})}function KZ(Q,Z){let X={name:Q,type:Z.type,required:Z.required===!0,multiple:Z.multiple===!0,short:Z.short,aliases:Z.aliases?[...Z.aliases].sort():[]};if(Z.description!==void 0)X.description=Z.description;if(Z.default!==void 0)X.default=kQ(Z.default);return X}function xZ(Q,Z){return Object.keys(Q).sort().map(($)=>{return RQ(Q[$],Z)})}function kQ(Q){if(Array.isArray(Q))return JSON.stringify(Q);return String(Q)}function m(Q){if(/[:#[\]{}&*!|>'"`,@?\\]|^\s|\s$|^---|[\n\r]/.test(Q))return`"${Q.replace(/\\/g,"\\\\").replace(/"/g,"\\\"").replace(/\n/g,"\\n").replace(/\r/g,"\\r")}"`;return Q}function EQ(Q){return Q.replace(/(?<!\\)\|/g,"\\|")}function jQ(Q,Z){let X=[],$=IQ(Q);X.push({path:"SKILL.md",content:GZ(Q,Z,$)});for(let W of $){let H=S(W),B=W.children.length>0?VZ(W,Q):UZ(W,Q);X.push({path:H,content:B})}return X}function IQ(Q){let Z=[Q];for(let X of Q.children)Z.push(...IQ(X));return Z}function S(Q){if(Q.path.length<=1)return`commands/${Q.name}.md`;return`commands/${Q.path.slice(1).join("/")}.md`}function HQ(Q){return Q.path.join(" ")}function WQ(Q,Z){let X=Q.split("/").slice(0,-1),$=Z.split("/"),W=0;while(W<X.length&&W<$.length&&X[W]===$[W])W++;let H=X.length-W,B=$.slice(W);if(H===0)return B.join("/");return[...Array.from({length:H},()=>".."),...B].join("/")}function GZ(Q,Z,X){let $=[];if($.push("---"),$.push(`name: ${m(Z.name)}`),$.push(`description: ${m(Z.description)}`),Z.license)$.push(`license: ${m(Z.license)}`);if(Z.compatibility)$.push(`compatibility: ${m(Z.compatibility)}`);if(Z.disableModelInvocation)$.push("disable-model-invocation: true");if(Z.allowedTools)$.push(`allowed-tools: ${m(Z.allowedTools)}`);if($.push("metadata:"),$.push(` version: "${Z.version}"`),$.push("---"),$.push(""),$.push(`# ${Z.name}`),$.push(""),Q.description)$.push(Q.description),$.push("");$.push(`You should use this skill when you need accurate help with \`${Z.name}\` commands, including command selection, syntax, arguments, flags, defaults, and subcommands.`),$.push("");let W=EZ(Z.instructions);if($.push("## How to Use This Skill"),$.push(""),$.push("1. You must find the command that best matches the user's task from the Command Reference below."),$.push("2. You must check the `Type` column before suggesting execution: `runnable` and `runnable, group` commands can be executed, while `group` commands are organizational only."),$.push("3. You should read only the linked file or files you need from `commands/`."),$.push("4. You must read a command's file before answering a command-specific question or suggesting that command."),$.push("5. You must treat the command file as the source of truth for usage, arguments, flags, aliases, and defaults."),$.push("6. If a flag, argument, alias, or default is not documented there, you must say it is not documented instead of guessing."),$.push(""),XQ(W))$.push("## General Guidance"),$.push(""),$.push(...W),$.push("");if($.push("## Command Reference"),$.push(""),$.push("You should use this table to locate the command file you need."),$.push(""),$.push(...FZ(X)),$.push(""),Q.runnable){$.push("## Usage"),$.push("");let H=S(Q);$.push(`The root command is directly executable. You should see [${Q.name}](${H}) for usage details.`),$.push("")}return $.join(`
3
+ `)}function FZ(Q){let Z=[];Z.push("| Command | Type | Documentation |"),Z.push("| ------- | ---- | ------------- |");for(let X of Q){let $=HQ(X),W=S(X),H=wZ(X);Z.push(`| \`${$}\` | ${H} | [${W}](${W}) |`)}return Z}function wZ(Q){if(Q.runnable&&Q.children.length>0)return"runnable, group";if(Q.runnable)return"runnable";return"group"}function UZ(Q,Z){let X=[];return X.push(...NQ(Q)),X.push(...LQ(Q)),X.push(...PQ(Q)),X.push(...yQ(Q,Z)),X.join(`
4
+ `)}function VZ(Q,Z){let X=[],$=S(Q);if(X.push(...NQ(Q)),X.push(...LQ(Q)),Q.runnable)X.push(...PQ(Q));return X.push(...TZ(Q,$)),X.push(...yQ(Q,Z)),X.join(`
5
+ `)}function MZ(Q){let Z=[...Q.path];for(let X of Q.args)if(X.variadic)Z.push(X.required?`<${X.name}...>`:`[${X.name}...]`);else Z.push(X.required?`<${X.name}>`:`[${X.name}]`);if(Q.flags.length>0)Z.push("[options]");return Z.join(" ")}function NQ(Q){let Z=[`# \`${HQ(Q)}\``,""];if(Q.description)Z.push(Q.description,"");return Z}function LQ(Q){let Z=Q.instructions??[];if(!XQ(Z))return[];return["## Command Instructions","",...CQ(Z),""]}function PQ(Q){let Z=["## Usage","","```",Q.usage??MZ(Q),"```",""];if(Q.args.length>0)Z.push("## Arguments","",..._Z(Q.args),"");if(Q.flags.length>0)Z.push("## Flags","",...OZ(Q.flags),"");return Z.push("## Command Documentation Authority","","You must treat only the arguments, flags, options, aliases, and defaults documented in this file as supported for this command.","You must not infer or invent additional command-line options.",""),Z}function TZ(Q,Z){let X=["## Subcommands",""];for(let $ of Q.children){let W=S($),H=WQ(Z,W),B=$.description?` - ${$.description}`:"";X.push(`- [\`${$.name}\`](${H})${B}`)}return X.push(""),X}function _Z(Q){let Z=[];Z.push("| Argument | Type | Required | Description |"),Z.push("| -------- | ---- | -------- | ----------- |");for(let X of Q){let $=X.variadic?`${X.name}...`:X.name,W=X.required?"Yes":"No",H=EQ(bZ(X));Z.push(`| \`${$}\` | ${X.type} | ${W} | ${H} |`)}return Z}function bZ(Q){let Z=[];if(Q.description)Z.push(Q.description);if(Q.default!==void 0)Z.push(`Default: \`${Q.default}\``);return Z.join(". ")||"-"}function OZ(Q){let Z=[];Z.push("| Flag | Type | Required | Description |"),Z.push("| ---- | ---- | -------- | ----------- |");for(let X of Q){let $=RZ(X),W=X.required?"Yes":"No",H=EQ(kZ(X));Z.push(`| ${$} | ${X.type} | ${W} | ${H} |`)}return Z}function RZ(Q){let Z=[`\`--${Q.name}\``];if(Q.short)Z.push(`\`-${Q.short}\``);for(let X of Q.aliases)Z.push(`\`--${X}\``);return Z.join(", ")}function kZ(Q){let Z=[];if(Q.description)Z.push(Q.description);if(Q.multiple)Z.push("Can be specified multiple times");if(Q.default!==void 0)Z.push(`Default: \`${Q.default}\``);return Z.join(". ")||"-"}function CQ(Q){return Q.map((Z)=>`- ${Z}`)}function EZ(Q){if(typeof Q==="string")return _Q(Q);return CQ(o(Q))}function yQ(Q,Z){let X=[],$=S(Q);if(X.push("---"),X.push(""),Q.path.length>1){let H=Q.path.slice(0,-1),B=DQ(Z,H);if(B){let K=S(B),z=WQ($,K),J=HQ(B);X.push(`Parent: [\`${J}\`](${z})`),X.push("")}}let W=WQ($,"SKILL.md");return X.push(`[Skill Overview](${W})`),X.push(""),X}function DQ(Q,Z){if(jZ(Q.path,Z))return Q;for(let X of Q.children){let $=DQ(X,Z);if($)return $}return}function jZ(Q,Z){if(Q.length!==Z.length)return!1;for(let X=0;X<Q.length;X++)if(Q[X]!==Z[X])return!1;return!0}import{readFile as IZ}from"fs/promises";import{join as NZ}from"path";var YQ="crust.json";async function A(Q){try{let Z=await IZ(NZ(Q,YQ),"utf-8"),X=JSON.parse(Z);if(typeof X==="object"&&X!==null&&"version"in X&&typeof X.version==="string")return X.version;return null}catch{return null}}var SZ="auto",uQ=/^[a-z0-9]+(-[a-z0-9]+)*$/;function cQ(Q){return Q.length>=1&&Q.length<=64&&uQ.test(Q)}function a(Q){return Q}function JQ(Q){return Q.startsWith("use-")?Q:`use-${Q}`}async function p(Q){let{command:Z,meta:X,agents:$,scope:W="global",clean:H=!0,force:B=!1,installMode:K=SZ}=Q,z=a(X.name),J=JQ(X.name);if(!cQ(z))throw Error(`Invalid skill name "${z}": must be 1\u201364 lowercase `+`alphanumeric characters and hyphens, no leading/trailing/consecutive hyphens. Pattern: ${uQ.source}`);let x=$[0];if(!x)return{agents:[]};let w={...X,name:z},G=OQ(Z),N=jQ(G,w),M=gZ(w),b=[...N,...M].sort((q,F)=>q.path<F.path?-1:q.path>F.path?1:0),R=b.map((q)=>q.path),T=new Map;for(let q of $){let F=O(q,W,w.name),U=T.get(F);if(U)U.push(q);else T.set(F,[q])}let _=P(W,w.name),k=P(W,J),L=new Map;for(let[q,F]of T){let U=F[0];if(!U)continue;L.set(q,await xQ({outputDir:q,legacyOutputDir:O(U,W,J),canonicalOutputDir:_,legacyCanonicalOutputDir:k}))}let s=await A(_);if((await KQ(_,_)).exists&&s===null&&!B)throw new I({agent:x,outputDir:_});let v=s!==w.version;if(v){if(H)await GQ(_);await mQ(_,b)}let f=[];for(let[q,F]of T){let U=F[0];if(!U)continue;let V=L.get(q);if(!V)continue;if(V.current.inspection.exists&&!V.current.isCrustManaged&&!B)throw new I({agent:U,outputDir:q});let e=await vZ({outputDir:q,canonicalOutputDir:_,allFiles:b,clean:H,installMode:K,inspection:V.current.inspection,installedVersion:V.preferredVersion,currentVersion:w.version}),oQ=await hZ(V),QQ=AZ({installedVersion:V.preferredVersion,currentVersion:w.version,canonicalChanged:v,pathChanged:e||oQ||V.preferredOutputDir!==q});for(let aQ of F)f.push({agent:aQ,outputDir:q,files:QQ==="up-to-date"?[]:R,status:QQ,previousVersion:QQ==="updated"?V.preferredVersion??void 0:void 0})}{let q=await A(k);if(k!==_&&q!==null&&!await BQ(J,W))await d(k,{recursive:!0,force:!0})}return{agents:f}}async function zQ(Q){let{name:Z,agents:X,scope:$="global"}=Q,W=a(Z),H=JQ(Z),B=P($,W),K=P($,H),z=[],J=new Map;for(let x of X){let w=O(x,$,W),G=J.get(w);if(G)G.push(x);else J.set(w,[x])}for(let[x,w]of J){let G=w[0];if(!G)continue;let N=O(G,$,H),M=await xQ({outputDir:x,legacyOutputDir:N,canonicalOutputDir:B,legacyCanonicalOutputDir:K}),b=await qQ(M.current),R=M.legacy.outputDir!==M.current.outputDir?await qQ(M.legacy):!1,T=b||R,_=b?x:R?N:x;for(let k of w)z.push({agent:k,outputDir:_,status:T?"removed":"not-found"})}if(await A(B)!==null&&!await BQ(W,$))await d(B,{recursive:!0,force:!0});{let x=await A(K);if(K!==B&&x!==null&&!await BQ(H,$))await d(K,{recursive:!0,force:!0})}return{agents:z}}async function r(Q){let{name:Z,agents:X,scope:$="global"}=Q,W=a(Z),H=JQ(Z),B=[],K=new Map;for(let z of X){let J=O(z,$,W),x=K.get(J);if(x)x.push(z);else K.set(J,[z])}for(let[z,J]of K){let x=J[0];if(!x)continue;let w=O(x,$,H),G=P($,W),N=P($,H),M=await xQ({outputDir:z,legacyOutputDir:w,canonicalOutputDir:G,legacyCanonicalOutputDir:N}),b=M.preferredOutputDir??z,R=M.preferredVersion;for(let T of J)B.push({agent:T,outputDir:b,installed:R!==null,version:R??void 0})}return{agents:B}}function AZ(Q){let{installedVersion:Z,currentVersion:X,canonicalChanged:$,pathChanged:W}=Q;if(Z===null)return"installed";if(Z===X&&!$&&!W)return"up-to-date";return"updated"}async function vZ(Q){let{outputDir:Z,canonicalOutputDir:X,allFiles:$,clean:W,installMode:H,inspection:B,installedVersion:K,currentVersion:z}=Q;if(H==="copy")return AQ({outputDir:Z,allFiles:$,clean:W,inspection:B,installedVersion:K,currentVersion:z});try{return await fZ({outputDir:Z,canonicalOutputDir:X,inspection:B})}catch(J){if(H==="symlink")throw Error(`Failed to create symlink at "${Z}" (installMode: symlink).`,{cause:J});let x=await KQ(Z,X);return AQ({outputDir:Z,allFiles:$,clean:W,inspection:x,installedVersion:K,currentVersion:z})}}async function AQ(Q){let{outputDir:Z,allFiles:X,clean:$,inspection:W,installedVersion:H,currentVersion:B}=Q;if(!(!W.exists||W.isSymlink||H!==B))return!1;if(W.isSymlink||$)await GQ(Z);return await mQ(Z,X),!0}async function fZ(Q){let{outputDir:Z,canonicalOutputDir:X,inspection:$}=Q;if($.exists&&$.isSymlink&&$.pointsToCanonical)return!1;if($.exists)await GQ(Z);return await pZ(X,Z),!0}async function KQ(Q,Z){let X;try{X=await LZ(Q)}catch{return{exists:!1,isSymlink:!1,pointsToCanonical:!1}}let $=process.platform==="win32"&&X.isDirectory()&&await hQ(Q)!==null;if(!(X.isSymbolicLink()||$))return{exists:!0,isSymlink:!1,pointsToCanonical:!1};let[H,B,K]=await Promise.all([fQ(Q),fQ(Z),hQ(Q)]);return{exists:!0,isSymlink:!0,pointsToCanonical:H!==null&&B!==null&&H===B||K===Z}}async function vQ(Q,Z){let[X,$]=await Promise.all([A(Q),KQ(Q,Z)]),W=X!==null||$.exists&&$.isSymlink&&$.pointsToCanonical;return{outputDir:Q,version:X,inspection:$,isCrustManaged:W}}async function xQ(Q){let{outputDir:Z,legacyOutputDir:X,canonicalOutputDir:$,legacyCanonicalOutputDir:W}=Q,H=await vQ(Z,$),B=X===Z?H:await vQ(X,W);if(H.isCrustManaged)return{current:H,legacy:B,preferredVersion:H.version,preferredOutputDir:H.outputDir};if(B.isCrustManaged)return{current:H,legacy:B,preferredVersion:B.version,preferredOutputDir:B.outputDir};return{current:H,legacy:B,preferredVersion:null,preferredOutputDir:null}}async function qQ(Q){if(!Q.isCrustManaged||!Q.inspection.exists)return!1;return await d(Q.outputDir,{recursive:!0,force:!0}),!0}async function hZ(Q){if(Q.legacy.outputDir===Q.current.outputDir)return!1;return qQ(Q.legacy)}async function fQ(Q){try{return await CZ(Q)}catch{return null}}async function hQ(Q){try{return await PZ(Q)}catch{return null}}async function pZ(Q,Z){await pQ(gQ(Z),{recursive:!0});let X=process.platform==="win32"?"junction":"dir";await yZ(Q,Z,X)}async function BQ(Q,Z){let X=new Set;for(let $ of c)X.add(O($,Z,Q));for(let $ of X)if(await A($)!==null)return!0;return!1}function gZ(Q){let Z={name:Q.name,description:Q.description,version:Q.version};return[{path:YQ,content:`${JSON.stringify(Z,null,"\t")}
6
+ `}]}async function GQ(Q){await d(Q,{recursive:!0,force:!0})}async function mQ(Q,Z){let X=new Set;for(let W of Z){let H=SQ(Q,W.path),B=gQ(H);X.add(B)}let $=[...X].sort();for(let W of $)await pQ(W,{recursive:!0});for(let W of Z){let H=SQ(Q,W.path);await DZ(H,W.content,"utf-8")}}import{Crust as rQ,VALIDATION_MODE_ENV as uZ}from"@crustjs/core";import{confirm as cZ,multiselect as mZ,select as dZ,spinner as l}from"@crustjs/prompts";import{bold as t,dim as g,yellow as lQ}from"@crustjs/style";var rZ="skill",sQ="global",n="__universal__";function lZ(Q){return Q==="global"||Q==="project"}async function iQ(Q,Z){if(Q!==void 0){if(!lZ(Q))throw Error(`Invalid --scope value: ${String(Q)}. Expected "project" or "global".`);return Q}if(Z.defaultScope)return Z.defaultScope;return dZ({message:"Select scope",choices:[{label:"Project",value:"project"},{label:"Global",value:"global"}],default:sQ})}function FQ(Q){let Z=new Set(D()),X=[];if(Q.some(($)=>Z.has($)))X.push("Universal");for(let $ of Q){if(Z.has($))continue;X.push(y[$])}return X}function dQ(Q){let Z=new Set(D()),X=[],$=Q.find((W)=>Z.has(W.agent));if($)X.push({label:"Universal",outputDir:$.outputDir});for(let W of Q){if(Z.has(W.agent))continue;X.push({label:y[W.agent],outputDir:W.outputDir})}return X}function wQ(Q,Z){return{name:Q.meta.name,description:Q.meta.description??"",version:Z.version,instructions:Z.instructions,license:Z.license,allowedTools:Z.allowedTools,compatibility:Z.compatibility,disableModelInvocation:Z.disableModelInvocation}}function UQ(Q,Z,X,$){if(!$.installed)return!1;let W=O(Q,Z,X.name);return $.version!==X.version||$.outputDir!==W}async function sZ(Q,Z){let X=[...D(),...h()];if(X.length===0)return;let $=wQ(Q,Z),W=[...new Set(["project","global"].map((H)=>u(H)))];for(let H of W){let K=(await r({name:$.name,agents:X,scope:H})).agents.filter((z)=>UQ(z.agent,H,$,z));if(K.length===0)continue;try{await l({message:`Updating ${H} skills...`,task:async({updateMessage:z})=>{let J=await p({command:Q,meta:$,agents:K.map((G)=>G.agent),scope:H,installMode:Z.installMode}),x=J.agents.filter((G)=>G.status==="updated").map((G)=>G.agent),w=FQ(x);if(w.length>0)z(`Updated skill "${$.name}" to v${$.version} for ${w.join(", ")} (${H})`);return J}})}catch(z){if(z instanceof I)console.warn(lQ(`Skill conflict: "${z.details.outputDir}" already exists but was not created by ${$.name}. Skipping auto-update for ${H}. Delete or rename the conflicting skill to resolve.`));else throw z}}}function iZ(Q){let Z;return{name:"skills",async setup(X,$){Z=X.rootCommand;let W=Q.command??rZ;if($.addSubCommand(Z,W,oZ(Z,Q,W)),process.env[uZ]==="1")return;if(X.argv[0]===W)return;if(Q.autoUpdate!==!1)await sZ(Z,Q)}}}function oZ(Q,Z,X){let $=aZ(Q,Z);return new rQ(X).meta({description:"Manage agent skill installations"}).flags({scope:{type:"string",description:"Install scope (project or global)"},all:{type:"boolean",description:"Install for all detected agents non-interactively (universal + detected)"}}).run(async(W)=>{let H=wQ(Q,Z),B=W.flags.all===!0,K=B?Z.defaultScope??sQ:await iQ(W.flags.scope,Z),z=await ZQ(),J=D(),x=h(),w=await r({name:H.name,agents:[...J,...x],scope:K}),G=new Set(w.agents.filter((q)=>q.installed).map((q)=>q.agent)),N=new Set(z),M=new Map(w.agents.map((q)=>[q.agent,q])),b=x.filter((q)=>{if(N.has(q))return!0;return M.get(q)?.installed===!0}),R=b.filter((q)=>G.has(q)),T=[];if(J.length>0){let q=J[0];if(!q)throw Error("Expected at least one universal agent");let U=M.get(q)?.outputDir??"path unavailable";T.push({label:"Universal",value:n,hint:U});let V=J.map((e)=>y[e]).join(", ");console.log(g(`Agents supporting universal skills: ${V}`))}for(let q of b){let U=M.get(q)?.outputDir??"path unavailable";T.push({label:y[q],value:q,hint:U})}let _=J.length>0&&J.every((q)=>G.has(q)),k=[...R.filter((q)=>!J.includes(q))];if(_)k.unshift(n);let L;if(B)L=[...J,...b];else{let q=T.length===0?[]:await mZ({message:"Select agents to install skills for",choices:T,default:k,required:!1}),F=new Set(q.filter((U)=>U!==n));if(q.includes(n))for(let U of J)F.add(U);L=[...F]}let s=L.filter((q)=>!G.has(q)),VQ=L.filter((q)=>{let F=M.get(q);return F!==void 0&&UQ(q,K,H,F)}),v=[...G].filter((q)=>!L.includes(q)),f=[...s,...VQ];if(f.length>0)try{let q=await l({message:"Installing skills...",task:async()=>p({command:Q,meta:H,agents:f,scope:K,installMode:Z.installMode})});console.log(`
7
+ ${t(`Installed "${H.name}" v${H.version}`)}`);for(let F of dQ(q.agents))console.log(g(` ${F.label} \u2192 ${F.outputDir}`))}catch(q){if(q instanceof I)if(B?!0:await cZ({message:`"${q.details.outputDir}" already exists but was not created by Crust. Overwrite?`,default:!1})){let U=await l({message:"Overwriting skill...",task:async()=>p({command:Q,meta:H,agents:[q.details.agent],scope:K,force:!0,installMode:Z.installMode})});console.log(`
8
+ ${t(`Installed "${H.name}" v${H.version}`)}`);for(let V of dQ(U.agents))console.log(g(` ${V.label} \u2192 ${V.outputDir}`))}else console.log(g(`
9
+ Skipped ${y[q.details.agent]}`));else throw q}if(v.length>0){let F=(await l({message:"Removing skills...",task:async()=>zQ({name:H.name,agents:v,scope:K})})).agents.filter((V)=>V.status==="removed").map((V)=>V.agent),U=FQ(F);if(U.length>0)console.log(`
10
+ ${t(`Removed from ${U.join(", ")}`)}`)}if(f.length===0&&v.length===0)console.log(g("No changes."))}).command($)._node}function aZ(Q,Z){return new rQ("update").meta({description:"Update installed skills to latest version"}).flags({scope:{type:"string",description:"Update scope (project or global)"}}).run(async(X)=>{let $=await iQ(X.flags.scope,Z),W=u($),H=[...D(),...h()],B=wQ(Q,Z),z=(await r({name:B.name,agents:H,scope:$})).agents.filter((J)=>UQ(J.agent,$,B,J));if(z.length===0){console.log(g(`No updates needed (${W}).`));return}try{let x=(await l({message:`Updating ${W} skills...`,task:async()=>p({command:Q,meta:B,agents:z.map((G)=>G.agent),scope:$,installMode:Z.installMode})})).agents.filter((G)=>G.status==="updated").map((G)=>G.agent),w=FQ(x);if(w.length>0)console.log(`
11
+ ${t(`Updated "${B.name}" to v${B.version} for ${w.join(", ")} (${W})`)}`)}catch(J){if(J instanceof I)console.warn(lQ(`Skipped ${y[J.details.agent]}: "${J.details.outputDir}" already exists but was not created by ${B.name}. Delete or rename the conflicting directory to resolve.`));else throw J}})}export{zQ as uninstallSkill,r as skillStatus,iZ as skillPlugin,a as resolveSkillName,P as resolveCanonicalSkillPath,cQ as isValidSkillName,XZ as isUniversalAgent,D as getUniversalAgents,h as getAdditionalAgents,p as generateSkill,ZQ as detectInstalledAgents,YZ as annotate,I as SkillConflictError};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crustjs/skills",
3
- "version": "0.0.20",
3
+ "version": "0.0.21",
4
4
  "description": "Agent skill generation from Crust command definitions",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -43,13 +43,13 @@
43
43
  "publish": "bun publish --no-git-checks || true"
44
44
  },
45
45
  "dependencies": {
46
- "@crustjs/prompts": "0.0.9",
47
- "@crustjs/style": "0.0.5"
46
+ "@crustjs/prompts": "0.0.10",
47
+ "@crustjs/style": "0.0.6"
48
48
  },
49
49
  "devDependencies": {
50
50
  "@crustjs/config": "0.0.0",
51
51
  "@crustjs/core": "0.0.15",
52
- "bunup": "^0.16.29"
52
+ "bunup": "^0.16.31"
53
53
  },
54
54
  "peerDependencies": {
55
55
  "@crustjs/core": "0.0.15",